terser 5.31.6 → 5.33.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.33.0
4
+
5
+ - `reduce_vars` improved when dealing with hoisted function definitions (#1544)
6
+
7
+ ## v5.32.0
8
+
9
+ - `import("module")` can now be input and output from ESTree AST (#1557)
10
+ - `BigInt` literals can now be input and output from ESTree AST (#1555)
11
+ - `typeof` an object or array (`typeof {}` and `typeof []`) can now be statically evaluated. (#1546)
12
+
3
13
  ## v5.31.6
4
14
  - Retain side effects in a `case` when the expression is a sequence (comma) expression
5
15
 
@@ -7571,6 +7571,19 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
7571
7571
  });
7572
7572
  },
7573
7573
 
7574
+ ImportExpression: function(M) {
7575
+ return new AST_Call({
7576
+ start: my_start_token(M),
7577
+ end: my_end_token(M),
7578
+ expression: from_moz({
7579
+ type: "Identifier",
7580
+ name: "import"
7581
+ }),
7582
+ optional: false,
7583
+ args: [from_moz(M.source)]
7584
+ });
7585
+ },
7586
+
7574
7587
  ExportAllDeclaration: function(M) {
7575
7588
  var foreign_name = M.exported == null ?
7576
7589
  new AST_SymbolExportForeign({ name: "*" }) :
@@ -7638,6 +7651,11 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
7638
7651
  args.value = { source, flags };
7639
7652
  return new AST_RegExp(args);
7640
7653
  }
7654
+ const bi = typeof M.value === "bigint" ? M.value.toString() : M.bigint;
7655
+ if (typeof bi === "string") {
7656
+ args.value = bi;
7657
+ return new AST_BigInt(args);
7658
+ }
7641
7659
  if (val === null) return new AST_Null(args);
7642
7660
  switch (typeof val) {
7643
7661
  case "string":
@@ -7705,14 +7723,6 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
7705
7723
  });
7706
7724
  },
7707
7725
 
7708
- BigIntLiteral(M) {
7709
- return new AST_BigInt({
7710
- start : my_start_token(M),
7711
- end : my_end_token(M),
7712
- value : M.value
7713
- });
7714
- },
7715
-
7716
7726
  EmptyStatement: function(M) {
7717
7727
  return new AST_EmptyStatement({
7718
7728
  start: my_start_token(M),
@@ -8185,6 +8195,14 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
8185
8195
  };
8186
8196
  });
