terser 5.0.0-beta.2 → 5.2.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.
@@ -78,6 +78,7 @@ import {
78
78
  AST_Call,
79
79
  AST_Case,
80
80
  AST_Catch,
81
+ AST_Chain,
81
82
  AST_Class,
82
83
  AST_ClassExpression,
83
84
  AST_ClassProperty,
@@ -216,7 +217,7 @@ const set_flag = (node, flag) => { node.flags |= flag; };
216
217
  const clear_flag = (node, flag) => { node.flags &= ~flag; };
217
218
 
218
219
  class Compressor extends TreeWalker {
219
- constructor(options, false_by_default) {
220
+ constructor(options, { false_by_default = false, mangle_options = false }) {
220
221
  super();
221
222
  if (options.defaults !== undefined && !options.defaults) false_by_default = true;
222
223
  this.options = defaults(options, {
@@ -274,7 +275,7 @@ class Compressor extends TreeWalker {
274
275
  unsafe_regexp : false,
275
276
  unsafe_undefined: false,
276
277
  unused : !false_by_default,
277
- warnings : false, // legacy
278
+ warnings : false // legacy
278
279
  }, true);
279
280
  var global_defs = this.options["global_defs"];
280
281
  if (typeof global_defs == "object") for (var key in global_defs) {
@@ -324,6 +325,7 @@ class Compressor extends TreeWalker {
324
325
  this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
325
326
  this.evaluated_regexps = new Map();
326
327
  this._toplevel = undefined;
328
+ this.mangle_options = mangle_options;
327
329
  }
328
330
 
329
331
  option(key) {
@@ -792,13 +794,53 @@ function is_modified(compressor, tw, node, value, level, immutable) {
792
794
  pop(tw);
793
795
  return true;
794
796
  });
797
+
798
+ def_reduce_vars(AST_Chain, function(tw, descend) {
799
+ // Chains' conditions apply left-to-right, cumulatively.
800
+ // If we walk normally we don't go in that order because we would pop before pushing again
801
+ // Solution: AST_PropAccess and AST_Call push when they are optional, and never pop.
802
+ // Then we pop everything when they are done being walked.
803
+ const safe_ids = tw.safe_ids;
804
+
805
+ descend();
806
+
807
+ // Unroll back to start
808
+ tw.safe_ids = safe_ids;
809
+ return true;
810
+ });
811
+ def_reduce_vars(AST_Call, function (tw) {
812
+ // TODO this block should just be { return } but
813
+ // for some reason the _walk function of AST_Call walks the callee last
814
+
815
+ this.expression.walk(tw);
816
+
817
+ if (this.optional) {
818
+ // Never pop -- it's popped at AST_Chain above
819
+ push(tw);
820
+ }
821
+
822
+ for (const arg of this.args) arg.walk(tw);
823
+
824
+ return true;
825
+ });
826
+ def_reduce_vars(AST_PropAccess, function (tw) {
827
+ if (!this.optional) return;
828
+
829
+ this.expression.walk(tw);
830
+
831
+ // Never pop -- it's popped at AST_Chain above
832
+ push(tw);
833
+
834
+ if (this.property instanceof AST_Node) this.property.walk(tw);
835
+
836
+ return true;
837
+ });
795
838
  def_reduce_vars(AST_Default, function(tw, descend) {
796
839
  push(tw);
797
840
  descend();
798
841
  pop(tw);
799
842
  return true;
800
843
  });
801
-
802
844
  function mark_lambda(tw, descend, compressor) {
803
845
  clear_flag(this, INLINED);
804
846
  push(tw);
@@ -1628,8 +1670,7 @@ function tighten_body(statements, compressor) {
1628
1670
  extract_candidates(expr.condition);
1629
1671
  extract_candidates(expr.consequent);
1630
1672
  extract_candidates(expr.alternative);
1631
- } else if (expr instanceof AST_Definitions
1632
- && (compressor.option("unused") || !(expr instanceof AST_Const))) {
1673
+ } else if (expr instanceof AST_Definitions) {
1633
1674
  var len = expr.definitions.length;
1634
1675
  // limit number of trailing variable definitions for consideration
1635
1676
  var i = len - 200;
@@ -1797,7 +1838,9 @@ function tighten_body(statements, compressor) {
1797
1838
  if (node === expr || node.body === expr) {
1798
1839
  found = true;
1799
1840
  if (node instanceof AST_VarDef) {
1800
- node.value = null;
1841
+ node.value = node.name instanceof AST_SymbolConst
1842
+ ? make_node(AST_Undefined, node.value) // `const` always needs value.
1843
+ : null;
1801
1844
  return node;
1802
1845
  }
1803
1846
  return in_list ? MAP.skip : null;
@@ -2113,7 +2156,7 @@ function tighten_body(statements, compressor) {
2113
2156
  statements.length = n;
2114
2157
  CHANGED = n != len;
2115
2158
  if (has_quit) has_quit.forEach(function(stat) {
2116
- extract_declarations_from_unreachable_code(compressor, stat, statements);
2159
+ trim_unreachable_code(compressor, stat, statements);
2117
2160
  });
2118
2161
  }
2119
2162
 
@@ -2371,7 +2414,7 @@ function tighten_body(statements, compressor) {
2371
2414
  }
2372
2415
  }
2373
2416
 
2374
- function extract_declarations_from_unreachable_code(compressor, stat, target) {
2417
+ function trim_unreachable_code(compressor, stat, target) {
2375
2418
  walk(stat, node => {
2376
2419
  if (node instanceof AST_Var) {
2377
2420
  node.remove_initializers();
@@ -2392,6 +2435,10 @@ function extract_declarations_from_unreachable_code(compressor, stat, target) {
2392
2435
  }));
2393
2436
  return true;
2394
2437
  }
2438
+ if (node instanceof AST_Export || node instanceof AST_Import) {
2439
+ target.push(node);
2440
+ return true;
2441
+ }
2395
2442
  if (node instanceof AST_Scope) {
2396
2443
  return true;
2397
2444
  }
@@ -2443,6 +2490,10 @@ function is_undefined(node, compressor) {
2443
2490
  if (this.properties[i]._dot_throw(compressor)) return true;
2444
2491
  return false;
2445
2492
  });
2493
+ // Do not be as strict with classes as we are with objects.
2494
+ // Hopefully the community is not going to abuse static getters and setters.
2495
+ // https://github.com/terser/terser/issues/724#issuecomment-643655656
2496
+ def_may_throw_on_access(AST_Class, return_false);
2446
2497
  def_may_throw_on_access(AST_ObjectProperty, return_false);
2447
2498
  def_may_throw_on_access(AST_ObjectGetter, return_true);
2448
2499
  def_may_throw_on_access(AST_Expansion, function(compressor) {
@@ -2471,11 +2522,15 @@ function is_undefined(node, compressor) {
2471
2522
  if (this.expression instanceof AST_Function && this.property == "prototype") return false;
2472
2523
  return true;
2473
2524
  });
2525
+ def_may_throw_on_access(AST_Chain, function(compressor) {
2526
+ return this.expression._dot_throw(compressor);
2527
+ });
2474
2528
  def_may_throw_on_access(AST_Sequence, function(compressor) {
2475
2529
  return this.tail_node()._dot_throw(compressor);
2476
2530
  });
2477
2531
  def_may_throw_on_access(AST_SymbolRef, function(compressor) {
2478
- if (is_undefined(this, compressor)) return true;
2532
+ if (this.name === "arguments") return false;
2533
+ if (has_flag(this, UNDEFINED)) return true;
2479
2534
  if (!is_strict(compressor)) return false;
2480
2535
  if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
2481
2536
  if (this.is_immutable()) return false;
@@ -2620,6 +2675,9 @@ function is_lhs(node, parent) {
2620
2675
  }));
2621
2676
  });
2622
2677
  def_find_defs(AST_Node, noop);
2678
+ def_find_defs(AST_Chain, function(compressor, suffix) {
2679
+ return this.expression._find_defs(compressor, suffix);
2680
+ });
2623
2681
  def_find_defs(AST_Dot, function(compressor, suffix) {
2624
2682
  return this.expression._find_defs(compressor, "." + this.property + suffix);
2625
2683
  });
@@ -2877,13 +2935,33 @@ var static_fns = convert_to_predicate({
2877
2935
  return this;
2878
2936
  });
2879
2937
  var non_converting_binary = makePredicate("&& || ?? === !==");
2938
+ const identity_comparison = makePredicate("== != === !==");
2939
+ const has_identity = value =>
2940
+ typeof value === "object"
2941
+ || typeof value === "function"
2942
+ || typeof value === "symbol";
2943
+
2880
2944
  def_eval(AST_Binary, function(compressor, depth) {
2881
2945
  if (!non_converting_binary.has(this.operator)) depth++;
2946
+
2882
2947
  var left = this.left._eval(compressor, depth);
2883
2948
  if (left === this.left) return this;
2884
2949
  var right = this.right._eval(compressor, depth);
2885
2950
  if (right === this.right) return this;
2886
2951
  var result;
2952
+
2953
+ if (
2954
+ left != null
2955
+ && right != null
2956
+ && identity_comparison.has(this.operator)
2957
+ && has_identity(left)
2958
+ && has_identity(right)
2959
+ && typeof left === typeof right
2960
+ ) {
2961
+ // Do not compare by reference
2962
+ return this;
2963
+ }
2964
+
2887
2965
  switch (this.operator) {
2888
2966
  case "&&" : result = left && right; break;
2889
2967
  case "||" : result = left || right; break;
@@ -2924,34 +3002,29 @@ var static_fns = convert_to_predicate({
2924
3002
  var value = node._eval(compressor, depth);
2925
3003
  return value === node ? this : value;
2926
3004
  });
3005
+
3006
+ // Set of AST_SymbolRef which are currently being evaluated.
3007
+ // Avoids infinite recursion of ._eval()
3008
+ const reentrant_ref_eval = new Set();
2927
3009
  def_eval(AST_SymbolRef, function(compressor, depth) {
3010
+ if (reentrant_ref_eval.has(this)) return this;
3011
+
2928
3012
  var fixed = this.fixed_value();
2929
3013
  if (!fixed) return this;
2930
- var value;
2931
- if (HOP(fixed, "_eval")) {
2932
- value = fixed._eval();
2933
- } else {
2934
- this._eval = return_this;
2935
- value = fixed._eval(compressor, depth);
2936
- delete this._eval;
2937
- if (value === fixed) return this;
2938
- fixed._eval = function() {
2939
- return value;
2940
- };
2941
- }
3014
+
3015
+ reentrant_ref_eval.add(this);
3016
+ const value = fixed._eval(compressor, depth);
3017
+ reentrant_ref_eval.delete(this);
3018
+
3019
+ if (value === fixed) return this;
3020
+
2942
3021
  if (value && typeof value == "object") {
2943
3022
  var escaped = this.definition().escaped;
2944
3023
  if (escaped && depth > escaped) return this;
2945
3024
  }
2946
3025
  return value;
2947
3026
  });
2948
- var global_objs = {
2949
- Array: Array,
2950
- Math: Math,
2951
- Number: Number,
2952
- Object: Object,
2953
- String: String,
2954
- };
3027
+ var global_objs = { Array, Math, Number, Object, String };
2955
3028
  var static_values = convert_to_predicate({
2956
3029
  Math: [
2957
3030
  "E",
@@ -2972,6 +3045,10 @@ var static_fns = convert_to_predicate({
2972
3045
  ],
2973
3046
  });
2974
3047
  def_eval(AST_PropAccess, function(compressor, depth) {
3048
+ if (this.optional) {
3049
+ const obj = this.expression._eval(compressor, depth);
3050
+ if (obj == null) return undefined;
3051
+ }
2975
3052
  if (compressor.option("unsafe")) {
2976
3053
  var key = this.property;
2977
3054
  if (key instanceof AST_Node) {
@@ -3013,8 +3090,15 @@ var static_fns = convert_to_predicate({
3013
3090
  }
3014
3091
  return this;
3015
3092
  });
3093
+ def_eval(AST_Chain, function(compressor, depth) {
3094
+ return this.expression._eval(compressor, depth);
3095
+ });
3016
3096
  def_eval(AST_Call, function(compressor, depth) {
3017
3097
  var exp = this.expression;
3098
+ if (this.optional) {
3099
+ const callee = this.expression._eval(compressor, depth);
3100
+ if (callee == null) return undefined;
3101
+ }
3018
3102
  if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
3019
3103
  var key = exp.property;
3020
3104
  if (key instanceof AST_Node) {
@@ -3216,9 +3300,11 @@ const pure_prop_access_globals = new Set([
3216
3300
  return any(this.body, compressor);
3217
3301
  });
3218
3302
  def_has_side_effects(AST_Call, function(compressor) {
3219
- if (!this.is_expr_pure(compressor)
3303
+ if (
3304
+ !this.is_expr_pure(compressor)
3220
3305
  && (!this.expression.is_call_pure(compressor)
3221
- || this.expression.has_side_effects(compressor))) {
3306
+ || this.expression.has_side_effects(compressor))
3307
+ ) {
3222
3308
  return true;
3223
3309
  }
3224
3310
  return any(this.args, compressor);
@@ -3301,14 +3387,21 @@ const pure_prop_access_globals = new Set([
3301
3387
  return any(this.elements, compressor);
3302
3388
  });
3303
3389
  def_has_side_effects(AST_Dot, function(compressor) {
3304
- return this.expression.may_throw_on_access(compressor)
3390
+ return !this.optional && this.expression.may_throw_on_access(compressor)
3305
3391
  || this.expression.has_side_effects(compressor);
3306
3392
  });
3307
3393
  def_has_side_effects(AST_Sub, function(compressor) {
3308
- return this.expression.may_throw_on_access(compressor)
3394
+ if (this.optional && is_nullish(this.expression)) {
3395
+ return false;
3396
+ }
3397
+
3398
+ return !this.optional && this.expression.may_throw_on_access(compressor)
3309
3399
  || this.expression.has_side_effects(compressor)
3310
3400
  || this.property.has_side_effects(compressor);
3311
3401
  });
3402
+ def_has_side_effects(AST_Chain, function (compressor) {
3403
+ return this.expression.has_side_effects(compressor);
3404
+ });
3312
3405
  def_has_side_effects(AST_Sequence, function(compressor) {
3313
3406
  return any(this.expressions, compressor);
3314
3407
  });
@@ -3368,6 +3461,7 @@ const pure_prop_access_globals = new Set([
3368
3461
  return any(this.body, compressor);
3369
3462
  });
3370
3463
  def_may_throw(AST_Call, function(compressor) {
3464
+ if (this.optional && is_nullish(this.expression)) return false;
3371
3465
  if (any(this.args, compressor)) return true;
3372
3466
  if (this.is_expr_pure(compressor)) return false;
3373
3467
  if (this.expression.may_throw(compressor)) return true;
@@ -3386,10 +3480,6 @@ const pure_prop_access_globals = new Set([
3386
3480
  def_may_throw(AST_Definitions, function(compressor) {
3387
3481
  return any(this.definitions, compressor);
3388
3482
  });
3389
- def_may_throw(AST_Dot, function(compressor) {
3390
- return this.expression.may_throw_on_access(compressor)
3391
- || this.expression.may_throw(compressor);
3392
- });
3393
3483
  def_may_throw(AST_If, function(compressor) {
3394
3484
  return this.condition.may_throw(compressor)
3395
3485
  || this.body && this.body.may_throw(compressor)
@@ -3429,11 +3519,20 @@ const pure_prop_access_globals = new Set([
3429
3519
  def_may_throw(AST_SimpleStatement, function(compressor) {
3430
3520
  return this.body.may_throw(compressor);
3431
3521
  });
3522
+ def_may_throw(AST_Dot, function(compressor) {
3523
+ return !this.optional && this.expression.may_throw_on_access(compressor)
3524
+ || this.expression.may_throw(compressor);
3525
+ });
3432
3526
  def_may_throw(AST_Sub, function(compressor) {
3433
- return this.expression.may_throw_on_access(compressor)
3527
+ if (this.optional && is_nullish(this.expression)) return false;
3528
+
3529
+ return !this.optional && this.expression.may_throw_on_access(compressor)
3434
3530
  || this.expression.may_throw(compressor)
3435
3531
  || this.property.may_throw(compressor);
3436
3532
  });
3533
+ def_may_throw(AST_Chain, function(compressor) {
3534
+ return this.expression.may_throw(compressor);
3535
+ });
3437
3536
  def_may_throw(AST_Switch, function(compressor) {
3438
3537
  return this.expression.may_throw(compressor)
3439
3538
  || any(this.body, compressor);
@@ -3591,9 +3690,9 @@ def_optimize(AST_Block, function(self, compressor) {
3591
3690
 
3592
3691
  function can_be_extracted_from_if_block(node) {
3593
3692
  return !(
3594
- node instanceof AST_Const ||
3595
- node instanceof AST_Let ||
3596
- node instanceof AST_Class
3693
+ node instanceof AST_Const
3694
+ || node instanceof AST_Let
3695
+ || node instanceof AST_Class
3597
3696
  );
3598
3697
  }
3599
3698
 
@@ -4227,6 +4326,10 @@ AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
4227
4326
  def_drop_side_effect_free(AST_Constant, return_null);
4228
4327
  def_drop_side_effect_free(AST_This, return_null);
4229
4328
  def_drop_side_effect_free(AST_Call, function(compressor, first_in_statement) {
4329
+ if (this.optional && is_nullish(this.expression)) {
4330
+ return make_node(AST_Undefined, this);
4331
+ }
4332
+
4230
4333
  if (!this.is_expr_pure(compressor)) {
4231
4334
  if (this.expression.is_call_pure(compressor)) {
4232
4335
  var exprs = this.args.slice();
@@ -4367,17 +4470,28 @@ AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
4367
4470
  return values && make_sequence(this, values);
4368
4471
  });
4369
4472
  def_drop_side_effect_free(AST_Dot, function(compressor, first_in_statement) {
4473
+ if (this.optional) {
4474
+ return is_nullish(this.expression) ? make_node(AST_Undefined, this) : this;
4475
+ }
4370
4476
  if (this.expression.may_throw_on_access(compressor)) return this;
4477
+
4371
4478
  return this.expression.drop_side_effect_free(compressor, first_in_statement);
4372
4479
  });
4373
4480
  def_drop_side_effect_free(AST_Sub, function(compressor, first_in_statement) {
4481
+ if (this.optional) {
4482
+ return is_nullish(this.expression) ? make_node(AST_Undefined, this): this;
4483
+ }
4374
4484
  if (this.expression.may_throw_on_access(compressor)) return this;
4485
+
4375
4486
  var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
4376
4487
  if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
4377
4488
  var property = this.property.drop_side_effect_free(compressor);
4378
4489
  if (!property) return expression;
4379
4490
  return make_sequence(this, [ expression, property ]);
4380
4491
  });
4492
+ def_drop_side_effect_free(AST_Chain, function (compressor, first_in_statement) {
4493
+ return this.expression.drop_side_effect_free(compressor, first_in_statement);
4494
+ });
4381
4495
  def_drop_side_effect_free(AST_Sequence, function(compressor) {
4382
4496
  var last = this.tail_node();
4383
4497
  var expr = last.drop_side_effect_free(compressor);
@@ -4477,7 +4591,7 @@ function if_break_in_loop(self, compressor) {
4477
4591
  body: self.condition
4478
4592
  }));
4479
4593
  }
4480
- extract_declarations_from_unreachable_code(compressor, self.body, body);
4594
+ trim_unreachable_code(compressor, self.body, body);
4481
4595
  return make_node(AST_BlockStatement, self, {
4482
4596
  body: body
4483
4597
  });
@@ -4548,7 +4662,7 @@ def_optimize(AST_For, function(self, compressor) {
4548
4662
  if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
4549
4663
  if (!cond) {
4550
4664
  var body = [];
4551
- extract_declarations_from_unreachable_code(compressor, self.body, body);
4665
+ trim_unreachable_code(compressor, self.body, body);
4552
4666
  if (self.init instanceof AST_Statement) {
4553
4667
  body.push(self.init);
4554
4668
  } else if (self.init) {
@@ -4584,7 +4698,7 @@ def_optimize(AST_If, function(self, compressor) {
4584
4698
  if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
4585
4699
  if (!cond) {
4586
4700
  var body = [];
4587
- extract_declarations_from_unreachable_code(compressor, self.body, body);
4701
+ trim_unreachable_code(compressor, self.body, body);
4588
4702
  body.push(make_node(AST_SimpleStatement, self.condition, {
4589
4703
  body: self.condition
4590
4704
  }));
@@ -4597,7 +4711,7 @@ def_optimize(AST_If, function(self, compressor) {
4597
4711
  }));
4598
4712
  body.push(self.body);
4599
4713
  if (self.alternative) {
4600
- extract_declarations_from_unreachable_code(compressor, self.alternative, body);
4714
+ trim_unreachable_code(compressor, self.alternative, body);
4601
4715
  }
4602
4716
  return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
4603
4717
  }
@@ -4809,7 +4923,7 @@ def_optimize(AST_Switch, function(self, compressor) {
4809
4923
  if (prev && !aborts(prev)) {
4810
4924
  prev.body = prev.body.concat(branch.body);
4811
4925
  } else {
4812
- extract_declarations_from_unreachable_code(compressor, branch, decl);
4926
+ trim_unreachable_code(compressor, branch, decl);
4813
4927
  }
4814
4928
  }
4815
4929
  });
@@ -4820,7 +4934,7 @@ def_optimize(AST_Try, function(self, compressor) {
4820
4934
  if (compressor.option("dead_code") && self.body.every(is_empty)) {
4821
4935
  var body = [];
4822
4936
  if (self.bcatch) {
4823
- extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
4937
+ trim_unreachable_code(compressor, self.bcatch, body);
4824
4938
  }
4825
4939
  if (self.bfinally) body.push(...self.bfinally.body);
4826
4940
  return make_node(AST_BlockStatement, self, {
@@ -4918,6 +5032,10 @@ def_optimize(AST_Call, function(self, compressor) {
4918
5032
  }
4919
5033
  }
4920
5034
 
5035
+ if (self.optional && is_nullish(fn)) {
5036
+ return make_node(AST_Undefined, self);
5037
+ }
5038
+
4921
5039
  var is_func = fn instanceof AST_Lambda;
4922
5040
 
4923
5041
  if (is_func && fn.pinned()) return self;
@@ -5129,6 +5247,7 @@ def_optimize(AST_Call, function(self, compressor) {
5129
5247
  return make_node(AST_Call, self, {
5130
5248
  expression: make_node(AST_Dot, exp, {
5131
5249
  expression: exp.expression,
5250
+ optional: false,
5132
5251
  property: "call"
5133
5252
  }),
5134
5253
  args: args
@@ -5175,7 +5294,9 @@ def_optimize(AST_Call, function(self, compressor) {
5175
5294
  var ast = parse(code);
5176
5295
  var mangle = { ie8: compressor.option("ie8") };
5177
5296
  ast.figure_out_scope(mangle);
5178
- var comp = new Compressor(compressor.options);
5297
+ var comp = new Compressor(compressor.options, {
5298
+ mangle_options: compressor.mangle_options
5299
+ });
5179
5300
  ast = ast.transform(comp);
5180
5301
  ast.figure_out_scope(mangle);
5181
5302
  base54.reset();
@@ -6358,8 +6479,8 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
6358
6479
  }
6359
6480
 
6360
6481
  if (replace) {
6361
- const name_length = def.name.length;
6362
- const replace_size = replace.size();
6482
+ const name_length = self.size(compressor);
6483
+ const replace_size = replace.size(compressor);
6363
6484
 
6364
6485
  let overhead = 0;
6365
6486
  if (compressor.option("unused") && !compressor.exposed(def)) {
@@ -6564,6 +6685,10 @@ function is_nullish(node) {
6564
6685
  && (fixed = node.definition().fixed) instanceof AST_Node
6565
6686
  && is_nullish(fixed)
6566
6687
  )
6688
+ // Recurse into those optional chains!
6689
+ || node instanceof AST_PropAccess && node.optional && is_nullish(node.expression)
6690
+ || node instanceof AST_Call && node.optional && is_nullish(node.expression)
6691
+ || node instanceof AST_Chain && is_nullish(node.expression)
6567
6692
  );
6568
6693
  }
6569
6694
 
@@ -6946,6 +7071,41 @@ function safe_to_flatten(value, compressor) {
6946
7071
  return compressor.parent() instanceof AST_New;
6947
7072
  }
6948
7073
 
7074
+ AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
7075
+ if (!compressor.option("properties")) return;
7076
+ var arrows = compressor.option("unsafe_arrows") && compressor.option("ecma") >= 2015;
7077
+ var expr = this.expression;
7078
+ if (expr instanceof AST_Object) {
7079
+ var props = expr.properties;
7080
+ for (var i = props.length; --i >= 0;) {
7081
+ var prop = props[i];
7082
+ if ("" + (prop instanceof AST_ConciseMethod ? prop.key.name : prop.key) == key) {
7083
+ if (!props.every((prop) => {
7084
+ return prop instanceof AST_ObjectKeyVal
7085
+ || arrows && prop instanceof AST_ConciseMethod && !prop.is_generator;
7086
+ })) break;
7087
+ if (!safe_to_flatten(prop.value, compressor)) break;
7088
+ return make_node(AST_Sub, this, {
7089
+ expression: make_node(AST_Array, expr, {
7090
+ elements: props.map(function(prop) {
7091
+ var v = prop.value;
7092
+ if (v instanceof AST_Accessor) v = make_node(AST_Function, v, v);
7093
+ var k = prop.key;
7094
+ if (k instanceof AST_Node && !(k instanceof AST_SymbolMethod)) {
7095
+ return make_sequence(prop, [ k, v ]);
7096
+ }
7097
+ return v;
7098
+ })
7099
+ }),
7100
+ property: make_node(AST_Number, this, {
7101
+ value: i
7102
+ })
7103
+ });
7104
+ }
7105
+ }
7106
+ }
7107
+ });
7108
+
6949
7109
  def_optimize(AST_Sub, function(self, compressor) {
6950
7110
  var expr = self.expression;
6951
7111
  var prop = self.property;
@@ -6968,6 +7128,7 @@ def_optimize(AST_Sub, function(self, compressor) {
6968
7128
  && property.length <= prop.size() + 1) {
6969
7129
  return make_node(AST_Dot, self, {
6970
7130
  expression: expr,
7131
+ optional: self.optional,
6971
7132
  property: property,
6972
7133
  quote: prop.quote,
6973
7134
  }).optimize(compressor);
@@ -7070,6 +7231,14 @@ def_optimize(AST_Sub, function(self, compressor) {
7070
7231
  ev = make_node_from_constant(ev, self).optimize(compressor);
7071
7232
  return best_of(compressor, ev, self);
7072
7233
  }
7234
+ if (self.optional && is_nullish(self.expression)) {
7235
+ return make_node(AST_Undefined, self);
7236
+ }
7237
+ return self;
7238
+ });
7239
+
7240
+ def_optimize(AST_Chain, function (self, compressor) {
7241
+ self.expression = self.expression.optimize(compressor);
7073
7242
  return self;
7074
7243
  });
7075
7244
 
@@ -7086,41 +7255,6 @@ AST_Lambda.DEFMETHOD("contains_this", function() {
7086
7255
  });
7087
7256
  });
7088
7257
 
7089
- AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
7090
- if (!compressor.option("properties")) return;
7091
- var arrows = compressor.option("unsafe_arrows") && compressor.option("ecma") >= 2015;
7092
- var expr = this.expression;
7093
- if (expr instanceof AST_Object) {
7094
- var props = expr.properties;
7095
- for (var i = props.length; --i >= 0;) {
7096
- var prop = props[i];
7097
- if ("" + (prop instanceof AST_ConciseMethod ? prop.key.name : prop.key) == key) {
7098
- if (!props.every((prop) => {
7099
- return prop instanceof AST_ObjectKeyVal
7100
- || arrows && prop instanceof AST_ConciseMethod && !prop.is_generator;
7101
- })) break;
7102
- if (!safe_to_flatten(prop.value, compressor)) break;
7103
- return make_node(AST_Sub, this, {
7104
- expression: make_node(AST_Array, expr, {
7105
- elements: props.map(function(prop) {
7106
- var v = prop.value;
7107
- if (v instanceof AST_Accessor) v = make_node(AST_Function, v, v);
7108
- var k = prop.key;
7109
- if (k instanceof AST_Node && !(k instanceof AST_SymbolMethod)) {
7110
- return make_sequence(prop, [ k, v ]);
7111
- }
7112
- return v;
7113
- })
7114
- }),
7115
- property: make_node(AST_Number, this, {
7116
- value: i
7117
- })
7118
- });
7119
- }
7120
- }
7121
- }
7122
- });
7123
-
7124
7258
  def_optimize(AST_Dot, function(self, compressor) {
7125
7259
  const parent = compressor.parent();
7126
7260
  if (is_lhs(self, parent)) return self;
@@ -7171,6 +7305,9 @@ def_optimize(AST_Dot, function(self, compressor) {
7171
7305
  ev = make_node_from_constant(ev, self).optimize(compressor);
7172
7306
  return best_of(compressor, ev, self);
7173
7307
  }
7308
+ if (self.optional && is_nullish(self.expression)) {
7309
+ return make_node(AST_Undefined, self);
7310
+ }
7174
7311
  return self;
7175
7312
  });
7176
7313
 
@@ -7254,10 +7391,10 @@ def_optimize(AST_Function, function(self, compressor) {
7254
7391
  && !self.is_generator
7255
7392
  && !self.uses_arguments
7256
7393
  && !self.pinned()) {
7257
- const has_special_symbol = walk(self, node => {
7394
+ const uses_this = walk(self, node => {
7258
7395
  if (node instanceof AST_This) return walk_abort;
7259
7396
  });
7260
- if (!has_special_symbol) return make_node(AST_Arrow, self, self).optimize(compressor);
7397
+ if (!uses_this) return make_node(AST_Arrow, self, self).optimize(compressor);
7261
7398
  }
7262
7399
  return self;
7263
7400
  });
@@ -7349,7 +7486,7 @@ function lift_key(self, compressor) {
7349
7486
  if (!compressor.option("computed_props")) return self;
7350
7487
  // save a comparison in the typical case
7351
7488
  if (!(self.key instanceof AST_Constant)) return self;
7352
- // whitelist acceptable props as not all AST_Constants are true constants
7489
+ // allow certain acceptable props as not all AST_Constants are true constants
7353
7490
  if (self.key instanceof AST_String || self.key instanceof AST_Number) {
7354
7491
  if (self.key.value === "__proto__") return self;
7355
7492
  if (self.key.value == "constructor"
@@ -26,6 +26,7 @@ import {
26
26
  AST_ForOf,
27
27
  AST_If,
28
28
  AST_Import,
29
+ AST_ImportMeta,
29
30
  AST_Jump,
30
31
  AST_LabeledStatement,
31
32
  AST_Lambda,
@@ -209,6 +210,8 @@ AST_Import.prototype.shallow_cmp = mkshallow({
209
210
  imported_names: "exist"
210
211
  });
211
212
 
213
+ AST_ImportMeta.prototype.shallow_cmp = pass_through;
214
+
212
215
  AST_Export.prototype.shallow_cmp = mkshallow({
213
216
  exported_definition: "exist",
214
217
  exported_value: "exist",
package/lib/minify.js CHANGED
@@ -181,7 +181,11 @@ async function minify(files, options) {
181
181
  toplevel.expand_names(options.mangle);
182
182
  }
183
183
  if (timings) timings.compress = Date.now();
184
- if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
184
+ if (options.compress) {
185
+ toplevel = new Compressor(options.compress, {
186
+ mangle_options: options.mangle
187
+ }).compress(toplevel);
188
+ }
185
189
  if (timings) timings.scope = Date.now();
186
190
  if (options.mangle) toplevel.figure_out_scope(options.mangle);
187
191
  if (timings) timings.mangle = Date.now();