terser 5.7.2 → 5.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1214,7 +1214,7 @@ function parse($TEXT, options) {
1214
1214
  // Example: /* I count */ ( /* I don't */ foo() )
1215
1215
  // Useful because comments_before property of call with parens outside
1216
1216
  // contains both comments inside and outside these parens. Used to find the
1217
- // right #__PURE__ comments for an expression
1217
+
1218
1218
  const outer_comments_before_counts = new WeakMap();
1219
1219
 
1220
1220
  options = defaults(options, {
@@ -1387,7 +1387,7 @@ function parse($TEXT, options) {
1387
1387
  }
1388
1388
  if (S.token.value == "import" && !is_token(peek(), "punc", "(") && !is_token(peek(), "punc", ".")) {
1389
1389
  next();
1390
- var node = import_();
1390
+ var node = import_statement();
1391
1391
  semicolon();
1392
1392
  return node;
1393
1393
  }
@@ -1539,7 +1539,7 @@ function parse($TEXT, options) {
1539
1539
  case "export":
1540
1540
  if (!is_token(peek(), "punc", "(")) {
1541
1541
  next();
1542
- var node = export_();
1542
+ var node = export_statement();
1543
1543
  if (is("punc", ";")) semicolon();
1544
1544
  return node;
1545
1545
  }
@@ -2716,7 +2716,7 @@ function parse($TEXT, options) {
2716
2716
  };
2717
2717
 
2718
2718
  const is_not_method_start = () =>
2719
- !is("punc", "(") && !is("punc", ",") && !is("punc", "}") && !is("operator", "=");
2719
+ !is("punc", "(") && !is("punc", ",") && !is("punc", "}") && !is("punc", ";") && !is("operator", "=");
2720
2720
 
2721
2721
  var is_async = false;
2722
2722
  var is_static = false;
@@ -2831,7 +2831,15 @@ function parse($TEXT, options) {
2831
2831
  }
2832
2832
  }
2833
2833
 
2834
- function import_() {
2834
+ function maybe_import_assertion() {
2835
+ if (is("name", "assert") && !has_newline_before(S.token)) {
2836
+ next();
2837
+ return object_or_destructuring_();
2838
+ }
2839
+ return null;
2840
+ }
2841
+
2842
+ function import_statement() {
2835
2843
  var start = prev();
2836
2844
 
2837
2845
  var imported_name;
@@ -2854,16 +2862,20 @@ function parse($TEXT, options) {
2854
2862
  unexpected();
2855
2863
  }
2856
2864
  next();
2865
+
2866
+ const assert_clause = maybe_import_assertion();
2867
+
2857
2868
  return new AST_Import({
2858
- start: start,
2859
- imported_name: imported_name,
2860
- imported_names: imported_names,
2869
+ start,
2870
+ imported_name,
2871
+ imported_names,
2861
2872
  module_name: new AST_String({
2862
2873
  start: mod_str,
2863
2874
  value: mod_str.value,
2864
2875
  quote: mod_str.quote,
2865
2876
  end: mod_str,
2866
2877
  }),
2878
+ assert_clause,
2867
2879
  end: S.token,
2868
2880
  });
2869
2881
  }
@@ -2971,7 +2983,7 @@ function parse($TEXT, options) {
2971
2983
  return names;
2972
2984
  }
2973
2985
 
2974
- function export_() {
2986
+ function export_statement() {
2975
2987
  var start = S.token;
2976
2988
  var is_default;
2977
2989
  var exported_names;
@@ -2989,6 +3001,8 @@ function parse($TEXT, options) {
2989
3001
  }
2990
3002
  next();
2991
3003
 
3004
+ const assert_clause = maybe_import_assertion();
3005
+
2992
3006
  return new AST_Export({
2993
3007
  start: start,
2994
3008
  is_default: is_default,
@@ -3000,6 +3014,7 @@ function parse($TEXT, options) {
3000
3014
  end: mod_str,
3001
3015
  }),
3002
3016
  end: prev(),
3017
+ assert_clause
3003
3018
  });
3004
3019
  } else {
3005
3020
  return new AST_Export({
@@ -3045,6 +3060,7 @@ function parse($TEXT, options) {
3045
3060
  exported_value: exported_value,
3046
3061
  exported_definition: exported_definition,
3047
3062
  end: prev(),
3063
+ assert_clause: null
3048
3064
  });
3049
3065
  }
3050
3066
 
@@ -4378,12 +4394,13 @@ var AST_NameMapping = DEFNODE("NameMapping", "foreign_name name", {
4378
4394
  },
4379
4395
  });
4380
4396
 
4381
- var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
4397
+ var AST_Import = DEFNODE("Import", "imported_name imported_names module_name assert_clause", {
4382
4398
  $documentation: "An `import` statement",
4383
4399
  $propdoc: {
4384
4400
  imported_name: "[AST_SymbolImport] The name of the variable holding the module's default export.",
4385
4401
  imported_names: "[AST_NameMapping*] The names of non-default imported variables",
4386
4402
  module_name: "[AST_String] String literal describing where this module came from",
4403
+ assert_clause: "[AST_Object?] The import assertion"
4387
4404
  },
4388
4405
  _walk: function(visitor) {
4389
4406
  return visitor._visit(this, function() {
@@ -4412,14 +4429,15 @@ var AST_ImportMeta = DEFNODE("ImportMeta", null, {
4412
4429
  $documentation: "A reference to import.meta",
4413
4430
  });
4414
4431
 
4415
- var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default exported_names module_name", {
4432
+ var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default exported_names module_name assert_clause", {
4416
4433
  $documentation: "An `export` statement",
4417
4434
  $propdoc: {
4418
4435
  exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition",
4419
4436
  exported_value: "[AST_Node?] An exported value",
4420
4437
  exported_names: "[AST_NameMapping*?] List of exported names",
4421
4438
  module_name: "[AST_String?] Name of the file to load exports from",
4422
- is_default: "[Boolean] Whether this is the default exported value of this module"
4439
+ is_default: "[Boolean] Whether this is the default exported value of this module",
4440
+ assert_clause: "[AST_Object?] The import assertion"
4423
4441
  },
4424
4442
  _walk: function (visitor) {
4425
4443
  return visitor._visit(this, function () {
@@ -4819,7 +4837,7 @@ var AST_ClassProperty = DEFNODE("ClassProperty", "static quote", {
4819
4837
  }
4820
4838
  }, AST_ObjectProperty);
4821
4839
 
4822
- var AST_ClassPrivateProperty = DEFNODE("ClassProperty", "", {
4840
+ var AST_ClassPrivateProperty = DEFNODE("ClassPrivateProperty", "", {
4823
4841
  $documentation: "A class property for a private property",
4824
4842
  }, AST_ClassProperty);
4825
4843
 
@@ -5521,7 +5539,7 @@ def_transform(AST_Sequence, function(self, tw) {
5521
5539
  : [new AST_Number({ value: 0 })];
5522
5540
  });
5523
5541
 
5524
- def_transform(AST_Dot, function(self, tw) {
5542
+ def_transform(AST_PropAccess, function(self, tw) {
5525
5543
  self.expression = self.expression.transform(tw);
5526
5544
  });
5527
5545
 
@@ -5672,6 +5690,24 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
5672
5690
  return body;
5673
5691
  };
5674
5692
 
5693
+ const assert_clause_from_moz = (assertions) => {
5694
+ if (assertions && assertions.length > 0) {
5695
+ return new AST_Object({
5696
+ start: my_start_token(assertions),
5697
+ end: my_end_token(assertions),
5698
+ properties: assertions.map((assertion_kv) =>
5699
+ new AST_ObjectKeyVal({
5700
+ start: my_start_token(assertion_kv),
5701
+ end: my_end_token(assertion_kv),
5702
+ key: assertion_kv.key.name || assertion_kv.key.value,
5703
+ value: from_moz(assertion_kv.value)
5704
+ })
5705
+ )
5706
+ });
5707
+ }
5708
+ return null;
5709
+ };
5710
+
5675
5711
  var MOZ_TO_ME = {
5676
5712
  Program: function(M) {
5677
5713
  return new AST_Toplevel({
@@ -5992,7 +6028,8 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
5992
6028
  end : my_end_token(M),
5993
6029
  imported_name: imported_name,
5994
6030
  imported_names : imported_names,
5995
- module_name : from_moz(M.source)
6031
+ module_name : from_moz(M.source),
6032
+ assert_clause: assert_clause_from_moz(M.assertions)
5996
6033
  });
5997
6034
  },
5998
6035
  ExportAllDeclaration: function(M) {
@@ -6005,7 +6042,8 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
6005
6042
  foreign_name: new AST_SymbolExportForeign({ name: "*" })
6006
6043
  })
6007
6044
  ],
6008
- module_name: from_moz(M.source)
6045
+ module_name: from_moz(M.source),
6046
+ assert_clause: assert_clause_from_moz(M.assertions)
6009
6047
  });
6010
6048
  },
6011
6049
  ExportNamedDeclaration: function(M) {
@@ -6019,7 +6057,8 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
6019
6057
  name: from_moz(specifier.local)
6020
6058
  });
6021
6059
  }) : null,
6022
- module_name: from_moz(M.source)
6060
+ module_name: from_moz(M.source),
6061
+ assert_clause: assert_clause_from_moz(M.assertions)
6023
6062
  });
6024
6063
  },
6025
6064
  ExportDefaultDeclaration: function(M) {
@@ -6311,12 +6350,30 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
6311
6350
  };
6312
6351
  });
6313
6352
 
6353
+ const assert_clause_to_moz = assert_clause => {
6354
+ const assertions = [];
6355
+ if (assert_clause) {
6356
+ for (const { key, value } of assert_clause.properties) {
6357
+ const key_moz = is_basic_identifier_string(key)
6358
+ ? { type: "Identifier", name: key }
6359
+ : { type: "Literal", value: key, raw: JSON.stringify(key) };
6360
+ assertions.push({
6361
+ type: "ImportAttribute",
6362
+ key: key_moz,
6363
+ value: to_moz(value)
6364
+ });
6365
+ }
6366
+ }
6367
+ return assertions;
6368
+ };
6369
+
6314
6370
  def_to_moz(AST_Export, function To_Moz_ExportDeclaration(M) {
6315
6371
  if (M.exported_names) {
6316
6372
  if (M.exported_names[0].name.name === "*") {
6317
6373
  return {
6318
6374
  type: "ExportAllDeclaration",
6319
- source: to_moz(M.module_name)
6375
+ source: to_moz(M.module_name),
6376
+ assertions: assert_clause_to_moz(M.assert_clause)
6320
6377
  };
6321
6378
  }
6322
6379
  return {
@@ -6329,7 +6386,8 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
6329
6386
  };
6330
6387
  }),
6331
6388
  declaration: to_moz(M.exported_definition),
6332
- source: to_moz(M.module_name)
6389
+ source: to_moz(M.module_name),
6390
+ assertions: assert_clause_to_moz(M.assert_clause)
6333
6391
  };
6334
6392
  }
6335
6393
  return {
@@ -6363,7 +6421,8 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
6363
6421
  return {
6364
6422
  type: "ImportDeclaration",
6365
6423
  specifiers: specifiers,
6366
- source: to_moz(M.module_name)
6424
+ source: to_moz(M.module_name),
6425
+ assertions: assert_clause_to_moz(M.assert_clause)
6367
6426
  };
6368
6427
  });
