terser 5.21.0 → 5.22.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 CHANGED
@@ -1,6 +1,11 @@
1
1
  # Changelog
2
2
 
3
- ## v5.20.1
3
+ ## v5.22.0
4
+ - Do not `unsafe`ly shorten expressions like a?.toString() when they're conditional.
5
+ - Avoid running drop_unused in nodes that aren't scopes. Fixes a rare crash.
6
+ - When 'module' is enabled, assume strict mode when figuring out scopes.
7
+
8
+ ## v5.21.0
4
9
  - Do not inline functions that would be retained in the toplevel (as this would cause code duplication).
5
10
  - Fix precedence of arrow function and ternary operator when formatting output.
6
11
 
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 will be enabled.
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
@@ -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.mangle_options;
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;
@@ -18406,11 +18414,17 @@ class Compressor extends TreeWalker {
18406
18414
  this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
18407
18415
  this.evaluated_regexps = new Map();
18408
18416
  this._toplevel = undefined;
18409
- this.mangle_options = mangle_options
18417
+ this._mangle_options = mangle_options
18410
18418
  ? format_mangler_options(mangle_options)
18411
18419
  : mangle_options;
18412
18420
  }
18413
18421
 
18422
+ mangle_options() {
18423
+ var nth_identifier = this._mangle_options && this._mangle_options.nth_identifier || base54;
18424
+ var module = this._mangle_options && this._mangle_options.module || this.option("module");
18425
+ return { ie8: this.option("ie8"), nth_identifier, module };
18426
+ }
18427
+
18414
18428
  option(key) {
18415
18429
  return this.options[key];
18416
18430
  }
@@ -18465,8 +18479,7 @@ class Compressor extends TreeWalker {
18465
18479
  var passes = +this.options.passes || 1;
18466
18480
  var min_count = 1 / 0;
18467
18481
  var stopping = false;
18468
- var nth_identifier = this.mangle_options && this.mangle_options.nth_identifier || base54;
18469
- var mangle = { ie8: this.option("ie8"), nth_identifier: nth_identifier };
18482
+ var mangle = this.mangle_options();
18470
18483
  for (var pass = 0; pass < passes; pass++) {
18471
18484
  this._toplevel.figure_out_scope(mangle);
18472
18485
  if (pass === 0 && this.option("drop_console")) {
@@ -19742,7 +19755,7 @@ def_optimize(AST_Call, function(self, compressor) {
19742
19755
  self.args.length = last;
19743
19756
  }
19744
19757
 
19745
- if (compressor.option("unsafe")) {
19758
+ if (compressor.option("unsafe") && !exp.contains_optional()) {
19746
19759
  if (exp instanceof AST_Dot && exp.start.value === "Array" && exp.property === "from" && self.args.length === 1) {
19747
19760
  const [argument] = self.args;
19748
19761
  if (argument instanceof AST_Array) {
@@ -19957,7 +19970,6 @@ def_optimize(AST_Call, function(self, compressor) {
19957
19970
  argnames: [],
19958
19971
  body: []
19959
19972
  }).optimize(compressor);
19960
- var nth_identifier = compressor.mangle_options && compressor.mangle_options.nth_identifier || base54;
19961
19973
  if (self.args.every((x) => x instanceof AST_String)) {
19962
19974
  // quite a corner-case, but we can handle it:
19963
19975
  // https://github.com/mishoo/UglifyJS2/issues/203
@@ -19967,10 +19979,10 @@ def_optimize(AST_Call, function(self, compressor) {
19967
19979
  return arg.value;
19968
19980
  }).join(",") + "){" + self.args[self.args.length - 1].value + "})";
19969
19981
  var ast = parse(code);
19970
- var mangle = { ie8: compressor.option("ie8"), nth_identifier: nth_identifier };
19982
+ var mangle = compressor.mangle_options();
19971
19983
  ast.figure_out_scope(mangle);
19972
19984
  var comp = new Compressor(compressor.options, {
19973
- mangle_options: compressor.mangle_options
19985
+ mangle_options: compressor._mangle_options
19974
19986
  });
19975
19987
  ast = ast.transform(comp);
19976
19988
  ast.figure_out_scope(mangle);
@@ -20009,6 +20021,23 @@ def_optimize(AST_Call, function(self, compressor) {
20009
20021
  return inline_into_call(self, compressor);
20010
20022
  });
20011
20023
 
20024
+ /** Does this node contain optional property access or optional call? */
20025
+ AST_Node.DEFMETHOD("contains_optional", function() {
20026
+ if (
20027
+ this instanceof AST_PropAccess
20028
+ || this instanceof AST_Call
20029
+ || this instanceof AST_Chain
20030
+ ) {
20031
+ if (this.optional) {
20032
+ return true;
20033
+ } else {
20034
+ return this.expression.contains_optional();
20035
+ }
20036
+ } else {
20037
+ return false;
20038
+ }
20039
+ });
20040
+
20012
20041
  def_optimize(AST_New, function(self, compressor) {
20013
20042
  if (
20014
20043
  compressor.option("unsafe") &&
@@ -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;
@@ -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.mangle_options = mangle_options
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 nth_identifier = this.mangle_options && this.mangle_options.nth_identifier || base54;
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 = { ie8: compressor.option("ie8"), nth_identifier: nth_identifier };
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.mangle_options
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") &&
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.mangle_options;
92
+ mangle_options = compressor && compressor._mangle_options;
93
93
 
94
94
  let size = 0;
95
95
  walk_parent(this, (node, info) => {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "homepage": "https://terser.org",
5
5
  "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
6
6
  "license": "BSD-2-Clause",
7
- "version": "5.21.0",
7
+ "version": "5.22.0",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },