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.
@@ -565,6 +565,15 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
565
565
 
566
566
  function peek() { return get_full_char(S.text, S.pos); }
567
567
 
568
+ // Used because parsing ?. involves a lookahead for a digit
569
+ function is_option_chain_op() {
570
+ const must_be_dot = S.text.charCodeAt(S.pos + 1) === 46;
571
+ if (!must_be_dot) return false;
572
+
573
+ const cannot_be_digit = S.text.charCodeAt(S.pos + 2);
574
+ return cannot_be_digit < 48 || cannot_be_digit > 57;
575
+ }
576
+
568
577
  function next(signal_eof, in_string) {
569
578
  var ch = get_full_char(S.text, S.pos++);
570
579
  if (signal_eof && !ch)
@@ -625,7 +634,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
625
634
  (type == "keyword" && KEYWORDS_BEFORE_EXPRESSION.has(value)) ||
626
635
  (type == "punc" && PUNC_BEFORE_EXPRESSION.has(value))) ||
627
636
  (type == "arrow");
628
- if (type == "punc" && value == ".") {
637
+ if (type == "punc" && (value == "." || value == "?.")) {
629
638
  prev_was_dot = true;
630
639
  } else if (!is_comment) {
631
640
  prev_was_dot = false;
@@ -672,12 +681,14 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
672
681
  }
673
682
 
674
683
  function read_num(prefix) {
675
- var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".", is_big_int = false;
684
+ var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".", is_big_int = false, numeric_separator = false;
676
685
  var num = read_while(function(ch, i) {
677
686
  if (is_big_int) return false;
678
687
 
679
688
  var code = ch.charCodeAt(0);
680
689
  switch (code) {
690
+ case 95: // _
691
+ return (numeric_separator = true);
681
692
  case 98: case 66: // bB
682
693
  return (has_x = true); // Can occur in hex sequence, don't return false yet
683
694
  case 111: case 79: // oO
@@ -705,6 +716,14 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
705
716
  if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) {
706
717
  parse_error("Legacy octal literals are not allowed in strict mode");
707
718
  }
719
+ if (numeric_separator) {
720
+ if (num.endsWith("_")) {
721
+ parse_error("Numeric separators are not allowed at the end of numeric literals");
722
+ } else if (num.includes("__")) {
723
+ parse_error("Only one underscore is allowed as numeric separator");
724
+ }
725
+ num = num.replace(/_/g, "");
726
+ }
708
727
  if (num.endsWith("n")) {
709
728
  const without_n = num.slice(0, -1);
710
729
  const allow_e = RE_HEX_NUMBER.test(without_n);
@@ -1051,6 +1070,14 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
1051
1070
  return tok;
1052
1071
  }
1053
1072
  case 61: return handle_eq_sign();
1073
+ case 63: {
1074
+ if (!is_option_chain_op()) break; // Handled below
1075
+
1076
+ next(); // ?
1077
+ next(); // .
1078
+
1079
+ return token("punc", "?.");
1080
+ }
1054
1081
  case 96: return read_template_characters(true);
1055
1082
  case 123:
1056
1083
  S.brace_counter++;
@@ -1169,7 +1196,7 @@ function parse($TEXT, options) {
1169
1196
 
1170
1197
  options = defaults(options, {
1171
1198
  bare_returns : false,
1172
- ecma : 2017,
1199
+ ecma : null, // Legacy
1173
1200
  expression : false,
1174
1201
  filename : null,
1175
1202
  html5_comments : true,
@@ -1277,7 +1304,7 @@ function parse($TEXT, options) {
1277
1304
  }
1278
1305
 
1279
1306
  function embed_tokens(parser) {
1280
- return function(...args) {
1307
+ return function _embed_tokens_wrapper(...args) {
1281
1308
  const start = S.token;
1282
1309
  const expr = parser(...args);
1283
1310
  expr.start = start;
@@ -1293,7 +1320,7 @@ function parse($TEXT, options) {
1293
1320
  }
1294
1321
  }
1295
1322
 
1296
- var statement = embed_tokens(function(is_export_default, is_for_body, is_if_body) {
1323
+ var statement = embed_tokens(function statement(is_export_default, is_for_body, is_if_body) {
1297
1324
  handle_regexp();
1298
1325
  switch (S.token.type) {
1299
1326
  case "string":
@@ -1328,7 +1355,7 @@ function parse($TEXT, options) {
1328
1355
  }
1329
1356
  return function_(AST_Defun, false, true, is_export_default);
1330
1357
  }
1331
- if (S.token.value == "import" && !is_token(peek(), "punc", "(")) {
1358
+ if (S.token.value == "import" && !is_token(peek(), "punc", "(") && !is_token(peek(), "punc", ".")) {
1332
1359
  next();
1333
1360
  var node = import_();
1334
1361
  semicolon();
@@ -1749,7 +1776,6 @@ function parse($TEXT, options) {
1749
1776
 
1750
1777
  if (!is("punc", ")")) {
1751
1778
  expect(",");
1752
- if (is("punc", ")") && options.ecma < 2017) unexpected();
1753
1779
  }
1754
1780
 
1755
1781
  if (param instanceof AST_Expansion) {
@@ -1994,7 +2020,6 @@ function parse($TEXT, options) {
1994
2020
  if (!is("punc", ")")) {
1995
2021
  expect(",");
1996
2022
  if (is("punc", ")")) {
1997
- if (options.ecma < 2017) unexpected();
1998
2023
  trailing_comma = prev();
1999
2024
  if (maybe_sequence) invalid_sequence = trailing_comma;
2000
2025
  }
@@ -2263,7 +2288,7 @@ function parse($TEXT, options) {
2263
2288
  var newexp = expr_atom(false), args;
2264
2289
  if (is("punc", "(")) {
2265
2290
  next();
2266
- args = expr_list(")", options.ecma >= 2017);
2291
+ args = expr_list(")", true);
2267
2292
  } else {
2268
2293
  args = [];
2269
2294
  }
@@ -2376,6 +2401,9 @@ function parse($TEXT, options) {
2376
2401
  if (is("operator", "new")) {
2377
2402
  return new_(allow_calls);
2378
2403
  }
2404
+ if (is("operator", "import")) {
2405
+ return import_meta();
2406
+ }
2379
2407
  var start = S.token;
2380
2408
  var peeked;
2381
2409
  var async = is("name", "async")
@@ -2753,6 +2781,7 @@ function parse($TEXT, options) {
2753
2781
 
2754
2782
  function import_() {
2755
2783
  var start = prev();
2784
+
2756
2785
  var imported_name;
2757
2786
  var imported_names;
2758
2787
  if (is("name")) {
@@ -2787,6 +2816,17 @@ function parse($TEXT, options) {
2787
2816
  });
2788
2817
  }
2789
2818
 
2819
+ function import_meta() {
2820
+ var start = S.token;
2821
+ expect_token("operator", "import");
2822
+ expect_token("punc", ".");
2823
+ expect_token("name", "meta");
2824
+ return subscripts(new AST_ImportMeta({
2825
+ start: start,
2826
+ end: prev()
2827
+ }), false);
2828
+ }
2829
+
2790
2830
  function map_name(is_import) {
2791
2831
  function make_symbol(type) {
2792
2832
  return new type({
@@ -2967,15 +3007,6 @@ function parse($TEXT, options) {
2967
3007
  }
2968
3008
  /* falls through */
2969
3009
  case "name":
2970
- if (tmp.value == "yield") {
2971
- if (is_in_generator()) {
2972
- token_error(tmp, "Yield cannot be used as identifier inside generators");
2973
- } else if (!is_token(peek(), "punc", ":")
2974
- && !is_token(peek(), "punc", "(")
2975
- && S.input.has_directive("use strict")) {
2976
- token_error(tmp, "Unexpected yield identifier inside strict mode");
2977
- }
2978
- }
2979
3010
  case "string":
2980
3011
  case "num":
2981
3012
  case "big_int":
@@ -3057,16 +3088,17 @@ function parse($TEXT, options) {
3057
3088
  }
3058
3089
  }
3059
3090
 
3060
- var subscripts = function(expr, allow_calls) {
3091
+ var subscripts = function(expr, allow_calls, is_chain) {
3061
3092
  var start = expr.start;
3062
3093
  if (is("punc", ".")) {
3063
3094
  next();
3064
3095
  return subscripts(new AST_Dot({
3065
3096
  start : start,
3066
3097
  expression : expr,
3098
+ optional : false,
3067
3099
  property : as_name(),
3068
3100
  end : prev()
3069
- }), allow_calls);
3101
+ }), allow_calls, is_chain);
3070
3102
  }
3071
3103
  if (is("punc", "[")) {
3072
3104
  next();
@@ -3075,22 +3107,80 @@ function parse($TEXT, options) {
3075
3107
  return subscripts(new AST_Sub({
3076
3108
  start : start,
3077
3109
  expression : expr,
3110
+ optional : false,
3078
3111
  property : prop,
3079
3112
  end : prev()
3080
- }), allow_calls);
3113
+ }), allow_calls, is_chain);
3081
3114
  }
3082
3115
  if (allow_calls && is("punc", "(")) {
3083
3116
  next();
3084
3117
  var call = new AST_Call({
3085
3118
  start : start,
3086
3119
  expression : expr,
3120
+ optional : false,
3087
3121
  args : call_args(),
3088
3122
  end : prev()
3089
3123
  });
3090
3124
  annotate(call);
3091
- return subscripts(call, true);
3125
+ return subscripts(call, true, is_chain);
3126
+ }
3127
+
3128
+ if (is("punc", "?.")) {
3129
+ next();
3130
+
3131
+ let chain_contents;
3132
+
3133
+ if (allow_calls && is("punc", "(")) {
3134
+ next();
3135
+
3136
+ const call = new AST_Call({
3137
+ start,
3138
+ optional: true,
3139
+ expression: expr,
3140
+ args: call_args(),
3141
+ end: prev()
3142
+ });
3143
+ annotate(call);
3144
+
3145
+ chain_contents = subscripts(call, true, true);
3146
+ } else if (is("name")) {
3147
+ chain_contents = subscripts(new AST_Dot({
3148
+ start,
3149
+ expression: expr,
3150
+ optional: true,
3151
+ property: as_name(),
3152
+ end: prev()
3153
+ }), allow_calls, true);
3154
+ } else if (is("punc", "[")) {
3155
+ next();
3156
+ const property = expression(true);
3157
+ expect("]");
3158
+ chain_contents = subscripts(new AST_Sub({
3159
+ start,
3160
+ expression: expr,
3161
+ optional: true,
3162
+ property,
3163
+ end: prev()
3164
+ }), allow_calls, true);
3165
+ }
3166
+
3167
+ if (!chain_contents) unexpected();
3168
+
3169
+ if (chain_contents instanceof AST_Chain) return chain_contents;
3170
+
3171
+ return new AST_Chain({
3172
+ start,
3173
+ expression: chain_contents,
3174
+ end: prev()
3175
+ });
3092
3176
  }
3177
+
3093
3178
  if (is("template_head")) {
3179
+ if (is_chain) {
3180
+ // a?.b`c` is a syntax error
3181
+ unexpected();
3182
+ }
3183
+
3094
3184
  return subscripts(new AST_PrefixedTemplateString({
3095
3185
  start: start,
3096
3186
  prefix: expr,
@@ -3098,6 +3188,7 @@ function parse($TEXT, options) {
3098
3188
  end: prev()
3099
3189
  }), allow_calls);
3100
3190
  }
3191
+
3101
3192
  return expr;
3102
3193
  };
3103
3194
 
@@ -3116,7 +3207,6 @@ function parse($TEXT, options) {
3116
3207
  }
3117
3208
  if (!is("punc", ")")) {
3118
3209
  expect(",");
3119
- if (is("punc", ")") && options.ecma < 2017) unexpected();
3120
3210
  }
3121
3211
  }
3122
3212
  next();
@@ -3320,13 +3410,14 @@ function parse($TEXT, options) {
3320
3410
  return expression(true);
3321
3411
  }
3322
3412
 
3323
- return (function() {
3413
+ return (function parse_toplevel() {
3324
3414
  var start = S.token;
3325
3415
  var body = [];
3326
3416
  S.input.push_directives_stack();
3327
3417
  if (options.module) S.input.add_directive("use strict");
3328
- while (!is("eof"))
3418
+ while (!is("eof")) {
3329
3419
  body.push(statement());
3420
+ }
3330
3421
  S.input.pop_directives_stack();
3331
3422
  var end = prev();
3332
3423
  var toplevel = options.toplevel;
@@ -3861,7 +3952,7 @@ var AST_PrefixedTemplateString = DEFNODE("PrefixedTemplateString", "template_str
3861
3952
  $documentation: "A templatestring with a prefix, such as String.raw`foobarbaz`",
3862
3953
  $propdoc: {
3863
3954
  template_string: "[AST_TemplateString] The template string",
3864
- prefix: "[AST_SymbolRef|AST_PropAccess] The prefix, which can be a symbol such as `foo` or a dotted expression such as `String.raw`."
3955
+ prefix: "[AST_Node] The prefix, which will get called."
3865
3956
  },
3866
3957
  _walk: function(visitor) {
3867
3958
  return visitor._visit(this, function () {
@@ -4198,6 +4289,10 @@ var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
4198
4289
  },
4199
4290
  });
4200
4291
 
4292
+ var AST_ImportMeta = DEFNODE("ImportMeta", null, {
4293
+ $documentation: "A reference to import.meta",
4294
+ });
4295
+
4201
4296
  var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default exported_names module_name", {
4202
4297
  $documentation: "An `export` statement",
4203
4298
  $propdoc: {
@@ -4238,11 +4333,12 @@ var AST_Export = DEFNODE("Export", "exported_definition exported_value is_defaul
4238
4333
 
4239
4334
  /* -----[ OTHER ]----- */
4240
4335
 
4241
- var AST_Call = DEFNODE("Call", "expression args _annotations", {
4336
+ var AST_Call = DEFNODE("Call", "expression args optional _annotations", {
4242
4337
  $documentation: "A function call expression",
4243
4338
  $propdoc: {
4244
4339
  expression: "[AST_Node] expression to invoke as function",
4245
4340
  args: "[AST_Node*] array of arguments",
4341
+ optional: "[boolean] whether this is an optional call (IE ?.() )",
4246
4342
  _annotations: "[number] bitfield containing information about the call"
4247
4343
  },
4248
4344
  initialize() {
@@ -4286,11 +4382,13 @@ var AST_Sequence = DEFNODE("Sequence", "expressions", {
4286
4382
  },
4287
4383
  });
4288
4384
 
4289
- var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
4385
+ var AST_PropAccess = DEFNODE("PropAccess", "expression property optional", {
4290
4386
  $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`",
4291
4387
  $propdoc: {
4292
4388
  expression: "[AST_Node] the “container” expression",
4293
- property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node"
4389
+ property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node",
4390
+
4391
+ optional: "[boolean] whether this is an optional property access (IE ?.)"
4294
4392
  }
4295
4393
  });
4296
4394
 
@@ -4323,6 +4421,21 @@ var AST_Sub = DEFNODE("Sub", null, {
4323
4421
  },
4324
4422
  }, AST_PropAccess);
4325
4423
 
4424
+ var AST_Chain = DEFNODE("Chain", "expression", {
4425
+ $documentation: "A chain expression like a?.b?.(c)?.[d]",
4426
+ $propdoc: {
4427
+ expression: "[AST_Call|AST_Dot|AST_Sub] chain element."
4428
+ },
4429
+ _walk: function (visitor) {
4430
+ return visitor._visit(this, function() {
4431
+ this.expression._walk(visitor);
4432
+ });
4433
+ },
4434
+ _children_backwards(push) {
4435
+ push(this.expression);
4436
+ },
4437
+ });
4438
+
4326
4439
  var AST_Unary = DEFNODE("Unary", "operator expression", {
4327
4440
  $documentation: "Base class for unary expressions",
4328
4441
  $propdoc: {
@@ -4945,6 +5058,7 @@ AST_Break: AST_Break,
4945
5058
  AST_Call: AST_Call,
4946
5059
  AST_Case: AST_Case,
4947
5060
  AST_Catch: AST_Catch,
5061
+ AST_Chain: AST_Chain,
4948
5062
  AST_Class: AST_Class,
4949
5063
  AST_ClassExpression: AST_ClassExpression,
4950
5064
  AST_ClassProperty: AST_ClassProperty,
@@ -4977,6 +5091,7 @@ AST_Function: AST_Function,
4977
5091
  AST_Hole: AST_Hole,
4978
5092
  AST_If: AST_If,
4979
5093
  AST_Import: AST_Import,
5094
+ AST_ImportMeta: AST_ImportMeta,
4980
5095
  AST_Infinity: AST_Infinity,
4981
5096
  AST_IterationStatement: AST_IterationStatement,
4982
5097
  AST_Jump: AST_Jump,
@@ -5632,7 +5747,15 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
5632
5747
  start : my_start_token(M),
5633
5748
  end : my_end_token(M),
5634
5749
  property : M.computed ? from_moz(M.property) : M.property.name,
5635
- expression : from_moz(M.object)
5750
+ expression : from_moz(M.object),
5751
+ optional : M.optional || false
5752
+ });
5753
+ },
5754
+ ChainExpression: function(M) {
5755
+ return new AST_Chain({
5756
+ start : my_start_token(M),
5757
+ end : my_end_token(M),
5758
+ expression : from_moz(M.expression)
5636
5759
  });
5637
5760
  },
5638
5761
  SwitchCase: function(M) {
@@ -5759,6 +5882,11 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
5759
5882
  start: my_start_token(M),
5760
5883
  end: my_end_token(M)
5761
5884
  });
5885
+ } else if (M.meta.name === "import" && M.property.name === "meta") {
5886
+ return new AST_ImportMeta({
5887
+ start: my_start_token(M),
5888
+ end: my_end_token(M)
5889
+ });
5762
5890
  }
5763
5891
  },
5764
5892
  Identifier: function(M) {
@@ -5843,7 +5971,7 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
5843
5971
  map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
5844
5972
  map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative");
5845
5973
  map("NewExpression", AST_New, "callee>expression, arguments@args");
5846
- map("CallExpression", AST_Call, "callee>expression, arguments@args");
5974
+ map("CallExpression", AST_Call, "callee>expression, optional=optional, arguments@args");
5847
5975
 
5848
5976
  def_to_moz(AST_Toplevel, function To_Moz_Program(M) {
5849
5977
  return to_moz_scope("Program", M);
@@ -6050,6 +6178,20 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
6050
6178
  };
6051
6179
  });
6052
6180
 
6181
+ def_to_moz(AST_ImportMeta, function To_Moz_MetaProperty() {
6182
+ return {
6183
+ type: "MetaProperty",
6184
+ meta: {
6185
+ type: "Identifier",
6186
+ name: "import"
6187
+ },
6188
+ property: {
6189
+ type: "Identifier",
6190
+ name: "meta"
6191
+ }
6192
+ };
6193
+ });
6194
+
6053
6195
  def_to_moz(AST_Sequence, function To_Moz_SequenceExpression(M) {
6054
6196
  return {
6055
6197
  type: "SequenceExpression",
@@ -6063,7 +6205,15 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
6063
6205
  type: "MemberExpression",
6064
6206
  object: to_moz(M.expression),
6065
6207
  computed: isComputed,
6066
- property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property}
6208
+ property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property},
6209
+ optional: M.optional
6210
+ };
6211
+ });
6212
+
6213
+ def_to_moz(AST_Chain, function To_Moz_ChainExpression(M) {
6214
+ return {
6215
+ type: "ChainExpression",
6216
+ expression: to_moz(M.expression)
6067
6217
  };
6068
6218
  });
6069
6219
 
@@ -7275,8 +7425,8 @@ function OutputStream(options) {
7275
7425
  return p instanceof AST_PropAccess && p.expression === this;
7276
7426
  });
7277
7427
 
7278
- // same goes for an object literal, because otherwise it would be
7279
- // interpreted as a block of code.
7428
+ // same goes for an object literal (as in AST_Function), because
7429
+ // otherwise {...} would be interpreted as a block of code.
7280
7430
  PARENS(AST_Object, function(output) {
7281
7431
  return !output.has_parens() && first_in_statement(output);
7282
7432
  });
@@ -7792,6 +7942,8 @@ function OutputStream(options) {
7792
7942
  || e instanceof AST_PropAccess
7793
7943
  || e instanceof AST_Unary
7794
7944
  || e instanceof AST_Constant
7945
+ || e instanceof AST_Await
7946
+ || e instanceof AST_Object
7795
7947
  );
7796
7948
  if (parens) output.print("(");
7797
7949
  self.expression.print(output);
@@ -7989,6 +8141,9 @@ function OutputStream(options) {
7989
8141
  self.module_name.print(output);
7990
8142
  output.semicolon();
7991
8143
  });
8144
+ DEFPRINT(AST_ImportMeta, function(self, output) {
8145
+ output.print("import.meta");
8146
+ });
7992
8147
 
7993
8148
  DEFPRINT(AST_NameMapping, function(self, output) {
7994
8149
  var is_import = output.parent() instanceof AST_Import;
@@ -8095,6 +8250,7 @@ function OutputStream(options) {
8095
8250
  if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
8096
8251
  output.add_mapping(self.start);
8097
8252
  }
8253
+ if (self.optional) output.print("?.");
8098
8254
  output.with_parens(function() {
8099
8255
  self.args.forEach(function(expr, i) {
8100
8256
  if (i) output.comma();
@@ -8138,6 +8294,9 @@ function OutputStream(options) {
8138
8294
  var print_computed = RESERVED_WORDS.has(prop)
8139
8295
  ? output.option("ie8")
8140
8296
  : !is_identifier_string(prop, output.option("ecma") >= 2015);
8297
+
8298
+ if (self.optional) output.print("?.");
8299
+
8141
8300
  if (print_computed) {
8142
8301
  output.print("[");
8143
8302
  output.add_mapping(self.end);
@@ -8149,7 +8308,7 @@ function OutputStream(options) {
8149
8308
  output.print(".");
8150
8309
  }
8151
8310
  }
8152
- output.print(".");
8311
+ if (!self.optional) output.print(".");
8153
8312
  // the name after dot would be mapped about here.
8154
8313
  output.add_mapping(self.end);
8155
8314
  output.print_name(prop);
@@ -8157,10 +8316,14 @@ function OutputStream(options) {
8157
8316
  });
8158
8317
  DEFPRINT(AST_Sub, function(self, output) {
8159
8318
  self.expression.print(output);
8319
+ if (self.optional) output.print("?.");
8160
8320
  output.print("[");
8161
8321
  self.property.print(output);
8162
8322
  output.print("]");
8163
8323
  });
8324
+ DEFPRINT(AST_Chain, function(self, output) {
8325
+ self.expression.print(output);
8326
+ });
8164
8327
  DEFPRINT(AST_UnaryPrefix, function(self, output) {
8165
8328
  var op = self.operator;
8166
8329
  output.print(op);
@@ -8716,6 +8879,8 @@ AST_Import.prototype.shallow_cmp = mkshallow({
8716
8879
  imported_names: "exist"
8717
8880
  });
8718
8881
 
8882
+ AST_ImportMeta.prototype.shallow_cmp = pass_through;
8883
+
8719
8884
  AST_Export.prototype.shallow_cmp = mkshallow({
8720
8885
  exported_definition: "exist",
8721
8886
  exported_value: "exist",
@@ -9702,7 +9867,7 @@ const base54 = (() => {
9702
9867
 
9703
9868
  let mangle_options = undefined;
9704
9869
  AST_Node.prototype.size = function (compressor, stack) {
9705
- mangle_options = undefined; // TODO get mangle_options somehow
9870
+ mangle_options = compressor && compressor.mangle_options;
9706
9871
 
9707
9872
  let size = 0;
9708
9873
  walk_parent(this, (node, info) => {
@@ -9780,7 +9945,7 @@ AST_Arrow.prototype._size = function () {
9780
9945
  args_and_arrow += 2;
9781
9946
  }
9782
9947
 
9783
- return lambda_modifiers(this) + args_and_arrow + Array.isArray(this.body) ? list_overhead(this.body) : this.body._size();
9948
+ return lambda_modifiers(this) + args_and_arrow + (Array.isArray(this.body) ? list_overhead(this.body) : this.body._size());
9784
9949
  };
9785
9950
 
9786
9951
  AST_Destructuring.prototype._size = () => 2;
@@ -9878,6 +10043,8 @@ AST_Import.prototype._size = function () {
9878
10043
  return size;
9879
10044
  };
9880
10045
 
10046
+ AST_ImportMeta.prototype._size = () => 11;
10047
+
9881
10048
  AST_Export.prototype._size = function () {
9882
10049
  let size = 7 + (this.is_default ? 8 : 0);
9883
10050
 
@@ -9899,6 +10066,9 @@ AST_Export.prototype._size = function () {
9899
10066
  };
9900
10067
 
9901
10068
  AST_Call.prototype._size = function () {
10069
+ if (this.optional) {
10070
+ return 4 + list_overhead(this.args);
10071
+ }
9902
10072
  return 2 + list_overhead(this.args);
9903
10073
  };
9904
10074
 
@@ -9911,10 +10081,15 @@ AST_Sequence.prototype._size = function () {
9911
10081
  };
9912
10082
 
9913
10083
  AST_Dot.prototype._size = function () {
10084
+ if (this.optional) {
10085
+ return this.property.length + 2;
10086
+ }
9914
10087
  return this.property.length + 1;
9915
10088
  };
9916
10089
 
9917
- AST_Sub.prototype._size = () => 2;
10090
+ AST_Sub.prototype._size = function () {
10091
+ return this.optional ? 4 : 2;
10092
+ };
9918
10093
 
9919
10094
  AST_Unary.prototype._size = function () {
9920
10095
  if (this.operator === "typeof") return 7;
@@ -9997,7 +10172,7 @@ AST_ClassProperty.prototype._size = function () {
9997
10172
  AST_Symbol.prototype._size = function () {
9998
10173
  return !mangle_options || this.definition().unmangleable(mangle_options)
9999
10174
  ? this.name.length
10000
- : 2;
10175
+ : 1;
10001
10176
  };
10002
10177
 
10003
10178
  // TODO take propmangle into account
@@ -10012,7 +10187,7 @@ AST_SymbolRef.prototype._size = AST_SymbolDeclaration.prototype._size = function
10012
10187
 
10013
10188
  if (name === "arguments") return 9;
10014
10189
 
10015
- return 2;
10190
+ return AST_Symbol.prototype._size.call(this);
10016
10191
  };
10017
10192
 
10018
10193
  AST_NewTarget.prototype._size = () => 10;
@@ -10134,7 +10309,7 @@ const set_flag = (node, flag) => { node.flags |= flag; };
10134
10309
  const clear_flag = (node, flag) => { node.flags &= ~flag; };
10135
10310
 
10136
10311
  class Compressor extends TreeWalker {
10137
- constructor(options, false_by_default) {
10312
+ constructor(options, { false_by_default = false, mangle_options = false }) {
10138
10313
  super();
10139
10314
  if (options.defaults !== undefined && !options.defaults) false_by_default = true;
10140
10315
  this.options = defaults(options, {
@@ -10192,7 +10367,7 @@ class Compressor extends TreeWalker {
10192
10367
  unsafe_regexp : false,
10193
10368
  unsafe_undefined: false,
10194
10369
  unused : !false_by_default,
10195
- warnings : false, // legacy
10370
+ warnings : false // legacy
10196
10371
  }, true);
10197
10372
  var global_defs = this.options["global_defs"];
10198
10373
  if (typeof global_defs == "object") for (var key in global_defs) {
@@ -10242,6 +10417,7 @@ class Compressor extends TreeWalker {
10242
10417
  this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
10243
10418
  this.evaluated_regexps = new Map();
10244
10419
  this._toplevel = undefined;
10420
+ this.mangle_options = mangle_options;
10245
10421
  }
10246
10422
 
10247
10423
  option(key) {
@@ -10710,13 +10886,53 @@ function is_modified(compressor, tw, node, value, level, immutable) {
10710
10886
  pop(tw);
10711
10887
  return true;
10712
10888
  });
10889
+
10890
+ def_reduce_vars(AST_Chain, function(tw, descend) {
10891
+ // Chains' conditions apply left-to-right, cumulatively.
10892
+ // If we walk normally we don't go in that order because we would pop before pushing again
10893
+ // Solution: AST_PropAccess and AST_Call push when they are optional, and never pop.
10894
+ // Then we pop everything when they are done being walked.
10895
+ const safe_ids = tw.safe_ids;
10896
+
10897
+ descend();
10898
+
10899
+ // Unroll back to start
10900
+ tw.safe_ids = safe_ids;
10901
+ return true;
10902
+ });
10903
+ def_reduce_vars(AST_Call, function (tw) {
10904
+ // TODO this block should just be { return } but
10905
+ // for some reason the _walk function of AST_Call walks the callee last
10906
+
10907
+ this.expression.walk(tw);
10908
+
10909
+ if (this.optional) {
10910
+ // Never pop -- it's popped at AST_Chain above
10911
+ push(tw);
10912
+ }
10913
+
10914
+ for (const arg of this.args) arg.walk(tw);
10915
+
10916
+ return true;
10917
+ });
10918
+ def_reduce_vars(AST_PropAccess, function (tw) {
10919
+ if (!this.optional) return;
10920
+
10921
+ this.expression.walk(tw);
10922
+
10923
+ // Never pop -- it's popped at AST_Chain above
10924
+ push(tw);
10925
+
10926
+ if (this.property instanceof AST_Node) this.property.walk(tw);
10927
+
10928
+ return true;
10929
+ });
10713
10930
  def_reduce_vars(AST_Default, function(tw, descend) {
10714
10931
  push(tw);
10715
10932
  descend();
10716
10933
  pop(tw);
10717
10934
  return true;
10718
10935
  });
10719
-
10720
10936
  function mark_lambda(tw, descend, compressor) {
10721
10937
  clear_flag(this, INLINED);
10722
10938
  push(tw);
@@ -11546,8 +11762,7 @@ function tighten_body(statements, compressor) {
11546
11762
  extract_candidates(expr.condition);
11547
11763
  extract_candidates(expr.consequent);
11548
11764
  extract_candidates(expr.alternative);
11549
- } else if (expr instanceof AST_Definitions
11550
- && (compressor.option("unused") || !(expr instanceof AST_Const))) {
11765
+ } else if (expr instanceof AST_Definitions) {
11551
11766
  var len = expr.definitions.length;
11552
11767
  // limit number of trailing variable definitions for consideration
11553
11768
  var i = len - 200;
@@ -11715,7 +11930,9 @@ function tighten_body(statements, compressor) {
11715
11930
  if (node === expr || node.body === expr) {
11716
11931
  found = true;
11717
11932
  if (node instanceof AST_VarDef) {
11718
- node.value = null;
11933
+ node.value = node.name instanceof AST_SymbolConst
11934
+ ? make_node(AST_Undefined, node.value) // `const` always needs value.
11935
+ : null;
11719
11936
  return node;
11720
11937
  }
11721
11938
  return in_list ? MAP.skip : null;
@@ -12031,7 +12248,7 @@ function tighten_body(statements, compressor) {
12031
12248
  statements.length = n;
12032
12249
  CHANGED = n != len;
12033
12250
  if (has_quit) has_quit.forEach(function(stat) {
12034
- extract_declarations_from_unreachable_code(compressor, stat, statements);
12251
+ trim_unreachable_code(compressor, stat, statements);
12035
12252
  });
12036
12253
  }
12037
12254
 
@@ -12289,7 +12506,7 @@ function tighten_body(statements, compressor) {
12289
12506
  }
12290
12507
  }
12291
12508
 
12292
- function extract_declarations_from_unreachable_code(compressor, stat, target) {
12509
+ function trim_unreachable_code(compressor, stat, target) {
12293
12510
  walk(stat, node => {
12294
12511
  if (node instanceof AST_Var) {
12295
12512
  node.remove_initializers();
@@ -12310,6 +12527,10 @@ function extract_declarations_from_unreachable_code(compressor, stat, target) {
12310
12527
  }));
12311
12528
  return true;
12312
12529
  }
12530
+ if (node instanceof AST_Export || node instanceof AST_Import) {
12531
+ target.push(node);
12532
+ return true;
12533
+ }
12313
12534
  if (node instanceof AST_Scope) {
12314
12535
  return true;
12315
12536
  }
@@ -12361,6 +12582,10 @@ function is_undefined(node, compressor) {
12361
12582
  if (this.properties[i]._dot_throw(compressor)) return true;
12362
12583
  return false;
12363
12584
  });
12585
+ // Do not be as strict with classes as we are with objects.
12586
+ // Hopefully the community is not going to abuse static getters and setters.
12587
+ // https://github.com/terser/terser/issues/724#issuecomment-643655656
12588
+ def_may_throw_on_access(AST_Class, return_false);
12364
12589
  def_may_throw_on_access(AST_ObjectProperty, return_false);
12365
12590
  def_may_throw_on_access(AST_ObjectGetter, return_true);
12366
12591
  def_may_throw_on_access(AST_Expansion, function(compressor) {
@@ -12389,11 +12614,15 @@ function is_undefined(node, compressor) {
12389
12614
  if (this.expression instanceof AST_Function && this.property == "prototype") return false;
12390
12615
  return true;
12391
12616
  });
12617
+ def_may_throw_on_access(AST_Chain, function(compressor) {
12618
+ return this.expression._dot_throw(compressor);
12619
+ });
12392
12620
  def_may_throw_on_access(AST_Sequence, function(compressor) {
12393
12621
  return this.tail_node()._dot_throw(compressor);
12394
12622
  });
12395
12623
  def_may_throw_on_access(AST_SymbolRef, function(compressor) {
12396
- if (is_undefined(this, compressor)) return true;
12624
+ if (this.name === "arguments") return false;
12625
+ if (has_flag(this, UNDEFINED)) return true;
12397
12626
  if (!is_strict(compressor)) return false;
12398
12627
  if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
12399
12628
  if (this.is_immutable()) return false;
@@ -12538,6 +12767,9 @@ function is_lhs(node, parent) {
12538
12767
  }));
12539
12768
  });
12540
12769
  def_find_defs(AST_Node, noop);
12770
+ def_find_defs(AST_Chain, function(compressor, suffix) {
12771
+ return this.expression._find_defs(compressor, suffix);
12772
+ });
12541
12773
  def_find_defs(AST_Dot, function(compressor, suffix) {
12542
12774
  return this.expression._find_defs(compressor, "." + this.property + suffix);
12543
12775
  });
@@ -12795,13 +13027,33 @@ var static_fns = convert_to_predicate({
12795
13027
  return this;
12796
13028
  });
12797
13029
  var non_converting_binary = makePredicate("&& || ?? === !==");
13030
+ const identity_comparison = makePredicate("== != === !==");
13031
+ const has_identity = value =>
13032
+ typeof value === "object"
13033
+ || typeof value === "function"
13034
+ || typeof value === "symbol";
13035
+
12798
13036
  def_eval(AST_Binary, function(compressor, depth) {
12799
13037
  if (!non_converting_binary.has(this.operator)) depth++;
13038
+
12800
13039
  var left = this.left._eval(compressor, depth);
12801
13040
  if (left === this.left) return this;
12802
13041
  var right = this.right._eval(compressor, depth);
12803
13042
  if (right === this.right) return this;
12804
13043
  var result;
13044
+
13045
+ if (
13046
+ left != null
13047
+ && right != null
13048
+ && identity_comparison.has(this.operator)
13049
+ && has_identity(left)
13050
+ && has_identity(right)
13051
+ && typeof left === typeof right
13052
+ ) {
13053
+ // Do not compare by reference
13054
+ return this;
13055
+ }
13056
+
12805
13057
  switch (this.operator) {
12806
13058
  case "&&" : result = left && right; break;
12807
13059
  case "||" : result = left || right; break;
@@ -12842,34 +13094,29 @@ var static_fns = convert_to_predicate({
12842
13094
  var value = node._eval(compressor, depth);
12843
13095
  return value === node ? this : value;
12844
13096
  });
13097
+
13098
+ // Set of AST_SymbolRef which are currently being evaluated.
13099
+ // Avoids infinite recursion of ._eval()
13100
+ const reentrant_ref_eval = new Set();
12845
13101
  def_eval(AST_SymbolRef, function(compressor, depth) {
13102
+ if (reentrant_ref_eval.has(this)) return this;
13103
+
12846
13104
  var fixed = this.fixed_value();
12847
13105
  if (!fixed) return this;
12848
- var value;
12849
- if (HOP(fixed, "_eval")) {
12850
- value = fixed._eval();
12851
- } else {
12852
- this._eval = return_this;
12853
- value = fixed._eval(compressor, depth);
12854
- delete this._eval;
12855
- if (value === fixed) return this;
12856
- fixed._eval = function() {
12857
- return value;
12858
- };
12859
- }
13106
+
13107
+ reentrant_ref_eval.add(this);
13108
+ const value = fixed._eval(compressor, depth);
13109
+ reentrant_ref_eval.delete(this);
13110
+
13111
+ if (value === fixed) return this;
13112
+
12860
13113
  if (value && typeof value == "object") {
12861
13114
  var escaped = this.definition().escaped;
12862
13115
  if (escaped && depth > escaped) return this;
12863
13116
  }
12864
13117
  return value;
12865
13118
  });
12866
- var global_objs = {
12867
- Array: Array,
12868
- Math: Math,
12869
- Number: Number,
12870
- Object: Object,
12871
- String: String,
12872
- };
13119
+ var global_objs = { Array, Math, Number, Object, String };
12873
13120
  var static_values = convert_to_predicate({
12874
13121
  Math: [
12875
13122
  "E",
@@ -12890,6 +13137,10 @@ var static_fns = convert_to_predicate({
12890
13137
  ],
12891
13138
  });
12892
13139
  def_eval(AST_PropAccess, function(compressor, depth) {
13140
+ if (this.optional) {
13141
+ const obj = this.expression._eval(compressor, depth);
13142
+ if (obj == null) return undefined;
13143
+ }
12893
13144
  if (compressor.option("unsafe")) {
12894
13145
  var key = this.property;
12895
13146
  if (key instanceof AST_Node) {
@@ -12931,8 +13182,15 @@ var static_fns = convert_to_predicate({
12931
13182
  }
12932
13183
  return this;
12933
13184
  });
13185
+ def_eval(AST_Chain, function(compressor, depth) {
13186
+ return this.expression._eval(compressor, depth);
13187
+ });
12934
13188
  def_eval(AST_Call, function(compressor, depth) {
12935
13189
  var exp = this.expression;
13190
+ if (this.optional) {
13191
+ const callee = this.expression._eval(compressor, depth);
13192
+ if (callee == null) return undefined;
13193
+ }
12936
13194
  if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
12937
13195
  var key = exp.property;
12938
13196
  if (key instanceof AST_Node) {
@@ -13134,9 +13392,11 @@ const pure_prop_access_globals = new Set([
13134
13392
  return any(this.body, compressor);
13135
13393
  });
13136
13394
  def_has_side_effects(AST_Call, function(compressor) {
13137
- if (!this.is_expr_pure(compressor)
13395
+ if (
13396
+ !this.is_expr_pure(compressor)
13138
13397
  && (!this.expression.is_call_pure(compressor)
13139
- || this.expression.has_side_effects(compressor))) {
13398
+ || this.expression.has_side_effects(compressor))
13399
+ ) {
13140
13400
  return true;
13141
13401
  }
13142
13402
  return any(this.args, compressor);
@@ -13219,14 +13479,21 @@ const pure_prop_access_globals = new Set([
13219
13479
  return any(this.elements, compressor);
13220
13480
  });
13221
13481
  def_has_side_effects(AST_Dot, function(compressor) {
13222
- return this.expression.may_throw_on_access(compressor)
13482
+ return !this.optional && this.expression.may_throw_on_access(compressor)
13223
13483
  || this.expression.has_side_effects(compressor);
13224
13484
  });
13225
13485
  def_has_side_effects(AST_Sub, function(compressor) {
13226
- return this.expression.may_throw_on_access(compressor)
13486
+ if (this.optional && is_nullish(this.expression)) {
13487
+ return false;
13488
+ }
13489
+
13490
+ return !this.optional && this.expression.may_throw_on_access(compressor)
13227
13491
  || this.expression.has_side_effects(compressor)
13228
13492
  || this.property.has_side_effects(compressor);
13229
13493
  });
13494
+ def_has_side_effects(AST_Chain, function (compressor) {
13495
+ return this.expression.has_side_effects(compressor);
13496
+ });
13230
13497
  def_has_side_effects(AST_Sequence, function(compressor) {
13231
13498
  return any(this.expressions, compressor);
13232
13499
  });
@@ -13286,6 +13553,7 @@ const pure_prop_access_globals = new Set([
13286
13553
  return any(this.body, compressor);
13287
13554
  });
13288
13555
  def_may_throw(AST_Call, function(compressor) {
13556
+ if (this.optional && is_nullish(this.expression)) return false;
13289
13557
  if (any(this.args, compressor)) return true;
13290
13558
  if (this.is_expr_pure(compressor)) return false;
13291
13559
  if (this.expression.may_throw(compressor)) return true;
@@ -13304,10 +13572,6 @@ const pure_prop_access_globals = new Set([
13304
13572
  def_may_throw(AST_Definitions, function(compressor) {
13305
13573
  return any(this.definitions, compressor);
13306
13574
  });
13307
- def_may_throw(AST_Dot, function(compressor) {
13308
- return this.expression.may_throw_on_access(compressor)
13309
- || this.expression.may_throw(compressor);
13310
- });
13311
13575
  def_may_throw(AST_If, function(compressor) {
13312
13576
  return this.condition.may_throw(compressor)
13313
13577
  || this.body && this.body.may_throw(compressor)
@@ -13347,11 +13611,20 @@ const pure_prop_access_globals = new Set([
13347
13611
  def_may_throw(AST_SimpleStatement, function(compressor) {
13348
13612
  return this.body.may_throw(compressor);
13349
13613
  });
13614
+ def_may_throw(AST_Dot, function(compressor) {
13615
+ return !this.optional && this.expression.may_throw_on_access(compressor)
13616
+ || this.expression.may_throw(compressor);
13617
+ });
13350
13618
  def_may_throw(AST_Sub, function(compressor) {
13351
- return this.expression.may_throw_on_access(compressor)
13619
+ if (this.optional && is_nullish(this.expression)) return false;
13620
+
13621
+ return !this.optional && this.expression.may_throw_on_access(compressor)
13352
13622
  || this.expression.may_throw(compressor)
13353
13623
  || this.property.may_throw(compressor);
13354
13624
  });
13625
+ def_may_throw(AST_Chain, function(compressor) {
13626
+ return this.expression.may_throw(compressor);
13627
+ });
13355
13628
  def_may_throw(AST_Switch, function(compressor) {
13356
13629
  return this.expression.may_throw(compressor)
13357
13630
  || any(this.body, compressor);
@@ -13509,9 +13782,9 @@ def_optimize(AST_Block, function(self, compressor) {
13509
13782
 
13510
13783
  function can_be_extracted_from_if_block(node) {
13511
13784
  return !(
13512
- node instanceof AST_Const ||
13513
- node instanceof AST_Let ||
13514
- node instanceof AST_Class
13785
+ node instanceof AST_Const
13786
+ || node instanceof AST_Let
13787
+ || node instanceof AST_Class
13515
13788
  );
13516
13789
  }
13517
13790
 
@@ -14145,6 +14418,10 @@ AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
14145
14418
  def_drop_side_effect_free(AST_Constant, return_null);
14146
14419
  def_drop_side_effect_free(AST_This, return_null);
14147
14420
  def_drop_side_effect_free(AST_Call, function(compressor, first_in_statement) {
14421
+ if (this.optional && is_nullish(this.expression)) {
14422
+ return make_node(AST_Undefined, this);
14423
+ }
14424
+
14148
14425
  if (!this.is_expr_pure(compressor)) {
14149
14426
  if (this.expression.is_call_pure(compressor)) {
14150
14427
  var exprs = this.args.slice();
@@ -14285,17 +14562,28 @@ AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
14285
14562
  return values && make_sequence(this, values);
14286
14563
  });
14287
14564
  def_drop_side_effect_free(AST_Dot, function(compressor, first_in_statement) {
14565
+ if (this.optional) {
14566
+ return is_nullish(this.expression) ? make_node(AST_Undefined, this) : this;
14567
+ }
14288
14568
  if (this.expression.may_throw_on_access(compressor)) return this;
14569
+
14289
14570
  return this.expression.drop_side_effect_free(compressor, first_in_statement);
14290
14571
  });
14291
14572
  def_drop_side_effect_free(AST_Sub, function(compressor, first_in_statement) {
14573
+ if (this.optional) {
14574
+ return is_nullish(this.expression) ? make_node(AST_Undefined, this): this;
14575
+ }
14292
14576
  if (this.expression.may_throw_on_access(compressor)) return this;
14577
+
14293
14578
  var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
14294
14579
  if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
14295
14580
  var property = this.property.drop_side_effect_free(compressor);
14296
14581
  if (!property) return expression;
14297
14582
  return make_sequence(this, [ expression, property ]);
14298
14583
  });
14584
+ def_drop_side_effect_free(AST_Chain, function (compressor, first_in_statement) {
14585
+ return this.expression.drop_side_effect_free(compressor, first_in_statement);
14586
+ });
14299
14587
  def_drop_side_effect_free(AST_Sequence, function(compressor) {
14300
14588
  var last = this.tail_node();
14301
14589
  var expr = last.drop_side_effect_free(compressor);
@@ -14395,7 +14683,7 @@ function if_break_in_loop(self, compressor) {
14395
14683
  body: self.condition
14396
14684
  }));
14397
14685
  }
14398
- extract_declarations_from_unreachable_code(compressor, self.body, body);
14686
+ trim_unreachable_code(compressor, self.body, body);
14399
14687
  return make_node(AST_BlockStatement, self, {
14400
14688
  body: body
14401
14689
  });
@@ -14466,7 +14754,7 @@ def_optimize(AST_For, function(self, compressor) {
14466
14754
  if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
14467
14755
  if (!cond) {
14468
14756
  var body = [];
14469
- extract_declarations_from_unreachable_code(compressor, self.body, body);
14757
+ trim_unreachable_code(compressor, self.body, body);
14470
14758
  if (self.init instanceof AST_Statement) {
14471
14759
  body.push(self.init);
14472
14760
  } else if (self.init) {
@@ -14502,7 +14790,7 @@ def_optimize(AST_If, function(self, compressor) {
14502
14790
  if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
14503
14791
  if (!cond) {
14504
14792
  var body = [];
14505
- extract_declarations_from_unreachable_code(compressor, self.body, body);
14793
+ trim_unreachable_code(compressor, self.body, body);
14506
14794
  body.push(make_node(AST_SimpleStatement, self.condition, {
14507
14795
  body: self.condition
14508
14796
  }));
@@ -14515,7 +14803,7 @@ def_optimize(AST_If, function(self, compressor) {
14515
14803
  }));
14516
14804
  body.push(self.body);
14517
14805
  if (self.alternative) {
14518
- extract_declarations_from_unreachable_code(compressor, self.alternative, body);
14806
+ trim_unreachable_code(compressor, self.alternative, body);
14519
14807
  }
14520
14808
  return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
14521
14809
  }
@@ -14727,7 +15015,7 @@ def_optimize(AST_Switch, function(self, compressor) {
14727
15015
  if (prev && !aborts(prev)) {
14728
15016
  prev.body = prev.body.concat(branch.body);
14729
15017
  } else {
14730
- extract_declarations_from_unreachable_code(compressor, branch, decl);
15018
+ trim_unreachable_code(compressor, branch, decl);
14731
15019
  }
14732
15020
  }
14733
15021
  });
@@ -14738,7 +15026,7 @@ def_optimize(AST_Try, function(self, compressor) {
14738
15026
  if (compressor.option("dead_code") && self.body.every(is_empty)) {
14739
15027
  var body = [];
14740
15028
  if (self.bcatch) {
14741
- extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
15029
+ trim_unreachable_code(compressor, self.bcatch, body);
14742
15030
  }
14743
15031
  if (self.bfinally) body.push(...self.bfinally.body);
14744
15032
  return make_node(AST_BlockStatement, self, {
@@ -14836,6 +15124,10 @@ def_optimize(AST_Call, function(self, compressor) {
14836
15124
  }
14837
15125
  }
14838
15126
 
15127
+ if (self.optional && is_nullish(fn)) {
15128
+ return make_node(AST_Undefined, self);
15129
+ }
15130
+
14839
15131
  var is_func = fn instanceof AST_Lambda;
14840
15132
 
14841
15133
  if (is_func && fn.pinned()) return self;
@@ -15047,6 +15339,7 @@ def_optimize(AST_Call, function(self, compressor) {
15047
15339
  return make_node(AST_Call, self, {
15048
15340
  expression: make_node(AST_Dot, exp, {
15049
15341
  expression: exp.expression,
15342
+ optional: false,
15050
15343
  property: "call"
15051
15344
  }),
15052
15345
  args: args
@@ -15093,7 +15386,9 @@ def_optimize(AST_Call, function(self, compressor) {
15093
15386
  var ast = parse(code);
15094
15387
  var mangle = { ie8: compressor.option("ie8") };
15095
15388
  ast.figure_out_scope(mangle);
15096
- var comp = new Compressor(compressor.options);
15389
+ var comp = new Compressor(compressor.options, {
15390
+ mangle_options: compressor.mangle_options
15391
+ });
15097
15392
  ast = ast.transform(comp);
15098
15393
  ast.figure_out_scope(mangle);
15099
15394
  base54.reset();
@@ -16276,8 +16571,8 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
16276
16571
  }
16277
16572
 
16278
16573
  if (replace) {
16279
- const name_length = def.name.length;
16280
- const replace_size = replace.size();
16574
+ const name_length = self.size(compressor);
16575
+ const replace_size = replace.size(compressor);
16281
16576
 
16282
16577
  let overhead = 0;
16283
16578
  if (compressor.option("unused") && !compressor.exposed(def)) {
@@ -16482,6 +16777,10 @@ function is_nullish(node) {
16482
16777
  && (fixed = node.definition().fixed) instanceof AST_Node
16483
16778
  && is_nullish(fixed)
16484
16779
  )
16780
+ // Recurse into those optional chains!
16781
+ || node instanceof AST_PropAccess && node.optional && is_nullish(node.expression)
16782
+ || node instanceof AST_Call && node.optional && is_nullish(node.expression)
16783
+ || node instanceof AST_Chain && is_nullish(node.expression)
16485
16784
  );
16486
16785
  }
16487
16786
 
@@ -16864,6 +17163,41 @@ function safe_to_flatten(value, compressor) {
16864
17163
  return compressor.parent() instanceof AST_New;
16865
17164
  }
16866
17165
 
17166
+ AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
17167
+ if (!compressor.option("properties")) return;
17168
+ var arrows = compressor.option("unsafe_arrows") && compressor.option("ecma") >= 2015;
17169
+ var expr = this.expression;
17170
+ if (expr instanceof AST_Object) {
17171
+ var props = expr.properties;
17172
+ for (var i = props.length; --i >= 0;) {
17173
+ var prop = props[i];
17174
+ if ("" + (prop instanceof AST_ConciseMethod ? prop.key.name : prop.key) == key) {
17175
+ if (!props.every((prop) => {
17176
+ return prop instanceof AST_ObjectKeyVal
17177
+ || arrows && prop instanceof AST_ConciseMethod && !prop.is_generator;
17178
+ })) break;
17179
+ if (!safe_to_flatten(prop.value, compressor)) break;
17180
+ return make_node(AST_Sub, this, {
17181
+ expression: make_node(AST_Array, expr, {
17182
+ elements: props.map(function(prop) {
17183
+ var v = prop.value;
17184
+ if (v instanceof AST_Accessor) v = make_node(AST_Function, v, v);
17185
+ var k = prop.key;
17186
+ if (k instanceof AST_Node && !(k instanceof AST_SymbolMethod)) {
17187
+ return make_sequence(prop, [ k, v ]);
17188
+ }
17189
+ return v;
17190
+ })
17191
+ }),
17192
+ property: make_node(AST_Number, this, {
17193
+ value: i
17194
+ })
17195
+ });
17196
+ }
17197
+ }
17198
+ }
17199
+ });
17200
+
16867
17201
  def_optimize(AST_Sub, function(self, compressor) {
16868
17202
  var expr = self.expression;
16869
17203
  var prop = self.property;
@@ -16886,6 +17220,7 @@ def_optimize(AST_Sub, function(self, compressor) {
16886
17220
  && property.length <= prop.size() + 1) {
16887
17221
  return make_node(AST_Dot, self, {
16888
17222
  expression: expr,
17223
+ optional: self.optional,
16889
17224
  property: property,
16890
17225
  quote: prop.quote,
16891
17226
  }).optimize(compressor);
@@ -16988,6 +17323,14 @@ def_optimize(AST_Sub, function(self, compressor) {
16988
17323
  ev = make_node_from_constant(ev, self).optimize(compressor);
16989
17324
  return best_of(compressor, ev, self);
16990
17325
  }
17326
+ if (self.optional && is_nullish(self.expression)) {
17327
+ return make_node(AST_Undefined, self);
17328
+ }
17329
+ return self;
17330
+ });
17331
+
17332
+ def_optimize(AST_Chain, function (self, compressor) {
17333
+ self.expression = self.expression.optimize(compressor);
16991
17334
  return self;
16992
17335
  });
16993
17336
 
@@ -17004,41 +17347,6 @@ AST_Lambda.DEFMETHOD("contains_this", function() {
17004
17347
  });
17005
17348
  });
17006
17349
 
17007
- AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
17008
- if (!compressor.option("properties")) return;
17009
- var arrows = compressor.option("unsafe_arrows") && compressor.option("ecma") >= 2015;
17010
- var expr = this.expression;
17011
- if (expr instanceof AST_Object) {
17012
- var props = expr.properties;
17013
- for (var i = props.length; --i >= 0;) {
17014
- var prop = props[i];
17015
- if ("" + (prop instanceof AST_ConciseMethod ? prop.key.name : prop.key) == key) {
17016
- if (!props.every((prop) => {
17017
- return prop instanceof AST_ObjectKeyVal
17018
- || arrows && prop instanceof AST_ConciseMethod && !prop.is_generator;
17019
- })) break;
17020
- if (!safe_to_flatten(prop.value, compressor)) break;
17021
- return make_node(AST_Sub, this, {
17022
- expression: make_node(AST_Array, expr, {
17023
- elements: props.map(function(prop) {
17024
- var v = prop.value;
17025
- if (v instanceof AST_Accessor) v = make_node(AST_Function, v, v);
17026
- var k = prop.key;
17027
- if (k instanceof AST_Node && !(k instanceof AST_SymbolMethod)) {
17028
- return make_sequence(prop, [ k, v ]);
17029
- }
17030
- return v;
17031
- })
17032
- }),
17033
- property: make_node(AST_Number, this, {
17034
- value: i
17035
- })
17036
- });
17037
- }
17038
- }
17039
- }
17040
- });
17041
-
17042
17350
  def_optimize(AST_Dot, function(self, compressor) {
17043
17351
  const parent = compressor.parent();
17044
17352
  if (is_lhs(self, parent)) return self;
@@ -17089,6 +17397,9 @@ def_optimize(AST_Dot, function(self, compressor) {
17089
17397
  ev = make_node_from_constant(ev, self).optimize(compressor);
17090
17398
  return best_of(compressor, ev, self);
17091
17399
  }
17400
+ if (self.optional && is_nullish(self.expression)) {
17401
+ return make_node(AST_Undefined, self);
17402
+ }
17092
17403
  return self;
17093
17404
  });
17094
17405
 
@@ -17172,10 +17483,10 @@ def_optimize(AST_Function, function(self, compressor) {
17172
17483
  && !self.is_generator
17173
17484
  && !self.uses_arguments
17174
17485
  && !self.pinned()) {
17175
- const has_special_symbol = walk(self, node => {
17486
+ const uses_this = walk(self, node => {
17176
17487
  if (node instanceof AST_This) return walk_abort;
17177
17488
  });
17178
- if (!has_special_symbol) return make_node(AST_Arrow, self, self).optimize(compressor);
17489
+ if (!uses_this) return make_node(AST_Arrow, self, self).optimize(compressor);
17179
17490
  }
17180
17491
  return self;
17181
17492
  });
@@ -17267,7 +17578,7 @@ function lift_key(self, compressor) {
17267
17578
  if (!compressor.option("computed_props")) return self;
17268
17579
  // save a comparison in the typical case
17269
17580
  if (!(self.key instanceof AST_Constant)) return self;
17270
- // whitelist acceptable props as not all AST_Constants are true constants
17581
+ // allow certain acceptable props as not all AST_Constants are true constants
17271
17582
  if (self.key instanceof AST_String || self.key instanceof AST_Number) {
17272
17583
  if (self.key.value === "__proto__") return self;
17273
17584
  if (self.key.value == "constructor"
@@ -23301,6 +23612,8 @@ function mangle_properties(ast, options) {
23301
23612
  } else if (node instanceof AST_Call
23302
23613
  && node.expression.print_to_string() == "Object.defineProperty") {
23303
23614
  addStrings(node.args[1], add);
23615
+ } else if (node instanceof AST_Binary && node.operator === "in") {
23616
+ addStrings(node.left, add);
23304
23617
  }
23305
23618
  }));
23306
23619
 
@@ -23325,6 +23638,8 @@ function mangle_properties(ast, options) {
23325
23638
  } else if (node instanceof AST_Call
23326
23639
  && node.expression.print_to_string() == "Object.defineProperty") {
23327
23640
  node.args[1] = mangleStrings(node.args[1]);
23641
+ } else if (node instanceof AST_Binary && node.operator === "in") {
23642
+ node.left = mangleStrings(node.left);
23328
23643
  }
23329
23644
  }));
23330
23645
 
@@ -23557,7 +23872,11 @@ async function minify(files, options) {
23557
23872
  }
23558
23873
  if (timings) timings.rename = Date.now();
23559
23874
  if (timings) timings.compress = Date.now();
23560
- if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
23875
+ if (options.compress) {
23876
+ toplevel = new Compressor(options.compress, {
23877
+ mangle_options: options.mangle
23878
+ }).compress(toplevel);
23879
+ }
23561
23880
  if (timings) timings.scope = Date.now();
23562
23881
  if (options.mangle) toplevel.figure_out_scope(options.mangle);
23563
23882
  if (timings) timings.mangle = Date.now();
@@ -23704,6 +24023,7 @@ async function run_cli({ program, packageJson, fs, path }) {
23704
24023
  options[name] = program[name];
23705
24024
  }
23706
24025
  });
24026
+
23707
24027
  if ("ecma" in program) {
23708
24028
  if (program.ecma != (program.ecma | 0)) fatal("ERROR: ecma must be an integer");
23709
24029
  const ecma = program.ecma | 0;
@@ -23933,7 +24253,7 @@ async function run_cli({ program, packageJson, fs, path }) {
23933
24253
  }
23934
24254
  } else if (program.output) {
23935
24255
  fs.writeFileSync(program.output, result.code);
23936
- if (options.sourceMap.url !== "inline" && result.map) {
24256
+ if (options.sourceMap && options.sourceMap.url !== "inline" && result.map) {
23937
24257
  fs.writeFileSync(program.output + ".map", result.map);
23938
24258
  }
23939
24259
  } else {