terser 5.9.0 → 5.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/output.js CHANGED
@@ -172,10 +172,52 @@ function is_some_comments(comment) {
172
172
  // multiline comment
173
173
  return (
174
174
  (comment.type === "comment2" || comment.type === "comment1")
175
- && /@preserve|@lic|@cc_on|^\**!/i.test(comment.value)
175
+ && /@preserve|@copyright|@lic|@cc_on|^\**!/i.test(comment.value)
176
176
  );
177
177
  }
178
178
 
179
+ class Rope {
180
+ constructor() {
181
+ this.committed = "";
182
+ this.current = "";
183
+ }
184
+
185
+ append(str) {
186
+ this.current += str;
187
+ }
188
+
189
+ insertAt(char, index) {
190
+ const { committed, current } = this;
191
+ if (index < committed.length) {
192
+ this.committed = committed.slice(0, index) + char + committed.slice(index);
193
+ } else if (index === committed.length) {
194
+ this.committed += char;
195
+ } else {
196
+ index -= committed.length;
197
+ this.committed += current.slice(0, index) + char;
198
+ this.current = current.slice(index);
199
+ }
200
+ }
201
+
202
+ charAt(index) {
203
+ const { committed } = this;
204
+ if (index < committed.length) return committed[index];
205
+ return this.current[index - committed.length];
206
+ }
207
+
208
+ curLength() {
209
+ return this.current.length;
210
+ }
211
+
212
+ length() {
213
+ return this.committed.length + this.current.length;
214
+ }
215
+
216
+ toString() {
217
+ return this.committed + this.current;
218
+ }
219
+ }
220
+
179
221
  function OutputStream(options) {
180
222
 
181
223
  var readonly = !options;
@@ -205,6 +247,8 @@ function OutputStream(options) {
205
247
  width : 80,
206
248
  wrap_iife : false,
207
249
  wrap_func_args : true,
250
+
251
+ _destroy_ast : false
208
252
  }, true);
209
253
 
210
254
  if (options.shorthand === undefined)
@@ -240,11 +284,11 @@ function OutputStream(options) {
240
284
  var current_col = 0;
241
285
  var current_line = 1;
242
286
  var current_pos = 0;
243
- var OUTPUT = "";
287
+ var OUTPUT = new Rope();
244
288
  let printed_comments = new Set();
245
289
 
246
- var to_utf8 = options.ascii_only ? function(str, identifier) {
247
- if (options.ecma >= 2015 && !options.safari10) {
290
+ var to_utf8 = options.ascii_only ? function(str, identifier = false, regexp = false) {
291
+ if (options.ecma >= 2015 && !options.safari10 && !regexp) {
248
292
  str = str.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function(ch) {
249
293
  var code = get_full_char_code(ch, 0).toString(16);
250
294
  return "\\u{" + code + "}";
@@ -371,19 +415,18 @@ function OutputStream(options) {
371
415
  var ensure_line_len = options.max_line_len ? function() {
372
416
  if (current_col > options.max_line_len) {
373
417
  if (might_add_newline) {
374
- var left = OUTPUT.slice(0, might_add_newline);
375
- var right = OUTPUT.slice(might_add_newline);
418
+ OUTPUT.insertAt("\n", might_add_newline);
419
+ const curLength = OUTPUT.curLength();
376
420
  if (mappings) {
377
- var delta = right.length - current_col;
421
+ var delta = curLength - current_col;
378
422
  mappings.forEach(function(mapping) {
379
423
  mapping.line++;
380
424
  mapping.col += delta;
381
425
  });
382
426
  }
383
- OUTPUT = left + "\n" + right;
384
427
  current_line++;
385
428
  current_pos++;
386
- current_col = right.length;
429
+ current_col = curLength;
387
430
  }
388
431
  }
389
432
  if (might_add_newline) {
@@ -417,13 +460,13 @@ function OutputStream(options) {
417
460
 
418
461
  if (prev === ":" && ch === "}" || (!ch || !";}".includes(ch)) && prev !== ";") {
419
462
  if (options.semicolons || requireSemicolonChars.has(ch)) {
420
- OUTPUT += ";";
463
+ OUTPUT.append(";");
421
464
  current_col++;
422
465
  current_pos++;
423
466
  } else {
424
467
  ensure_line_len();
425
468
  if (current_col > 0) {
426
- OUTPUT += "\n";
469
+ OUTPUT.append("\n");
427
470
  current_pos++;
428
471
  current_line++;
429
472
  current_col = 0;
@@ -447,7 +490,7 @@ function OutputStream(options) {
447
490
  || (ch == "/" && ch == prev)
448
491
  || ((ch == "+" || ch == "-") && ch == last)
449
492
  ) {
450
- OUTPUT += " ";
493
+ OUTPUT.append(" ");
451
494
  current_col++;
452
495
  current_pos++;
453
496
  }
@@ -465,7 +508,7 @@ function OutputStream(options) {
465
508
  if (!might_add_newline) do_add_mapping();
466
509
  }
467
510
 
468
- OUTPUT += str;
511
+ OUTPUT.append(str);
469
512
  has_parens = str[str.length - 1] == "(";
470
513
  current_pos += str.length;
471
514
  var a = str.split(/\r?\n/), n = a.length - 1;
@@ -505,15 +548,15 @@ function OutputStream(options) {
505
548
 
506
549
  var newline = options.beautify ? function() {
507
550
  if (newline_insert < 0) return print("\n");
508
- if (OUTPUT[newline_insert] != "\n") {
509
- OUTPUT = OUTPUT.slice(0, newline_insert) + "\n" + OUTPUT.slice(newline_insert);
551
+ if (OUTPUT.charAt(newline_insert) != "\n") {
552
+ OUTPUT.insertAt("\n", newline_insert);
510
553
  current_pos++;
511
554
  current_line++;
512
555
  }
513
556
  newline_insert++;
514
557
  } : options.max_line_len ? function() {
515
558
  ensure_line_len();
516
- might_add_newline = OUTPUT.length;
559
+ might_add_newline = OUTPUT.length();
517
560
  } : noop;
518
561
 
519
562
  var semicolon = options.beautify ? function() {
@@ -579,13 +622,14 @@ function OutputStream(options) {
579
622
  if (might_add_newline) {
580
623
  ensure_line_len();
581
624
  }
582
- return OUTPUT;
625
+ return OUTPUT.toString();
583
626
  }
584
627
 
585
628
  function has_nlb() {
586
- let n = OUTPUT.length - 1;
629
+ const output = OUTPUT.toString();
630
+ let n = output.length - 1;
587
631
  while (n >= 0) {
588
- const code = OUTPUT.charCodeAt(n);
632
+ const code = output.charCodeAt(n);
589
633
  if (code === CODE_LINE_BREAK) {
590
634
  return true;
591
635
  }
@@ -722,7 +766,7 @@ function OutputStream(options) {
722
766
  !/comment[134]/.test(c.type)
723
767
  ))) return;
724
768
  printed_comments.add(comments);
725
- var insert = OUTPUT.length;
769
+ var insert = OUTPUT.length();
726
770
  comments.filter(comment_filter, node).forEach(function(c, i) {
727
771
  if (printed_comments.has(c)) return;
728
772
  printed_comments.add(c);
@@ -751,9 +795,21 @@ function OutputStream(options) {
751
795
  need_space = true;
752
796
  }
753
797
  });
754
- if (OUTPUT.length > insert) newline_insert = insert;
798
+ if (OUTPUT.length() > insert) newline_insert = insert;
755
799
  }
756
800
 
801
+ /**
802
+ * When output.option("_destroy_ast") is enabled, destroy the function.
803
+ * Call this after printing it.
804
+ */
805
+ const gc_scope =
806
+ options["_destroy_ast"]
807
+ ? function gc_scope(scope) {
808
+ scope.body.length = 0;
809
+ scope.argnames.length = 0;
810
+ }
811
+ : noop;
812
+
757
813
  var stack = [];
758
814
  return {
759
815
  get : get,
@@ -781,7 +837,7 @@ function OutputStream(options) {
781
837
  var encoded = encode_string(str, quote);
782
838
  if (escape_directive === true && !encoded.includes("\\")) {
783
839
  // Insert semicolons to break directive prologue
784
- if (!EXPECT_DIRECTIVE.test(OUTPUT)) {
840
+ if (!EXPECT_DIRECTIVE.test(OUTPUT.toString())) {
785
841
  force_semicolon();
786
842
  }
787
843
  force_semicolon();
@@ -800,6 +856,7 @@ function OutputStream(options) {
800
856
  with_square : with_square,
801
857
  add_mapping : add_mapping,
802
858
  option : function(opt) { return options[opt]; },
859
+ gc_scope,
803
860
  printed_comments: printed_comments,
804
861
  prepend_comments: readonly ? noop : prepend_comments,
805
862
  append_comments : readonly || comment_filter === return_false ? noop : append_comments,
@@ -1056,7 +1113,8 @@ function OutputStream(options) {
1056
1113
  var p = output.parent();
1057
1114
  if (this.args.length === 0
1058
1115
  && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
1059
- || p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
1116
+ || p instanceof AST_Call && p.expression === this
1117
+ || p instanceof AST_PrefixedTemplateString && p.prefix === this)) // (new foo)(bar)
1060
1118
  return true;
1061
1119
  });
1062
1120
 
@@ -1189,12 +1247,14 @@ function OutputStream(options) {
1189
1247
  output.with_indent(output.next_indent(), function() {
1190
1248
  output.append_comments(self, true);
1191
1249
  });
1250
+ output.add_mapping(self.end);
1192
1251
  output.print("}");
1193
1252
  }
1194
1253
  function print_braced(self, output, allow_directives) {
1195
1254
  if (self.body.length > 0) {
1196
1255
  output.with_block(function() {
1197
1256
  display_body(self.body, false, output, allow_directives);
1257
+ output.add_mapping(self.end);
1198
1258
  });
1199
1259
  } else print_braced_empty(self, output);
1200
1260
  }
@@ -1315,6 +1375,7 @@ function OutputStream(options) {
1315
1375
  });
1316
1376
  DEFPRINT(AST_Lambda, function(self, output) {
1317
1377
  self._do_print(output);
1378
+ output.gc_scope(self);
1318
1379
  });
1319
1380
 
1320
1381
  DEFPRINT(AST_PrefixedTemplateString, function(self, output) {
@@ -1394,6 +1455,7 @@ function OutputStream(options) {
1394
1455
  print_braced(self, output);
1395
1456
  }
1396
1457
  if (needs_parens) { output.print(")"); }
1458
+ output.gc_scope(self);
1397
1459
  });
1398
1460
 
1399
1461
  /* -----[ exits ]----- */
@@ -1637,6 +1699,10 @@ function OutputStream(options) {
1637
1699
  output.space();
1638
1700
  }
1639
1701
  self.module_name.print(output);
1702
+ if (self.assert_clause) {
1703
+ output.print("assert");
1704
+ self.assert_clause.print(output);
1705
+ }
1640
1706
  output.semicolon();
1641
1707
  });
1642
1708
  DEFPRINT(AST_ImportMeta, function(self, output) {
@@ -1702,6 +1768,10 @@ function OutputStream(options) {
1702
1768
  output.space();
1703
1769
  self.module_name.print(output);
1704
1770
  }
1771
+ if (self.assert_clause) {
1772
+ output.print("assert");
1773
+ self.assert_clause.print(output);
1774
+ }
1705
1775
  if (self.exported_value
1706
1776
  && !(self.exported_value instanceof AST_Defun ||
1707
1777
  self.exported_value instanceof AST_Function ||
@@ -2163,7 +2233,7 @@ function OutputStream(options) {
2163
2233
  flags = flags ? sort_regexp_flags(flags) : "";
2164
2234
  source = source.replace(r_slash_script, slash_script_replace);
2165
2235
 
2166
- output.print(output.to_utf8(`/${source}/${flags}`));
2236
+ output.print(output.to_utf8(`/${source}/${flags}`, false, true));
2167
2237
 
2168
2238
  const parent = output.parent();
2169
2239
  if (
package/lib/parse.js CHANGED
@@ -162,7 +162,7 @@ import {
162
162
  } from "./ast.js";
163
163
 
164
164
  var LATEST_RAW = ""; // Only used for numbers and template strings
165
- var LATEST_TEMPLATE_END = true;
165
+ var TEMPLATE_RAWS = new Map(); // Raw template strings
166
166
 
167
167
  var KEYWORDS = "break case catch class const continue debugger default delete do else export extends finally for function if in instanceof let new return switch throw try typeof var void while with";
168
168
  var KEYWORDS_ATOM = "false null true";
@@ -693,8 +693,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
693
693
  next(true, true);
694
694
  S.brace_counter++;
695
695
  tok = token(begin ? "template_head" : "template_substitution", content);
696
- LATEST_RAW = raw;
697
- LATEST_TEMPLATE_END = false;
696
+ TEMPLATE_RAWS.set(tok, raw);
697
+ tok.template_end = false;
698
698
  return tok;
699
699
  }
700
700
 
@@ -710,8 +710,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
710
710
  }
711
711
  S.template_braces.pop();
712
712
  tok = token(begin ? "template_head" : "template_substitution", content);
713
- LATEST_RAW = raw;
714
- LATEST_TEMPLATE_END = true;
713
+ TEMPLATE_RAWS.set(tok, raw);
714
+ tok.template_end = true;
715
715
  return tok;
716
716
  });
717
717
 
@@ -1222,7 +1222,7 @@ function parse($TEXT, options) {
1222
1222
  }
1223
1223
  if (S.token.value == "import" && !is_token(peek(), "punc", "(") && !is_token(peek(), "punc", ".")) {
1224
1224
  next();
1225
- var node = import_();
1225
+ var node = import_statement();
1226
1226
  semicolon();
1227
1227
  return node;
1228
1228
  }
@@ -1374,7 +1374,7 @@ function parse($TEXT, options) {
1374
1374
  case "export":
1375
1375
  if (!is_token(peek(), "punc", "(")) {
1376
1376
  next();
1377
- var node = export_();
1377
+ var node = export_statement();
1378
1378
  if (is("punc", ";")) semicolon();
1379
1379
  return node;
1380
1380
  }
@@ -1572,66 +1572,66 @@ function parse($TEXT, options) {
1572
1572
  });
1573
1573
  };
1574
1574
 
1575
- function track_used_binding_identifiers(is_parameter, strict) {
1576
- var parameters = new Set();
1577
- var duplicate = false;
1578
- var default_assignment = false;
1579
- var spread = false;
1580
- var strict_mode = !!strict;
1581
- var tracker = {
1582
- add_parameter: function(token) {
1583
- if (parameters.has(token.value)) {
1584
- if (duplicate === false) {
1585
- duplicate = token;
1586
- }
1587
- tracker.check_strict();
1588
- } else {
1589
- parameters.add(token.value);
1590
- if (is_parameter) {
1591
- switch (token.value) {
1592
- case "arguments":
1593
- case "eval":
1594
- case "yield":
1595
- if (strict_mode) {
1596
- token_error(token, "Unexpected " + token.value + " identifier as parameter inside strict mode");
1597
- }
1598
- break;
1599
- default:
1600
- if (RESERVED_WORDS.has(token.value)) {
1601
- unexpected();
1602
- }
1575
+ class UsedParametersTracker {
1576
+ constructor(is_parameter, strict, duplicates_ok = false) {
1577
+ this.is_parameter = is_parameter;
1578
+ this.duplicates_ok = duplicates_ok;
1579
+ this.parameters = new Set();
1580
+ this.duplicate = null;
1581
+ this.default_assignment = false;
1582
+ this.spread = false;
1583
+ this.strict_mode = !!strict;
1584
+ }
1585
+ add_parameter(token) {
1586
+ if (this.parameters.has(token.value)) {
1587
+ if (this.duplicate === null) {
1588
+ this.duplicate = token;
1589
+ }
1590
+ this.check_strict();
1591
+ } else {
1592
+ this.parameters.add(token.value);
1593
+ if (this.is_parameter) {
1594
+ switch (token.value) {
1595
+ case "arguments":
1596
+ case "eval":
1597
+ case "yield":
1598
+ if (this.strict_mode) {
1599
+ token_error(token, "Unexpected " + token.value + " identifier as parameter inside strict mode");
1600
+ }
1601
+ break;
1602
+ default:
1603
+ if (RESERVED_WORDS.has(token.value)) {
1604
+ unexpected();
1603
1605
  }
1604
1606
  }
1605
1607
  }
1606
- },
1607
- mark_default_assignment: function(token) {
1608
- if (default_assignment === false) {
1609
- default_assignment = token;
1610
- }
1611
- },
1612
- mark_spread: function(token) {
1613
- if (spread === false) {
1614
- spread = token;
1615
- }
1616
- },
1617
- mark_strict_mode: function() {
1618
- strict_mode = true;
1619
- },
1620
- is_strict: function() {
1621
- return default_assignment !== false || spread !== false || strict_mode;
1622
- },
1623
- check_strict: function() {
1624
- if (tracker.is_strict() && duplicate !== false) {
1625
- token_error(duplicate, "Parameter " + duplicate.value + " was used already");
1626
- }
1627
1608
  }
1628
- };
1629
-
1630
- return tracker;
1609
+ }
1610
+ mark_default_assignment(token) {
1611
+ if (this.default_assignment === false) {
1612
+ this.default_assignment = token;
1613
+ }
1614
+ }
1615
+ mark_spread(token) {
1616
+ if (this.spread === false) {
1617
+ this.spread = token;
1618
+ }
1619
+ }
1620
+ mark_strict_mode() {
1621
+ this.strict_mode = true;
1622
+ }
1623
+ is_strict() {
1624
+ return this.default_assignment !== false || this.spread !== false || this.strict_mode;
1625
+ }
1626
+ check_strict() {
1627
+ if (this.is_strict() && this.duplicate !== null && !this.duplicates_ok) {
1628
+ token_error(this.duplicate, "Parameter " + this.duplicate.value + " was used already");
1629
+ }
1630
+ }
1631
1631
  }
1632
1632
 
1633
1633
  function parameters(params) {
1634
- var used_parameters = track_used_binding_identifiers(true, S.input.has_directive("use strict"));
1634
+ var used_parameters = new UsedParametersTracker(true, S.input.has_directive("use strict"));
1635
1635
 
1636
1636
  expect("(");
1637
1637
 
@@ -1655,7 +1655,7 @@ function parse($TEXT, options) {
1655
1655
  var param;
1656
1656
  var expand = false;
1657
1657
  if (used_parameters === undefined) {
1658
- used_parameters = track_used_binding_identifiers(true, S.input.has_directive("use strict"));
1658
+ used_parameters = new UsedParametersTracker(true, S.input.has_directive("use strict"));
1659
1659
  }
1660
1660
  if (is("expand", "...")) {
1661
1661
  expand = S.token;
@@ -1698,7 +1698,9 @@ function parse($TEXT, options) {
1698
1698
  var expand_token;
1699
1699
  var first_token = S.token;
1700
1700
  if (used_parameters === undefined) {
1701
- used_parameters = track_used_binding_identifiers(false, S.input.has_directive("use strict"));
1701
+ const strict = S.input.has_directive("use strict");
1702
+ const duplicates_ok = symbol_type === AST_SymbolVar;
1703
+ used_parameters = new UsedParametersTracker(false, strict, duplicates_ok);
1702
1704
  }
1703
1705
  symbol_type = symbol_type === undefined ? AST_SymbolFunarg : symbol_type;
1704
1706
  if (is("punc", "[")) {
@@ -2091,7 +2093,7 @@ function parse($TEXT, options) {
2091
2093
  if (is("punc", "{") || is("punc", "[")) {
2092
2094
  def = new AST_VarDef({
2093
2095
  start: S.token,
2094
- name: binding_element(undefined ,sym_type),
2096
+ name: binding_element(undefined, sym_type),
2095
2097
  value: is("operator", "=") ? (expect_token("operator", "="), expression(false, no_in)) : null,
2096
2098
  end: prev()
2097
2099
  });
@@ -2365,19 +2367,19 @@ function parse($TEXT, options) {
2365
2367
 
2366
2368
  segments.push(new AST_TemplateSegment({
2367
2369
  start: S.token,
2368
- raw: LATEST_RAW,
2370
+ raw: TEMPLATE_RAWS.get(S.token),
2369
2371
  value: S.token.value,
2370
2372
  end: S.token
2371
2373
  }));
2372
2374
 
2373
- while (!LATEST_TEMPLATE_END) {
2375
+ while (!S.token.template_end) {
2374
2376
  next();
2375
2377
  handle_regexp();
2376
2378
  segments.push(expression(true));
2377
2379
 
2378
2380
  segments.push(new AST_TemplateSegment({
2379
2381
  start: S.token,
2380
- raw: LATEST_RAW,
2382
+ raw: TEMPLATE_RAWS.get(S.token),
2381
2383
  value: S.token.value,
2382
2384
  end: S.token
2383
2385
  }));
@@ -2551,7 +2553,7 @@ function parse($TEXT, options) {
2551
2553
  };
2552
2554
 
2553
2555
  const is_not_method_start = () =>
2554
- !is("punc", "(") && !is("punc", ",") && !is("punc", "}") && !is("operator", "=");
2556
+ !is("punc", "(") && !is("punc", ",") && !is("punc", "}") && !is("punc", ";") && !is("operator", "=");
2555
2557
 
2556
2558
  var is_async = false;
2557
2559
  var is_static = false;
@@ -2666,7 +2668,15 @@ function parse($TEXT, options) {
2666
2668
  }
2667
2669
  }
2668
2670
 
2669
- function import_() {
2671
+ function maybe_import_assertion() {
2672
+ if (is("name", "assert") && !has_newline_before(S.token)) {
2673
+ next();
2674
+ return object_or_destructuring_();
2675
+ }
2676
+ return null;
2677
+ }
2678
+
2679
+ function import_statement() {
2670
2680
  var start = prev();
2671
2681
 
2672
2682
  var imported_name;
@@ -2689,16 +2699,20 @@ function parse($TEXT, options) {
2689
2699
  unexpected();
2690
2700
  }
2691
2701
  next();
2702
+
2703
+ const assert_clause = maybe_import_assertion();
2704
+
2692
2705
  return new AST_Import({
2693
- start: start,
2694
- imported_name: imported_name,
2695
- imported_names: imported_names,
2706
+ start,
2707
+ imported_name,
2708
+ imported_names,
2696
2709
  module_name: new AST_String({
2697
2710
  start: mod_str,
2698
2711
  value: mod_str.value,
2699
2712
  quote: mod_str.quote,
2700
2713
  end: mod_str,
2701
2714
  }),
2715
+ assert_clause,
2702
2716
  end: S.token,
2703
2717
  });
2704
2718
  }
@@ -2806,7 +2820,7 @@ function parse($TEXT, options) {
2806
2820
  return names;
2807
2821
  }
2808
2822
 
2809
- function export_() {
2823
+ function export_statement() {
2810
2824
  var start = S.token;
2811
2825
  var is_default;
2812
2826
  var exported_names;
@@ -2824,6 +2838,8 @@ function parse($TEXT, options) {
2824
2838
  }
2825
2839
  next();
2826
2840
 
2841
+ const assert_clause = maybe_import_assertion();
2842
+
2827
2843
  return new AST_Export({
2828
2844
  start: start,
2829
2845
  is_default: is_default,
@@ -2835,6 +2851,7 @@ function parse($TEXT, options) {
2835
2851
  end: mod_str,
2836
2852
  }),
2837
2853
  end: prev(),
2854
+ assert_clause
2838
2855
  });
2839
2856
  } else {
2840
2857
  return new AST_Export({
@@ -2880,6 +2897,7 @@ function parse($TEXT, options) {
2880
2897
  exported_value: exported_value,
2881
2898
  exported_definition: exported_definition,
2882
2899
  end: prev(),
2900
+ assert_clause: null
2883
2901
  });
2884
2902
  }
2885
2903
 
@@ -3324,6 +3342,7 @@ function parse($TEXT, options) {
3324
3342
  } else {
3325
3343
  toplevel = new AST_Toplevel({ start: start, body: body, end: end });
3326
3344
  }
3345
+ TEMPLATE_RAWS = new Map();
3327
3346
  return toplevel;
3328
3347
  })();
3329
3348
 
package/lib/propmangle.js CHANGED
@@ -78,7 +78,7 @@ function find_builtins(reserved) {
78
78
  var global_ref = typeof global === "object" ? global : self;
79
79
 
80
80
  new_globals.forEach(function (new_global) {
81
- objects[new_global] = global_ref[new_global] || new Function();
81
+ objects[new_global] = global_ref[new_global] || function() {};
82
82
  });
83
83
 
84
84
  [
package/lib/scope.js CHANGED
@@ -116,6 +116,11 @@ const MASK_EXPORT_WANT_MANGLE = 1 << 1;
116
116
 
117
117
  let function_defs = null;
118
118
  let unmangleable_names = null;
119
+ /**
120
+ * When defined, there is a function declaration somewhere that's inside of a block.
121
+ * See https://tc39.es/ecma262/multipage/additional-ecmascript-features-for-web-browsers.html#sec-block-level-function-declarations-web-legacy-compatibility-semantics
122
+ */
123
+ let scopes_with_block_defuns = null;
119
124
 
120
125
  class SymbolDef {
121
126
  constructor(scope, orig, init) {
@@ -666,6 +671,15 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
666
671
  });
667
672
 
668
673
  function next_mangled(scope, options) {
674
+ let defun_scope;
675
+ if (
676
+ scopes_with_block_defuns
677
+ && (defun_scope = scope.get_defun_scope())
678
+ && scopes_with_block_defuns.has(defun_scope)
679
+ ) {
680
+ scope = defun_scope;
681
+ }
682
+
669
683
  var ext = scope.enclosed;
670
684
  var nth_identifier = options.nth_identifier;
671
685
  out: while (true) {
@@ -781,6 +795,8 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
781
795
  }
782
796
 
783
797
  const mangled_names = this.mangled_names = new Set();
798
+ unmangleable_names = new Set();
799
+
784
800
  if (options.cache) {
785
801
  this.globals.forEach(collect);
786
802
  if (options.cache.props) {
@@ -798,6 +814,13 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
798
814
  lname = save_nesting;
799
815
  return true; // don't descend again in TreeWalker
800
816
  }
817
+ if (
818
+ node instanceof AST_Defun
819
+ && !(tw.parent() instanceof AST_Scope)
820
+ ) {
821
+ scopes_with_block_defuns = scopes_with_block_defuns || new Set();
822
+ scopes_with_block_defuns.add(node.parent_scope.get_defun_scope());
823
+ }
801
824
  if (node instanceof AST_Scope) {
802
825
  node.variables.forEach(collect);
803
826
  return;
@@ -833,7 +856,6 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
833
856
  this.walk(tw);
834
857
 
835
858
  if (options.keep_fnames || options.keep_classnames) {
836
- unmangleable_names = new Set();
837
859
  // Collect a set of short names which are unmangleable,
838
860
  // for use in avoiding collisions in next_mangled.
839
861
  to_mangle.forEach(def => {
@@ -847,11 +869,12 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
847
869
 
848
870
  function_defs = null;
849
871
  unmangleable_names = null;
872
+ scopes_with_block_defuns = null;
850
873
 
851
874
  function collect(symbol) {
852
- const should_mangle = !options.reserved.has(symbol.name)
853
- && !(symbol.export & MASK_EXPORT_DONT_MANGLE);
854
- if (should_mangle) {
875
+ if (symbol.export & MASK_EXPORT_DONT_MANGLE) {
876
+ unmangleable_names.add(symbol.name);
877
+ } else if (!options.reserved.has(symbol.name)) {
855
878
  to_mangle.push(symbol);
856
879
  }
857
880
  }