terser 5.18.0 → 5.18.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/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Changelog
2
2
 
3
- ## v7.18.0
3
+ ## v5.18.2
4
+ - Stop using recursion in hoisted defuns fix.
5
+
6
+ ## v5.18.1
7
+ - Fix major performance issue caused by hoisted defuns' scopes bugfix.
8
+
9
+ ## v5.18.0
4
10
  - Add new `/*@__MANGLE_PROP__*/` annotation, to mark properties that should be mangled.
5
11
 
6
12
  ## v5.17.7
package/README.md CHANGED
@@ -52,7 +52,9 @@ From NPM for programmatic use:
52
52
 
53
53
  <!-- CLI_USAGE:START -->
54
54
 
55
- terser [input files] [options]
55
+ ```
56
+ terser [input files] [options]
57
+ ```
56
58
 
57
59
  Terser can take multiple input files. It's recommended that you pass the
58
60
  input files first, then pass the options. Terser will parse input files
@@ -674,6 +676,10 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
674
676
 
675
677
  If you happen to need the source map as a raw object, set `sourceMap.asObject` to `true`.
676
678
 
679
+ <!-- API_REFERENCE:END -->
680
+
681
+ <!-- OPTIONS:START -->
682
+
677
683
  ## Parse options
678
684
 
679
685
  - `bare_returns` (default `false`) -- support top level `return` statements
@@ -1062,8 +1068,14 @@ as "output options".
1062
1068
  function expressions that are passed as arguments, in parenthesis. See
1063
1069
  [OptimizeJS](https://github.com/nolanlawson/optimize-js) for more details.
1064
1070
 
1071
+
1072
+ <!-- OPTIONS:END -->
1073
+
1074
+
1065
1075
  # Miscellaneous
1066
1076
 
1077
+ <!-- MISCELLANEOUS:START -->
1078
+
1067
1079
  ### Keeping copyright notices or other comments
1068
1080
 
1069
1081
  You can pass `--comments` to retain certain comments in the output. By
@@ -1328,19 +1340,27 @@ $ rm -rf node_modules yarn.lock
1328
1340
  $ yarn
1329
1341
  ```
1330
1342
 
1331
- <!-- API_REFERENCE:END -->
1343
+ <!-- MISCELLANEOUS:END -->
1332
1344
 
1333
1345
  # Reporting issues
1334
1346
 
1335
- In the terser CLI we use [source-map-support](https://npmjs.com/source-map-support) to produce good error stacks. In your own app, you're expected to enable source-map-support (read their docs) to have nice stack traces that will help you write good issues.
1347
+ <!-- REPORTING_ISSUES:START -->
1348
+
1349
+ ## A minimal, reproducible example
1350
+
1351
+ You're expected to provide a [minimal reproducible example] of input code that will demonstrate your issue.
1352
+
1353
+ To get to this example, you can remove bits of your code and stop if your issue ceases to reproduce.
1336
1354
 
1337
1355
  ## Obtaining the source code given to Terser
1338
1356
 
1339
- Because users often don't control the call to `await minify()` or its arguments, Terser provides a `TERSER_DEBUG_DIR` environment variable to make terser output some debug logs. If you're using a bundler or a project that includes a bundler and are not sure what went wrong with your code, pass that variable like so:
1357
+ Because users often don't control the call to `await minify()` or its arguments, Terser provides a `TERSER_DEBUG_DIR` environment variable to make terser output some debug logs.
1340
1358
 
1341
- ```
1342
- $ TERSER_DEBUG_DIR=/path/to/logs command-that-uses-terser
1343
- $ ls /path/to/logs
1359
+ These logs will contain the input code and options of each `minify()` call.
1360
+
1361
+ ```bash
1362
+ TERSER_DEBUG_DIR=/tmp/terser-log-dir command-that-uses-terser
1363
+ ls /tmp/terser-log-dir
1344
1364
  terser-debug-123456.log
1345
1365
  ```
1346
1366
 
@@ -1350,6 +1370,12 @@ If you're not sure how to set an environment variable on your shell (the above e
1350
1370
  > npx cross-env TERSER_DEBUG_DIR=/path/to/logs command-that-uses-terser
1351
1371
  ```
1352
1372
 
1373
+ ## Stack traces
1374
+
1375
+ In the terser CLI we use [source-map-support](https://npmjs.com/source-map-support) to produce good error stacks. In your own app, you're expected to enable source-map-support (read their docs) to have nice stack traces that will help you write good issues.
1376
+
1377
+ <!-- REPORTING_ISSUES:END -->
1378
+
1353
1379
  # README.md Patrons:
1354
1380
 
1355
1381
  *note*: <s>You can support this project on patreon: [link]</s> **The Terser Patreon is shutting down in favor of opencollective**. Check out [PATRONS.md](https://github.com/terser/terser/blob/master/PATRONS.md) for our first-tier patrons.
@@ -15934,6 +15934,10 @@ function handle_defined_after_hoist(parent) {
15934
15934
  ) return true;
15935
15935
  });
15936
15936
 
15937
+ const symbols_of_interest = new Set();
15938
+ const defuns_of_interest = new Set();
15939
+ const potential_conflicts = [];
15940
+
15937
15941
  for (const defun of defuns) {
15938
15942
  const fname_def = defun.name.definition();
15939
15943
  const found_self_ref_in_other_defuns = defuns.some(
@@ -15963,35 +15967,79 @@ function handle_defined_after_hoist(parent) {
15963
15967
  continue;
15964
15968
  }
15965
15969
 
15966
- // Detect `call_defun(); var used_in_defun = X`
15967
- // Because `used_in_defun` is not certainly X when it's defined after.
15968
- let found_defun_ref = false;
15969
- let found_def_after_defun = false;
15970
- walk_parent(parent, (node, info) => {
15971
- if (node === defun) return true;
15970
+ // for the slower checks below this loop
15971
+ potential_conflicts.push({ defun, def, fname_def });
15972
+ symbols_of_interest.add(def.id);
15973
+ symbols_of_interest.add(fname_def.id);
15974
+ defuns_of_interest.add(defun);
15975
+ }
15976
+ }
15972
15977
 
15973
- // Step 1: find `call_defun()` or other refs to the defun
15974
- if (
15975
- !found_defun_ref
15976
- && node.thedef === fname_def
15977
- && node instanceof AST_Symbol
15978
- ) {
15979
- found_defun_ref = true;
15978
+ // linearize all symbols, and locate defs that are read after the defun
15979
+ if (potential_conflicts.length) {
15980
+ // All "symbols of interest", that is, defuns or defs, that we found.
15981
+ // These are placed in order so we can check which is after which.
15982
+ const found_symbols = [];
15983
+ // Indices of `found_symbols` which are writes
15984
+ const found_symbol_writes = new Set();
15985
+ // Defun ranges are recorded because we don't care if a function uses the def internally
15986
+ const defun_ranges = new Map();
15987
+
15988
+ let tw;
15989
+ parent.walk((tw = new TreeWalker((node, descend) => {
15990
+ if (node instanceof AST_Defun && defuns_of_interest.has(node)) {
15991
+ const start = found_symbols.length;
15992
+ descend();
15993
+ const end = found_symbols.length;
15994
+
15995
+ defun_ranges.set(node, { start, end });
15996
+ return true;
15997
+ }
15998
+ // if we found a defun on the list, mark IN_DEFUN=id and descend
15999
+
16000
+ if (node instanceof AST_Symbol && node.thedef) {
16001
+ const id = node.definition().id;
16002
+ if (symbols_of_interest.has(id)) {
16003
+ if (node instanceof AST_SymbolDeclaration || is_lhs(node, tw)) {
16004
+ found_symbol_writes.add(found_symbols.length);
16005
+ }
16006
+ found_symbols.push(id);
15980
16007
  }
16008
+ }
16009
+ })));
15981
16010
 
15982
- // Step 2: if Step 1 occurred, find a var the defun uses
15983
- if (
15984
- found_defun_ref
15985
- && node.thedef === def
15986
- && (node instanceof AST_SymbolDeclaration
15987
- || is_lhs(node, info))
15988
- ) {
15989
- found_def_after_defun = true;
15990
- return walk_abort;
16011
+ for (const { def, defun, fname_def } of potential_conflicts) {
16012
+ const defun_range = defun_ranges.get(defun);
16013
+
16014
+ // find the index in `found_symbols`, with some special rules:
16015
+ const find = (sym_id, starting_at = 0, must_be_write = false) => {
16016
+ let index = starting_at;
16017
+
16018
+ for (;;) {
16019
+ index = found_symbols.indexOf(sym_id, index);
16020
+
16021
+ if (index === -1) {
16022
+ break;
16023
+ } else if (index >= defun_range.start && index < defun_range.end) {
16024
+ index = defun_range.end;
16025
+ continue;
16026
+ } else if (must_be_write && !found_symbol_writes.has(index)) {
16027
+ index++;
16028
+ continue;
16029
+ } else {
16030
+ break;
16031
+ }
15991
16032
  }
15992
- });
15993
16033
 
15994
- if (found_def_after_defun) {
16034
+ return index;
16035
+ };
16036
+
16037
+ const read_defun_at = find(fname_def.id);
16038
+ const wrote_def_at = find(def.id, read_defun_at + 1, true);
16039
+
16040
+ const wrote_def_after_reading_defun = read_defun_at != -1 && wrote_def_at != -1 && wrote_def_at > read_defun_at;
16041
+
16042
+ if (wrote_def_after_reading_defun) {
15995
16043
  def.fixed = false;
15996
16044
  }
15997
16045
  }
@@ -93,10 +93,10 @@ import {
93
93
  AST_Yield,
94
94
 
95
95
  walk,
96
- walk_parent,
97
- walk_abort,
98
96
  walk_body,
99
97
 
98
+ TreeWalker,
99
+
100
100
  _INLINE,
101
101
  _NOINLINE,
102
102
  _PURE
@@ -513,6 +513,10 @@ function handle_defined_after_hoist(parent) {
513
513
  ) return true;
514
514
  });
515
515
 
516
+ const symbols_of_interest = new Set();
517
+ const defuns_of_interest = new Set();
518
+ const potential_conflicts = [];
519
+
516
520
  for (const defun of defuns) {
517
521
  const fname_def = defun.name.definition();
518
522
  const found_self_ref_in_other_defuns = defuns.some(
@@ -542,35 +546,79 @@ function handle_defined_after_hoist(parent) {
542
546
  continue;
543
547
  }
544
548
 
545
- // Detect `call_defun(); var used_in_defun = X`
546
- // Because `used_in_defun` is not certainly X when it's defined after.
547
- let found_defun_ref = false;
548
- let found_def_after_defun = false;
549
- walk_parent(parent, (node, info) => {
550
- if (node === defun) return true;
551
-
552
- // Step 1: find `call_defun()` or other refs to the defun
553
- if (
554
- !found_defun_ref
555
- && node.thedef === fname_def
556
- && node instanceof AST_Symbol
557
- ) {
558
- found_defun_ref = true;
559
- }
549
+ // for the slower checks below this loop
550
+ potential_conflicts.push({ defun, def, fname_def });
551
+ symbols_of_interest.add(def.id);
552
+ symbols_of_interest.add(fname_def.id);
553
+ defuns_of_interest.add(defun);
554
+ }
555
+ }
560
556
 
561
- // Step 2: if Step 1 occurred, find a var the defun uses
562
- if (
563
- found_defun_ref
564
- && node.thedef === def
565
- && (node instanceof AST_SymbolDeclaration
566
- || is_lhs(node, info))
567
- ) {
568
- found_def_after_defun = true;
569
- return walk_abort;
557
+ // linearize all symbols, and locate defs that are read after the defun
558
+ if (potential_conflicts.length) {
559
+ // All "symbols of interest", that is, defuns or defs, that we found.
560
+ // These are placed in order so we can check which is after which.
561
+ const found_symbols = [];
562
+ // Indices of `found_symbols` which are writes
563
+ const found_symbol_writes = new Set();
564
+ // Defun ranges are recorded because we don't care if a function uses the def internally
565
+ const defun_ranges = new Map();
566
+
567
+ let tw;
568
+ parent.walk((tw = new TreeWalker((node, descend) => {
569
+ if (node instanceof AST_Defun && defuns_of_interest.has(node)) {
570
+ const start = found_symbols.length;
571
+ descend();
572
+ const end = found_symbols.length;
573
+
574
+ defun_ranges.set(node, { start, end });
575
+ return true;
576
+ }
577
+ // if we found a defun on the list, mark IN_DEFUN=id and descend
578
+
579
+ if (node instanceof AST_Symbol && node.thedef) {
580
+ const id = node.definition().id;
581
+ if (symbols_of_interest.has(id)) {
582
+ if (node instanceof AST_SymbolDeclaration || is_lhs(node, tw)) {
583
+ found_symbol_writes.add(found_symbols.length);
584
+ }
585
+ found_symbols.push(id);
570
586
  }
571
- });
587
+ }
588
+ })));
589
+
590
+ for (const { def, defun, fname_def } of potential_conflicts) {
591
+ const defun_range = defun_ranges.get(defun);
592
+
593
+ // find the index in `found_symbols`, with some special rules:
594
+ const find = (sym_id, starting_at = 0, must_be_write = false) => {
595
+ let index = starting_at;
596
+
597
+ for (;;) {
598
+ index = found_symbols.indexOf(sym_id, index);
599
+
600
+ if (index === -1) {
601
+ break;
602
+ } else if (index >= defun_range.start && index < defun_range.end) {
603
+ index = defun_range.end;
604
+ continue;
605
+ } else if (must_be_write && !found_symbol_writes.has(index)) {
606
+ index++;
607
+ continue;
608
+ } else {
609
+ break;
610
+ }
611
+ }
612
+
613
+ return index;
614
+ };
615
+
616
+ const read_defun_at = find(fname_def.id);
617
+ const wrote_def_at = find(def.id, read_defun_at + 1, true);
618
+
619
+ const wrote_def_after_reading_defun = read_defun_at != -1 && wrote_def_at != -1 && wrote_def_at > read_defun_at;
572
620
 
573
- if (found_def_after_defun) {
621
+ if (wrote_def_after_reading_defun) {
574
622
  def.fixed = false;
575
623
  }
576
624
  }
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.18.0",
7
+ "version": "5.18.2",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },