terser 5.20.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,5 +1,14 @@
1
1
  # Changelog
2
2
 
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
9
+ - Do not inline functions that would be retained in the toplevel (as this would cause code duplication).
10
+ - Fix precedence of arrow function and ternary operator when formatting output.
11
+
3
12
  ## v5.20.0
4
13
  - Passing `minify()` zero files will now throw a clean exception (#1450)
5
14
  - `drop_console` supports passing in an array of `console.*` method names (#1445)
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
@@ -9742,7 +9742,8 @@ function OutputStream(options) {
9742
9742
  ) {
9743
9743
  return true;
9744
9744
  }
9745
- return p instanceof AST_PropAccess && p.expression === this;
9745
+ return p instanceof AST_PropAccess && p.expression === this
9746
+ || p instanceof AST_Conditional && p.condition === this;
9746
9747
  });
9747
9748
 
9748
9749
  // same goes for an object literal (as in AST_Function), because
@@ -11572,6 +11573,7 @@ AST_Scope.DEFMETHOD("figure_out_scope", function(options, { parent_scope = null,
11572
11573
  cache: null,
11573
11574
  ie8: false,
11574
11575
  safari10: false,
11576
+ module: false,
11575
11577
  });
11576
11578
 
11577
11579
  if (!(toplevel instanceof AST_Toplevel)) {
@@ -11739,6 +11741,11 @@ AST_Scope.DEFMETHOD("figure_out_scope", function(options, { parent_scope = null,
11739
11741
  );
11740
11742
  }
11741
11743
  });
11744
+
11745
+ if (options.module) {
11746
+ tw.directives["use strict"] = true;
11747
+ }
11748
+
11742
11749
  this.walk(tw);
11743
11750
 
11744
11751
  function mark_export(def, level) {
@@ -12423,7 +12430,7 @@ const base54 = (() => {
12423
12430
 
12424
12431
  let mangle_options = undefined;
12425
12432
  AST_Node.prototype.size = function (compressor, stack) {
12426
- mangle_options = compressor && compressor.mangle_options;
12433
+ mangle_options = compressor && compressor._mangle_options;
12427
12434
 
12428
12435
  let size = 0;
12429
12436
  walk_parent(this, (node, info) => {
@@ -13190,7 +13197,7 @@ function retain_top_func(fn, compressor) {
13190
13197
  && fn instanceof AST_Defun
13191
13198
  && has_flag(fn, TOP)
13192
13199
  && fn.name
13193
- && compressor.top_retain(fn.name);
13200
+ && compressor.top_retain(fn.name.definition());
13194
13201
  }
13195
13202
 
13196
13203
  /***********************************************************************
@@ -14039,80 +14046,6 @@ function is_lhs(node, parent) {
14039
14046
  if (parent instanceof AST_ForIn && parent.init === node) return node;
14040
14047
  }
14041
14048
 
14042
- (function(def_find_defs) {
14043
- function to_node(value, orig) {
14044
- if (value instanceof AST_Node) {
14045
- if (!(value instanceof AST_Constant)) {
14046
- // Value may be a function, an array including functions and even a complex assign / block expression,
14047
- // so it should never be shared in different places.
14048
- // Otherwise wrong information may be used in the compression phase
14049
- value = value.clone(true);
14050
- }
14051
- return make_node(value.CTOR, orig, value);
14052
- }
14053
- if (Array.isArray(value)) return make_node(AST_Array, orig, {
14054
- elements: value.map(function(value) {
14055
- return to_node(value, orig);
14056
- })
14057
- });
14058
- if (value && typeof value == "object") {
14059
- var props = [];
14060
- for (var key in value) if (HOP(value, key)) {
14061
- props.push(make_node(AST_ObjectKeyVal, orig, {
14062
- key: key,
14063
- value: to_node(value[key], orig)
14064
- }));
14065
- }
14066
- return make_node(AST_Object, orig, {
14067
- properties: props
14068
- });
14069
- }
14070
- return make_node_from_constant(value, orig);
14071
- }
14072
-
14073
- AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
14074
- if (!compressor.option("global_defs")) return this;
14075
- this.figure_out_scope({ ie8: compressor.option("ie8") });
14076
- return this.transform(new TreeTransformer(function(node) {
14077
- var def = node._find_defs(compressor, "");
14078
- if (!def) return;
14079
- var level = 0, child = node, parent;
14080
- while (parent = this.parent(level++)) {
14081
- if (!(parent instanceof AST_PropAccess)) break;
14082
- if (parent.expression !== child) break;
14083
- child = parent;
14084
- }
14085
- if (is_lhs(child, parent)) {
14086
- return;
14087
- }
14088
- return def;
14089
- }));
14090
- });
14091
- def_find_defs(AST_Node, noop);
14092
- def_find_defs(AST_Chain, function(compressor, suffix) {
14093
- return this.expression._find_defs(compressor, suffix);
14094
- });
14095
- def_find_defs(AST_Dot, function(compressor, suffix) {
14096
- return this.expression._find_defs(compressor, "." + this.property + suffix);
14097
- });
14098
- def_find_defs(AST_SymbolDeclaration, function() {
14099
- if (!this.global()) return;
14100
- });
14101
- def_find_defs(AST_SymbolRef, function(compressor, suffix) {
14102
- if (!this.global()) return;
14103
- var defines = compressor.option("global_defs");
14104
- var name = this.name + suffix;
14105
- if (HOP(defines, name)) return to_node(defines[name], this);
14106
- });
14107
- def_find_defs(AST_ImportMeta, function(compressor, suffix) {
14108
- var defines = compressor.option("global_defs");
14109
- var name = "import.meta" + suffix;
14110
- if (HOP(defines, name)) return to_node(defines[name], this);
14111
- });
14112
- })(function(node, func) {
14113
- node.DEFMETHOD("_find_defs", func);
14114
- });
14115
-
14116
14049
  // method to negate an expression
14117
14050
  (function(def_negate) {
14118
14051
  function basic_negation(exp) {
@@ -15150,6 +15083,8 @@ const r_keep_assign = /keep_assign/;
15150
15083
  AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15151
15084
  if (!compressor.option("unused")) return;
15152
15085
  if (compressor.has_directive("use asm")) return;
15086
+ if (!this.variables) return; // not really a scope (eg: AST_Class)
15087
+
15153
15088
  var self = this;
15154
15089
  if (self.pinned()) return;
15155
15090
  var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
@@ -15169,7 +15104,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15169
15104
  var fixed_ids = new Map();
15170
15105
  if (self instanceof AST_Toplevel && compressor.top_retain) {
15171
15106
  self.variables.forEach(function(def) {
15172
- if (compressor.top_retain(def) && !in_use_ids.has(def.id)) {
15107
+ if (compressor.top_retain(def)) {
15173
15108
  in_use_ids.set(def.id, def);
15174
15109
  }
15175
15110
  });
@@ -15184,9 +15119,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15184
15119
  node.argnames.forEach(function(argname) {
15185
15120
  if (!(argname instanceof AST_SymbolDeclaration)) return;
15186
15121
  var def = argname.definition();
15187
- if (!in_use_ids.has(def.id)) {
15188
- in_use_ids.set(def.id, def);
15189
- }
15122
+ in_use_ids.set(def.id, def);
15190
15123
  });
15191
15124
  }
15192
15125
  if (node === self) return;
@@ -15199,7 +15132,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15199
15132
  var node_def = node.name.definition();
15200
15133
  const in_export = tw.parent() instanceof AST_Export;
15201
15134
  if (in_export || !drop_funcs && scope === self) {
15202
- if (node_def.global && !in_use_ids.has(node_def.id)) {
15135
+ if (node_def.global) {
15203
15136
  in_use_ids.set(node_def.id, node_def);
15204
15137
  }
15205
15138
  }
@@ -15207,10 +15140,12 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15207
15140
  map_add(initializations, node_def.id, node);
15208
15141
  return true; // don't go in nested scopes
15209
15142
  }
15210
- if (node instanceof AST_SymbolFunarg && scope === self) {
15143
+ // In the root scope, we drop things. In inner scopes, we just check for uses.
15144
+ const in_root_scope = scope === self;
15145
+ if (node instanceof AST_SymbolFunarg && in_root_scope) {
15211
15146
  map_add(var_defs_by_id, node.definition().id, node);
15212
15147
  }
15213
- if (node instanceof AST_Definitions && scope === self) {
15148
+ if (node instanceof AST_Definitions && in_root_scope) {
15214
15149
  const in_export = tw.parent() instanceof AST_Export;
15215
15150
  node.definitions.forEach(function(def) {
15216
15151
  if (def.name instanceof AST_SymbolVar) {
@@ -15220,7 +15155,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15220
15155
  walk(def.name, node => {
15221
15156
  if (node instanceof AST_SymbolDeclaration) {
15222
15157
  const def = node.definition();
15223
- if (def.global && !in_use_ids.has(def.id)) {
15158
+ if (def.global) {
15224
15159
  in_use_ids.set(def.id, def);
15225
15160
  }
15226
15161
  }
@@ -17903,8 +17838,9 @@ function inline_into_symbolref(self, compressor) {
17903
17838
  return self;
17904
17839
  }
17905
17840
 
17906
- function inline_into_call(self, fn, compressor) {
17841
+ function inline_into_call(self, compressor) {
17907
17842
  var exp = self.expression;
17843
+ var fn = exp;
17908
17844
  var simple_args = self.args.every((arg) => !(arg instanceof AST_Expansion));
17909
17845
 
17910
17846
  if (compressor.option("reduce_vars")
@@ -17912,9 +17848,15 @@ function inline_into_call(self, fn, compressor) {
17912
17848
  && !has_annotation(self, _NOINLINE)
17913
17849
  ) {
17914
17850
  const fixed = fn.fixed_value();
17915
- if (!retain_top_func(fixed, compressor)) {
17916
- fn = fixed;
17851
+
17852
+ if (
17853
+ retain_top_func(fixed, compressor)
17854
+ || !compressor.toplevel.funcs && exp.definition().global
17855
+ ) {
17856
+ return self;
17917
17857
  }
17858
+
17859
+ fn = fixed;
17918
17860
  }
17919
17861
 
17920
17862
  var is_func = fn instanceof AST_Lambda;
@@ -18245,6 +18187,80 @@ function inline_into_call(self, fn, compressor) {
18245
18187
  }
18246
18188
  }
18247
18189
 
18190
+ (function(def_find_defs) {
18191
+ function to_node(value, orig) {
18192
+ if (value instanceof AST_Node) {
18193
+ if (!(value instanceof AST_Constant)) {
18194
+ // Value may be a function, an array including functions and even a complex assign / block expression,
18195
+ // so it should never be shared in different places.
18196
+ // Otherwise wrong information may be used in the compression phase
18197
+ value = value.clone(true);
18198
+ }
18199
+ return make_node(value.CTOR, orig, value);
18200
+ }
18201
+ if (Array.isArray(value)) return make_node(AST_Array, orig, {
18202
+ elements: value.map(function(value) {
18203
+ return to_node(value, orig);
18204
+ })
18205
+ });
18206
+ if (value && typeof value == "object") {
18207
+ var props = [];
18208
+ for (var key in value) if (HOP(value, key)) {
18209
+ props.push(make_node(AST_ObjectKeyVal, orig, {
18210
+ key: key,
18211
+ value: to_node(value[key], orig)
18212
+ }));
18213
+ }
18214
+ return make_node(AST_Object, orig, {
18215
+ properties: props
18216
+ });
18217
+ }
18218
+ return make_node_from_constant(value, orig);
18219
+ }
18220
+
18221
+ AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
18222
+ if (!compressor.option("global_defs")) return this;
18223
+ this.figure_out_scope({ ie8: compressor.option("ie8") });
18224
+ return this.transform(new TreeTransformer(function(node) {
18225
+ var def = node._find_defs(compressor, "");
18226
+ if (!def) return;
18227
+ var level = 0, child = node, parent;
18228
+ while (parent = this.parent(level++)) {
18229
+ if (!(parent instanceof AST_PropAccess)) break;
18230
+ if (parent.expression !== child) break;
18231
+ child = parent;
18232
+ }
18233
+ if (is_lhs(child, parent)) {
18234
+ return;
18235
+ }
18236
+ return def;
18237
+ }));
18238
+ });
18239
+ def_find_defs(AST_Node, noop);
18240
+ def_find_defs(AST_Chain, function(compressor, suffix) {
18241
+ return this.expression._find_defs(compressor, suffix);
18242
+ });
18243
+ def_find_defs(AST_Dot, function(compressor, suffix) {
18244
+ return this.expression._find_defs(compressor, "." + this.property + suffix);
18245
+ });
18246
+ def_find_defs(AST_SymbolDeclaration, function() {
18247
+ if (!this.global()) return;
18248
+ });
18249
+ def_find_defs(AST_SymbolRef, function(compressor, suffix) {
18250
+ if (!this.global()) return;
18251
+ var defines = compressor.option("global_defs");
18252
+ var name = this.name + suffix;
18253
+ if (HOP(defines, name)) return to_node(defines[name], this);
18254
+ });
18255
+ def_find_defs(AST_ImportMeta, function(compressor, suffix) {
18256
+ var defines = compressor.option("global_defs");
18257
+ var name = "import.meta" + suffix;
18258
+ if (HOP(defines, name)) return to_node(defines[name], this);
18259
+ });
18260
+ })(function(node, func) {
18261
+ node.DEFMETHOD("_find_defs", func);
18262
+ });
18263
+
18248
18264
  /***********************************************************************
18249
18265
 
18250
18266
  A JavaScript tokenizer / parser / beautifier / compressor.
@@ -18398,11 +18414,17 @@ class Compressor extends TreeWalker {
18398
18414
  this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
18399
18415
  this.evaluated_regexps = new Map();
18400
18416
  this._toplevel = undefined;
18401
- this.mangle_options = mangle_options
18417
+ this._mangle_options = mangle_options
18402
18418
  ? format_mangler_options(mangle_options)
18403
18419
  : mangle_options;
18404
18420
  }
18405
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
+
18406
18428
  option(key) {
18407
18429
  return this.options[key];
18408
18430
  }
@@ -18457,8 +18479,7 @@ class Compressor extends TreeWalker {
18457
18479
  var passes = +this.options.passes || 1;
18458
18480
  var min_count = 1 / 0;
18459
18481
  var stopping = false;
18460
- var nth_identifier = this.mangle_options && this.mangle_options.nth_identifier || base54;
18461
- var mangle = { ie8: this.option("ie8"), nth_identifier: nth_identifier };
18482
+ var mangle = this.mangle_options();
18462
18483
  for (var pass = 0; pass < passes; pass++) {
18463
18484
  this._toplevel.figure_out_scope(mangle);
18464
18485
  if (pass === 0 && this.option("drop_console")) {
@@ -19687,18 +19708,10 @@ def_optimize(AST_Call, function(self, compressor) {
19687
19708
  var exp = self.expression;
19688
19709
  var fn = exp;
19689
19710
  inline_array_like_spread(self.args);
19690
- var simple_args = self.args.every((arg) =>
19691
- !(arg instanceof AST_Expansion)
19692
- );
19711
+ var simple_args = self.args.every((arg) => !(arg instanceof AST_Expansion));
19693
19712
 
19694
- if (compressor.option("reduce_vars")
19695
- && fn instanceof AST_SymbolRef
19696
- && !has_annotation(self, _NOINLINE)
19697
- ) {
19698
- const fixed = fn.fixed_value();
19699
- if (!retain_top_func(fixed, compressor)) {
19700
- fn = fixed;
19701
- }
19713
+ if (compressor.option("reduce_vars") && fn instanceof AST_SymbolRef) {
19714
+ fn = fn.fixed_value();
19702
19715
  }
19703
19716
 
19704
19717
  var is_func = fn instanceof AST_Lambda;
@@ -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);
@@ -20006,7 +20018,24 @@ def_optimize(AST_Call, function(self, compressor) {
20006
20018
  }
20007
20019
  }
20008
20020
 
20009
- return inline_into_call(self, fn, compressor);
20021
+ return inline_into_call(self, compressor);
20022
+ });
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
+ }
20010
20039
  });
20011
20040
 
20012
20041
  def_optimize(AST_New, function(self, compressor) {
@@ -21839,10 +21868,10 @@ def_optimize(AST_Destructuring, function(self, compressor) {
21839
21868
  if (def.references.length) return true;
21840
21869
  if (!def.global) return false;
21841
21870
  if (compressor.toplevel.vars) {
21842
- if (compressor.top_retain) {
21843
- return compressor.top_retain(def);
21844
- }
21845
- return false;
21871
+ if (compressor.top_retain) {
21872
+ return compressor.top_retain(def);
21873
+ }
21874
+ return false;
21846
21875
  }
21847
21876
  return true;
21848
21877
  }
@@ -344,5 +344,5 @@ export function retain_top_func(fn, compressor) {
344
344
  && fn instanceof AST_Defun
345
345
  && has_flag(fn, TOP)
346
346
  && fn.name
347
- && compressor.top_retain(fn.name);
347
+ && compressor.top_retain(fn.name.definition());
348
348
  }
@@ -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;
@@ -128,7 +130,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
128
130
  var fixed_ids = new Map();
129
131
  if (self instanceof AST_Toplevel && compressor.top_retain) {
130
132
  self.variables.forEach(function(def) {
131
- if (compressor.top_retain(def) && !in_use_ids.has(def.id)) {
133
+ if (compressor.top_retain(def)) {
132
134
  in_use_ids.set(def.id, def);
133
135
  }
134
136
  });
@@ -143,9 +145,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
143
145
  node.argnames.forEach(function(argname) {
144
146
  if (!(argname instanceof AST_SymbolDeclaration)) return;
145
147
  var def = argname.definition();
146
- if (!in_use_ids.has(def.id)) {
147
- in_use_ids.set(def.id, def);
148
- }
148
+ in_use_ids.set(def.id, def);
149
149
  });
150
150
  }
151
151
  if (node === self) return;
@@ -158,7 +158,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
158
158
  var node_def = node.name.definition();
159
159
  const in_export = tw.parent() instanceof AST_Export;
160
160
  if (in_export || !drop_funcs && scope === self) {
161
- if (node_def.global && !in_use_ids.has(node_def.id)) {
161
+ if (node_def.global) {
162
162
  in_use_ids.set(node_def.id, node_def);
163
163
  }
164
164
  }
@@ -166,10 +166,12 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
166
166
  map_add(initializations, node_def.id, node);
167
167
  return true; // don't go in nested scopes
168
168
  }
169
- if (node instanceof AST_SymbolFunarg && scope === self) {
169
+ // In the root scope, we drop things. In inner scopes, we just check for uses.
170
+ const in_root_scope = scope === self;
171
+ if (node instanceof AST_SymbolFunarg && in_root_scope) {
170
172
  map_add(var_defs_by_id, node.definition().id, node);
171
173
  }
172
- if (node instanceof AST_Definitions && scope === self) {
174
+ if (node instanceof AST_Definitions && in_root_scope) {
173
175
  const in_export = tw.parent() instanceof AST_Export;
174
176
  node.definitions.forEach(function(def) {
175
177
  if (def.name instanceof AST_SymbolVar) {
@@ -179,7 +181,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
179
181
  walk(def.name, node => {
180
182
  if (node instanceof AST_SymbolDeclaration) {
181
183
  const def = node.definition();
182
- if (def.global && !in_use_ids.has(def.id)) {
184
+ if (def.global) {
183
185
  in_use_ids.set(def.id, def);
184
186
  }
185
187
  }
@@ -0,0 +1,92 @@
1
+ import {
2
+ AST_Array,
3
+ AST_Chain,
4
+ AST_Constant,
5
+ AST_Dot,
6
+ AST_ImportMeta,
7
+ AST_Node,
8
+ AST_Object,
9
+ AST_ObjectKeyVal,
10
+ AST_PropAccess,
11
+ AST_SymbolDeclaration,
12
+ AST_SymbolRef,
13
+ AST_Toplevel,
14
+ TreeTransformer,
15
+ } from "../ast.js";
16
+ import { make_node, noop, HOP } from "../utils/index.js";
17
+ import { make_node_from_constant } from "./common.js";
18
+ import { is_lhs } from "./inference.js";
19
+
20
+ (function(def_find_defs) {
21
+ function to_node(value, orig) {
22
+ if (value instanceof AST_Node) {
23
+ if (!(value instanceof AST_Constant)) {
24
+ // Value may be a function, an array including functions and even a complex assign / block expression,
25
+ // so it should never be shared in different places.
26
+ // Otherwise wrong information may be used in the compression phase
27
+ value = value.clone(true);
28
+ }
29
+ return make_node(value.CTOR, orig, value);
30
+ }
31
+ if (Array.isArray(value)) return make_node(AST_Array, orig, {
32
+ elements: value.map(function(value) {
33
+ return to_node(value, orig);
34
+ })
35
+ });
36
+ if (value && typeof value == "object") {
37
+ var props = [];
38
+ for (var key in value) if (HOP(value, key)) {
39
+ props.push(make_node(AST_ObjectKeyVal, orig, {
40
+ key: key,
41
+ value: to_node(value[key], orig)
42
+ }));
43
+ }
44
+ return make_node(AST_Object, orig, {
45
+ properties: props
46
+ });
47
+ }
48
+ return make_node_from_constant(value, orig);
49
+ }
50
+
51
+ AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
52
+ if (!compressor.option("global_defs")) return this;
53
+ this.figure_out_scope({ ie8: compressor.option("ie8") });
54
+ return this.transform(new TreeTransformer(function(node) {
55
+ var def = node._find_defs(compressor, "");
56
+ if (!def) return;
57
+ var level = 0, child = node, parent;
58
+ while (parent = this.parent(level++)) {
59
+ if (!(parent instanceof AST_PropAccess)) break;
60
+ if (parent.expression !== child) break;
61
+ child = parent;
62
+ }
63
+ if (is_lhs(child, parent)) {
64
+ return;
65
+ }
66
+ return def;
67
+ }));
68
+ });
69
+ def_find_defs(AST_Node, noop);
70
+ def_find_defs(AST_Chain, function(compressor, suffix) {
71
+ return this.expression._find_defs(compressor, suffix);
72
+ });
73
+ def_find_defs(AST_Dot, function(compressor, suffix) {
74
+ return this.expression._find_defs(compressor, "." + this.property + suffix);
75
+ });
76
+ def_find_defs(AST_SymbolDeclaration, function() {
77
+ if (!this.global()) return;
78
+ });
79
+ def_find_defs(AST_SymbolRef, function(compressor, suffix) {
80
+ if (!this.global()) return;
81
+ var defines = compressor.option("global_defs");
82
+ var name = this.name + suffix;
83
+ if (HOP(defines, name)) return to_node(defines[name], this);
84
+ });
85
+ def_find_defs(AST_ImportMeta, function(compressor, suffix) {
86
+ var defines = compressor.option("global_defs");
87
+ var name = "import.meta" + suffix;
88
+ if (HOP(defines, name)) return to_node(defines[name], this);
89
+ });
90
+ })(function(node, func) {
91
+ node.DEFMETHOD("_find_defs", func);
92
+ });
@@ -204,11 +204,11 @@ import {
204
204
  is_reachable,
205
205
  can_be_evicted_from_block,
206
206
  as_statement_array,
207
- retain_top_func,
208
207
  is_func_expr,
209
208
  } from "./common.js";
210
209
  import { tighten_body, trim_unreachable_code } from "./tighten-body.js";
211
210
  import { inline_into_symbolref, inline_into_call } from "./inline.js";
211
+ import "./global-defs.js";
212
212
 
213
213
  class Compressor extends TreeWalker {
214
214
  constructor(options, { false_by_default = false, mangle_options = false }) {
@@ -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")) {
@@ -1609,18 +1614,10 @@ def_optimize(AST_Call, function(self, compressor) {
1609
1614
  var exp = self.expression;
1610
1615
  var fn = exp;
1611
1616
  inline_array_like_spread(self.args);
1612
- var simple_args = self.args.every((arg) =>
1613
- !(arg instanceof AST_Expansion)
1614
- );
1617
+ var simple_args = self.args.every((arg) => !(arg instanceof AST_Expansion));
1615
1618
 
1616
- if (compressor.option("reduce_vars")
1617
- && fn instanceof AST_SymbolRef
1618
- && !has_annotation(self, _NOINLINE)
1619
- ) {
1620
- const fixed = fn.fixed_value();
1621
- if (!retain_top_func(fixed, compressor)) {
1622
- fn = fixed;
1623
- }
1619
+ if (compressor.option("reduce_vars") && fn instanceof AST_SymbolRef) {
1620
+ fn = fn.fixed_value();
1624
1621
  }
1625
1622
 
1626
1623
  var is_func = fn instanceof AST_Lambda;
@@ -1664,7 +1661,7 @@ def_optimize(AST_Call, function(self, compressor) {
1664
1661
  self.args.length = last;
1665
1662
  }
1666
1663
 
1667
- if (compressor.option("unsafe")) {
1664
+ if (compressor.option("unsafe") && !exp.contains_optional()) {
1668
1665
  if (exp instanceof AST_Dot && exp.start.value === "Array" && exp.property === "from" && self.args.length === 1) {
1669
1666
  const [argument] = self.args;
1670
1667
  if (argument instanceof AST_Array) {
@@ -1879,7 +1876,6 @@ def_optimize(AST_Call, function(self, compressor) {
1879
1876
  argnames: [],
1880
1877
  body: []
1881
1878
  }).optimize(compressor);
1882
- var nth_identifier = compressor.mangle_options && compressor.mangle_options.nth_identifier || base54;
1883
1879
  if (self.args.every((x) => x instanceof AST_String)) {
1884
1880
  // quite a corner-case, but we can handle it:
1885
1881
  // https://github.com/mishoo/UglifyJS2/issues/203
@@ -1889,10 +1885,10 @@ def_optimize(AST_Call, function(self, compressor) {
1889
1885
  return arg.value;
1890
1886
  }).join(",") + "){" + self.args[self.args.length - 1].value + "})";
1891
1887
  var ast = parse(code);
1892
- var mangle = { ie8: compressor.option("ie8"), nth_identifier: nth_identifier };
1888
+ var mangle = compressor.mangle_options();
1893
1889
  ast.figure_out_scope(mangle);
1894
1890
  var comp = new Compressor(compressor.options, {
1895
- mangle_options: compressor.mangle_options
1891
+ mangle_options: compressor._mangle_options
1896
1892
  });
1897
1893
  ast = ast.transform(comp);
1898
1894
  ast.figure_out_scope(mangle);
@@ -1928,7 +1924,24 @@ def_optimize(AST_Call, function(self, compressor) {
1928
1924
  }
1929
1925
  }
1930
1926
 
1931
- return inline_into_call(self, fn, compressor);
1927
+ return inline_into_call(self, compressor);
1928
+ });
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
+ }
1932
1945
  });
1933
1946
 
1934
1947
  def_optimize(AST_New, function(self, compressor) {
@@ -3761,10 +3774,10 @@ def_optimize(AST_Destructuring, function(self, compressor) {
3761
3774
  if (def.references.length) return true;
3762
3775
  if (!def.global) return false;
3763
3776
  if (compressor.toplevel.vars) {
3764
- if (compressor.top_retain) {
3765
- return compressor.top_retain(def);
3766
- }
3767
- return false;
3777
+ if (compressor.top_retain) {
3778
+ return compressor.top_retain(def);
3779
+ }
3780
+ return false;
3768
3781
  }
3769
3782
  return true;
3770
3783
  }
@@ -98,7 +98,6 @@ import {
98
98
  AST_TemplateSegment,
99
99
  AST_TemplateString,
100
100
  AST_This,
101
- AST_Toplevel,
102
101
  AST_True,
103
102
  AST_Try,
104
103
  AST_Unary,
@@ -107,7 +106,6 @@ import {
107
106
  AST_Undefined,
108
107
  AST_VarDef,
109
108
 
110
- TreeTransformer,
111
109
  walk,
112
110
  walk_abort,
113
111
 
@@ -121,11 +119,9 @@ import {
121
119
  return_this,
122
120
  make_node,
123
121
  member,
124
- noop,
125
122
  has_annotation,
126
- HOP
127
123
  } from "../utils/index.js";
128
- import { make_node_from_constant, make_sequence, best_of_expression, read_property } from "./common.js";
124
+ import { make_sequence, best_of_expression, read_property } from "./common.js";
129
125
 
130
126
  import { INLINED, UNDEFINED, has_flag } from "./compressor-flags.js";
131
127
  import { pure_prop_access_globals, is_pure_native_fn, is_pure_native_method } from "./native-objects.js";
@@ -728,80 +724,6 @@ export function is_lhs(node, parent) {
728
724
  if (parent instanceof AST_ForIn && parent.init === node) return node;
729
725
  }
730
726
 
731
- (function(def_find_defs) {
732
- function to_node(value, orig) {
733
- if (value instanceof AST_Node) {
734
- if (!(value instanceof AST_Constant)) {
735
- // Value may be a function, an array including functions and even a complex assign / block expression,
736
- // so it should never be shared in different places.
737
- // Otherwise wrong information may be used in the compression phase
738
- value = value.clone(true);
739
- }
740
- return make_node(value.CTOR, orig, value);
741
- }
742
- if (Array.isArray(value)) return make_node(AST_Array, orig, {
743
- elements: value.map(function(value) {
744
- return to_node(value, orig);
745
- })
746
- });
747
- if (value && typeof value == "object") {
748
- var props = [];
749
- for (var key in value) if (HOP(value, key)) {
750
- props.push(make_node(AST_ObjectKeyVal, orig, {
751
- key: key,
752
- value: to_node(value[key], orig)
753
- }));
754
- }
755
- return make_node(AST_Object, orig, {
756
- properties: props
757
- });
758
- }
759
- return make_node_from_constant(value, orig);
760
- }
761
-
762
- AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
763
- if (!compressor.option("global_defs")) return this;
764
- this.figure_out_scope({ ie8: compressor.option("ie8") });
765
- return this.transform(new TreeTransformer(function(node) {
766
- var def = node._find_defs(compressor, "");
767
- if (!def) return;
768
- var level = 0, child = node, parent;
769
- while (parent = this.parent(level++)) {
770
- if (!(parent instanceof AST_PropAccess)) break;
771
- if (parent.expression !== child) break;
772
- child = parent;
773
- }
774
- if (is_lhs(child, parent)) {
775
- return;
776
- }
777
- return def;
778
- }));
779
- });
780
- def_find_defs(AST_Node, noop);
781
- def_find_defs(AST_Chain, function(compressor, suffix) {
782
- return this.expression._find_defs(compressor, suffix);
783
- });
784
- def_find_defs(AST_Dot, function(compressor, suffix) {
785
- return this.expression._find_defs(compressor, "." + this.property + suffix);
786
- });
787
- def_find_defs(AST_SymbolDeclaration, function() {
788
- if (!this.global()) return;
789
- });
790
- def_find_defs(AST_SymbolRef, function(compressor, suffix) {
791
- if (!this.global()) return;
792
- var defines = compressor.option("global_defs");
793
- var name = this.name + suffix;
794
- if (HOP(defines, name)) return to_node(defines[name], this);
795
- });
796
- def_find_defs(AST_ImportMeta, function(compressor, suffix) {
797
- var defines = compressor.option("global_defs");
798
- var name = "import.meta" + suffix;
799
- if (HOP(defines, name)) return to_node(defines[name], this);
800
- });
801
- })(function(node, func) {
802
- node.DEFMETHOD("_find_defs", func);
803
- });
804
-
805
727
  // method to negate an expression
806
728
  (function(def_negate) {
807
729
  function basic_negation(exp) {
@@ -290,8 +290,9 @@ export function inline_into_symbolref(self, compressor) {
290
290
  return self;
291
291
  }
292
292
 
293
- export function inline_into_call(self, fn, compressor) {
293
+ export function inline_into_call(self, compressor) {
294
294
  var exp = self.expression;
295
+ var fn = exp;
295
296
  var simple_args = self.args.every((arg) => !(arg instanceof AST_Expansion));
296
297
 
297
298
  if (compressor.option("reduce_vars")
@@ -299,9 +300,15 @@ export function inline_into_call(self, fn, compressor) {
299
300
  && !has_annotation(self, _NOINLINE)
300
301
  ) {
301
302
  const fixed = fn.fixed_value();
302
- if (!retain_top_func(fixed, compressor)) {
303
- fn = fixed;
303
+
304
+ if (
305
+ retain_top_func(fixed, compressor)
306
+ || !compressor.toplevel.funcs && exp.definition().global
307
+ ) {
308
+ return self;
304
309
  }
310
+
311
+ fn = fixed;
305
312
  }
306
313
 
307
314
  var is_func = fn instanceof AST_Lambda;
package/lib/output.js CHANGED
@@ -980,7 +980,8 @@ function OutputStream(options) {
980
980
  ) {
981
981
  return true;
982
982
  }
983
- return p instanceof AST_PropAccess && p.expression === this;
983
+ return p instanceof AST_PropAccess && p.expression === this
984
+ || p instanceof AST_Conditional && p.condition === this;
984
985
  });
985
986
 
986
987
  // same goes for an object literal (as in AST_Function), because
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.20.0",
7
+ "version": "5.22.0",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },