terser 5.6.0-beta → 5.7.1

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/lib/ast.js CHANGED
@@ -214,8 +214,6 @@ function walk_body(node, visitor) {
214
214
  function clone_block_scope(deep) {
215
215
  var clone = this._clone(deep);
216
216
  if (this.block_scope) {
217
- // TODO this is sometimes undefined during compression.
218
- // But it should always have a value!
219
217
  clone.block_scope = this.block_scope.clone();
220
218
  }
221
219
  return clone;
@@ -399,7 +397,6 @@ var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent
399
397
  $documentation: "Base class for all statements introducing a lexical scope",
400
398
  $propdoc: {
401
399
  variables: "[Map/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
402
- functions: "[Map/S] like `variables`, but only lists function declarations",
403
400
  uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
404
401
  uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
405
402
  parent_scope: "[AST_Scope?/S] link to the parent scope",
@@ -422,7 +419,6 @@ var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent
422
419
  });
423
420
  } else {
424
421
  if (this.variables) node.variables = new Map(this.variables);
425
- if (this.functions) node.functions = new Map(this.functions);
426
422
  if (this.enclosed) node.enclosed = this.enclosed.slice();
427
423
  if (this._block_scope) node._block_scope = this._block_scope;
428
424
  }
@@ -522,6 +518,21 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator as
522
518
 
523
519
  if (this.name) push(this.name);
524
520
  },
521
+ is_braceless() {
522
+ return this.body[0] instanceof AST_Return && this.body[0].value;
523
+ },
524
+ // Default args and expansion don't count, so .argnames.length doesn't cut it
525
+ length_property() {
526
+ let length = 0;
527
+
528
+ for (const arg of this.argnames) {
529
+ if (arg instanceof AST_SymbolFunarg || arg instanceof AST_Destructuring) {
530
+ length++;
531
+ }
532
+ }
533
+
534
+ return length;
535
+ }
525
536
  }, AST_Scope);
526
537
 
527
538
  var AST_Accessor = DEFNODE("Accessor", null, {
package/lib/cli.js CHANGED
@@ -264,7 +264,6 @@ export async function run_cli({ program, packageJson, fs, path }) {
264
264
  case "enclosed":
265
265
  return value.length ? value.map(symdef) : undefined;
266
266
  case "variables":
267
- case "functions":
268
267
  case "globals":
269
268
  return value.size ? collect_from_map(value, symdef) : undefined;
270
269
  }
@@ -277,7 +276,6 @@ export async function run_cli({ program, packageJson, fs, path }) {
277
276
  };
278
277
  if (value.block_scope) {
279
278
  result.variables = value.block_scope.variables;
280
- result.functions = value.block_scope.functions;
281
279
  result.enclosed = value.block_scope.enclosed;
282
280
  }
283
281
  value.CTOR.PROPS.forEach(function(prop) {
@@ -209,11 +209,8 @@ const TOP = 0b0000010000000000;
209
209
 
210
210
  const CLEAR_BETWEEN_PASSES = SQUEEZED | OPTIMIZED | TOP;
211
211
 
212
- /*@__INLINE__*/
213
212
  const has_flag = (node, flag) => node.flags & flag;
214
- /*@__INLINE__*/
215
213
  const set_flag = (node, flag) => { node.flags |= flag; };
216
- /*@__INLINE__*/
217
214
  const clear_flag = (node, flag) => { node.flags &= ~flag; };
218
215
 
219
216
  class Compressor extends TreeWalker {
@@ -256,7 +253,7 @@ class Compressor extends TreeWalker {
256
253
  properties : !false_by_default,
257
254
  pure_getters : !false_by_default && "strict",
258
255
  pure_funcs : null,
259
- reduce_funcs : null, // legacy
256
+ reduce_funcs : !false_by_default,
260
257
  reduce_vars : !false_by_default,
261
258
  sequences : !false_by_default,
262
259
  side_effects : !false_by_default,
@@ -556,7 +553,7 @@ function is_modified(compressor, tw, node, value, level, immutable) {
556
553
  && parent.expression === node
557
554
  && !(value instanceof AST_Arrow)
558
555
  && !(value instanceof AST_Class)
559
- && !parent.is_expr_pure(compressor)
556
+ && !parent.is_callee_pure(compressor)
560
557
  && (!(value instanceof AST_Function)
561
558
  || !(parent instanceof AST_New) && value.contains_this())) {
562
559
  return true;
@@ -861,9 +858,6 @@ function is_modified(compressor, tw, node, value, level, immutable) {
861
858
  return true;
862
859
  });
863
860
  def_reduce_vars(AST_Call, function (tw) {
864
- // TODO this block should just be { return } but
865
- // for some reason the _walk function of AST_Call walks the callee last
866
-
867
861
  this.expression.walk(tw);
868
862
 
869
863
  if (this.optional) {
@@ -1416,8 +1410,11 @@ function tighten_body(statements, compressor) {
1416
1410
  || node instanceof AST_Debugger
1417
1411
  || node instanceof AST_Destructuring
1418
1412
  || node instanceof AST_Expansion
1419
- && node.expression instanceof AST_Symbol
1420
- && node.expression.definition().references.length > 1
1413
+ && node.expression instanceof AST_Symbol
1414
+ && (
1415
+ node.expression instanceof AST_This
1416
+ || node.expression.definition().references.length > 1
1417
+ )
1421
1418
  || node instanceof AST_IterationStatement && !(node instanceof AST_For)
1422
1419
  || node instanceof AST_LoopControl
1423
1420
  || node instanceof AST_Try
@@ -1511,6 +1508,7 @@ function tighten_body(statements, compressor) {
1511
1508
  if (stop_after === node) abort = true;
1512
1509
  if (stop_if_hit === node) stop_if_hit = null;
1513
1510
  });
1511
+
1514
1512
  var multi_replacer = new TreeTransformer(function(node) {
1515
1513
  if (abort) return node;
1516
1514
  // Skip nodes before `candidate` as quickly as possible
@@ -1533,6 +1531,7 @@ function tighten_body(statements, compressor) {
1533
1531
  // Skip (non-executed) functions and (leading) default case in switch statements
1534
1532
  if (node instanceof AST_Default || node instanceof AST_Scope) return node;
1535
1533
  });
1534
+
1536
1535
  while (--stat_index >= 0) {
1537
1536
  // Treat parameters as collapsible in IIFE, i.e.
1538
1537
  // function(a, b){ ... }(x());
@@ -1708,7 +1707,10 @@ function tighten_body(statements, compressor) {
1708
1707
  function extract_candidates(expr) {
1709
1708
  hit_stack.push(expr);
1710
1709
  if (expr instanceof AST_Assign) {
1711
- if (!expr.left.has_side_effects(compressor)) {
1710
+ if (
1711
+ !expr.left.has_side_effects(compressor)
1712
+ && !(expr.right instanceof AST_Chain)
1713
+ ) {
1712
1714
  candidates.push(hit_stack.slice());
1713
1715
  }
1714
1716
  extract_candidates(expr.right);
@@ -1771,7 +1773,7 @@ function tighten_body(statements, compressor) {
1771
1773
  candidates.push(hit_stack.slice());
1772
1774
  }
1773
1775
  } else if (expr instanceof AST_VarDef) {
1774
- if (expr.value) {
1776
+ if (expr.value && !(expr.value instanceof AST_Chain)) {
1775
1777
  candidates.push(hit_stack.slice());
1776
1778
  extract_candidates(expr.value);
1777
1779
  }
@@ -2586,7 +2588,13 @@ function is_undefined(node, compressor) {
2586
2588
  });
2587
2589
  def_may_throw_on_access(AST_Dot, function(compressor) {
2588
2590
  if (!is_strict(compressor)) return false;
2589
- if (this.expression instanceof AST_Function && this.property == "prototype") return false;
2591
+
2592
+ if (this.property == "prototype") {
2593
+ return !(
2594
+ this.expression instanceof AST_Function
2595
+ || this.expression instanceof AST_Class
2596
+ );
2597
+ }
2590
2598
  return true;
2591
2599
  });
2592
2600
  def_may_throw_on_access(AST_Chain, function(compressor) {
@@ -2928,9 +2936,7 @@ var static_fns = convert_to_predicate({
2928
2936
  if (compressor.option("unsafe")) {
2929
2937
  var fn = function() {};
2930
2938
  fn.node = this;
2931
- fn.toString = function() {
2932
- return this.node.print_to_string();
2933
- };
2939
+ fn.toString = () => this.print_to_string();
2934
2940
  return fn;
2935
2941
  }
2936
2942
  return this;
@@ -3111,6 +3117,14 @@ var static_fns = convert_to_predicate({
3111
3117
  "POSITIVE_INFINITY",
3112
3118
  ],
3113
3119
  });
3120
+ const regexp_flags = new Set([
3121
+ "dotAll",
3122
+ "global",
3123
+ "ignoreCase",
3124
+ "multiline",
3125
+ "sticky",
3126
+ "unicode",
3127
+ ]);
3114
3128
  def_eval(AST_PropAccess, function(compressor, depth) {
3115
3129
  if (this.optional) {
3116
3130
  const obj = this.expression._eval(compressor, depth);
@@ -3143,12 +3157,19 @@ var static_fns = convert_to_predicate({
3143
3157
  val = global_objs[exp.name];
3144
3158
  } else {
3145
3159
  val = exp._eval(compressor, depth + 1);
3160
+ if (val instanceof RegExp) {
3161
+ if (key == "source") {
3162
+ return regexp_source_fix(val.source);
3163
+ } else if (key == "flags" || regexp_flags.has(key)) {
3164
+ return val[key];
3165
+ }
3166
+ }
3146
3167
  if (!val || val === exp || !HOP(val, key)) return this;
3147
3168
  if (typeof val == "function") switch (key) {
3148
3169
  case "name":
3149
3170
  return val.node.name ? val.node.name.name : "";
3150
3171
  case "length":
3151
- return val.node.argnames.length;
3172
+ return val.node.length_property();
3152
3173
  default:
3153
3174
  return this;
3154
3175
  }
@@ -3200,6 +3221,7 @@ var static_fns = convert_to_predicate({
3200
3221
  var arg = this.args[i];
3201
3222
  var value = arg._eval(compressor, depth);
3202
3223
  if (arg === value) return this;
3224
+ if (arg instanceof AST_Lambda) return this;
3203
3225
  args.push(value);
3204
3226
  }
3205
3227
  try {
@@ -3299,7 +3321,7 @@ var static_fns = convert_to_predicate({
3299
3321
  });
3300
3322
 
3301
3323
  var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError");
3302
- AST_Call.DEFMETHOD("is_expr_pure", function(compressor) {
3324
+ AST_Call.DEFMETHOD("is_callee_pure", function(compressor) {
3303
3325
  if (compressor.option("unsafe")) {
3304
3326
  var expr = this.expression;
3305
3327
  var first_arg = (this.args && this.args[0] && this.args[0].evaluate(compressor));
@@ -3369,7 +3391,7 @@ const pure_prop_access_globals = new Set([
3369
3391
  });
3370
3392
  def_has_side_effects(AST_Call, function(compressor) {
3371
3393
  if (
3372
- !this.is_expr_pure(compressor)
3394
+ !this.is_callee_pure(compressor)
3373
3395
  && (!this.expression.is_call_pure(compressor)
3374
3396
  || this.expression.has_side_effects(compressor))
3375
3397
  ) {
@@ -3459,7 +3481,7 @@ const pure_prop_access_globals = new Set([
3459
3481
  || this.expression.has_side_effects(compressor);
3460
3482
  });
3461
3483
  def_has_side_effects(AST_Sub, function(compressor) {
3462
- if (this.optional && is_nullish(this.expression)) {
3484
+ if (this.optional && is_nullish(this.expression, compressor)) {
3463
3485
  return false;
3464
3486
  }
3465
3487
 
@@ -3529,9 +3551,9 @@ const pure_prop_access_globals = new Set([
3529
3551
  return any(this.body, compressor);
3530
3552
  });
3531
3553
  def_may_throw(AST_Call, function(compressor) {
3532
- if (this.optional && is_nullish(this.expression)) return false;
3554
+ if (this.optional && is_nullish(this.expression, compressor)) return false;
3533
3555
  if (any(this.args, compressor)) return true;
3534
- if (this.is_expr_pure(compressor)) return false;
3556
+ if (this.is_callee_pure(compressor)) return false;
3535
3557
  if (this.expression.may_throw(compressor)) return true;
3536
3558
  return !(this.expression instanceof AST_Lambda)
3537
3559
  || any(this.expression.body, compressor);
@@ -3592,7 +3614,7 @@ const pure_prop_access_globals = new Set([
3592
3614
  || this.expression.may_throw(compressor);
3593
3615
  });
3594
3616
  def_may_throw(AST_Sub, function(compressor) {
3595
- if (this.optional && is_nullish(this.expression)) return false;
3617
+ if (this.optional && is_nullish(this.expression, compressor)) return false;
3596
3618
 
3597
3619
  return !this.optional && this.expression.may_throw_on_access(compressor)
3598
3620
  || this.expression.may_throw(compressor)
@@ -4405,11 +4427,11 @@ AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
4405
4427
  def_drop_side_effect_free(AST_Constant, return_null);
4406
4428
  def_drop_side_effect_free(AST_This, return_null);
4407
4429
  def_drop_side_effect_free(AST_Call, function(compressor, first_in_statement) {
4408
- if (this.optional && is_nullish(this.expression)) {
4430
+ if (this.optional && is_nullish(this.expression, compressor)) {
4409
4431
  return make_node(AST_Undefined, this);
4410
4432
  }
4411
4433
 
4412
- if (!this.is_expr_pure(compressor)) {
4434
+ if (!this.is_callee_pure(compressor)) {
4413
4435
  if (this.expression.is_call_pure(compressor)) {
4414
4436
  var exprs = this.args.slice();
4415
4437
  exprs.unshift(this.expression.expression);
@@ -4552,7 +4574,7 @@ AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
4552
4574
  });
4553
4575
  def_drop_side_effect_free(AST_Dot, function(compressor, first_in_statement) {
4554
4576
  if (this.optional) {
4555
- return is_nullish(this.expression) ? make_node(AST_Undefined, this) : this;
4577
+ return is_nullish(this.expression, compressor) ? make_node(AST_Undefined, this) : this;
4556
4578
  }
4557
4579
  if (this.expression.may_throw_on_access(compressor)) return this;
4558
4580
 
@@ -4560,7 +4582,7 @@ AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
4560
4582
  });
4561
4583
  def_drop_side_effect_free(AST_Sub, function(compressor, first_in_statement) {
4562
4584
  if (this.optional) {
4563
- return is_nullish(this.expression) ? make_node(AST_Undefined, this): this;
4585
+ return is_nullish(this.expression, compressor) ? make_node(AST_Undefined, this): this;
4564
4586
  }
4565
4587
  if (this.expression.may_throw_on_access(compressor)) return this;
4566
4588
 
@@ -5085,11 +5107,11 @@ def_optimize(AST_Definitions, function(self) {
5085
5107
  return self;
5086
5108
  });
5087
5109
 
5088
- def_optimize(AST_VarDef, function(self) {
5110
+ def_optimize(AST_VarDef, function(self, compressor) {
5089
5111
  if (
5090
5112
  self.name instanceof AST_SymbolLet
5091
5113
  && self.value != null
5092
- && is_undefined(self.value)
5114
+ && is_undefined(self.value, compressor)
5093
5115
  ) {
5094
5116
  self.value = null;
5095
5117
  }
@@ -5127,7 +5149,7 @@ def_optimize(AST_Call, function(self, compressor) {
5127
5149
  }
5128
5150
  }
5129
5151
 
5130
- if (self.optional && is_nullish(fn)) {
5152
+ if (self.optional && is_nullish(fn, compressor)) {
5131
5153
  return make_node(AST_Undefined, self);
5132
5154
  }
5133
5155
 
@@ -5429,7 +5451,7 @@ def_optimize(AST_Call, function(self, compressor) {
5429
5451
 
5430
5452
  var stat = is_func && fn.body[0];
5431
5453
  var is_regular_func = is_func && !fn.is_generator && !fn.async;
5432
- var can_inline = is_regular_func && compressor.option("inline") && !self.is_expr_pure(compressor);
5454
+ var can_inline = is_regular_func && compressor.option("inline") && !self.is_callee_pure(compressor);
5433
5455
  if (can_inline && stat instanceof AST_Return) {
5434
5456
  let returned = stat.value;
5435
5457
  if (!returned || returned.is_constant_expression()) {
@@ -6153,7 +6175,7 @@ def_optimize(AST_Binary, function(self, compressor) {
6153
6175
  }
6154
6176
  break;
6155
6177
  case "??":
6156
- if (is_nullish(self.left)) {
6178
+ if (is_nullish(self.left, compressor)) {
6157
6179
  return self.right;
6158
6180
  }
6159
6181
 
@@ -6455,19 +6477,26 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
6455
6477
  let fixed = self.fixed_value();
6456
6478
  let single_use = def.single_use
6457
6479
  && !(parent instanceof AST_Call
6458
- && (parent.is_expr_pure(compressor))
6480
+ && (parent.is_callee_pure(compressor))
6459
6481
  || has_annotation(parent, _NOINLINE))
6460
6482
  && !(parent instanceof AST_Export
6461
6483
  && fixed instanceof AST_Lambda
6462
6484
  && fixed.name);
6463
6485
 
6486
+ if (single_use && fixed instanceof AST_Node) {
6487
+ single_use =
6488
+ !fixed.has_side_effects(compressor)
6489
+ && !fixed.may_throw(compressor);
6490
+ }
6491
+
6464
6492
  if (single_use && (fixed instanceof AST_Lambda || fixed instanceof AST_Class)) {
6465
6493
  if (retain_top_func(fixed, compressor)) {
6466
6494
  single_use = false;
6467
6495
  } else if (def.scope !== self.scope
6468
6496
  && (def.escaped == 1
6469
6497
  || has_flag(fixed, INLINED)
6470
- || within_array_or_object_literal(compressor))) {
6498
+ || within_array_or_object_literal(compressor)
6499
+ || !compressor.option("reduce_funcs"))) {
6471
6500
  single_use = false;
6472
6501
  } else if (recursive_ref(compressor, def)) {
6473
6502
  single_use = false;
@@ -6483,6 +6512,7 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
6483
6512
  }
6484
6513
  }
6485
6514
  }
6515
+
6486
6516
  if (single_use && fixed instanceof AST_Lambda) {
6487
6517
  single_use =
6488
6518
  def.scope === self.scope
@@ -6492,15 +6522,6 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
6492
6522
  && !scope_encloses_variables_in_this_scope(nearest_scope, fixed)
6493
6523
  && !(fixed.name && fixed.name.definition().recursive_refs > 0);
6494
6524
  }
6495
- if (single_use && fixed instanceof AST_Class) {
6496
- const extends_inert = !fixed.extends
6497
- || !fixed.extends.may_throw(compressor)
6498
- && !fixed.extends.has_side_effects(compressor);
6499
- single_use = extends_inert
6500
- && !fixed.properties.some(prop =>
6501
- prop.may_throw(compressor) || prop.has_side_effects(compressor)
6502
- );
6503
- }
6504
6525
 
6505
6526
  if (single_use && fixed) {
6506
6527
  if (fixed instanceof AST_DefClass) {
@@ -6577,6 +6598,7 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
6577
6598
  }
6578
6599
  }
6579
6600
  }
6601
+
6580
6602
  return self;
6581
6603
  });
6582
6604
 
@@ -6761,20 +6783,20 @@ def_optimize(AST_DefaultAssign, function(self, compressor) {
6761
6783
  return self;
6762
6784
  });
6763
6785
 
6764
- function is_nullish(node) {
6786
+ function is_nullish(node, compressor) {
6765
6787
  let fixed;
6766
6788
  return (
6767
6789
  node instanceof AST_Null
6768
- || is_undefined(node)
6790
+ || is_undefined(node, compressor)
6769
6791
  || (
6770
6792
  node instanceof AST_SymbolRef
6771
6793
  && (fixed = node.definition().fixed) instanceof AST_Node
6772
- && is_nullish(fixed)
6794
+ && is_nullish(fixed, compressor)
6773
6795
  )
6774
6796
  // Recurse into those optional chains!
6775
- || node instanceof AST_PropAccess && node.optional && is_nullish(node.expression)
6776
- || node instanceof AST_Call && node.optional && is_nullish(node.expression)
6777
- || node instanceof AST_Chain && is_nullish(node.expression)
6797
+ || node instanceof AST_PropAccess && node.optional && is_nullish(node.expression, compressor)
6798
+ || node instanceof AST_Call && node.optional && is_nullish(node.expression, compressor)
6799
+ || node instanceof AST_Chain && is_nullish(node.expression, compressor)
6778
6800
  );
6779
6801
  }
6780
6802
 
@@ -6789,8 +6811,8 @@ function is_nullish_check(check, check_subject, compressor) {
6789
6811
  && check.operator === "=="
6790
6812
  // which side is nullish?
6791
6813
  && (
6792
- (nullish_side = is_nullish(check.left) && check.left)
6793
- || (nullish_side = is_nullish(check.right) && check.right)
6814
+ (nullish_side = is_nullish(check.left, compressor) && check.left)
6815
+ || (nullish_side = is_nullish(check.right, compressor) && check.right)
6794
6816
  )
6795
6817
  // is the other side the same as the check_subject
6796
6818
  && (
@@ -6828,12 +6850,12 @@ function is_nullish_check(check, check_subject, compressor) {
6828
6850
  null_cmp = cmp;
6829
6851
  defined_side = cmp.left;
6830
6852
  }
6831
- if (is_undefined(cmp.left)) {
6853
+ if (is_undefined(cmp.left, compressor)) {
6832
6854
  found++;
6833
6855
  undefined_cmp = cmp;
6834
6856
  defined_side = cmp.right;
6835
6857
  }
6836
- if (is_undefined(cmp.right)) {
6858
+ if (is_undefined(cmp.right, compressor)) {
6837
6859
  found++;
6838
6860
  undefined_cmp = cmp;
6839
6861
  defined_side = cmp.left;
@@ -7163,27 +7185,40 @@ function safe_to_flatten(value, compressor) {
7163
7185
 
7164
7186
  AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
7165
7187
  if (!compressor.option("properties")) return;
7188
+ if (key === "__proto__") return;
7189
+
7166
7190
  var arrows = compressor.option("unsafe_arrows") && compressor.option("ecma") >= 2015;
7167
7191
  var expr = this.expression;
7168
7192
  if (expr instanceof AST_Object) {
7169
7193
  var props = expr.properties;
7194
+
7170
7195
  for (var i = props.length; --i >= 0;) {
7171
7196
  var prop = props[i];
7197
+
7172
7198
  if ("" + (prop instanceof AST_ConciseMethod ? prop.key.name : prop.key) == key) {
7173
- if (!props.every((prop) => {
7174
- return prop instanceof AST_ObjectKeyVal
7175
- || arrows && prop instanceof AST_ConciseMethod && !prop.is_generator;
7176
- })) break;
7177
- if (!safe_to_flatten(prop.value, compressor)) break;
7199
+ const all_props_flattenable = props.every((p) =>
7200
+ (p instanceof AST_ObjectKeyVal
7201
+ || arrows && p instanceof AST_ConciseMethod && !p.is_generator
7202
+ )
7203
+ && !p.computed_key()
7204
+ );
7205
+
7206
+ if (!all_props_flattenable) return;
7207
+ if (!safe_to_flatten(prop.value, compressor)) return;
7208
+
7178
7209
  return make_node(AST_Sub, this, {
7179
7210
  expression: make_node(AST_Array, expr, {
7180
7211
  elements: props.map(function(prop) {
7181
7212
  var v = prop.value;
7182
- if (v instanceof AST_Accessor) v = make_node(AST_Function, v, v);
7213
+ if (v instanceof AST_Accessor) {
7214
+ v = make_node(AST_Function, v, v);
7215
+ }
7216
+
7183
7217
  var k = prop.key;
7184
7218
  if (k instanceof AST_Node && !(k instanceof AST_SymbolMethod)) {
7185
7219
  return make_sequence(prop, [ k, v ]);
7186
7220
  }
7221
+
7187
7222
  return v;
7188
7223
  })
7189
7224
  }),
@@ -7321,7 +7356,7 @@ def_optimize(AST_Sub, function(self, compressor) {
7321
7356
  ev = make_node_from_constant(ev, self).optimize(compressor);
7322
7357
  return best_of(compressor, ev, self);
7323
7358
  }
7324
- if (self.optional && is_nullish(self.expression)) {
7359
+ if (self.optional && is_nullish(self.expression, compressor)) {
7325
7360
  return make_node(AST_Undefined, self);
7326
7361
  }
7327
7362
  return self;
@@ -7395,7 +7430,7 @@ def_optimize(AST_Dot, function(self, compressor) {
7395
7430
  ev = make_node_from_constant(ev, self).optimize(compressor);
7396
7431
  return best_of(compressor, ev, self);
7397
7432
  }
7398
- if (self.optional && is_nullish(self.expression)) {
7433
+ if (self.optional && is_nullish(self.expression, compressor)) {
7399
7434
  return make_node(AST_Undefined, self);
7400
7435
  }
7401
7436
  return self;
@@ -7439,7 +7474,7 @@ def_optimize(AST_Array, function(self, compressor) {
7439
7474
  return self;
7440
7475
  });
7441
7476
 
7442
- function inline_object_prop_spread(props) {
7477
+ function inline_object_prop_spread(props, compressor) {
7443
7478
  for (var i = 0; i < props.length; i++) {
7444
7479
  var prop = props[i];
7445
7480
  if (prop instanceof AST_Expansion) {
@@ -7457,6 +7492,9 @@ function inline_object_prop_spread(props) {
7457
7492
  // non-iterable value silently does nothing; it is thus safe
7458
7493
  // to remove. AST_String is the only iterable AST_Constant.
7459
7494
  props.splice(i, 1);
7495
+ } else if (is_nullish(expr, compressor)) {
7496
+ // Likewise, null and undefined can be silently removed.
7497
+ props.splice(i, 1);
7460
7498
  }
7461
7499
  }
7462
7500
  }
@@ -7467,7 +7505,7 @@ def_optimize(AST_Object, function(self, compressor) {
7467
7505
  if (optimized !== self) {
7468
7506
  return optimized;
7469
7507
  }
7470
- inline_object_prop_spread(self.properties);
7508
+ inline_object_prop_spread(self.properties, compressor);
7471
7509
  return self;
7472
7510
  });
7473
7511
 
@@ -7560,7 +7598,7 @@ def_optimize(AST_TemplateString, function(self, compressor) {
7560
7598
  && (
7561
7599
  segments[1].is_string(compressor)
7562
7600
  || segments[1].is_number(compressor)
7563
- || is_nullish(segments[1])
7601
+ || is_nullish(segments[1], compressor)
7564
7602
  || compressor.option("unsafe")
7565
7603
  )
7566
7604
  ) {
@@ -18,6 +18,7 @@ import {
18
18
  AST_Directive,
19
19
  AST_Do,
20
20
  AST_Dot,
21
+ AST_DotHash,
21
22
  AST_EmptyStatement,
22
23
  AST_Expansion,
23
24
  AST_Export,
@@ -233,6 +234,10 @@ AST_Dot.prototype.shallow_cmp = mkshallow({
233
234
  property: "eq"
234
235
  });
235
236
 
237
+ AST_DotHash.prototype.shallow_cmp = mkshallow({
238
+ property: "eq"
239
+ });
240
+
236
241
  AST_Unary.prototype.shallow_cmp = mkshallow({
237
242
  operator: "eq"
238
243
  });
package/lib/minify.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  map_to_object,
8
8
  HOP,
9
9
  } from "./utils/index.js";
10
- import { AST_Toplevel } from "./ast.js";
10
+ import { AST_Toplevel, AST_Node } from "./ast.js";
11
11
  import { parse } from "./parse.js";
12
12
  import { OutputStream } from "./output.js";
13
13
  import { Compressor } from "./compress/index.js";
@@ -77,6 +77,7 @@ async function minify(files, options) {
77
77
  rename: undefined,
78
78
  safari10: false,
79
79
  sourceMap: false,
80
+ spidermonkey: false,
80
81
  timings: false,
81
82
  toplevel: false,
82
83
  warnings: false,
@@ -148,20 +149,32 @@ async function minify(files, options) {
148
149
  if (files instanceof AST_Toplevel) {
149
150
  toplevel = files;
150
151
  } else {
151
- if (typeof files == "string") {
152
+ if (typeof files == "string" || (options.parse.spidermonkey && !Array.isArray(files))) {
152
153
  files = [ files ];
153
154
  }
154
155
  options.parse = options.parse || {};
155
156
  options.parse.toplevel = null;
156
- for (var name in files) if (HOP(files, name)) {
157
- options.parse.filename = name;
158
- options.parse.toplevel = parse(files[name], options.parse);
159
- if (options.sourceMap && options.sourceMap.content == "inline") {
160
- if (Object.keys(files).length > 1)
161
- throw new Error("inline source map only works with singular input");
162
- options.sourceMap.content = read_source_map(files[name]);
157
+
158
+ if (options.parse.spidermonkey) {
159
+ options.parse.toplevel = AST_Node.from_mozilla_ast(Object.keys(files).reduce(function(toplevel, name) {
160
+ if (!toplevel) return files[name];
161
+ toplevel.body = toplevel.body.concat(files[name].body);
162
+ return toplevel;
163
+ }, null));
164
+ } else {
165
+ delete options.parse.spidermonkey;
166
+
167
+ for (var name in files) if (HOP(files, name)) {
168
+ options.parse.filename = name;
169
+ options.parse.toplevel = parse(files[name], options.parse);
170
+ if (options.sourceMap && options.sourceMap.content == "inline") {
171
+ if (Object.keys(files).length > 1)
172
+ throw new Error("inline source map only works with singular input");
173
+ options.sourceMap.content = read_source_map(files[name]);
174
+ }
163
175
  }
164
176
  }
177
+
165
178
  toplevel = options.parse.toplevel;
166
179
  }
167
180
  if (quoted_props && options.mangle.properties.keep_quoted !== "strict") {
@@ -203,6 +216,9 @@ async function minify(files, options) {
203
216
  if (options.format.ast) {
204
217
  result.ast = toplevel;
205
218
  }
219
+ if (options.format.spidermonkey) {
220
+ result.ast = toplevel.to_mozilla_ast();
221
+ }
206
222
  if (!HOP(options.format, "code") || options.format.code) {
207
223
  if (options.sourceMap) {
208
224
  options.format.source_map = await SourceMap({
@@ -220,6 +236,7 @@ async function minify(files, options) {
220
236
  }
221
237
  delete options.format.ast;
222
238
  delete options.format.code;
239
+ delete options.format.spidermonkey;
223
240
  var stream = OutputStream(options.format);
224
241
  toplevel.print(stream);
225
242
  result.code = stream.get();
package/lib/output.js CHANGED
@@ -349,11 +349,15 @@ function OutputStream(options) {
349
349
  var do_add_mapping = mappings ? function() {
350
350
  mappings.forEach(function(mapping) {
351
351
  try {
352
+ let name = !mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name;
353
+ if (name instanceof AST_Symbol) {
354
+ name = name.name;
355
+ }
352
356
  options.source_map.add(
353
357
  mapping.token.file,
354
358
  mapping.line, mapping.col,
355
359
  mapping.token.line, mapping.token.col,
356
- !mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name
360
+ is_basic_identifier_string(name) ? name : undefined
357
361
  );
358
362
  } catch(ex) {
359
363
  // Ignore bad mapping
@@ -933,6 +937,7 @@ function OutputStream(options) {
933
937
  var p = output.parent();
934
938
  return p instanceof AST_PropAccess && p.expression === this
935
939
  || p instanceof AST_Call && p.expression === this
940
+ || p instanceof AST_Binary && p.operator === "**" && p.left === this
936
941
  || output.option("safari10") && p instanceof AST_UnaryPrefix;
937
942
  });
938
943
 
@@ -2150,7 +2155,9 @@ function OutputStream(options) {
2150
2155
  source = regexp_source_fix(source);
2151
2156
  flags = flags ? sort_regexp_flags(flags) : "";
2152
2157
  source = source.replace(r_slash_script, slash_script_replace);
2158
+
2153
2159
  output.print(output.to_utf8(`/${source}/${flags}`));
2160
+
2154
2161
  const parent = output.parent();
2155
2162
  if (
2156
2163
  parent instanceof AST_Binary