メインコンテンツにスキップ

その他

--commentsを渡すことで、出力に特定のコメントを保持することができます。デフォルトでは、"!"で始まるコメントと、"@preserve"、"@copyright"、"@license"、または"@cc_on"(IEの条件付きコンパイル)を含むJSDocスタイルのコメントを保持します。すべてのコメントを保持するには--comments allを、この正規表現に一致するコメントのみを保持するには有効なJavaScriptの正規表現を渡すことができます。たとえば、--comments /^!//*! Copyright Notice */のようなコメントを保持します。

ただし、コメントが失われる可能性のある状況があることに注意してください。例えば

function f() {
/** @preserve Foo Bar */
function g() {
// this function is never called
}
return something();
}

"@preserve"を持っていても、内部関数g(コメントが添付されているASTノード)がコンプレッサーによって参照されないため破棄されるため、コメントは失われます。

著作権情報(または出力に保持する必要があるその他の情報)を配置するのに最も安全なコメントは、トップレベルノードに添付されたコメントです。

unsafe compressオプション

これにより、特定の人工的なケースではコードロジックを破壊する可能性があるいくつかの変換が可能になりますが、ほとんどのコードでは問題ないはずです。標準の組み込みECMAScript関数とクラスが変更または置換されていないことを前提としています。ご自身のコードでお試しになることをお勧めします。縮小されたサイズを小さくするはずです。このオプションが有効な場合に実行される最適化の例をいくつか示します。

  • new Array(1, 2, 3) または Array(1, 2, 3)[ 1, 2, 3 ]
  • Array.from([1, 2, 3])[1, 2, 3]
  • new Object(){}
  • String(exp) または exp.toString()"" + exp
  • new Object/RegExp/Function/Error/Array (...)newを破棄します。
  • "foo bar".substr(4)"bar"

条件付きコンパイル

--define (-d) スイッチを使用して、Terserが定数と見なすグローバル変数を宣言できます(スコープ内で定義されていない場合)。たとえば、--define DEBUG=falseを渡した場合、デッドコード削除と組み合わせて、Terserは出力から以下を破棄します。

if (DEBUG) {
console.log("debug stuff");
}

--define env.DEBUG=falseの形式でネストされた定数を指定できます。

別の方法としては、グローバル変数を別のファイルに定数として宣言し、ビルドに含めることです。たとえば、次のようなbuild/defines.jsファイルを作成できます。

var DEBUG = false;
var PRODUCTION = true;
// etc.

そして、次のようにコードをビルドします。

terser build/defines.js js/foo.js js/bar.js... -c

Terserは定数に気づき、変更できないため、それらへの参照を値自体に評価し、通常どおり到達不能なコードを削除します。ビルドには、定数を使用している場合は、const宣言が含まれます。constをサポートしない< ES6環境をターゲットにしている場合は、reduce_vars(デフォルトで有効)を使用したvarで十分です。

条件付きコンパイルAPI

プログラムによるAPIを介して条件付きコンパイルを使用することもできます。プロパティ名がglobal_defsであり、コンプレッサープロパティである点が異なります。

var result = await minify(fs.readFileSync("input.js", "utf8"), {
compress: {
dead_code: true,
global_defs: {
DEBUG: false
}
}
});

識別子を任意の非定数式に置き換えるには、global_defsキーに"@"をプレフィックスとして付加し、値を式として解析するようにTerserに指示する必要があります。

await minify("alert('hello');", {
compress: {
global_defs: {
"@alert": "console.log"
}
}
}).code;
// returns: 'console.log("hello");'

そうしないと、文字列リテラルとして置き換えられます。

await minify("alert('hello');", {
compress: {
global_defs: {
"alert": "console.log"
}
}
}).code;
// returns: '"console.log"("hello");'

注釈

Terserの注釈は、特定の関数呼び出しを異なる方法で処理するように指示する方法です。次の注釈が利用可能です。

  • /*@__INLINE__*/ - 関数を強制的にどこかにインライン化します。
  • /*@__NOINLINE__*/ - 呼び出された関数が呼び出しサイトにインライン化されないようにします。
  • /*@__PURE__*/ - 関数呼び出しを純粋としてマークします。つまり、安全に削除できます。
  • /*@__KEY__*/ - 文字列リテラルを、プロパティをマングルする際にマングルするためのプロパティとしてマークします。
  • /*@__MANGLE_PROP__*/ - プロパティマングラーが有効になっている場合に、オブジェクトプロパティ(またはクラスフィールド)をマングル対象としてオプトインします。

先頭に@記号または#を使用できます。

次に、それらの使用方法の例をいくつか示します。

/*@__INLINE__*/
function_always_inlined_here()

/*#__NOINLINE__*/
function_cant_be_inlined_into_here()

const x = /*#__PURE__*/i_am_dropped_if_x_is_not_used()

function lookup(object, key) { return object[key]; }
lookup({ i_will_be_mangled_too: "bar" }, /*@__KEY__*/ "i_will_be_mangled_too");

ESTree / SpiderMonkey AST

Terserには独自の抽象構文木形式があります。 実際的な理由から、内部的にSpiderMonkey ASTを使用するように簡単に変更することはできません。ただし、Terserには、SpiderMonkey ASTをインポートできるコンバーターが用意されています。

たとえば、Acornは、SpiderMonkey ASTを生成する非常に高速なパーサーです。1つのファイルを解析し、標準出力にJSONでASTをダンプする小さなCLIユーティリティがあります。Terserを使用してマングルおよび圧縮するには、次のようにします。

acorn file.js | terser -p spidermonkey -m -c

-p spidermonkeyオプションは、すべての入力ファイルがJavaScriptではなく、JSONで記述されたSpiderMonkey ASTのJSコードであることをTerserに指示します。したがって、この場合は独自のパーサーを使用せず、そのASTを内部ASTに変換するだけです。

spidermonkeyは、spidermonkey ASTを受け入れたり、生成したりするためのparseおよびformatオプションとしてminifyでも使用できます。

解析にAcornを使用する

さらに楽しむために、すべての解析を行うためにAcornを使用する-p acornオプションを追加しました。このオプションを渡すと、Terserはrequire("acorn")を使用します。

Acornは非常に高速です(たとえば、一部の650Kコードで380msではなく250ms)が、Acornが生成するSpiderMonkeyツリーの変換にはさらに150msかかるため、合計するとTerser自身のパーサーを使用するよりも少し時間がかかります。

Terserの高速minifyモード

あまり知られていませんが、空白の削除とシンボルのマングルは、ほとんどのJavaScriptで、minifyされたコードのサイズ削減の95%を占めています - 精巧なコード変換ではありません。compressを無効にするだけで、Terserビルドを3〜4倍高速化できます。

d3.jsサイズgzipサイズ時間(秒)
オリジナル451,131108,733-
terser@3.7.5 mangle=false, compress=false316,60085,2450.82
terser@3.7.5 mangle=true, compress=false220,21672,7301.45
terser@3.7.5 mangle=true, compress=true212,04670,9545.87
babili@0.1.4210,71372,14012.64
babel-minify@0.4.3210,32172,24248.67
babel-minify@0.5.0-alpha.01eac1c3210,42172,23814.17

CLIから高速minifyモードを有効にするには、次のように使用します。

terser file.js -m

APIで高速minifyモードを有効にするには、次のように使用します。

await minify(code, { compress: false, mangle: true });

ソースマップとデバッグ

コードを簡略化、再配置、インライン化、削除するさまざまなcompress変換は、ソースマップを使用したデバッグに悪影響を及ぼすことが知られています。これは、コードが最適化され、一部のコードが存在しなくなるため、マッピングが不可能な場合が多いため、予想される動作です。ソースマップのデバッグで最高の忠実度を得るには、compressオプションを無効にし、mangleのみを使用してください。

デバッグを行う場合は、マングルされた変数名を元の名前にマッピングするために、必ず「マップスコープ」機能を有効にしてください。
これがないと、すべての変数値がundefinedになります。詳細については、https://github.com/terser/terser/issues/1367を参照してください。



image

コンパイラの仮定

より良い最適化を可能にするために、コンパイラーはさまざまな仮定を行います。

  • .toString().valueOf()は副作用がなく、組み込みオブジェクトの場合、上書きされていません。
  • undefinedNaN、およびInfinityは外部から再定義されていません。
  • arguments.calleearguments.caller、およびFunction.prototype.callerは使用されていません。
  • コードは、Function.prototype.toString()またはError.prototype.stackの内容が特に何であるかを期待していません。
  • プレーンオブジェクトのプロパティの取得と設定は、他の副作用(.watch()またはProxyの使用)を引き起こしません。
  • オブジェクトプロパティは、追加、削除、および変更できます(Object.defineProperty()Object.defineProperties()Object.freeze()Object.preventExtensions()、またはObject.seal()で阻止されない)。
  • document.all== nullではありません。
  • クラスへのプロパティの割り当ては副作用がなく、エラーをスローしません。

Terserを使用するビルドツールとアダプター

https://www.npmjs.com/browse/depended/terser

yarnを使用するプロジェクトでuglify-esterserに置き換える

多くのJSバンドラーとuglifyラッパーは、依然としてバグのあるバージョンのuglify-esを使用しており、まだterserにアップグレードしていません。yarnを使用している場合は、プロジェクトのpackage.jsonファイルに次のエイリアスを追加できます。

  "resolutions": {
"uglify-es": "npm:terser"
}

コードを変更することなく、深くネストされたすべての依存関係でuglify-esの代わりにterserを使用します。

注: この変更を有効にするには、既存のyarnロックファイルを削除し、すべてのパッケージを再インストールするために、以下のコマンドを実行する必要があります。

$ rm -rf node_modules yarn.lock
$ yarn