terser 5.21.0 → 5.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -1
- package/README.md +3 -2
- package/dist/bundle.min.js +75 -15
- package/lib/compress/drop-unused.js +2 -0
- package/lib/compress/index.js +36 -9
- package/lib/compress/inline.js +29 -4
- package/lib/scope.js +6 -0
- package/lib/size.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## v5.
|
3
|
+
## v5.23.0
|
4
|
+
- When top_retain will keep a variable assignment around, inline the assignee when it's shorter than the name (#1434)
|
5
|
+
- Remove empty class `static {}` blocks.
|
6
|
+
|
7
|
+
## v5.22.0
|
8
|
+
- Do not `unsafe`ly shorten expressions like a?.toString() when they're conditional.
|
9
|
+
- Avoid running drop_unused in nodes that aren't scopes. Fixes a rare crash.
|
10
|
+
- When 'module' is enabled, assume strict mode when figuring out scopes.
|
11
|
+
|
12
|
+
## v5.21.0
|
4
13
|
- Do not inline functions that would be retained in the toplevel (as this would cause code duplication).
|
5
14
|
- Fix precedence of arrow function and ternary operator when formatting output.
|
6
15
|
|
package/README.md
CHANGED
@@ -154,7 +154,8 @@ a double dash to prevent input files being used as option arguments:
|
|
154
154
|
--keep-fnames Do not mangle/drop function names. Useful for
|
155
155
|
code relying on Function.prototype.name.
|
156
156
|
--module Input is an ES6 module. If `compress` or `mangle` is
|
157
|
-
enabled then the `toplevel` option
|
157
|
+
enabled then the `toplevel` option, as well as strict mode,
|
158
|
+
will be enabled.
|
158
159
|
--name-cache <file> File to hold mangled name mappings.
|
159
160
|
--safari10 Support non-standard Safari 10/11.
|
160
161
|
Equivalent to setting `safari10: true` in `minify()`
|
@@ -912,7 +913,7 @@ If you happen to need the source map as a raw object, set `sourceMap.asObject` t
|
|
912
913
|
[compress option](#compress-options).
|
913
914
|
|
914
915
|
- `module` (default `false`) -- Pass `true` an ES6 modules, where the toplevel
|
915
|
-
scope is not the global scope. Implies `toplevel
|
916
|
+
scope is not the global scope. Implies `toplevel` and assumes input code is strict mode JS.
|
916
917
|
|
917
918
|
- `nth_identifier` (default: an internal mangler that weights based on character
|
918
919
|
frequency analysis) -- Pass an object with a `get(n)` function that converts an
|
package/dist/bundle.min.js
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@jridgewell/source-map')) :
|
3
3
|
typeof define === 'function' && define.amd ? define(['exports', '@jridgewell/source-map'], factory) :
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Terser = {}, global.sourceMap));
|
5
|
-
}(this, (function (exports, sourceMap) { 'use strict';
|
5
|
+
})(this, (function (exports, sourceMap) { 'use strict';
|
6
6
|
|
7
7
|
/***********************************************************************
|
8
8
|
|
@@ -11573,6 +11573,7 @@ AST_Scope.DEFMETHOD("figure_out_scope", function(options, { parent_scope = null,
|
|
11573
11573
|
cache: null,
|
11574
11574
|
ie8: false,
|
11575
11575
|
safari10: false,
|
11576
|
+
module: false,
|
11576
11577
|
});
|
11577
11578
|
|
11578
11579
|
if (!(toplevel instanceof AST_Toplevel)) {
|
@@ -11740,6 +11741,11 @@ AST_Scope.DEFMETHOD("figure_out_scope", function(options, { parent_scope = null,
|
|
11740
11741
|
);
|
11741
11742
|
}
|
11742
11743
|
});
|
11744
|
+
|
11745
|
+
if (options.module) {
|
11746
|
+
tw.directives["use strict"] = true;
|
11747
|
+
}
|
11748
|
+
|
11743
11749
|
this.walk(tw);
|
11744
11750
|
|
11745
11751
|
function mark_export(def, level) {
|
@@ -12424,7 +12430,7 @@ const base54 = (() => {
|
|
12424
12430
|
|
12425
12431
|
let mangle_options = undefined;
|
12426
12432
|
AST_Node.prototype.size = function (compressor, stack) {
|
12427
|
-
mangle_options = compressor && compressor.
|
12433
|
+
mangle_options = compressor && compressor._mangle_options;
|
12428
12434
|
|
12429
12435
|
let size = 0;
|
12430
12436
|
walk_parent(this, (node, info) => {
|
@@ -15077,6 +15083,8 @@ const r_keep_assign = /keep_assign/;
|
|
15077
15083
|
AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
15078
15084
|
if (!compressor.option("unused")) return;
|
15079
15085
|
if (compressor.has_directive("use asm")) return;
|
15086
|
+
if (!this.variables) return; // not really a scope (eg: AST_Class)
|
15087
|
+
|
15080
15088
|
var self = this;
|
15081
15089
|
if (self.pinned()) return;
|
15082
15090
|
var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
|
@@ -17688,18 +17696,43 @@ function scope_encloses_variables_in_this_scope(scope, pulled_scope) {
|
|
17688
17696
|
return false;
|
17689
17697
|
}
|
17690
17698
|
|
17699
|
+
/**
|
17700
|
+
* An extra check function for `top_retain` option, compare the length of const identifier
|
17701
|
+
* and init value length and return true if init value is longer than identifier. for example:
|
17702
|
+
* ```
|
17703
|
+
* // top_retain: ["example"]
|
17704
|
+
* const example = 100
|
17705
|
+
* ```
|
17706
|
+
* it will return false because length of "100" is short than identifier "example".
|
17707
|
+
*/
|
17708
|
+
function is_const_symbol_short_than_init_value(def, fixed_value) {
|
17709
|
+
if (def.orig.length === 1 && fixed_value) {
|
17710
|
+
const init_value_length = fixed_value.size();
|
17711
|
+
const identifer_length = def.name.length;
|
17712
|
+
return init_value_length > identifer_length;
|
17713
|
+
}
|
17714
|
+
return true;
|
17715
|
+
}
|
17716
|
+
|
17691
17717
|
function inline_into_symbolref(self, compressor) {
|
17692
17718
|
const parent = compressor.parent();
|
17693
|
-
|
17694
17719
|
const def = self.definition();
|
17695
17720
|
const nearest_scope = compressor.find_scope();
|
17696
|
-
|
17721
|
+
let fixed = self.fixed_value();
|
17722
|
+
if (
|
17723
|
+
compressor.top_retain &&
|
17724
|
+
def.global &&
|
17725
|
+
compressor.top_retain(def) &&
|
17726
|
+
// when identifier is in top_retain option dose not mean we can always inline it.
|
17727
|
+
// if identifier name is longer then init value, we can replace it.
|
17728
|
+
is_const_symbol_short_than_init_value(def, fixed)
|
17729
|
+
) {
|
17730
|
+
// keep it
|
17697
17731
|
def.fixed = false;
|
17698
17732
|
def.single_use = false;
|
17699
17733
|
return self;
|
17700
17734
|
}
|
17701
17735
|
|
17702
|
-
let fixed = self.fixed_value();
|
17703
17736
|
let single_use = def.single_use
|
17704
17737
|
&& !(parent instanceof AST_Call
|
17705
17738
|
&& (parent.is_callee_pure(compressor))
|
@@ -18406,11 +18439,17 @@ class Compressor extends TreeWalker {
|
|
18406
18439
|
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
|
18407
18440
|
this.evaluated_regexps = new Map();
|
18408
18441
|
this._toplevel = undefined;
|
18409
|
-
this.
|
18442
|
+
this._mangle_options = mangle_options
|
18410
18443
|
? format_mangler_options(mangle_options)
|
18411
18444
|
: mangle_options;
|
18412
18445
|
}
|
18413
18446
|
|
18447
|
+
mangle_options() {
|
18448
|
+
var nth_identifier = this._mangle_options && this._mangle_options.nth_identifier || base54;
|
18449
|
+
var module = this._mangle_options && this._mangle_options.module || this.option("module");
|
18450
|
+
return { ie8: this.option("ie8"), nth_identifier, module };
|
18451
|
+
}
|
18452
|
+
|
18414
18453
|
option(key) {
|
18415
18454
|
return this.options[key];
|
18416
18455
|
}
|
@@ -18465,8 +18504,7 @@ class Compressor extends TreeWalker {
|
|
18465
18504
|
var passes = +this.options.passes || 1;
|
18466
18505
|
var min_count = 1 / 0;
|
18467
18506
|
var stopping = false;
|
18468
|
-
var
|
18469
|
-
var mangle = { ie8: this.option("ie8"), nth_identifier: nth_identifier };
|
18507
|
+
var mangle = this.mangle_options();
|
18470
18508
|
for (var pass = 0; pass < passes; pass++) {
|
18471
18509
|
this._toplevel.figure_out_scope(mangle);
|
18472
18510
|
if (pass === 0 && this.option("drop_console")) {
|
@@ -19742,7 +19780,7 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
19742
19780
|
self.args.length = last;
|
19743
19781
|
}
|
19744
19782
|
|
19745
|
-
if (compressor.option("unsafe")) {
|
19783
|
+
if (compressor.option("unsafe") && !exp.contains_optional()) {
|
19746
19784
|
if (exp instanceof AST_Dot && exp.start.value === "Array" && exp.property === "from" && self.args.length === 1) {
|
19747
19785
|
const [argument] = self.args;
|
19748
19786
|
if (argument instanceof AST_Array) {
|
@@ -19957,7 +19995,6 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
19957
19995
|
argnames: [],
|
19958
19996
|
body: []
|
19959
19997
|
}).optimize(compressor);
|
19960
|
-
var nth_identifier = compressor.mangle_options && compressor.mangle_options.nth_identifier || base54;
|
19961
19998
|
if (self.args.every((x) => x instanceof AST_String)) {
|
19962
19999
|
// quite a corner-case, but we can handle it:
|
19963
20000
|
// https://github.com/mishoo/UglifyJS2/issues/203
|
@@ -19967,10 +20004,10 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
19967
20004
|
return arg.value;
|
19968
20005
|
}).join(",") + "){" + self.args[self.args.length - 1].value + "})";
|
19969
20006
|
var ast = parse(code);
|
19970
|
-
var mangle =
|
20007
|
+
var mangle = compressor.mangle_options();
|
19971
20008
|
ast.figure_out_scope(mangle);
|
19972
20009
|
var comp = new Compressor(compressor.options, {
|
19973
|
-
mangle_options: compressor.
|
20010
|
+
mangle_options: compressor._mangle_options
|
19974
20011
|
});
|
19975
20012
|
ast = ast.transform(comp);
|
19976
20013
|
ast.figure_out_scope(mangle);
|
@@ -20009,6 +20046,23 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
20009
20046
|
return inline_into_call(self, compressor);
|
20010
20047
|
});
|
20011
20048
|
|
20049
|
+
/** Does this node contain optional property access or optional call? */
|
20050
|
+
AST_Node.DEFMETHOD("contains_optional", function() {
|
20051
|
+
if (
|
20052
|
+
this instanceof AST_PropAccess
|
20053
|
+
|| this instanceof AST_Call
|
20054
|
+
|| this instanceof AST_Chain
|
20055
|
+
) {
|
20056
|
+
if (this.optional) {
|
20057
|
+
return true;
|
20058
|
+
} else {
|
20059
|
+
return this.expression.contains_optional();
|
20060
|
+
}
|
20061
|
+
} else {
|
20062
|
+
return false;
|
20063
|
+
}
|
20064
|
+
});
|
20065
|
+
|
20012
20066
|
def_optimize(AST_New, function(self, compressor) {
|
20013
20067
|
if (
|
20014
20068
|
compressor.option("unsafe") &&
|
@@ -21614,8 +21668,14 @@ def_optimize(AST_Function, function(self, compressor) {
|
|
21614
21668
|
});
|
21615
21669
|
|
21616
21670
|
def_optimize(AST_Class, function(self) {
|
21617
|
-
|
21618
|
-
|
21671
|
+
for (let i = 0; i < self.properties.length; i++) {
|
21672
|
+
const prop = self.properties[i];
|
21673
|
+
if (prop instanceof AST_ClassStaticBlock && prop.body.length == 0) {
|
21674
|
+
self.properties.splice(i, 1);
|
21675
|
+
i--;
|
21676
|
+
}
|
21677
|
+
}
|
21678
|
+
|
21619
21679
|
return self;
|
21620
21680
|
});
|
21621
21681
|
|
@@ -31235,4 +31295,4 @@ exports._default_options = _default_options;
|
|
31235
31295
|
exports._run_cli = run_cli;
|
31236
31296
|
exports.minify = minify;
|
31237
31297
|
|
31238
|
-
}))
|
31298
|
+
}));
|
@@ -109,6 +109,8 @@ const r_keep_assign = /keep_assign/;
|
|
109
109
|
AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
110
110
|
if (!compressor.option("unused")) return;
|
111
111
|
if (compressor.has_directive("use asm")) return;
|
112
|
+
if (!this.variables) return; // not really a scope (eg: AST_Class)
|
113
|
+
|
112
114
|
var self = this;
|
113
115
|
if (self.pinned()) return;
|
114
116
|
var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
|
package/lib/compress/index.js
CHANGED
@@ -320,11 +320,17 @@ class Compressor extends TreeWalker {
|
|
320
320
|
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
|
321
321
|
this.evaluated_regexps = new Map();
|
322
322
|
this._toplevel = undefined;
|
323
|
-
this.
|
323
|
+
this._mangle_options = mangle_options
|
324
324
|
? format_mangler_options(mangle_options)
|
325
325
|
: mangle_options;
|
326
326
|
}
|
327
327
|
|
328
|
+
mangle_options() {
|
329
|
+
var nth_identifier = this._mangle_options && this._mangle_options.nth_identifier || base54;
|
330
|
+
var module = this._mangle_options && this._mangle_options.module || this.option("module");
|
331
|
+
return { ie8: this.option("ie8"), nth_identifier, module };
|
332
|
+
}
|
333
|
+
|
328
334
|
option(key) {
|
329
335
|
return this.options[key];
|
330
336
|
}
|
@@ -379,8 +385,7 @@ class Compressor extends TreeWalker {
|
|
379
385
|
var passes = +this.options.passes || 1;
|
380
386
|
var min_count = 1 / 0;
|
381
387
|
var stopping = false;
|
382
|
-
var
|
383
|
-
var mangle = { ie8: this.option("ie8"), nth_identifier: nth_identifier };
|
388
|
+
var mangle = this.mangle_options();
|
384
389
|
for (var pass = 0; pass < passes; pass++) {
|
385
390
|
this._toplevel.figure_out_scope(mangle);
|
386
391
|
if (pass === 0 && this.option("drop_console")) {
|
@@ -1656,7 +1661,7 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
1656
1661
|
self.args.length = last;
|
1657
1662
|
}
|
1658
1663
|
|
1659
|
-
if (compressor.option("unsafe")) {
|
1664
|
+
if (compressor.option("unsafe") && !exp.contains_optional()) {
|
1660
1665
|
if (exp instanceof AST_Dot && exp.start.value === "Array" && exp.property === "from" && self.args.length === 1) {
|
1661
1666
|
const [argument] = self.args;
|
1662
1667
|
if (argument instanceof AST_Array) {
|
@@ -1871,7 +1876,6 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
1871
1876
|
argnames: [],
|
1872
1877
|
body: []
|
1873
1878
|
}).optimize(compressor);
|
1874
|
-
var nth_identifier = compressor.mangle_options && compressor.mangle_options.nth_identifier || base54;
|
1875
1879
|
if (self.args.every((x) => x instanceof AST_String)) {
|
1876
1880
|
// quite a corner-case, but we can handle it:
|
1877
1881
|
// https://github.com/mishoo/UglifyJS2/issues/203
|
@@ -1881,10 +1885,10 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
1881
1885
|
return arg.value;
|
1882
1886
|
}).join(",") + "){" + self.args[self.args.length - 1].value + "})";
|
1883
1887
|
var ast = parse(code);
|
1884
|
-
var mangle =
|
1888
|
+
var mangle = compressor.mangle_options();
|
1885
1889
|
ast.figure_out_scope(mangle);
|
1886
1890
|
var comp = new Compressor(compressor.options, {
|
1887
|
-
mangle_options: compressor.
|
1891
|
+
mangle_options: compressor._mangle_options
|
1888
1892
|
});
|
1889
1893
|
ast = ast.transform(comp);
|
1890
1894
|
ast.figure_out_scope(mangle);
|
@@ -1923,6 +1927,23 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
1923
1927
|
return inline_into_call(self, compressor);
|
1924
1928
|
});
|
1925
1929
|
|
1930
|
+
/** Does this node contain optional property access or optional call? */
|
1931
|
+
AST_Node.DEFMETHOD("contains_optional", function() {
|
1932
|
+
if (
|
1933
|
+
this instanceof AST_PropAccess
|
1934
|
+
|| this instanceof AST_Call
|
1935
|
+
|| this instanceof AST_Chain
|
1936
|
+
) {
|
1937
|
+
if (this.optional) {
|
1938
|
+
return true;
|
1939
|
+
} else {
|
1940
|
+
return this.expression.contains_optional();
|
1941
|
+
}
|
1942
|
+
} else {
|
1943
|
+
return false;
|
1944
|
+
}
|
1945
|
+
});
|
1946
|
+
|
1926
1947
|
def_optimize(AST_New, function(self, compressor) {
|
1927
1948
|
if (
|
1928
1949
|
compressor.option("unsafe") &&
|
@@ -3528,8 +3549,14 @@ def_optimize(AST_Function, function(self, compressor) {
|
|
3528
3549
|
});
|
3529
3550
|
|
3530
3551
|
def_optimize(AST_Class, function(self) {
|
3531
|
-
|
3532
|
-
|
3552
|
+
for (let i = 0; i < self.properties.length; i++) {
|
3553
|
+
const prop = self.properties[i];
|
3554
|
+
if (prop instanceof AST_ClassStaticBlock && prop.body.length == 0) {
|
3555
|
+
self.properties.splice(i, 1);
|
3556
|
+
i--;
|
3557
|
+
}
|
3558
|
+
}
|
3559
|
+
|
3533
3560
|
return self;
|
3534
3561
|
});
|
3535
3562
|
|
package/lib/compress/inline.js
CHANGED
@@ -84,7 +84,7 @@ import {
|
|
84
84
|
|
85
85
|
_INLINE,
|
86
86
|
_NOINLINE,
|
87
|
-
_PURE
|
87
|
+
_PURE,
|
88
88
|
} from "../ast.js";
|
89
89
|
import { make_node, has_annotation } from "../utils/index.js";
|
90
90
|
import "../size.js";
|
@@ -148,18 +148,43 @@ function scope_encloses_variables_in_this_scope(scope, pulled_scope) {
|
|
148
148
|
return false;
|
149
149
|
}
|
150
150
|
|
151
|
+
/**
|
152
|
+
* An extra check function for `top_retain` option, compare the length of const identifier
|
153
|
+
* and init value length and return true if init value is longer than identifier. for example:
|
154
|
+
* ```
|
155
|
+
* // top_retain: ["example"]
|
156
|
+
* const example = 100
|
157
|
+
* ```
|
158
|
+
* it will return false because length of "100" is short than identifier "example".
|
159
|
+
*/
|
160
|
+
function is_const_symbol_short_than_init_value(def, fixed_value) {
|
161
|
+
if (def.orig.length === 1 && fixed_value) {
|
162
|
+
const init_value_length = fixed_value.size();
|
163
|
+
const identifer_length = def.name.length;
|
164
|
+
return init_value_length > identifer_length;
|
165
|
+
}
|
166
|
+
return true;
|
167
|
+
}
|
168
|
+
|
151
169
|
export function inline_into_symbolref(self, compressor) {
|
152
170
|
const parent = compressor.parent();
|
153
|
-
|
154
171
|
const def = self.definition();
|
155
172
|
const nearest_scope = compressor.find_scope();
|
156
|
-
|
173
|
+
let fixed = self.fixed_value();
|
174
|
+
if (
|
175
|
+
compressor.top_retain &&
|
176
|
+
def.global &&
|
177
|
+
compressor.top_retain(def) &&
|
178
|
+
// when identifier is in top_retain option dose not mean we can always inline it.
|
179
|
+
// if identifier name is longer then init value, we can replace it.
|
180
|
+
is_const_symbol_short_than_init_value(def, fixed)
|
181
|
+
) {
|
182
|
+
// keep it
|
157
183
|
def.fixed = false;
|
158
184
|
def.single_use = false;
|
159
185
|
return self;
|
160
186
|
}
|
161
187
|
|
162
|
-
let fixed = self.fixed_value();
|
163
188
|
let single_use = def.single_use
|
164
189
|
&& !(parent instanceof AST_Call
|
165
190
|
&& (parent.is_callee_pure(compressor))
|
package/lib/scope.js
CHANGED
@@ -205,6 +205,7 @@ AST_Scope.DEFMETHOD("figure_out_scope", function(options, { parent_scope = null,
|
|
205
205
|
cache: null,
|
206
206
|
ie8: false,
|
207
207
|
safari10: false,
|
208
|
+
module: false,
|
208
209
|
});
|
209
210
|
|
210
211
|
if (!(toplevel instanceof AST_Toplevel)) {
|
@@ -372,6 +373,11 @@ AST_Scope.DEFMETHOD("figure_out_scope", function(options, { parent_scope = null,
|
|
372
373
|
);
|
373
374
|
}
|
374
375
|
});
|
376
|
+
|
377
|
+
if (options.module) {
|
378
|
+
tw.directives["use strict"] = true;
|
379
|
+
}
|
380
|
+
|
375
381
|
this.walk(tw);
|
376
382
|
|
377
383
|
function mark_export(def, level) {
|
package/lib/size.js
CHANGED
@@ -89,7 +89,7 @@ import { first_in_statement } from "./utils/first_in_statement.js";
|
|
89
89
|
|
90
90
|
let mangle_options = undefined;
|
91
91
|
AST_Node.prototype.size = function (compressor, stack) {
|
92
|
-
mangle_options = compressor && compressor.
|
92
|
+
mangle_options = compressor && compressor._mangle_options;
|
93
93
|
|
94
94
|
let size = 0;
|
95
95
|
walk_parent(this, (node, info) => {
|