8187
8197
  def_to_moz(AST_Call, function To_Moz_CallExpression(M) {
8198
+ if (M.expression instanceof AST_SymbolRef && M.expression.name === "import") {
8199
+ const [source] = M.args.map(to_moz);
8200
+ return {
8201
+ type: "ImportExpression",
8202
+ source,
8203
+ };
8204
+ }
8205
+
8188
8206
  return {
8189
8207
  type: "CallExpression",
8190
8208
  callee: to_moz(M.expression),
@@ -8730,8 +8748,13 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
8730
8748
  });
8731
8749
 
8732
8750
  def_to_moz(AST_BigInt, M => ({
8733
- type: "BigIntLiteral",
8734
- value: M.value
8751
+ type: "Literal",
8752
+ // value cannot be represented natively
8753
+ // see: https://github.com/estree/estree/blob/master/es2020.md#bigintliteral
8754
+ value: null,
8755
+ // `M.value` is a string that may be a hex number representation.
8756
+ // but "bigint" property should have only decimal digits
8757
+ bigint: typeof BigInt === "function" ? BigInt(M.value).toString() : M.value,
8735
8758
  }));
8736
8759
 
8737
8760
  AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
@@ -13268,9 +13291,9 @@ function is_reachable(scope_node, defs) {
13268
13291
  }
13269
13292
 
13270
13293
  /** Check if a ref refers to the name of a function/class it's defined within */
13271
- function is_recursive_ref(compressor, def) {
13294
+ function is_recursive_ref(tw, def) {
13272
13295
  var node;
13273
- for (var i = 0; node = compressor.parent(i); i++) {
13296
+ for (var i = 0; node = tw.parent(i); i++) {
13274
13297
  if (node instanceof AST_Lambda || node instanceof AST_Class) {
13275
13298
  var name = node.name;
13276
13299
  if (name && name.definition() === def) {
@@ -14579,14 +14602,25 @@ def_eval(AST_Object, function (compressor, depth) {
14579
14602
  var non_converting_unary = makePredicate("! typeof void");
14580
14603
  def_eval(AST_UnaryPrefix, function (compressor, depth) {
14581
14604
  var e = this.expression;
14582
- // Function would be evaluated to an array and so typeof would
14583
- // incorrectly return 'object'. Hence making is a special case.
14584
14605
  if (compressor.option("typeofs")
14585
- && this.operator == "typeof"
14586
- && (e instanceof AST_Lambda
14606
+ && this.operator == "typeof") {
14607
+ // Function would be evaluated to an array and so typeof would
14608
+ // incorrectly return 'object'. Hence making is a special case.
14609
+ if (e instanceof AST_Lambda
14587
14610
  || e instanceof AST_SymbolRef
14588
- && e.fixed_value() instanceof AST_Lambda)) {
14589
- return typeof function () { };
14611
+ && e.fixed_value() instanceof AST_Lambda) {
14612
+ return typeof function () { };
14613
+ }
14614
+ if (
14615
+ (e instanceof AST_Object
14616
+ || e instanceof AST_Array
14617
+ || (e instanceof AST_SymbolRef
14618
+ && (e.fixed_value() instanceof AST_Object
14619
+ || e.fixed_value() instanceof AST_Array)))
14620
+ && !e.has_side_effects(compressor)
14621
+ ) {
14622
+ return typeof {};
14623
+ }
14590
14624
  }
14591
14625
  if (!non_converting_unary.has(this.operator))
14592
14626
  depth++;
@@ -16113,28 +16147,50 @@ function mark_lambda(tw, descend, compressor) {
16113
16147
  * // use defined_after
16114
16148
  * }
16115
16149
  *
16116
- * This function is called on the parent to handle this issue.
16150
+ * Or even indirectly:
16151
+ *
16152
+ * B();
16153
+ * var defined_after = true;
16154
+ * function A() {
16155
+ * // use defined_after
16156
+ * }
16157
+ * function B() {
16158
+ * A();
16159
+ * }
16160
+ *
16161
+ * Access a variable before declaration will either throw a ReferenceError
16162
+ * (if the variable is declared with `let` or `const`),
16163
+ * or get an `undefined` (if the variable is declared with `var`).
16164
+ *
16165
+ * If the variable is inlined into the function, the behavior will change.
16166
+ *
16167
+ * This function is called on the parent to disallow inlining of such variables,
16117
16168
  */
16118
16169
  function handle_defined_after_hoist(parent) {
16119
16170
  const defuns = [];
16120
16171
  walk(parent, node => {
16121
16172
  if (node === parent) return;
16122
- if (node instanceof AST_Defun) defuns.push(node);
16173
+ if (node instanceof AST_Defun) {
16174
+ defuns.push(node);
16175
+ return true;
16176
+ }
16123
16177
  if (
16124
16178
  node instanceof AST_Scope
16125
16179
  || node instanceof AST_SimpleStatement
16126
16180
  ) return true;
16127
16181
  });
16128
16182
 
16183
+ // `defun` id to array of `defun` it uses
16184
+ const defun_dependencies_map = new Map();
16185
+ // `defun` id to array of enclosing `def` that are used by the function
16186
+ const dependencies_map = new Map();
16187
+ // all symbol ids that will be tracked for read/write
16129
16188
  const symbols_of_interest = new Set();
16130
16189
  const defuns_of_interest = new Set();
16131
- const potential_conflicts = [];
16132
16190
 
16133
16191
  for (const defun of defuns) {
16134
16192
  const fname_def = defun.name.definition();
16135
- const found_self_ref_in_other_defuns = defuns.some(
16136
- d => d !== defun && d.enclosed.indexOf(fname_def) !== -1
16137
- );
16193
+ const enclosing_defs = [];
16138
16194
 
16139
16195
  for (const def of defun.enclosed) {
16140
16196
  if (
@@ -16145,93 +16201,107 @@ function handle_defined_after_hoist(parent) {
16145
16201
  continue;
16146
16202
  }
16147
16203
 
16148
- // defun is hoisted, so always safe
16204
+ symbols_of_interest.add(def.id);
16205
+
16206
+ // found a reference to another function
16149
16207
  if (
16150
16208
  def.assignments === 0
16151
16209
  && def.orig.length === 1
16152
16210
  && def.orig[0] instanceof AST_SymbolDefun
16153
16211
  ) {
16154
- continue;
16155
- }
16212
+ defuns_of_interest.add(def.id);
16213
+ symbols_of_interest.add(def.id);
16214
+
16215
+ defuns_of_interest.add(fname_def.id);
16216
+ symbols_of_interest.add(fname_def.id);
16217
+
16218
+ if (!defun_dependencies_map.has(fname_def.id)) {
16219
+ defun_dependencies_map.set(fname_def.id, []);
16220
+ }
16221
+ defun_dependencies_map.get(fname_def.id).push(def.id);
16156
16222
 
16157
- if (found_self_ref_in_other_defuns) {
16158
- def.fixed = false;
16159
16223
  continue;
16160
16224
  }
16161
16225
 
16162
- // for the slower checks below this loop
16163
- potential_conflicts.push({ defun, def, fname_def });
16164
- symbols_of_interest.add(def.id);
16226
+ enclosing_defs.push(def);
16227
+ }
16228
+
16229
+ if (enclosing_defs.length) {
16230
+ dependencies_map.set(fname_def.id, enclosing_defs);
16231
+ defuns_of_interest.add(fname_def.id);
16165
16232
  symbols_of_interest.add(fname_def.id);
16166
- defuns_of_interest.add(defun);
16167
16233
  }
16168
16234
  }
16169
16235
 
16170
- // linearize all symbols, and locate defs that are read after the defun
16171
- if (potential_conflicts.length) {
16172
- // All "symbols of interest", that is, defuns or defs, that we found.
16173
- // These are placed in order so we can check which is after which.
16174
- const found_symbols = [];
16175
- // Indices of `found_symbols` which are writes
16176
- const found_symbol_writes = new Set();
16177
- // Defun ranges are recorded because we don't care if a function uses the def internally
16178
- const defun_ranges = new Map();
16236
+ // No defuns use outside constants
16237
+ if (!dependencies_map.size) {
16238
+ return;
16239
+ }
16240
+
16241
+ // Increment to count "symbols of interest" (defuns or defs) that we found.
16242
+ // These are tracked in AST order so we can check which is after which.
16243
+ let symbol_index = 1;
16244
+ // Map a defun ID to its first read (a `symbol_index`)
16245
+ const defun_first_read_map = new Map();
16246
+ // Map a symbol ID to its last write (a `symbol_index`)
16247
+ const symbol_last_write_map = new Map();
16179
16248
 
16180
- let tw;
16181
- parent.walk((tw = new TreeWalker((node, descend) => {
16182
- if (node instanceof AST_Defun && defuns_of_interest.has(node)) {
16183
- const start = found_symbols.length;
16184
- descend();
16185
- const end = found_symbols.length;
16249
+ walk_parent(parent, (node, walk_info) => {
16250
+ if (node instanceof AST_Symbol && node.thedef) {
16251
+ const id = node.definition().id;
16186
16252
 
16187
- defun_ranges.set(node, { start, end });
16188
- return true;
16189
- }
16190
- // if we found a defun on the list, mark IN_DEFUN=id and descend
16253
+ symbol_index++;
16191
16254
 
16192
- if (node instanceof AST_Symbol && node.thedef) {
16193
- const id = node.definition().id;
16194
- if (symbols_of_interest.has(id)) {
16195
- if (node instanceof AST_SymbolDeclaration || is_lhs(node, tw)) {
16196
- found_symbol_writes.add(found_symbols.length);
16197
- }
16198
- found_symbols.push(id);
16255
+ // Track last-writes to symbols
16256
+ if (symbols_of_interest.has(id)) {
16257
+ if (node instanceof AST_SymbolDeclaration || is_lhs(node, walk_info.parent())) {
16258
+ symbol_last_write_map.set(id, symbol_index);
16199
16259
  }
16200
16260
  }
16201
- })));
16202
16261
 
16203
- for (const { def, defun, fname_def } of potential_conflicts) {
16204
- const defun_range = defun_ranges.get(defun);
16262
+ // Track first-reads of defuns (refined later)
16263
+ if (defuns_of_interest.has(id)) {
16264
+ if (!defun_first_read_map.has(id) && !is_recursive_ref(walk_info, id)) {
16265
+ defun_first_read_map.set(id, symbol_index);
16266
+ }
16267
+ }
16268
+ }
16269
+ });
16205
16270
 
16206
- // find the index in `found_symbols`, with some special rules:
16207
- const find = (sym_id, starting_at = 0, must_be_write = false) => {
16208
- let index = starting_at;
16271
+ // Refine `defun_first_read_map` to be as high as possible
16272
+ for (const [defun, defun_first_read] of defun_first_read_map) {
16273
+ // Update all depdencies of `defun`
16274
+ const queue = new Set(defun_dependencies_map.get(defun));
16275
+ for (const enclosed_defun of queue) {
16276
+ let enclosed_defun_first_read = defun_first_read_map.get(enclosed_defun);
16277
+ if (enclosed_defun_first_read != null && enclosed_defun_first_read < defun_first_read) {
16278
+ continue;
16279
+ }
16209
16280
 
16210
- for (;;) {
16211
- index = found_symbols.indexOf(sym_id, index);
16281
+ defun_first_read_map.set(enclosed_defun, defun_first_read);
16212
16282
 
16213
- if (index === -1) {
16214
- break;
16215
- } else if (index >= defun_range.start && index < defun_range.end) {
16216
- index = defun_range.end;
16217
- continue;
16218
- } else if (must_be_write && !found_symbol_writes.has(index)) {
16219
- index++;
16220
- continue;
16221
- } else {
16222
- break;
16223
- }
16224
- }
16283
+ for (const enclosed_enclosed_defun of defun_dependencies_map.get(enclosed_defun) || []) {
16284
+ queue.add(enclosed_enclosed_defun);
16285
+ }
16286
+ }
16287
+ }
16225
16288
 
16226
- return index;
16227
- };
16289
+ // ensure write-then-read order, otherwise clear `fixed`
16290
+ // This is safe because last-writes (found_symbol_writes) are assumed to be as late as possible, and first-reads (defun_first_read_map) are assumed to be as early as possible.
16291
+ for (const [defun, defs] of dependencies_map) {
16292
+ const defun_first_read = defun_first_read_map.get(defun);
16293
+ if (defun_first_read === undefined) {
16294
+ continue;
16295
+ }
16228
16296
 
16229
- const read_defun_at = find(fname_def.id);
16230
- const wrote_def_at = find(def.id, read_defun_at + 1, true);
16297
+ for (const def of defs) {
16298
+ if (def.fixed === false) {
16299
+ continue;
16300
+ }
16231
16301
 
16232
- const wrote_def_after_reading_defun = read_defun_at != -1 && wrote_def_at != -1 && wrote_def_at > read_defun_at;
16302
+ let def_last_write = symbol_last_write_map.get(def.id) || 0;
16233
16303
 
16234
- if (wrote_def_after_reading_defun) {
16304
+ if (defun_first_read < def_last_write) {
16235
16305
  def.fixed = false;
16236
16306
  }
16237
16307
  }
@@ -333,9 +333,9 @@ export function is_reachable(scope_node, defs) {
333
333
  }
334
334
 
335
335
  /** Check if a ref refers to the name of a function/class it's defined within */
336
- export function is_recursive_ref(compressor, def) {
336
+ export function is_recursive_ref(tw, def) {
337
337
  var node;
338
- for (var i = 0; node = compressor.parent(i); i++) {
338
+ for (var i = 0; node = tw.parent(i); i++) {
339
339
  if (node instanceof AST_Lambda || node instanceof AST_Class) {
340
340
  var name = node.name;
341
341
  if (name && name.definition() === def) {
@@ -218,14 +218,25 @@ def_eval(AST_Object, function (compressor, depth) {
218
218
  var non_converting_unary = makePredicate("! typeof void");
219
219
  def_eval(AST_UnaryPrefix, function (compressor, depth) {
220
220
  var e = this.expression;
221
- // Function would be evaluated to an array and so typeof would
222
- // incorrectly return 'object'. Hence making is a special case.
223
221
  if (compressor.option("typeofs")
224
- && this.operator == "typeof"
225
- && (e instanceof AST_Lambda
222
+ && this.operator == "typeof") {
223
+ // Function would be evaluated to an array and so typeof would
224
+ // incorrectly return 'object'. Hence making is a special case.
225
+ if (e instanceof AST_Lambda
226
226
  || e instanceof AST_SymbolRef
227
- && e.fixed_value() instanceof AST_Lambda)) {
228
- return typeof function () { };
227
+ && e.fixed_value() instanceof AST_Lambda) {
228
+ return typeof function () { };
229
+ }
230
+ if (
231
+ (e instanceof AST_Object
232
+ || e instanceof AST_Array
233
+ || (e instanceof AST_SymbolRef
234
+ && (e.fixed_value() instanceof AST_Object
235
+ || e.fixed_value() instanceof AST_Array)))
236
+ && !e.has_side_effects(compressor)
237
+ ) {
238
+ return typeof {};
239
+ }
229
240
  }
230
241
  if (!non_converting_unary.has(this.operator))
231
242
  depth++;
@@ -94,8 +94,7 @@ import {
94
94
 
95
95
  walk,
96
96
  walk_body,
97
-
98
- TreeWalker,
97
+ walk_parent,
99
98
  } from "../ast.js";
100
99
  import { HOP, make_node, noop } from "../utils/index.js";
101
100
 
@@ -496,28 +495,50 @@ function mark_lambda(tw, descend, compressor) {
496
495
  * // use defined_after
497
496
  * }
498
497
  *
499
- * This function is called on the parent to handle this issue.
498
+ * Or even indirectly:
499
+ *
500
+ * B();
501
+ * var defined_after = true;
502
+ * function A() {
503
+ * // use defined_after
504
+ * }
505
+ * function B() {
506
+ * A();
507
+ * }
508
+ *
509
+ * Access a variable before declaration will either throw a ReferenceError
510
+ * (if the variable is declared with `let` or `const`),
511
+ * or get an `undefined` (if the variable is declared with `var`).
512
+ *
513
+ * If the variable is inlined into the function, the behavior will change.
514
+ *
515
+ * This function is called on the parent to disallow inlining of such variables,
500
516
  */
501
517
  function handle_defined_after_hoist(parent) {
502
518
  const defuns = [];
503
519
  walk(parent, node => {
504
520
  if (node === parent) return;
505
- if (node instanceof AST_Defun) defuns.push(node);
521
+ if (node instanceof AST_Defun) {
522
+ defuns.push(node);
523
+ return true;
524
+ }
506
525
  if (
507
526
  node instanceof AST_Scope
508
527
  || node instanceof AST_SimpleStatement
509
528
  ) return true;
510
529
  });
511
530
 
531
+ // `defun` id to array of `defun` it uses
532
+ const defun_dependencies_map = new Map();
533
+ // `defun` id to array of enclosing `def` that are used by the function
534
+ const dependencies_map = new Map();
535
+ // all symbol ids that will be tracked for read/write
512
536
  const symbols_of_interest = new Set();
513
537
  const defuns_of_interest = new Set();
514
- const potential_conflicts = [];
515
538
 
516
539
  for (const defun of defuns) {
517
540
  const fname_def = defun.name.definition();
518
- const found_self_ref_in_other_defuns = defuns.some(
519
- d => d !== defun && d.enclosed.indexOf(fname_def) !== -1
520
- );
541
+ const enclosing_defs = [];
521
542
 
522
543
  for (const def of defun.enclosed) {
523
544
  if (
@@ -528,93 +549,107 @@ function handle_defined_after_hoist(parent) {
528
549
  continue;
529
550
  }
530
551
 
531
- // defun is hoisted, so always safe
552
+ symbols_of_interest.add(def.id);
553
+
554
+ // found a reference to another function
532
555
  if (
533
556
  def.assignments === 0
534
557
  && def.orig.length === 1
535
558
  && def.orig[0] instanceof AST_SymbolDefun
536
559
  ) {
537
- continue;
538
- }
560
+ defuns_of_interest.add(def.id);
561
+ symbols_of_interest.add(def.id);
562
+
563
+ defuns_of_interest.add(fname_def.id);
564
+ symbols_of_interest.add(fname_def.id);
565
+
566
+ if (!defun_dependencies_map.has(fname_def.id)) {
567
+ defun_dependencies_map.set(fname_def.id, []);
568
+ }
569
+ defun_dependencies_map.get(fname_def.id).push(def.id);
539
570
 
540
- if (found_self_ref_in_other_defuns) {
541
- def.fixed = false;
542
571
  continue;
543
572
  }
544
573
 
545
- // for the slower checks below this loop
546
- potential_conflicts.push({ defun, def, fname_def });
547
- symbols_of_interest.add(def.id);
574
+ enclosing_defs.push(def);
575
+ }
576
+
577
+ if (enclosing_defs.length) {
578
+ dependencies_map.set(fname_def.id, enclosing_defs);
579
+ defuns_of_interest.add(fname_def.id);
548
580
  symbols_of_interest.add(fname_def.id);
549
- defuns_of_interest.add(defun);
550
581
  }
551
582
  }
552
583
 
553
- // linearize all symbols, and locate defs that are read after the defun
554
- if (potential_conflicts.length) {
555
- // All "symbols of interest", that is, defuns or defs, that we found.
556
- // These are placed in order so we can check which is after which.
557
- const found_symbols = [];
558
- // Indices of `found_symbols` which are writes
559
- const found_symbol_writes = new Set();
560
- // Defun ranges are recorded because we don't care if a function uses the def internally
561
- const defun_ranges = new Map();
562
-
563
- let tw;
564
- parent.walk((tw = new TreeWalker((node, descend) => {
565
- if (node instanceof AST_Defun && defuns_of_interest.has(node)) {
566
- const start = found_symbols.length;
567
- descend();
568
- const end = found_symbols.length;
569
-
570
- defun_ranges.set(node, { start, end });
571
- return true;
572
- }
573
- // if we found a defun on the list, mark IN_DEFUN=id and descend
574
-
575
- if (node instanceof AST_Symbol && node.thedef) {
576
- const id = node.definition().id;
577
- if (symbols_of_interest.has(id)) {
578
- if (node instanceof AST_SymbolDeclaration || is_lhs(node, tw)) {
579
- found_symbol_writes.add(found_symbols.length);
580
- }
581
- found_symbols.push(id);
584
+ // No defuns use outside constants
585
+ if (!dependencies_map.size) {
586
+ return;
587
+ }
588
+
589
+ // Increment to count "symbols of interest" (defuns or defs) that we found.
590
+ // These are tracked in AST order so we can check which is after which.
591
+ let symbol_index = 1;
592
+ // Map a defun ID to its first read (a `symbol_index`)
593
+ const defun_first_read_map = new Map();
594
+ // Map a symbol ID to its last write (a `symbol_index`)
595
+ const symbol_last_write_map = new Map();
596
+
597
+ walk_parent(parent, (node, walk_info) => {
598
+ if (node instanceof AST_Symbol && node.thedef) {
599
+ const id = node.definition().id;
600
+
601
+ symbol_index++;
602
+
603
+ // Track last-writes to symbols
604
+ if (symbols_of_interest.has(id)) {
605
+ if (node instanceof AST_SymbolDeclaration || is_lhs(node, walk_info.parent())) {
606
+ symbol_last_write_map.set(id, symbol_index);
582
607
  }
583
608
  }
584
- })));
585
-
586
- for (const { def, defun, fname_def } of potential_conflicts) {
587
- const defun_range = defun_ranges.get(defun);
588
-
589
- // find the index in `found_symbols`, with some special rules:
590
- const find = (sym_id, starting_at = 0, must_be_write = false) => {
591
- let index = starting_at;
592
-
593
- for (;;) {
594
- index = found_symbols.indexOf(sym_id, index);
595
-
596
- if (index === -1) {
597
- break;
598
- } else if (index >= defun_range.start && index < defun_range.end) {
599
- index = defun_range.end;
600
- continue;
601
- } else if (must_be_write && !found_symbol_writes.has(index)) {
602
- index++;
603
- continue;
604
- } else {
605
- break;
606
- }
609
+
610
+ // Track first-reads of defuns (refined later)
611
+ if (defuns_of_interest.has(id)) {
612
+ if (!defun_first_read_map.has(id) && !is_recursive_ref(walk_info, id)) {
613
+ defun_first_read_map.set(id, symbol_index);
607
614
  }
615
+ }
616
+ }
617
+ });
608
618
 
609
- return index;
610
- };
619
+ // Refine `defun_first_read_map` to be as high as possible
620
+ for (const [defun, defun_first_read] of defun_first_read_map) {
621
+ // Update all depdencies of `defun`
622
+ const queue = new Set(defun_dependencies_map.get(defun));
623
+ for (const enclosed_defun of queue) {
624
+ let enclosed_defun_first_read = defun_first_read_map.get(enclosed_defun);
625
+ if (enclosed_defun_first_read != null && enclosed_defun_first_read < defun_first_read) {
626
+ continue;
627
+ }
628
+
629
+ defun_first_read_map.set(enclosed_defun, defun_first_read);
611
630
 
612
- const read_defun_at = find(fname_def.id);
613
- const wrote_def_at = find(def.id, read_defun_at + 1, true);
631
+ for (const enclosed_enclosed_defun of defun_dependencies_map.get(enclosed_defun) || []) {
632
+ queue.add(enclosed_enclosed_defun);
633
+ }
634
+ }
635
+ }
636
+
637
+ // ensure write-then-read order, otherwise clear `fixed`
638
+ // This is safe because last-writes (found_symbol_writes) are assumed to be as late as possible, and first-reads (defun_first_read_map) are assumed to be as early as possible.
639
+ for (const [defun, defs] of dependencies_map) {
640
+ const defun_first_read = defun_first_read_map.get(defun);
641
+ if (defun_first_read === undefined) {
642
+ continue;
643
+ }
644
+
645
+ for (const def of defs) {
646
+ if (def.fixed === false) {
647
+ continue;
648
+ }
614
649
 
615
- const wrote_def_after_reading_defun = read_defun_at != -1 && wrote_def_at != -1 && wrote_def_at > read_defun_at;
650
+ let def_last_write = symbol_last_write_map.get(def.id) || 0;
616
651
 
617
- if (wrote_def_after_reading_defun) {
652
+ if (defun_first_read < def_last_write) {
618
653
  def.fixed = false;
619
654
  }
620
655
  }
@@ -595,6 +595,19 @@ import { is_basic_identifier_string } from "./parse.js";
595
595
  });
596
596
  },
597
597
 
598
+ ImportExpression: function(M) {
599
+ return new AST_Call({
600
+ start: my_start_token(M),
601
+ end: my_end_token(M),
602
+ expression: from_moz({
603
+ type: "Identifier",
604
+ name: "import"
605
+ }),
606
+ optional: false,
607
+ args: [from_moz(M.source)]
608
+ });
609
+ },
610
+
598
611
  ExportAllDeclaration: function(M) {
599
612
  var foreign_name = M.exported == null ?
600
613
  new AST_SymbolExportForeign({ name: "*" }) :
@@ -662,6 +675,11 @@ import { is_basic_identifier_string } from "./parse.js";
662
675
  args.value = { source, flags };
663
676
  return new AST_RegExp(args);
664
677
  }
678
+ const bi = typeof M.value === "bigint" ? M.value.toString() : M.bigint;
679
+ if (typeof bi === "string") {
680
+ args.value = bi;
681
+ return new AST_BigInt(args);
682
+ }
665
683
  if (val === null) return new AST_Null(args);
666
684
  switch (typeof val) {
667
685
  case "string":
@@ -729,14 +747,6 @@ import { is_basic_identifier_string } from "./parse.js";
729
747
  });
730
748
  },
731
749
 
732
- BigIntLiteral(M) {
733
- return new AST_BigInt({
734
- start : my_start_token(M),
735
- end : my_end_token(M),
736
- value : M.value
737
- });
738
- },
739
-
740
750
  EmptyStatement: function(M) {
741
751
  return new AST_EmptyStatement({
742
752
  start: my_start_token(M),
@@ -1209,6 +1219,14 @@ import { is_basic_identifier_string } from "./parse.js";
1209
1219
  };
1210
1220
  });
1211
1221
  def_to_moz(AST_Call, function To_Moz_CallExpression(M) {
1222
+ if (M.expression instanceof AST_SymbolRef && M.expression.name === "import") {
1223
+ const [source] = M.args.map(to_moz);
1224
+ return {
1225
+ type: "ImportExpression",
1226
+ source,
1227
+ };
1228
+ }
1229
+
1212
1230
  return {
1213
1231
  type: "CallExpression",
1214
1232
  callee: to_moz(M.expression),
@@ -1754,8 +1772,13 @@ import { is_basic_identifier_string } from "./parse.js";
1754
1772
  });
1755
1773
 
1756
1774
  def_to_moz(AST_BigInt, M => ({
1757
- type: "BigIntLiteral",
1758
- value: M.value
1775
+ type: "Literal",
1776
+ // value cannot be represented natively
1777
+ // see: https://github.com/estree/estree/blob/master/es2020.md#bigintliteral
1778
+ value: null,
1779
+ // `M.value` is a string that may be a hex number representation.
1780
+ // but "bigint" property should have only decimal digits
1781
+ bigint: typeof BigInt === "function" ? BigInt(M.value).toString() : M.value,
1759
1782
  }));
1760
1783
 
1761
1784
  AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "homepage": "https://terser.org",
5
5
  "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
6
6
  "license": "BSD-2-Clause",
7
- "version": "5.31.6",
7
+ "version": "5.33.0",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },