その他
著作権表示やその他のコメントの保持
--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,131 | 108,733 | - |
terser@3.7.5 mangle=false, compress=false | 316,600 | 85,245 | 0.82 |
terser@3.7.5 mangle=true, compress=false | 220,216 | 72,730 | 1.45 |
terser@3.7.5 mangle=true, compress=true | 212,046 | 70,954 | 5.87 |
babili@0.1.4 | 210,713 | 72,140 | 12.64 |
babel-minify@0.4.3 | 210,321 | 72,242 | 48.67 |
babel-minify@0.5.0-alpha.01eac1c3 | 210,421 | 72,238 | 14.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を参照してください。
コンパイラの仮定
より良い最適化を可能にするために、コンパイラーはさまざまな仮定を行います。
.toString()
と.valueOf()
は副作用がなく、組み込みオブジェクトの場合、上書きされていません。undefined
、NaN
、およびInfinity
は外部から再定義されていません。arguments.callee
、arguments.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-es
をterser
に置き換える
多くのJSバンドラーとuglifyラッパーは、依然としてバグのあるバージョンのuglify-es
を使用しており、まだterser
にアップグレードしていません。yarn
を使用している場合は、プロジェクトのpackage.json
ファイルに次のエイリアスを追加できます。
"resolutions": {
"uglify-es": "npm:terser"
}
コードを変更することなく、深くネストされたすべての依存関係でuglify-es
の代わりにterser
を使用します。
注: この変更を有効にするには、既存のyarn
ロックファイルを削除し、すべてのパッケージを再インストールするために、以下のコマンドを実行する必要があります。
$ rm -rf node_modules yarn.lock
$ yarn