6369
6428
 
@@ -6920,6 +6979,48 @@ function is_some_comments(comment) {
6920
6979
  );
6921
6980
  }
6922
6981
 
6982
+ class Rope {
6983
+ constructor() {
6984
+ this.committed = "";
6985
+ this.current = "";
6986
+ }
6987
+
6988
+ append(str) {
6989
+ this.current += str;
6990
+ }
6991
+
6992
+ insertAt(char, index) {
6993
+ const { committed, current } = this;
6994
+ if (index < committed.length) {
6995
+ this.committed = committed.slice(0, index) + char + committed.slice(index);
6996
+ } else if (index === committed.length) {
6997
+ this.committed += char;
6998
+ } else {
6999
+ index -= committed.length;
7000
+ this.committed += current.slice(0, index) + char;
7001
+ this.current = current.slice(index);
7002
+ }
7003
+ }
7004
+
7005
+ charAt(index) {
7006
+ const { committed } = this;
7007
+ if (index < committed.length) return committed[index];
7008
+ return this.current[index - committed.length];
7009
+ }
7010
+
7011
+ curLength() {
7012
+ return this.current.length;
7013
+ }
7014
+
7015
+ length() {
7016
+ return this.committed.length + this.current.length;
7017
+ }
7018
+
7019
+ toString() {
7020
+ return this.committed + this.current;
7021
+ }
7022
+ }
7023
+
6923
7024
  function OutputStream(options) {
6924
7025
 
6925
7026
  var readonly = !options;
@@ -6984,11 +7085,11 @@ function OutputStream(options) {
6984
7085
  var current_col = 0;
6985
7086
  var current_line = 1;
6986
7087
  var current_pos = 0;
6987
- var OUTPUT = "";
7088
+ var OUTPUT = new Rope();
6988
7089
  let printed_comments = new Set();
6989
7090
 
6990
- var to_utf8 = options.ascii_only ? function(str, identifier) {
6991
- if (options.ecma >= 2015 && !options.safari10) {
7091
+ var to_utf8 = options.ascii_only ? function(str, identifier = false, regexp = false) {
7092
+ if (options.ecma >= 2015 && !options.safari10 && !regexp) {
6992
7093
  str = str.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function(ch) {
6993
7094
  var code = get_full_char_code(ch, 0).toString(16);
6994
7095
  return "\\u{" + code + "}";
@@ -7093,9 +7194,11 @@ function OutputStream(options) {
7093
7194
  var do_add_mapping = mappings ? function() {
7094
7195
  mappings.forEach(function(mapping) {
7095
7196
  try {
7096
- let name = !mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name;
7097
- if (name instanceof AST_Symbol) {
7098
- name = name.name;
7197
+ let { name, token } = mapping;
7198
+ if (token.type == "name" || token.type === "privatename") {
7199
+ name = token.value;
7200
+ } else if (name instanceof AST_Symbol) {
7201
+ name = token.type === "string" ? token.value : name.name;
7099
7202
  }
7100
7203
  options.source_map.add(
7101
7204
  mapping.token.file,
@@ -7113,19 +7216,18 @@ function OutputStream(options) {
7113
7216
  var ensure_line_len = options.max_line_len ? function() {
7114
7217
  if (current_col > options.max_line_len) {
7115
7218
  if (might_add_newline) {
7116
- var left = OUTPUT.slice(0, might_add_newline);
7117
- var right = OUTPUT.slice(might_add_newline);
7219
+ OUTPUT.insertAt("\n", might_add_newline);
7220
+ const curLength = OUTPUT.curLength();
7118
7221
  if (mappings) {
7119
- var delta = right.length - current_col;
7222
+ var delta = curLength - current_col;
7120
7223
  mappings.forEach(function(mapping) {
7121
7224
  mapping.line++;
7122
7225
  mapping.col += delta;
7123
7226
  });
7124
7227
  }
7125
- OUTPUT = left + "\n" + right;
7126
7228
  current_line++;
7127
7229
  current_pos++;
7128
- current_col = right.length;
7230
+ current_col = curLength;
7129
7231
  }
7130
7232
  }
7131
7233
  if (might_add_newline) {
@@ -7159,13 +7261,13 @@ function OutputStream(options) {
7159
7261
 
7160
7262
  if (prev === ":" && ch === "}" || (!ch || !";}".includes(ch)) && prev !== ";") {
7161
7263
  if (options.semicolons || requireSemicolonChars.has(ch)) {
7162
- OUTPUT += ";";
7264
+ OUTPUT.append(";");
7163
7265
  current_col++;
7164
7266
  current_pos++;
7165
7267
  } else {
7166
7268
  ensure_line_len();
7167
7269
  if (current_col > 0) {
7168
- OUTPUT += "\n";
7270
+ OUTPUT.append("\n");
7169
7271
  current_pos++;
7170
7272
  current_line++;
7171
7273
  current_col = 0;
@@ -7189,7 +7291,7 @@ function OutputStream(options) {
7189
7291
  || (ch == "/" && ch == prev)
7190
7292
  || ((ch == "+" || ch == "-") && ch == last)
7191
7293
  ) {
7192
- OUTPUT += " ";
7294
+ OUTPUT.append(" ");
7193
7295
  current_col++;
7194
7296
  current_pos++;
7195
7297
  }
@@ -7207,7 +7309,7 @@ function OutputStream(options) {
7207
7309
  if (!might_add_newline) do_add_mapping();
7208
7310
  }
7209
7311
 
7210
- OUTPUT += str;
7312
+ OUTPUT.append(str);
7211
7313
  has_parens = str[str.length - 1] == "(";
7212
7314
  current_pos += str.length;
7213
7315
  var a = str.split(/\r?\n/), n = a.length - 1;
@@ -7247,15 +7349,15 @@ function OutputStream(options) {
7247
7349
 
7248
7350
  var newline = options.beautify ? function() {
7249
7351
  if (newline_insert < 0) return print("\n");
7250
- if (OUTPUT[newline_insert] != "\n") {
7251
- OUTPUT = OUTPUT.slice(0, newline_insert) + "\n" + OUTPUT.slice(newline_insert);
7352
+ if (OUTPUT.charAt(newline_insert) != "\n") {
7353
+ OUTPUT.insertAt("\n", newline_insert);
7252
7354
  current_pos++;
7253
7355
  current_line++;
7254
7356
  }
7255
7357
  newline_insert++;
7256
7358
  } : options.max_line_len ? function() {
7257
7359
  ensure_line_len();
7258
- might_add_newline = OUTPUT.length;
7360
+ might_add_newline = OUTPUT.length();
7259
7361
  } : noop;
7260
7362
 
7261
7363
  var semicolon = options.beautify ? function() {
@@ -7321,13 +7423,14 @@ function OutputStream(options) {
7321
7423
  if (might_add_newline) {
7322
7424
  ensure_line_len();
7323
7425
  }
7324
- return OUTPUT;
7426
+ return OUTPUT.toString();
7325
7427
  }
7326
7428
 
7327
7429
  function has_nlb() {
7328
- let n = OUTPUT.length - 1;
7430
+ const output = OUTPUT.toString();
7431
+ let n = output.length - 1;
7329
7432
  while (n >= 0) {
7330
- const code = OUTPUT.charCodeAt(n);
7433
+ const code = output.charCodeAt(n);
7331
7434
  if (code === CODE_LINE_BREAK) {
7332
7435
  return true;
7333
7436
  }
@@ -7464,7 +7567,7 @@ function OutputStream(options) {
7464
7567
  !/comment[134]/.test(c.type)
7465
7568
  ))) return;
7466
7569
  printed_comments.add(comments);
7467
- var insert = OUTPUT.length;
7570
+ var insert = OUTPUT.length();
7468
7571
  comments.filter(comment_filter, node).forEach(function(c, i) {
7469
7572
  if (printed_comments.has(c)) return;
7470
7573
  printed_comments.add(c);
@@ -7493,7 +7596,7 @@ function OutputStream(options) {
7493
7596
  need_space = true;
7494
7597
  }
7495
7598
  });
7496
- if (OUTPUT.length > insert) newline_insert = insert;
7599
+ if (OUTPUT.length() > insert) newline_insert = insert;
7497
7600
  }
7498
7601
 
7499
7602
  var stack = [];
@@ -7523,7 +7626,7 @@ function OutputStream(options) {
7523
7626
  var encoded = encode_string(str, quote);
7524
7627
  if (escape_directive === true && !encoded.includes("\\")) {
7525
7628
  // Insert semicolons to break directive prologue
7526
- if (!EXPECT_DIRECTIVE.test(OUTPUT)) {
7629
+ if (!EXPECT_DIRECTIVE.test(OUTPUT.toString())) {
7527
7630
  force_semicolon();
7528
7631
  }
7529
7632
  force_semicolon();
@@ -8379,6 +8482,10 @@ function OutputStream(options) {
8379
8482
  output.space();
8380
8483
  }
8381
8484
  self.module_name.print(output);
8485
+ if (self.assert_clause) {
8486
+ output.print("assert");
8487
+ self.assert_clause.print(output);
8488
+ }
8382
8489
  output.semicolon();
8383
8490
  });
8384
8491
  DEFPRINT(AST_ImportMeta, function(self, output) {
@@ -8444,6 +8551,10 @@ function OutputStream(options) {
8444
8551
  output.space();
8445
8552
  self.module_name.print(output);
8446
8553
  }
8554
+ if (self.assert_clause) {
8555
+ output.print("assert");
8556
+ self.assert_clause.print(output);
8557
+ }
8447
8558
  if (self.exported_value
8448
8559
  && !(self.exported_value instanceof AST_Defun ||
8449
8560
  self.exported_value instanceof AST_Function ||
@@ -8568,6 +8679,7 @@ function OutputStream(options) {
8568
8679
 
8569
8680
  if (self.optional) output.print("?");
8570
8681
  output.print(".#");
8682
+ output.add_mapping(self.end);
8571
8683
  output.print_name(prop);
8572
8684
  });
8573
8685
  DEFPRINT(AST_Sub, function(self, output) {
@@ -8904,7 +9016,7 @@ function OutputStream(options) {
8904
9016
  flags = flags ? sort_regexp_flags(flags) : "";
8905
9017
  source = source.replace(r_slash_script, slash_script_replace);
8906
9018
 
8907
- output.print(output.to_utf8(`/${source}/${flags}`));
9019
+ output.print(output.to_utf8(`/${source}/${flags}`, false, true));
8908
9020
 
8909
9021
  const parent = output.parent();
8910
9022
  if (
@@ -9021,8 +9133,10 @@ function OutputStream(options) {
9021
9133
  DEFMAP([
9022
9134
  AST_ObjectGetter,
9023
9135
  AST_ObjectSetter,
9136
+ AST_PrivateGetter,
9137
+ AST_PrivateSetter,
9024
9138
  ], function(output) {
9025
- output.add_mapping(this.start, this.key.name);
9139
+ output.add_mapping(this.key.end, this.key.name);
9026
9140
  });
9027
9141
 
9028
9142
  DEFMAP([ AST_ObjectProperty ], function(output) {
@@ -9877,8 +9991,9 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
9877
9991
 
9878
9992
  function next_mangled(scope, options) {
9879
9993
  var ext = scope.enclosed;
9994
+ var nth_identifier = options.nth_identifier;
9880
9995
  out: while (true) {
9881
- var m = base54(++scope.cname);
9996
+ var m = nth_identifier.get(++scope.cname);
9882
9997
  if (ALL_RESERVED_WORDS.has(m)) continue; // skip over "do"
9883
9998
 
9884
9999
  // https://github.com/mishoo/UglifyJS2/issues/242 -- do not
@@ -9954,6 +10069,7 @@ AST_Symbol.DEFMETHOD("global", function() {
9954
10069
  AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
9955
10070
  options = defaults(options, {
9956
10071
  eval : false,
10072
+ nth_identifier : base54,
9957
10073
  ie8 : false,
9958
10074
  keep_classnames: false,
9959
10075
  keep_fnames : false,
@@ -9975,6 +10091,7 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
9975
10091
 
9976
10092
  AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
9977
10093
  options = this._default_mangler_options(options);
10094
+ var nth_identifier = options.nth_identifier;
9978
10095
 
9979
10096
  // We only need to mangle declaration nodes. Special logic wired
9980
10097
  // into the code generator will display the mangled name if it's
@@ -9988,6 +10105,8 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
9988
10105
  }
9989
10106
 
9990
10107
  const mangled_names = this.mangled_names = new Set();
10108
+ unmangleable_names = new Set();
10109
+
9991
10110
  if (options.cache) {
9992
10111
  this.globals.forEach(collect);
9993
10112
  if (options.cache.props) {
@@ -10026,7 +10145,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
10026
10145
  if (node instanceof AST_Label) {
10027
10146
  let name;
10028
10147
  do {
10029
- name = base54(++lname);
10148
+ name = nth_identifier.get(++lname);
10030
10149
  } while (ALL_RESERVED_WORDS.has(name));
10031
10150
  node.mangled_name = name;
10032
10151
  return true;
@@ -10040,7 +10159,6 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
10040
10159
  this.walk(tw);
10041
10160
 
10042
10161
  if (options.keep_fnames || options.keep_classnames) {
10043
- unmangleable_names = new Set();
10044
10162
  // Collect a set of short names which are unmangleable,
10045
10163
  // for use in avoiding collisions in next_mangled.
10046
10164
  to_mangle.forEach(def => {
@@ -10056,9 +10174,9 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
10056
10174
  unmangleable_names = null;
10057
10175
 
10058
10176
  function collect(symbol) {
10059
- const should_mangle = !options.reserved.has(symbol.name)
10060
- && !(symbol.export & MASK_EXPORT_DONT_MANGLE);
10061
- if (should_mangle) {
10177
+ if (symbol.export & MASK_EXPORT_DONT_MANGLE) {
10178
+ unmangleable_names.add(symbol.name);
10179
+ } else if (!options.reserved.has(symbol.name)) {
10062
10180
  to_mangle.push(symbol);
10063
10181
  }
10064
10182
  }
@@ -10088,9 +10206,12 @@ AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
10088
10206
  });
10089
10207
 
10090
10208
  AST_Toplevel.DEFMETHOD("expand_names", function(options) {
10091
- base54.reset();
10092
- base54.sort();
10093
10209
  options = this._default_mangler_options(options);
10210
+ var nth_identifier = options.nth_identifier;
10211
+ if (nth_identifier.reset && nth_identifier.sort) {
10212
+ nth_identifier.reset();
10213
+ nth_identifier.sort();
10214
+ }
10094
10215
  var avoid = this.find_colliding_names(options);
10095
10216
  var cname = 0;
10096
10217
  this.globals.forEach(rename);
@@ -10102,7 +10223,7 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
10102
10223
  function next_name() {
10103
10224
  var name;
10104
10225
  do {
10105
- name = base54(cname++);
10226
+ name = nth_identifier.get(cname++);
10106
10227
  } while (avoid.has(name) || ALL_RESERVED_WORDS.has(name));
10107
10228
  return name;
10108
10229
  }
@@ -10129,30 +10250,37 @@ AST_Sequence.DEFMETHOD("tail_node", function() {
10129
10250
 
10130
10251
  AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
10131
10252
  options = this._default_mangler_options(options);
10253
+ var nth_identifier = options.nth_identifier;
10254
+ if (!nth_identifier.reset || !nth_identifier.consider || !nth_identifier.sort) {
10255
+ // If the identifier mangler is invariant, skip computing character frequency.
10256
+ return;
10257
+ }
10258
+ nth_identifier.reset();
10259
+
10132
10260
  try {
10133
10261
  AST_Node.prototype.print = function(stream, force_parens) {
10134
10262
  this._print(stream, force_parens);
10135
10263
  if (this instanceof AST_Symbol && !this.unmangleable(options)) {
10136
- base54.consider(this.name, -1);
10264
+ nth_identifier.consider(this.name, -1);
10137
10265
  } else if (options.properties) {
10138
10266
  if (this instanceof AST_DotHash) {
10139
- base54.consider("#" + this.property, -1);
10267
+ nth_identifier.consider("#" + this.property, -1);
10140
10268
  } else if (this instanceof AST_Dot) {
10141
- base54.consider(this.property, -1);
10269
+ nth_identifier.consider(this.property, -1);
10142
10270
  } else if (this instanceof AST_Sub) {
10143
10271
  skip_string(this.property);
10144
10272
  }
10145
10273
  }
10146
10274
  };
10147
- base54.consider(this.print_to_string(), 1);
10275
+ nth_identifier.consider(this.print_to_string(), 1);
10148
10276
  } finally {
10149
10277
  AST_Node.prototype.print = AST_Node.prototype._print;
10150
10278
  }
10151
- base54.sort();
10279
+ nth_identifier.sort();
10152
10280
 
10153
10281
  function skip_string(node) {
10154
10282
  if (node instanceof AST_String) {
10155
- base54.consider(node.value, -1);
10283
+ nth_identifier.consider(node.value, -1);
10156
10284
  } else if (node instanceof AST_Conditional) {
10157
10285
  skip_string(node.consequent);
10158
10286
  skip_string(node.alternative);
@@ -10176,19 +10304,20 @@ const base54 = (() => {
10176
10304
  frequency.set(ch, 0);
10177
10305
  });
10178
10306
  }
10179
- base54.consider = function(str, delta) {
10307
+ function consider(str, delta) {
10180
10308
  for (var i = str.length; --i >= 0;) {
10181
10309
  frequency.set(str[i], frequency.get(str[i]) + delta);
10182
10310
  }
10183
- };
10311
+ }
10184
10312
  function compare(a, b) {
10185
10313
  return frequency.get(b) - frequency.get(a);
10186
10314
  }
10187
- base54.sort = function() {
10315
+ function sort() {
10188
10316
  chars = mergeSort(leading, compare).concat(mergeSort(digits, compare));
10189
- };
10190
- base54.reset = reset;
10317
+ }
10318
+ // Ensure this is in a usable initial state.
10191
10319
  reset();
10320
+ sort();
10192
10321
  function base54(num) {
10193
10322
  var ret = "", base = 54;
10194
10323
  num++;
@@ -10200,7 +10329,13 @@ const base54 = (() => {
10200
10329
  } while (num > 0);
10201
10330
  return ret;
10202
10331
  }
10203
- return base54;
10332
+
10333
+ return {
10334
+ get: base54,
10335
+ consider,
10336
+ reset,
10337
+ sort
10338
+ };
10204
10339
  })();
10205
10340
 
10206
10341
  let mangle_options = undefined;
@@ -11079,6 +11214,7 @@ const is_pure_native_fn = make_nested_lookup({
11079
11214
  "isExtensible",
11080
11215
  "isFrozen",
11081
11216
  "isSealed",
11217
+ "hasOwn",
11082
11218
  "keys",
11083
11219
  ],
11084
11220
  String: [
@@ -11252,9 +11388,8 @@ function is_undefined(node, compressor) {
11252
11388
  );
11253
11389
  }
11254
11390
 
11255
- // Find out if something is == null
11256
- // Used to optimize ?. and ??
11257
- function is_nullish(node, compressor) {
11391
+ // Is the node explicitly null or undefined.
11392
+ function is_null_or_undefined(node, compressor) {
11258
11393
  let fixed;
11259
11394
  return (
11260
11395
  node instanceof AST_Null
@@ -11264,13 +11399,29 @@ function is_nullish(node, compressor) {
11264
11399
  && (fixed = node.definition().fixed) instanceof AST_Node
11265
11400
  && is_nullish(fixed, compressor)
11266
11401
  )
11267
- // Recurse into those optional chains!
11268
- || node instanceof AST_PropAccess && node.optional && is_nullish(node.expression, compressor)
11269
- || node instanceof AST_Call && node.optional && is_nullish(node.expression, compressor)
11270
- || node instanceof AST_Chain && is_nullish(node.expression, compressor)
11271
11402
  );
11272
11403
  }
11273
11404
 
11405
+ // Find out if this expression is optionally chained from a base-point that we
11406
+ // can statically analyze as null or undefined.
11407
+ function is_nullish_shortcircuited(node, compressor) {
11408
+ if (node instanceof AST_PropAccess || node instanceof AST_Call) {
11409
+ return (
11410
+ (node.optional && is_null_or_undefined(node.expression, compressor))
11411
+ || is_nullish_shortcircuited(node.expression, compressor)
11412
+ );
11413
+ }
11414
+ if (node instanceof AST_Chain) return is_nullish_shortcircuited(node.expression, compressor);
11415
+ return false;
11416
+ }
11417
+
11418
+ // Find out if something is == null, or can short circuit into nullish.
11419
+ // Used to optimize ?. and ??
11420
+ function is_nullish(node, compressor) {
11421
+ if (is_null_or_undefined(node, compressor)) return true;
11422
+ return is_nullish_shortcircuited(node, compressor);
11423
+ }
11424
+
11274
11425
  // Determine if expression might cause side effects
11275
11426
  // If there's a possibility that a node may change something when it's executed, this returns true
11276
11427
  (function(def_has_side_effects) {
@@ -11378,13 +11529,12 @@ function is_nullish(node, compressor) {
11378
11529
  return any(this.elements, compressor);
11379
11530
  });
11380
11531
  def_has_side_effects(AST_Dot, function(compressor) {
11532
+ if (is_nullish(this, compressor)) return false;
11381
11533
  return !this.optional && this.expression.may_throw_on_access(compressor)
11382
11534
  || this.expression.has_side_effects(compressor);
11383
11535
  });
11384
11536
  def_has_side_effects(AST_Sub, function(compressor) {
11385
- if (this.optional && is_nullish(this.expression, compressor)) {
11386
- return false;
11387
- }
11537
+ if (is_nullish(this, compressor)) return false;
11388
11538
 
11389
11539
  return !this.optional && this.expression.may_throw_on_access(compressor)
11390
11540
  || this.expression.has_side_effects(compressor)
@@ -11452,7 +11602,7 @@ function is_nullish(node, compressor) {
11452
11602
  return any(this.body, compressor);
11453
11603
  });
11454
11604
  def_may_throw(AST_Call, function(compressor) {
11455
- if (this.optional && is_nullish(this.expression, compressor)) return false;
11605
+ if (is_nullish(this, compressor)) return false;
11456
11606
  if (any(this.args, compressor)) return true;
11457
11607
  if (this.is_callee_pure(compressor)) return false;
11458
11608
  if (this.expression.may_throw(compressor)) return true;
@@ -11511,12 +11661,12 @@ function is_nullish(node, compressor) {
11511
11661
  return this.body.may_throw(compressor);
11512
11662
  });
11513
11663
  def_may_throw(AST_Dot, function(compressor) {
11664
+ if (is_nullish(this, compressor)) return false;
11514
11665
  return !this.optional && this.expression.may_throw_on_access(compressor)
11515
11666
  || this.expression.may_throw(compressor);
11516
11667
  });
11517
11668
  def_may_throw(AST_Sub, function(compressor) {
11518
- if (this.optional && is_nullish(this.expression, compressor)) return false;
11519
-
11669
+ if (is_nullish(this, compressor)) return false;
11520
11670
  return !this.optional && this.expression.may_throw_on_access(compressor)
11521
11671
  || this.expression.may_throw(compressor)
11522
11672
  || this.property.may_throw(compressor);
@@ -12008,6 +12158,9 @@ function def_eval(node, func) {
12008
12158
  node.DEFMETHOD("_eval", func);
12009
12159
  }
12010
12160
 
12161
+ // Used to propagate a nullish short-circuit signal upwards through the chain.
12162
+ const nullish = Symbol("This AST_Chain is nullish");
12163
+
12011
12164
  // If the node has been successfully reduced to a constant,
12012
12165
  // then its value is returned; otherwise the element itself
12013
12166
  // is returned.
@@ -12019,7 +12172,7 @@ AST_Node.DEFMETHOD("evaluate", function (compressor) {
12019
12172
  var val = this._eval(compressor, 1);
12020
12173
  if (!val || val instanceof RegExp)
12021
12174
  return val;
12022
- if (typeof val == "function" || typeof val == "object")
12175
+ if (typeof val == "function" || typeof val == "object" || val == nullish)
12023
12176
  return this;
12024
12177
  return val;
12025
12178
  });
@@ -12264,11 +12417,8 @@ const regexp_flags = new Set([
12264
12417
  ]);
12265
12418
 
12266
12419
  def_eval(AST_PropAccess, function (compressor, depth) {
12267
- if (this.optional) {
12268
- const obj = this.expression._eval(compressor, depth);
12269
- if (obj == null)
12270
- return undefined;
12271
- }
12420
+ const obj = this.expression._eval(compressor, depth);
12421
+ if (obj === nullish || (this.optional && obj == null)) return nullish;
12272
12422
  if (compressor.option("unsafe")) {
12273
12423
  var key = this.property;
12274
12424
  if (key instanceof AST_Node) {
@@ -12323,16 +12473,19 @@ def_eval(AST_PropAccess, function (compressor, depth) {
12323
12473
 
12324
12474
  def_eval(AST_Chain, function (compressor, depth) {
12325
12475
  const evaluated = this.expression._eval(compressor, depth);
12326
- return evaluated === this.expression ? this : evaluated;
12476
+ return evaluated === nullish
12477
+ ? undefined
12478
+ : evaluated === this.expression
12479
+ ? this
12480
+ : evaluated;
12327
12481
  });
12328
12482
 
12329
12483
  def_eval(AST_Call, function (compressor, depth) {
12330
12484
  var exp = this.expression;
12331
- if (this.optional) {
12332
- const callee = this.expression._eval(compressor, depth);
12333
- if (callee == null)
12334
- return undefined;
12335
- }
12485
+
12486
+ const callee = exp._eval(compressor, depth);
12487
+ if (callee === nullish || (this.optional && callee == null)) return nullish;
12488
+
12336
12489
  if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
12337
12490
  var key = exp.property;
12338
12491
  if (key instanceof AST_Node) {
@@ -12464,8 +12617,8 @@ def_drop_side_effect_free(AST_Constant, return_null);
12464
12617
  def_drop_side_effect_free(AST_This, return_null);
12465
12618
 
12466
12619
  def_drop_side_effect_free(AST_Call, function (compressor, first_in_statement) {
12467
- if (this.optional && is_nullish(this.expression, compressor)) {
12468
- return make_node(AST_Undefined, this);
12620
+ if (is_nullish_shortcircuited(this, compressor)) {
12621
+ return this.expression.drop_side_effect_free(compressor, first_in_statement);
12469
12622
  }
12470
12623
 
12471
12624
  if (!this.is_callee_pure(compressor)) {
@@ -12641,21 +12794,19 @@ def_drop_side_effect_free(AST_Array, function (compressor, first_in_statement) {
12641
12794
  });
12642
12795
 
12643
12796
  def_drop_side_effect_free(AST_Dot, function (compressor, first_in_statement) {
12644
- if (this.optional) {
12645
- return is_nullish(this.expression, compressor) ? make_node(AST_Undefined, this) : this;
12797
+ if (is_nullish_shortcircuited(this, compressor)) {
12798
+ return this.expression.drop_side_effect_free(compressor, first_in_statement);
12646
12799
  }
12647
- if (this.expression.may_throw_on_access(compressor))
12648
- return this;
12800
+ if (this.expression.may_throw_on_access(compressor)) return this;
12649
12801
 
12650
12802
  return this.expression.drop_side_effect_free(compressor, first_in_statement);
12651
12803
  });
12652
12804
 
12653
12805
  def_drop_side_effect_free(AST_Sub, function (compressor, first_in_statement) {
12654
- if (this.optional) {
12655
- return is_nullish(this.expression, compressor) ? make_node(AST_Undefined, this) : this;
12806
+ if (is_nullish_shortcircuited(this, compressor)) {
12807
+ return this.expression.drop_side_effect_free(compressor, first_in_statement);
12656
12808
  }
12657
- if (this.expression.may_throw_on_access(compressor))
12658
- return this;
12809
+ if (this.expression.may_throw_on_access(compressor)) return this;
12659
12810
 
12660
12811
  var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
12661
12812
  if (!expression)
@@ -13527,7 +13678,9 @@ function tighten_body(statements, compressor) {
13527
13678
  // Replace variable with assignment when found
13528
13679
  if (can_replace
13529
13680
  && !(node instanceof AST_SymbolDeclaration)
13530
- && lhs.equivalent_to(node)) {
13681
+ && lhs.equivalent_to(node)
13682
+ && !shadows(node.scope, lvalues)
13683
+ ) {
13531
13684
  if (stop_if_hit) {
13532
13685
  abort = true;
13533
13686
  return node;
@@ -13575,7 +13728,7 @@ function tighten_body(statements, compressor) {
13575
13728
  || node instanceof AST_PropAccess
13576
13729
  && (side_effects || node.expression.may_throw_on_access(compressor))
13577
13730
  || node instanceof AST_SymbolRef
13578
- && (lvalues.get(node.name) || side_effects && may_modify(node))
13731
+ && ((lvalues.has(node.name) && lvalues.get(node.name).modified) || side_effects && may_modify(node))
13579
13732
  || node instanceof AST_VarDef && node.value
13580
13733
  && (lvalues.has(node.name.name) || side_effects && may_modify(node.name))
13581
13734
  || (sym = is_lhs(node.left, node))
@@ -13648,8 +13801,9 @@ function tighten_body(statements, compressor) {
13648
13801
  // Locate symbols which may execute code outside of scanning range
13649
13802
  var lvalues = get_lvalues(candidate);
13650
13803
  var lhs_local = is_lhs_local(lhs);
13651
- if (lhs instanceof AST_SymbolRef)
13652
- lvalues.set(lhs.name, false);
13804
+ if (lhs instanceof AST_SymbolRef) {
13805
+ lvalues.set(lhs.name, { def: lhs.definition(), modified: false });
13806
+ }
13653
13807
  var side_effects = value_has_side_effects(candidate);
13654
13808
  var replace_all = replace_all_symbols();
13655
13809
  var may_throw = candidate.may_throw(compressor);
@@ -13715,8 +13869,9 @@ function tighten_body(statements, compressor) {
13715
13869
  return false;
13716
13870
  let cur_scope = def.scope;
13717
13871
  while (cur_scope && cur_scope !== scope) {
13718
- if (cur_scope.variables.has(def.name))
13872
+ if (cur_scope.variables.has(def.name)) {
13719
13873
  return true;
13874
+ }
13720
13875
  cur_scope = cur_scope.parent_scope;
13721
13876
  }
13722
13877
  return false;
@@ -13997,8 +14152,14 @@ function tighten_body(statements, compressor) {
13997
14152
  var sym = node;
13998
14153
  while (sym instanceof AST_PropAccess)
13999
14154
  sym = sym.expression;
14000
- if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
14001
- lvalues.set(sym.name, lvalues.get(sym.name) || is_modified(compressor, tw, node, node, 0));
14155
+ if (sym instanceof AST_SymbolRef) {
14156
+ const prev = lvalues.get(sym.name);
14157
+ if (!prev || !prev.modified) {
14158
+ lvalues.set(sym.name, {
14159
+ def: sym.definition(),
14160
+ modified: is_modified(compressor, tw, node, node, 0)
14161
+ });
14162
+ }
14002
14163
  }
14003
14164
  });
14004
14165
  get_rvalue(expr).walk(tw);
@@ -14110,6 +14271,18 @@ function tighten_body(statements, compressor) {
14110
14271
  }
14111
14272
  return false;
14112
14273
  }
14274
+
14275
+ function shadows(newScope, lvalues) {
14276
+ for (const {def} of lvalues.values()) {
14277
+ let current = newScope;
14278
+ while (current && current !== def.scope) {
14279
+ let nested_def = current.variables.get(def.name);
14280
+ if (nested_def && nested_def !== def) return true;
14281
+ current = current.parent_scope;
14282
+ }
14283
+ }
14284
+ return false;
14285
+ }
14113
14286
  }
14114
14287
 
14115
14288
  function eliminate_spurious_blocks(statements) {
@@ -14853,7 +15026,8 @@ class Compressor extends TreeWalker {
14853
15026
  var passes = +this.options.passes || 1;
14854
15027
  var min_count = 1 / 0;
14855
15028
  var stopping = false;
14856
- var mangle = { ie8: this.option("ie8") };
15029
+ var nth_identifier = this.mangle_options && this.mangle_options.nth_identifier || base54;
15030
+ var mangle = { ie8: this.option("ie8"), nth_identifier: nth_identifier };
14857
15031
  for (var pass = 0; pass < passes; pass++) {
14858
15032
  this._toplevel.figure_out_scope(mangle);
14859
15033
  if (pass === 0 && this.option("drop_console")) {
@@ -16055,58 +16229,294 @@ def_optimize(AST_Switch, function(self, compressor) {
16055
16229
  }
16056
16230
  }
16057
16231
  }
16058
- if (aborts(branch)) {
16059
- var prev = body[body.length - 1];
16060
- if (aborts(prev) && prev.body.length == branch.body.length
16061
- && make_node(AST_BlockStatement, prev, prev).equivalent_to(make_node(AST_BlockStatement, branch, branch))) {
16062
- prev.body = [];
16063
- }
16064
- }
16065
16232
  body.push(branch);
16066
16233
  }
16067
16234
  while (i < len) eliminate_branch(self.body[i++], body[body.length - 1]);
16235
+ self.body = body;
16236
+
16237
+ let default_or_exact = default_branch || exact_match;
16238
+ default_branch = null;
16239
+ exact_match = null;
16240
+
16241
+ // group equivalent branches so they will be located next to each other,
16242
+ // that way the next micro-optimization will merge them.
16243
+ // ** bail micro-optimization if not a simple switch case with breaks
16244
+ if (body.every((branch, i) =>
16245
+ (branch === default_or_exact || branch.expression instanceof AST_Constant)
16246
+ && (branch.body.length === 0 || aborts(branch) || body.length - 1 === i))
16247
+ ) {
16248
+ for (let i = 0; i < body.length; i++) {
16249
+ const branch = body[i];
16250
+ for (let j = i + 1; j < body.length; j++) {
16251
+ const next = body[j];
16252
+ if (next.body.length === 0) continue;
16253
+ const last_branch = j === (body.length - 1);
16254
+ const equivalentBranch = branches_equivalent(next, branch, false);
16255
+ if (equivalentBranch || (last_branch && branches_equivalent(next, branch, true))) {
16256
+ if (!equivalentBranch && last_branch) {
16257
+ next.body.push(make_node(AST_Break));
16258
+ }
16259
+
16260
+ // let's find previous siblings with inert fallthrough...
16261
+ let x = j - 1;
16262
+ let fallthroughDepth = 0;
16263
+ while (x > i) {
16264
+ if (is_inert_body(body[x--])) {
16265
+ fallthroughDepth++;
16266
+ } else {
16267
+ break;
16268
+ }
16269
+ }
16270
+
16271
+ const plucked = body.splice(j - fallthroughDepth, 1 + fallthroughDepth);
16272
+ body.splice(i + 1, 0, ...plucked);
16273
+ i += plucked.length;
16274
+ }
16275
+ }
16276
+ }
16277
+ }
16278
+
16279
+ // merge equivalent branches in a row
16280
+ for (let i = 0; i < body.length; i++) {
16281
+ let branch = body[i];
16282
+ if (branch.body.length === 0) continue;
16283
+ if (!aborts(branch)) continue;
16284
+
16285
+ for (let j = i + 1; j < body.length; i++, j++) {
16286
+ let next = body[j];
16287
+ if (next.body.length === 0) continue;
16288
+ if (
16289
+ branches_equivalent(next, branch, false)
16290
+ || (j === body.length - 1 && branches_equivalent(next, branch, true))
16291
+ ) {
16292
+ branch.body = [];
16293
+ branch = next;
16294
+ continue;
16295
+ }
16296
+ break;
16297
+ }
16298
+ }
16299
+
16300
+ // Prune any empty branches at the end of the switch statement.
16301
+ {
16302
+ let i = body.length - 1;
16303
+ for (; i >= 0; i--) {
16304
+ let bbody = body[i].body;
16305
+ if (is_break(bbody[bbody.length - 1], compressor)) bbody.pop();
16306
+ if (!is_inert_body(body[i])) break;
16307
+ }
16308
+ // i now points to the index of a branch that contains a body. By incrementing, it's
16309
+ // pointing to the first branch that's empty.
16310
+ i++;
16311
+ if (!default_or_exact || body.indexOf(default_or_exact) >= i) {
16312
+ // The default behavior is to do nothing. We can take advantage of that to
16313
+ // remove all case expressions that are side-effect free that also do
16314
+ // nothing, since they'll default to doing nothing. But we can't remove any
16315
+ // case expressions before one that would side-effect, since they may cause
16316
+ // the side-effect to be skipped.
16317
+ for (let j = body.length - 1; j >= i; j--) {
16318
+ let branch = body[j];
16319
+ if (branch === default_or_exact) {
16320
+ default_or_exact = null;
16321
+ body.pop();
16322
+ } else if (!branch.expression.has_side_effects(compressor)) {
16323
+ body.pop();
16324
+ } else {
16325
+ break;
16326
+ }
16327
+ }
16328
+ }
16329
+ }
16330
+
16331
+
16332
+ // Prune side-effect free branches that fall into default.
16333
+ DEFAULT: if (default_or_exact) {
16334
+ let default_index = body.indexOf(default_or_exact);
16335
+ let default_body_index = default_index;
16336
+ for (; default_body_index < body.length - 1; default_body_index++) {
16337
+ if (!is_inert_body(body[default_body_index])) break;
16338
+ }
16339
+ if (default_body_index < body.length - 1) {
16340
+ break DEFAULT;
16341
+ }
16342
+
16343
+ let side_effect_index = body.length - 1;
16344
+ for (; side_effect_index >= 0; side_effect_index--) {
16345
+ let branch = body[side_effect_index];
16346
+ if (branch === default_or_exact) continue;
16347
+ if (branch.expression.has_side_effects(compressor)) break;
16348
+ }
16349
+ // If the default behavior comes after any side-effect case expressions,
16350
+ // then we can fold all side-effect free cases into the default branch.
16351
+ // If the side-effect case is after the default, then any side-effect
16352
+ // free cases could prevent the side-effect from occurring.
16353
+ if (default_body_index > side_effect_index) {
16354
+ let prev_body_index = default_index - 1;
16355
+ for (; prev_body_index >= 0; prev_body_index--) {
16356
+ if (!is_inert_body(body[prev_body_index])) break;
16357
+ }
16358
+ let before = Math.max(side_effect_index, prev_body_index) + 1;
16359
+ let after = default_index;
16360
+ if (side_effect_index > default_index) {
16361
+ // If the default falls into the same body as a side-effect
16362
+ // case, then we need preserve that case and only prune the
16363
+ // cases after it.
16364
+ after = side_effect_index;
16365
+ body[side_effect_index].body = body[default_body_index].body;
16366
+ } else {
16367
+ // The default will be the last branch.
16368
+ default_or_exact.body = body[default_body_index].body;
16369
+ }
16370
+
16371
+ // Prune everything after the default (or last side-effect case)
16372
+ // until the next case with a body.
16373
+ body.splice(after + 1, default_body_index - after);
16374
+ // Prune everything before the default that falls into it.
16375
+ body.splice(before, default_index - before);
16376
+ }
16377
+ }
16378
+
16379
+ // See if we can remove the switch entirely if all cases (the default) fall into the same case body.
16380
+ DEFAULT: if (default_or_exact) {
16381
+ let i = body.findIndex(branch => !is_inert_body(branch));
16382
+ let caseBody;
16383
+ // `i` is equal to one of the following:
16384
+ // - `-1`, there is no body in the switch statement.
16385
+ // - `body.length - 1`, all cases fall into the same body.
16386
+ // - anything else, there are multiple bodies in the switch.
16387
+ if (i === body.length - 1) {
16388
+ // All cases fall into the case body.
16389
+ let branch = body[i];
16390
+ if (has_nested_break(self)) break DEFAULT;
16391
+
16392
+ // This is the last case body, and we've already pruned any breaks, so it's
16393
+ // safe to hoist.
16394
+ caseBody = make_node(AST_BlockStatement, branch, {
16395
+ body: branch.body
16396
+ });
16397
+ branch.body = [];
16398
+ } else if (i !== -1) {
16399
+ // If there are multiple bodies, then we cannot optimize anything.
16400
+ break DEFAULT;
16401
+ }
16402
+
16403
+ let sideEffect = body.find(branch => {
16404
+ return (
16405
+ branch !== default_or_exact
16406
+ && branch.expression.has_side_effects(compressor)
16407
+ );
16408
+ });
16409
+ // If no cases cause a side-effect, we can eliminate the switch entirely.
16410
+ if (!sideEffect) {
16411
+ return make_node(AST_BlockStatement, self, {
16412
+ body: decl.concat(
16413
+ statement(self.expression),
16414
+ default_or_exact.expression ? statement(default_or_exact.expression) : [],
16415
+ caseBody || []
16416
+ )
16417
+ }).optimize(compressor);
16418
+ }
16419
+
16420
+ // If we're this far, either there was no body or all cases fell into the same body.
16421
+ // If there was no body, then we don't need a default branch (because the default is
16422
+ // do nothing). If there was a body, we'll extract it to after the switch, so the
16423
+ // switch's new default is to do nothing and we can still prune it.
16424
+ const default_index = body.indexOf(default_or_exact);
16425
+ body.splice(default_index, 1);
16426
+ default_or_exact = null;
16427
+
16428
+ if (caseBody) {
16429
+ // Recurse into switch statement one more time so that we can append the case body
16430
+ // outside of the switch. This recursion will only happen once since we've pruned
16431
+ // the default case.
16432
+ return make_node(AST_BlockStatement, self, {
16433
+ body: decl.concat(self, caseBody)
16434
+ }).optimize(compressor);
16435
+ }
16436
+ // If we fall here, there is a default branch somewhere, there are no case bodies,
16437
+ // and there's a side-effect somewhere. Just let the below paths take care of it.
16438
+ }
16439
+
16068
16440
  if (body.length > 0) {
16069
16441
  body[0].body = decl.concat(body[0].body);
16070
16442
  }
16071
- self.body = body;
16072
- while (branch = body[body.length - 1]) {
16073
- var stat = branch.body[branch.body.length - 1];
16074
- if (stat instanceof AST_Break && compressor.loopcontrol_target(stat) === self)
16075
- branch.body.pop();
16076
- if (branch.body.length || branch instanceof AST_Case
16077
- && (default_branch || branch.expression.has_side_effects(compressor))) break;
16078
- if (body.pop() === default_branch) default_branch = null;
16079
- }
16443
+
16080
16444
  if (body.length == 0) {
16081
16445
  return make_node(AST_BlockStatement, self, {
16082
- body: decl.concat(make_node(AST_SimpleStatement, self.expression, {
16083
- body: self.expression
16084
- }))
16446
+ body: decl.concat(statement(self.expression))
16085
16447
  }).optimize(compressor);
16086
16448
  }
16087
- if (body.length == 1 && (body[0] === exact_match || body[0] === default_branch)) {
16088
- var has_break = false;
16089
- var tw = new TreeWalker(function(node) {
16090
- if (has_break
16091
- || node instanceof AST_Lambda
16092
- || node instanceof AST_SimpleStatement) return true;
16093
- if (node instanceof AST_Break && tw.loopcontrol_target(node) === self)
16094
- has_break = true;
16095
- });
16096
- self.walk(tw);
16097
- if (!has_break) {
16098
- var statements = body[0].body.slice();
16099
- var exp = body[0].expression;
16100
- if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
16101
- body: exp
16102
- }));
16103
- statements.unshift(make_node(AST_SimpleStatement, self.expression, {
16104
- body:self.expression
16105
- }));
16106
- return make_node(AST_BlockStatement, self, {
16107
- body: statements
16449
+ if (body.length == 1 && !has_nested_break(self)) {
16450
+ // This is the last case body, and we've already pruned any breaks, so it's
16451
+ // safe to hoist.
16452
+ let branch = body[0];
16453
+ return make_node(AST_If, self, {
16454
+ condition: make_node(AST_Binary, self, {
16455
+ operator: "===",
16456
+ left: self.expression,
16457
+ right: branch.expression,
16458
+ }),
16459
+ body: make_node(AST_BlockStatement, branch, {
16460
+ body: branch.body
16461
+ }),
16462
+ alternative: null
16463
+ }).optimize(compressor);
16464
+ }
16465
+ if (body.length === 2 && default_or_exact && !has_nested_break(self)) {
16466
+ let branch = body[0] === default_or_exact ? body[1] : body[0];
16467
+ let exact_exp = default_or_exact.expression && statement(default_or_exact.expression);
16468
+ if (aborts(body[0])) {
16469
+ // Only the first branch body could have a break (at the last statement)
16470
+ let first = body[0];
16471
+ if (is_break(first.body[first.body.length - 1], compressor)) {
16472
+ first.body.pop();
16473
+ }
16474
+ return make_node(AST_If, self, {
16475
+ condition: make_node(AST_Binary, self, {
16476
+ operator: "===",
16477
+ left: self.expression,
16478
+ right: branch.expression,
16479
+ }),
16480
+ body: make_node(AST_BlockStatement, branch, {
16481
+ body: branch.body
16482
+ }),
16483
+ alternative: make_node(AST_BlockStatement, default_or_exact, {
16484
+ body: [].concat(
16485
+ exact_exp || [],
16486
+ default_or_exact.body
16487
+ )
16488
+ })
16108
16489
  }).optimize(compressor);
16109
16490
  }
16491
+ let operator = "===";
16492
+ let consequent = make_node(AST_BlockStatement, branch, {
16493
+ body: branch.body,
16494
+ });
16495
+ let always = make_node(AST_BlockStatement, default_or_exact, {
16496
+ body: [].concat(
16497
+ exact_exp || [],
16498
+ default_or_exact.body
16499
+ )
16500
+ });
16501
+ if (body[0] === default_or_exact) {
16502
+ operator = "!==";
16503
+ let tmp = always;
16504
+ always = consequent;
16505
+ consequent = tmp;
16506
+ }
16507
+ return make_node(AST_BlockStatement, self, {
16508
+ body: [
16509
+ make_node(AST_If, self, {
16510
+ condition: make_node(AST_Binary, self, {
16511
+ operator: operator,
16512
+ left: self.expression,
16513
+ right: branch.expression,
16514
+ }),
16515
+ body: consequent,
16516
+ alternative: null
16517
+ })
16518
+ ].concat(always)
16519
+ }).optimize(compressor);
16110
16520
  }
16111
16521
  return self;
16112
16522
 
@@ -16117,6 +16527,50 @@ def_optimize(AST_Switch, function(self, compressor) {
16117
16527
  trim_unreachable_code(compressor, branch, decl);
16118
16528
  }
16119
16529
  }
16530
+ function branches_equivalent(branch, prev, insertBreak) {
16531
+ let bbody = branch.body;
16532
+ let pbody = prev.body;
16533
+ if (insertBreak) {
16534
+ bbody = bbody.concat(make_node(AST_Break));
16535
+ }
16536
+ if (bbody.length !== pbody.length) return false;
16537
+ let bblock = make_node(AST_BlockStatement, branch, { body: bbody });
16538
+ let pblock = make_node(AST_BlockStatement, prev, { body: pbody });
16539
+ return bblock.equivalent_to(pblock);
16540
+ }
16541
+ function statement(expression) {
16542
+ return make_node(AST_SimpleStatement, expression, {
16543
+ body: expression
16544
+ });
16545
+ }
16546
+ function has_nested_break(root) {
16547
+ let has_break = false;
16548
+ let tw = new TreeWalker(node => {
16549
+ if (has_break) return true;
16550
+ if (node instanceof AST_Lambda) return true;
16551
+ if (node instanceof AST_SimpleStatement) return true;
16552
+ if (!is_break(node, tw)) return;
16553
+ let parent = tw.parent();
16554
+ if (
16555
+ parent instanceof AST_SwitchBranch
16556
+ && parent.body[parent.body.length - 1] === node
16557
+ ) {
16558
+ return;
16559
+ }
16560
+ has_break = true;
16561
+ });
16562
+ root.walk(tw);
16563
+ return has_break;
16564
+ }
16565
+ function is_break(node, stack) {
16566
+ return node instanceof AST_Break
16567
+ && stack.loopcontrol_target(node) === self;
16568
+ }
16569
+ function is_inert_body(branch) {
16570
+ return !aborts(branch) && !make_node(AST_BlockStatement, branch, {
16571
+ body: branch.body
16572
+ }).has_side_effects(compressor);
16573
+ }
16120
16574
  });
16121
16575
 
16122
16576
  def_optimize(AST_Try, function(self, compressor) {
@@ -16237,10 +16691,6 @@ def_optimize(AST_Call, function(self, compressor) {
16237
16691
  }
16238
16692
  }
16239
16693
 
16240
- if (self.optional && is_nullish(fn, compressor)) {
16241
- return make_node(AST_Undefined, self);
16242
- }
16243
-
16244
16694
  var is_func = fn instanceof AST_Lambda;
16245
16695
 
16246
16696
  if (is_func && fn.pinned()) return self;
@@ -16283,6 +16733,14 @@ def_optimize(AST_Call, function(self, compressor) {
16283
16733
  }
16284
16734
 
16285
16735
  if (compressor.option("unsafe")) {
16736
+ if (exp instanceof AST_Dot && exp.start.value === "Array" && exp.property === "from" && self.args.length === 1) {
16737
+ const [argument] = self.args;
16738
+ if (argument instanceof AST_Array) {
16739
+ return make_node(AST_Array, argument, {
16740
+ elements: argument.elements
16741
+ }).optimize(compressor);
16742
+ }
16743
+ }
16286
16744
  if (is_undeclared_ref(exp)) switch (exp.name) {
16287
16745
  case "Array":
16288
16746
  if (self.args.length != 1) {
@@ -16488,6 +16946,7 @@ def_optimize(AST_Call, function(self, compressor) {
16488
16946
  argnames: [],
16489
16947
  body: []
16490
16948
  }).optimize(compressor);
16949
+ var nth_identifier = compressor.mangle_options && compressor.mangle_options.nth_identifier || base54;
16491
16950
  if (self.args.every((x) => x instanceof AST_String)) {
16492
16951
  // quite a corner-case, but we can handle it:
16493
16952
  // https://github.com/mishoo/UglifyJS2/issues/203
@@ -16497,14 +16956,13 @@ def_optimize(AST_Call, function(self, compressor) {
16497
16956
  return arg.value;
16498
16957
  }).join(",") + "){" + self.args[self.args.length - 1].value + "})";
16499
16958
  var ast = parse(code);
16500
- var mangle = { ie8: compressor.option("ie8") };
16959
+ var mangle = { ie8: compressor.option("ie8"), nth_identifier: nth_identifier };
16501
16960
  ast.figure_out_scope(mangle);
16502
16961
  var comp = new Compressor(compressor.options, {
16503
16962
  mangle_options: compressor.mangle_options
16504
16963
  });
16505
16964
  ast = ast.transform(comp);
16506
16965
  ast.figure_out_scope(mangle);
16507
- base54.reset();
16508
16966
  ast.compute_char_frequency(mangle);
16509
16967
  ast.mangle_names(mangle);
16510
16968
  var fun;
@@ -16629,6 +17087,7 @@ def_optimize(AST_Call, function(self, compressor) {
16629
17087
  if (can_inline && has_annotation(self, _INLINE)) {
16630
17088
  set_flag(fn, SQUEEZED);
16631
17089
  fn = make_node(fn.CTOR === AST_Defun ? AST_Function : fn.CTOR, fn, fn);
17090
+ fn = fn.clone(true);
16632
17091
  fn.figure_out_scope({}, {
16633
17092
  parent_scope: find_scope(compressor),
16634
17093
  toplevel: compressor.get_toplevel()
@@ -16927,16 +17386,16 @@ def_optimize(AST_UnaryPostfix, function(self, compressor) {
16927
17386
 
16928
17387
  def_optimize(AST_UnaryPrefix, function(self, compressor) {
16929
17388
  var e = self.expression;
16930
- if (self.operator == "delete"
16931
- && !(e instanceof AST_SymbolRef
16932
- || e instanceof AST_PropAccess
16933
- || is_identifier_atom(e))) {
16934
- if (e instanceof AST_Sequence) {
16935
- const exprs = e.expressions.slice();
16936
- exprs.push(make_node(AST_True, self));
16937
- return make_sequence(self, exprs).optimize(compressor);
16938
- }
16939
- return make_sequence(self, [ e, make_node(AST_True, self) ]).optimize(compressor);
17389
+ if (
17390
+ self.operator == "delete" &&
17391
+ !(
17392
+ e instanceof AST_SymbolRef ||
17393
+ e instanceof AST_PropAccess ||
17394
+ e instanceof AST_Chain ||
17395
+ is_identifier_atom(e)
17396
+ )
17397
+ ) {
17398
+ return make_sequence(self, [e, make_node(AST_True, self)]).optimize(compressor);
16940
17399
  }
16941
17400
  var seq = self.lift_sequences(compressor);
16942
17401
  if (seq !== self) {
@@ -17768,7 +18227,15 @@ function is_reachable(self, defs) {
17768
18227
  if (node instanceof AST_Scope && node !== self) {
17769
18228
  var parent = info.parent();
17770
18229
 
17771
- if (parent instanceof AST_Call && parent.expression === node) return;
18230
+ if (
18231
+ parent instanceof AST_Call
18232
+ && parent.expression === node
18233
+ // Async/Generators aren't guaranteed to sync evaluate all of
18234
+ // their body steps, so it's possible they close over the variable.
18235
+ && !(node.async || node.is_generator)
18236
+ ) {
18237
+ return;
18238
+ }
17772
18239
 
17773
18240
  if (walk(node, find_ref)) return walk_abort;
17774
18241
 
@@ -18416,14 +18883,20 @@ def_optimize(AST_Sub, function(self, compressor) {
18416
18883
  ev = make_node_from_constant(ev, self).optimize(compressor);
18417
18884
  return best_of(compressor, ev, self);
18418
18885
  }
18419
- if (self.optional && is_nullish(self.expression, compressor)) {
18420
- return make_node(AST_Undefined, self);
18421
- }
18422
18886
  return self;
18423
18887
  });
18424
18888
 
18425
18889
  def_optimize(AST_Chain, function (self, compressor) {
18426
- self.expression = self.expression.optimize(compressor);
18890
+ if (is_nullish(self.expression, compressor)) {
18891
+ let parent = compressor.parent();
18892
+ // It's valid to delete a nullish optional chain, but if we optimized
18893
+ // this to `delete undefined` then it would appear to be a syntax error
18894
+ // when we try to optimize the delete. Thankfully, `delete 0` is fine.
18895
+ if (parent instanceof AST_UnaryPrefix && parent.operator === "delete") {
18896
+ return make_node_from_constant(0, self);
18897
+ }
18898
+ return make_node(AST_Undefined, self);
18899
+ }
18427
18900
  return self;
18428
18901
  });
18429
18902
 
@@ -18490,9 +18963,6 @@ def_optimize(AST_Dot, function(self, compressor) {
18490
18963
  ev = make_node_from_constant(ev, self).optimize(compressor);
18491
18964
  return best_of(compressor, ev, self);
18492
18965
  }
18493
- if (self.optional && is_nullish(self.expression, compressor)) {
18494
- return make_node(AST_Undefined, self);
18495
- }
18496
18966
  return self;
18497
18967
  });
18498
18968
 
@@ -18552,9 +19022,11 @@ function inline_object_prop_spread(props, compressor) {
18552
19022
  // non-iterable value silently does nothing; it is thus safe
18553
19023
  // to remove. AST_String is the only iterable AST_Constant.
18554
19024
  props.splice(i, 1);
19025
+ i--;
18555
19026
  } else if (is_nullish(expr, compressor)) {
18556
19027
  // Likewise, null and undefined can be silently removed.
18557
19028
  props.splice(i, 1);
19029
+ i--;
18558
19030
  }
18559
19031
  }
18560
19032
  }
@@ -18702,12 +19174,15 @@ function lift_key(self, compressor) {
18702
19174
  if (self.key.value == "constructor"
18703
19175
  && compressor.parent() instanceof AST_Class) return self;
18704
19176
  if (self instanceof AST_ObjectKeyVal) {
19177
+ self.quote = self.key.quote;
18705
19178
  self.key = self.key.value;
18706
19179
  } else if (self instanceof AST_ClassProperty) {
19180
+ self.quote = self.key.quote;
18707
19181
  self.key = make_node(AST_SymbolClassProperty, self.key, {
18708
19182
  name: self.key.value
18709
19183
  });
18710
19184
  } else {
19185
+ self.quote = self.key.quote;
18711
19186
  self.key = make_node(AST_SymbolMethod, self.key, {
18712
19187
  name: self.key.value
18713
19188
  });
@@ -26815,33 +27290,61 @@ function addStrings(node, add) {
26815
27290
  }));
26816
27291
  }
26817
27292
 
27293
+ function mangle_private_properties(ast, options) {
27294
+ var cprivate = -1;
27295
+ var private_cache = new Map();
27296
+ var nth_identifier = options.nth_identifier || base54;
27297
+
27298
+ ast = ast.transform(new TreeTransformer(function(node) {
27299
+ if (
27300
+ node instanceof AST_ClassPrivateProperty
27301
+ || node instanceof AST_PrivateMethod
27302
+ || node instanceof AST_PrivateGetter
27303
+ || node instanceof AST_PrivateSetter
27304
+ ) {
27305
+ node.key.name = mangle_private(node.key.name);
27306
+ } else if (node instanceof AST_DotHash) {
27307
+ node.property = mangle_private(node.property);
27308
+ }
27309
+ }));
27310
+ return ast;
27311
+
27312
+ function mangle_private(name) {
27313
+ let mangled = private_cache.get(name);
27314
+ if (!mangled) {
27315
+ mangled = nth_identifier.get(++cprivate);
27316
+ private_cache.set(name, mangled);
27317
+ }
27318
+
27319
+ return mangled;
27320
+ }
27321
+ }
27322
+
26818
27323
  function mangle_properties(ast, options) {
26819
27324
  options = defaults(options, {
26820
27325
  builtins: false,
26821
27326
  cache: null,
26822
27327
  debug: false,
26823
27328
  keep_quoted: false,
27329
+ nth_identifier: base54,
26824
27330
  only_cache: false,
26825
27331
  regex: null,
26826
27332
  reserved: null,
26827
27333
  undeclared: false,
26828
27334
  }, true);
26829
27335
 
27336
+ var nth_identifier = options.nth_identifier;
27337
+
26830
27338
  var reserved_option = options.reserved;
26831
27339
  if (!Array.isArray(reserved_option)) reserved_option = [reserved_option];
26832
27340
  var reserved = new Set(reserved_option);
26833
27341
  if (!options.builtins) find_builtins(reserved);
26834
27342
 
26835
27343
  var cname = -1;
26836
- var cprivate = -1;
26837
27344
 
26838
27345
  var cache;
26839
- var private_cache = new Map();
26840
27346
  if (options.cache) {
26841
27347
  cache = options.cache.props;
26842
- cache.forEach(function(mangled_name) {
26843
- reserved.add(mangled_name);
26844
- });
26845
27348
  } else {
26846
27349
  cache = new Map();
26847
27350
  }
@@ -26859,27 +27362,27 @@ function mangle_properties(ast, options) {
26859
27362
 
26860
27363
  var names_to_mangle = new Set();
26861
27364
  var unmangleable = new Set();
26862
- var private_properties = new Set();
27365
+ // Track each already-mangled name to prevent nth_identifier from generating
27366
+ // the same name.
27367
+ cache.forEach((mangled_name) => unmangleable.add(mangled_name));
26863
27368
 
26864
- var keep_quoted_strict = options.keep_quoted === "strict";
27369
+ var keep_quoted = !!options.keep_quoted;
26865
27370
 
26866
27371
  // step 1: find candidates to mangle
26867
27372
  ast.walk(new TreeWalker(function(node) {
26868
27373
  if (
26869
27374
  node instanceof AST_ClassPrivateProperty
26870
27375
  || node instanceof AST_PrivateMethod
26871
- ) {
26872
- private_properties.add(node.key.name);
26873
- } else if (node instanceof AST_DotHash) {
26874
- private_properties.add(node.property);
26875
- } else if (node instanceof AST_ObjectKeyVal) {
26876
- if (typeof node.key == "string" &&
26877
- (!keep_quoted_strict || !node.quote)) {
27376
+ || node instanceof AST_PrivateGetter
27377
+ || node instanceof AST_PrivateSetter
27378
+ || node instanceof AST_DotHash
27379
+ ) ; else if (node instanceof AST_ObjectKeyVal) {
27380
+ if (typeof node.key == "string" && (!keep_quoted || !node.quote)) {
26878
27381
  add(node.key);
26879
27382
  }
26880
27383
  } else if (node instanceof AST_ObjectProperty) {
26881
27384
  // setter or getter, since KeyVal is handled above
26882
- if (!keep_quoted_strict || !node.key.end.quote) {
27385
+ if (!keep_quoted || !node.quote) {
26883
27386
  add(node.key.name);
26884
27387
  }
26885
27388
  } else if (node instanceof AST_Dot) {
@@ -26892,11 +27395,11 @@ function mangle_properties(ast, options) {
26892
27395
  declared = !(root.thedef && root.thedef.undeclared);
26893
27396
  }
26894
27397
  if (declared &&
26895
- (!keep_quoted_strict || !node.quote)) {
27398
+ (!keep_quoted || !node.quote)) {
26896
27399
  add(node.property);
26897
27400
  }
26898
27401
  } else if (node instanceof AST_Sub) {
26899
- if (!keep_quoted_strict) {
27402
+ if (!keep_quoted) {
26900
27403
  addStrings(node.property, add);
26901
27404
  }
26902
27405
  } else if (node instanceof AST_Call
@@ -26912,25 +27415,23 @@ function mangle_properties(ast, options) {
26912
27415
  if (
26913
27416
  node instanceof AST_ClassPrivateProperty
26914
27417
  || node instanceof AST_PrivateMethod
26915
- ) {
26916
- node.key.name = mangle_private(node.key.name);
26917
- } else if (node instanceof AST_DotHash) {
26918
- node.property = mangle_private(node.property);
26919
- } else if (node instanceof AST_ObjectKeyVal) {
26920
- if (typeof node.key == "string" &&
26921
- (!keep_quoted_strict || !node.quote)) {
27418
+ || node instanceof AST_PrivateGetter
27419
+ || node instanceof AST_PrivateSetter
27420
+ || node instanceof AST_DotHash
27421
+ ) ; else if (node instanceof AST_ObjectKeyVal) {
27422
+ if (typeof node.key == "string" && (!keep_quoted || !node.quote)) {
26922
27423
  node.key = mangle(node.key);
26923
27424
  }
26924
27425
  } else if (node instanceof AST_ObjectProperty) {
26925
27426
  // setter, getter, method or class field
26926
- if (!keep_quoted_strict || !node.key.end.quote) {
27427
+ if (!keep_quoted || !node.quote) {
26927
27428
  node.key.name = mangle(node.key.name);
26928
27429
  }
26929
27430
  } else if (node instanceof AST_Dot) {
26930
- if (!keep_quoted_strict || !node.quote) {
27431
+ if (!keep_quoted || !node.quote) {
26931
27432
  node.property = mangle(node.property);
26932
27433
  }
26933
- } else if (!options.keep_quoted && node instanceof AST_Sub) {
27434
+ } else if (!keep_quoted && node instanceof AST_Sub) {
26934
27435
  node.property = mangleStrings(node.property);
26935
27436
  } else if (node instanceof AST_Call
26936
27437
  && node.expression.print_to_string() == "Object.defineProperty") {
@@ -26987,7 +27488,7 @@ function mangle_properties(ast, options) {
26987
27488
  // either debug mode is off, or it is on and we could not use the mangled name
26988
27489
  if (!mangled) {
26989
27490
  do {
26990
- mangled = base54(++cname);
27491
+ mangled = nth_identifier.get(++cname);
26991
27492
  } while (!can_mangle(mangled));
26992
27493
  }
26993
27494
 
@@ -26996,16 +27497,6 @@ function mangle_properties(ast, options) {
26996
27497
  return mangled;
26997
27498
  }
26998
27499
 
26999
- function mangle_private(name) {
27000
- let mangled = private_cache.get(name);
27001
- if (!mangled) {
27002
- mangled = base54(++cprivate);
27003
- private_cache.set(name, mangled);
27004
- }
27005
-
27006
- return mangled;
27007
- }
27008
-
27009
27500
  function mangleStrings(node) {
27010
27501
  return node.transform(new TreeTransformer(function(node) {
27011
27502
  if (node instanceof AST_Sequence) {
@@ -27117,6 +27608,7 @@ async function minify(files, options) {
27117
27608
  keep_classnames: false,
27118
27609
  keep_fnames: false,
27119
27610
  module: false,
27611
+ nth_identifier: base54,
27120
27612
  properties: false,
27121
27613
  reserved: [],
27122
27614
  safari10: false,
@@ -27201,9 +27693,9 @@ async function minify(files, options) {
27201
27693
  if (options.mangle) toplevel.figure_out_scope(options.mangle);
27202
27694
  if (timings) timings.mangle = Date.now();
27203
27695
  if (options.mangle) {
27204
- base54.reset();
27205
27696
  toplevel.compute_char_frequency(options.mangle);
27206
27697
  toplevel.mangle_names(options.mangle);
27698
+ toplevel = mangle_private_properties(toplevel, options.mangle);
27207
27699
  }
27208
27700
  if (timings) timings.properties = Date.now();
27209
27701
  if (options.mangle && options.mangle.properties) {