terser 5.17.7 → 5.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +35 -7
- package/dist/bundle.min.js +154 -56
- package/lib/ast.js +12 -4
- package/lib/compress/reduce-vars.js +70 -24
- package/lib/parse.js +38 -28
- package/lib/propmangle.js +44 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v7.18.1
|
4
|
+
- Fix major performance issue caused by hoisted defuns' scopes bugfix.
|
5
|
+
|
6
|
+
## v7.18.0
|
7
|
+
- Add new `/*@__MANGLE_PROP__*/` annotation, to mark properties that should be mangled.
|
8
|
+
|
3
9
|
## v5.17.7
|
4
10
|
- Update some dependencies
|
5
11
|
- Add consistent sorting for `v` RegExp flag
|
package/README.md
CHANGED
@@ -52,7 +52,9 @@ From NPM for programmatic use:
|
|
52
52
|
|
53
53
|
<!-- CLI_USAGE:START -->
|
54
54
|
|
55
|
-
|
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
|
@@ -106,6 +108,7 @@ a double dash to prevent input files being used as option arguments:
|
|
106
108
|
`strict` disables quoted properties
|
107
109
|
being automatically reserved.
|
108
110
|
`regex` Only mangle matched property names.
|
111
|
+
`only_annotated` Only mangle properties defined with /*@__MANGLE_PROP__*/.
|
109
112
|
`reserved` List of names that should not be mangled.
|
110
113
|
-f, --format [options] Specify format options.
|
111
114
|
`preamble` Preamble to prepend to the output. You
|
@@ -673,6 +676,10 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
|
673
676
|
|
674
677
|
If you happen to need the source map as a raw object, set `sourceMap.asObject` to `true`.
|
675
678
|
|
679
|
+
<!-- API_REFERENCE:END -->
|
680
|
+
|
681
|
+
<!-- OPTIONS:START -->
|
682
|
+
|
676
683
|
## Parse options
|
677
684
|
|
678
685
|
- `bare_returns` (default `false`) -- support top level `return` statements
|
@@ -1061,8 +1068,14 @@ as "output options".
|
|
1061
1068
|
function expressions that are passed as arguments, in parenthesis. See
|
1062
1069
|
[OptimizeJS](https://github.com/nolanlawson/optimize-js) for more details.
|
1063
1070
|
|
1071
|
+
|
1072
|
+
<!-- OPTIONS:END -->
|
1073
|
+
|
1074
|
+
|
1064
1075
|
# Miscellaneous
|
1065
1076
|
|
1077
|
+
<!-- MISCELLANEOUS:START -->
|
1078
|
+
|
1066
1079
|
### Keeping copyright notices or other comments
|
1067
1080
|
|
1068
1081
|
You can pass `--comments` to retain certain comments in the output. By
|
@@ -1189,6 +1202,7 @@ Annotations in Terser are a way to tell it to treat a certain function call diff
|
|
1189
1202
|
* `/*@__NOINLINE__*/` - Makes sure the called function is not inlined into the call site.
|
1190
1203
|
* `/*@__PURE__*/` - Marks a function call as pure. That means, it can safely be dropped.
|
1191
1204
|
* `/*@__KEY__*/` - Marks a string literal as a property to also mangle it when mangling properties.
|
1205
|
+
* `/*@__MANGLE_PROP__*/` - Opts-in an object property (or class field) for mangling, when the property mangler is enabled.
|
1192
1206
|
|
1193
1207
|
You can use either a `@` sign at the start, or a `#`.
|
1194
1208
|
|
@@ -1326,19 +1340,27 @@ $ rm -rf node_modules yarn.lock
|
|
1326
1340
|
$ yarn
|
1327
1341
|
```
|
1328
1342
|
|
1329
|
-
<!--
|
1343
|
+
<!-- MISCELLANEOUS:END -->
|
1330
1344
|
|
1331
1345
|
# Reporting issues
|
1332
1346
|
|
1333
|
-
|
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.
|
1334
1354
|
|
1335
1355
|
## Obtaining the source code given to Terser
|
1336
1356
|
|
1337
|
-
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.
|
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.
|
1338
1358
|
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
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
|
1342
1364
|
terser-debug-123456.log
|
1343
1365
|
```
|
1344
1366
|
|
@@ -1348,6 +1370,12 @@ If you're not sure how to set an environment variable on your shell (the above e
|
|
1348
1370
|
> npx cross-env TERSER_DEBUG_DIR=/path/to/logs command-that-uses-terser
|
1349
1371
|
```
|
1350
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
|
+
|
1351
1379
|
# README.md Patrons:
|
1352
1380
|
|
1353
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.
|
package/dist/bundle.min.js
CHANGED
@@ -2372,7 +2372,7 @@ function parse($TEXT, options) {
|
|
2372
2372
|
value : tok.value,
|
2373
2373
|
quote : tok.quote
|
2374
2374
|
});
|
2375
|
-
annotate(ret);
|
2375
|
+
annotate(ret);
|
2376
2376
|
break;
|
2377
2377
|
case "regexp":
|
2378
2378
|
const [_, source, flags] = tok.value.match(/^\/(.*)\/(\w*)$/);
|
@@ -2684,13 +2684,14 @@ function parse($TEXT, options) {
|
|
2684
2684
|
}
|
2685
2685
|
|
2686
2686
|
// Create property
|
2687
|
-
|
2687
|
+
const kv = new AST_ObjectKeyVal({
|
2688
2688
|
start: start,
|
2689
2689
|
quote: start.quote,
|
2690
2690
|
key: name instanceof AST_Node ? name : "" + name,
|
2691
2691
|
value: value,
|
2692
2692
|
end: prev()
|
2693
|
-
})
|
2693
|
+
});
|
2694
|
+
a.push(annotate(kv));
|
2694
2695
|
}
|
2695
2696
|
next();
|
2696
2697
|
return new AST_Object({ properties: a });
|
@@ -2803,26 +2804,26 @@ function parse($TEXT, options) {
|
|
2803
2804
|
: AST_ObjectSetter;
|
2804
2805
|
|
2805
2806
|
name = get_symbol_ast(name);
|
2806
|
-
return new AccessorClass({
|
2807
|
+
return annotate(new AccessorClass({
|
2807
2808
|
start,
|
2808
2809
|
static: is_static,
|
2809
2810
|
key: name,
|
2810
2811
|
quote: name instanceof AST_SymbolMethod ? property_token.quote : undefined,
|
2811
2812
|
value: create_accessor(),
|
2812
2813
|
end: prev()
|
2813
|
-
});
|
2814
|
+
}));
|
2814
2815
|
} else {
|
2815
2816
|
const AccessorClass = accessor_type === "get"
|
2816
2817
|
? AST_PrivateGetter
|
2817
2818
|
: AST_PrivateSetter;
|
2818
2819
|
|
2819
|
-
return new AccessorClass({
|
2820
|
+
return annotate(new AccessorClass({
|
2820
2821
|
start,
|
2821
2822
|
static: is_static,
|
2822
2823
|
key: get_symbol_ast(name),
|
2823
2824
|
value: create_accessor(),
|
2824
2825
|
end: prev(),
|
2825
|
-
});
|
2826
|
+
}));
|
2826
2827
|
}
|
2827
2828
|
}
|
2828
2829
|
|
@@ -2842,7 +2843,7 @@ function parse($TEXT, options) {
|
|
2842
2843
|
value : create_accessor(is_generator, is_async),
|
2843
2844
|
end : prev()
|
2844
2845
|
});
|
2845
|
-
return node;
|
2846
|
+
return annotate(node);
|
2846
2847
|
}
|
2847
2848
|
|
2848
2849
|
if (is_class) {
|
@@ -2855,14 +2856,16 @@ function parse($TEXT, options) {
|
|
2855
2856
|
: AST_ClassProperty;
|
2856
2857
|
if (is("operator", "=")) {
|
2857
2858
|
next();
|
2858
|
-
return
|
2859
|
-
|
2860
|
-
|
2861
|
-
|
2862
|
-
|
2863
|
-
|
2864
|
-
|
2865
|
-
|
2859
|
+
return annotate(
|
2860
|
+
new AST_ClassPropertyVariant({
|
2861
|
+
start,
|
2862
|
+
static: is_static,
|
2863
|
+
quote,
|
2864
|
+
key,
|
2865
|
+
value: expression(false),
|
2866
|
+
end: prev()
|
2867
|
+
})
|
2868
|
+
);
|
2866
2869
|
} else if (
|
2867
2870
|
is("name")
|
2868
2871
|
|| is("privatename")
|
@@ -2870,13 +2873,15 @@ function parse($TEXT, options) {
|
|
2870
2873
|
|| is("punc", ";")
|
2871
2874
|
|| is("punc", "}")
|
2872
2875
|
) {
|
2873
|
-
return
|
2874
|
-
|
2875
|
-
|
2876
|
-
|
2877
|
-
|
2878
|
-
|
2879
|
-
|
2876
|
+
return annotate(
|
2877
|
+
new AST_ClassPropertyVariant({
|
2878
|
+
start,
|
2879
|
+
static: is_static,
|
2880
|
+
quote,
|
2881
|
+
key,
|
2882
|
+
end: prev()
|
2883
|
+
})
|
2884
|
+
);
|
2880
2885
|
}
|
2881
2886
|
}
|
2882
2887
|
}
|
@@ -3239,10 +3244,9 @@ function parse($TEXT, options) {
|
|
3239
3244
|
}
|
3240
3245
|
|
3241
3246
|
// Annotate AST_Call, AST_Lambda or AST_New with the special comments
|
3242
|
-
function annotate(node) {
|
3243
|
-
var
|
3244
|
-
|
3245
|
-
const comments_outside_parens = outer_comments_before_counts.get(start);
|
3247
|
+
function annotate(node, before_token = node.start) {
|
3248
|
+
var comments = before_token.comments_before;
|
3249
|
+
const comments_outside_parens = outer_comments_before_counts.get(before_token);
|
3246
3250
|
var i = comments_outside_parens != null ? comments_outside_parens : comments.length;
|
3247
3251
|
while (--i >= 0) {
|
3248
3252
|
var comment = comments[i];
|
@@ -3263,8 +3267,13 @@ function parse($TEXT, options) {
|
|
3263
3267
|
set_annotation(node, _KEY);
|
3264
3268
|
break;
|
3265
3269
|
}
|
3270
|
+
if (/[@#]__MANGLE_PROP__/.test(comment.value)) {
|
3271
|
+
set_annotation(node, _MANGLEPROP);
|
3272
|
+
break;
|
3273
|
+
}
|
3266
3274
|
}
|
3267
3275
|
}
|
3276
|
+
return node;
|
3268
3277
|
}
|
3269
3278
|
|
3270
3279
|
var subscripts = function(expr, allow_calls, is_chain) {
|
@@ -5623,6 +5632,7 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", function AST_Obj
|
|
5623
5632
|
this.value = props.value;
|
5624
5633
|
this.start = props.start;
|
5625
5634
|
this.end = props.end;
|
5635
|
+
this._annotations = props._annotations;
|
5626
5636
|
}
|
5627
5637
|
|
5628
5638
|
this.flags = 0;
|
@@ -5652,6 +5662,7 @@ var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", function AST_ObjectKeyVa
|
|
5652
5662
|
this.value = props.value;
|
5653
5663
|
this.start = props.start;
|
5654
5664
|
this.end = props.end;
|
5665
|
+
this._annotations = props._annotations;
|
5655
5666
|
}
|
5656
5667
|
|
5657
5668
|
this.flags = 0;
|
@@ -5713,6 +5724,7 @@ var AST_ObjectSetter = DEFNODE("ObjectSetter", "quote static", function AST_Obje
|
|
5713
5724
|
this.value = props.value;
|
5714
5725
|
this.start = props.start;
|
5715
5726
|
this.end = props.end;
|
5727
|
+
this._annotations = props._annotations;
|
5716
5728
|
}
|
5717
5729
|
|
5718
5730
|
this.flags = 0;
|
@@ -5735,6 +5747,7 @@ var AST_ObjectGetter = DEFNODE("ObjectGetter", "quote static", function AST_Obje
|
|
5735
5747
|
this.value = props.value;
|
5736
5748
|
this.start = props.start;
|
5737
5749
|
this.end = props.end;
|
5750
|
+
this._annotations = props._annotations;
|
5738
5751
|
}
|
5739
5752
|
|
5740
5753
|
this.flags = 0;
|
@@ -5762,6 +5775,7 @@ var AST_ConciseMethod = DEFNODE(
|
|
5762
5775
|
this.value = props.value;
|
5763
5776
|
this.start = props.start;
|
5764
5777
|
this.end = props.end;
|
5778
|
+
this._annotations = props._annotations;
|
5765
5779
|
}
|
5766
5780
|
|
5767
5781
|
this.flags = 0;
|
@@ -5884,6 +5898,7 @@ var AST_ClassProperty = DEFNODE("ClassProperty", "static quote", function AST_Cl
|
|
5884
5898
|
this.value = props.value;
|
5885
5899
|
this.start = props.start;
|
5886
5900
|
this.end = props.end;
|
5901
|
+
this._annotations = props._annotations;
|
5887
5902
|
}
|
5888
5903
|
|
5889
5904
|
this.flags = 0;
|
@@ -6787,10 +6802,11 @@ class TreeTransformer extends TreeWalker {
|
|
6787
6802
|
}
|
6788
6803
|
}
|
6789
6804
|
|
6790
|
-
const _PURE
|
6791
|
-
const _INLINE
|
6792
|
-
const _NOINLINE
|
6793
|
-
const _KEY
|
6805
|
+
const _PURE = 0b00000001;
|
6806
|
+
const _INLINE = 0b00000010;
|
6807
|
+
const _NOINLINE = 0b00000100;
|
6808
|
+
const _KEY = 0b00001000;
|
6809
|
+
const _MANGLEPROP = 0b00010000;
|
6794
6810
|
|
6795
6811
|
/***********************************************************************
|
6796
6812
|
|
@@ -15918,6 +15934,10 @@ function handle_defined_after_hoist(parent) {
|
|
15918
15934
|
) return true;
|
15919
15935
|
});
|
15920
15936
|
|
15937
|
+
const symbols_of_interest = new Set();
|
15938
|
+
const defuns_of_interest = new Set();
|
15939
|
+
const potential_conflicts = [];
|
15940
|
+
|
15921
15941
|
for (const defun of defuns) {
|
15922
15942
|
const fname_def = defun.name.definition();
|
15923
15943
|
const found_self_ref_in_other_defuns = defuns.some(
|
@@ -15947,35 +15967,77 @@ function handle_defined_after_hoist(parent) {
|
|
15947
15967
|
continue;
|
15948
15968
|
}
|
15949
15969
|
|
15950
|
-
//
|
15951
|
-
|
15952
|
-
|
15953
|
-
|
15954
|
-
|
15955
|
-
|
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
|
+
}
|
15956
15977
|
|
15957
|
-
|
15958
|
-
|
15959
|
-
|
15960
|
-
|
15961
|
-
|
15962
|
-
|
15963
|
-
|
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);
|
15964
16007
|
}
|
16008
|
+
}
|
16009
|
+
})));
|
16010
|
+
|
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
|
+
const index = found_symbols.indexOf(sym_id, starting_at);
|
15965
16017
|
|
15966
|
-
// Step 2: if Step 1 occurred, find a var the defun uses
|
15967
16018
|
if (
|
15968
|
-
|
15969
|
-
&&
|
15970
|
-
&&
|
15971
|
-
|| is_lhs(node, info))
|
16019
|
+
defun_range
|
16020
|
+
&& index >= defun_range.start
|
16021
|
+
&& index < defun_range.end
|
15972
16022
|
) {
|
15973
|
-
|
15974
|
-
|
16023
|
+
return find(sym_id, defun_range.end, must_be_write);
|
16024
|
+
} else if (
|
16025
|
+
must_be_write
|
16026
|
+
&& index >= 0
|
16027
|
+
&& !found_symbol_writes.has(index)
|
16028
|
+
) {
|
16029
|
+
return find(sym_id, index + 1, must_be_write);
|
16030
|
+
} else {
|
16031
|
+
return index;
|
15975
16032
|
}
|
15976
|
-
}
|
16033
|
+
};
|
15977
16034
|
|
15978
|
-
|
16035
|
+
const read_defun_at = find(fname_def.id);
|
16036
|
+
const wrote_def_at = find(def.id, read_defun_at + 1, true);
|
16037
|
+
|
16038
|
+
const wrote_def_after_reading_defun = read_defun_at != -1 && wrote_def_at != -1 && wrote_def_at > read_defun_at;
|
16039
|
+
|
16040
|
+
if (wrote_def_after_reading_defun) {
|
15979
16041
|
def.fixed = false;
|
15980
16042
|
}
|
15981
16043
|
}
|
@@ -29819,6 +29881,7 @@ function mangle_properties(ast, options) {
|
|
29819
29881
|
regex: null,
|
29820
29882
|
reserved: null,
|
29821
29883
|
undeclared: false,
|
29884
|
+
only_annotated: false,
|
29822
29885
|
}, true);
|
29823
29886
|
|
29824
29887
|
var nth_identifier = options.nth_identifier;
|
@@ -29837,6 +29900,7 @@ function mangle_properties(ast, options) {
|
|
29837
29900
|
cache = new Map();
|
29838
29901
|
}
|
29839
29902
|
|
29903
|
+
var only_annotated = options.only_annotated;
|
29840
29904
|
var regex = options.regex && new RegExp(options.regex);
|
29841
29905
|
|
29842
29906
|
// note debug is either false (disabled), or a string of the debug suffix to use (enabled).
|
@@ -29848,6 +29912,7 @@ function mangle_properties(ast, options) {
|
|
29848
29912
|
debug_name_suffix = (options.debug === true ? "" : options.debug);
|
29849
29913
|
}
|
29850
29914
|
|
29915
|
+
var annotated_names = new Set();
|
29851
29916
|
var names_to_mangle = new Set();
|
29852
29917
|
var unmangleable = new Set();
|
29853
29918
|
// Track each already-mangled name to prevent nth_identifier from generating
|
@@ -29856,7 +29921,36 @@ function mangle_properties(ast, options) {
|
|
29856
29921
|
|
29857
29922
|
var keep_quoted = !!options.keep_quoted;
|
29858
29923
|
|
29859
|
-
//
|
29924
|
+
// Step 1: Find all annotated /*@__MANGLEPROP__*/
|
29925
|
+
walk(ast, node => {
|
29926
|
+
if (
|
29927
|
+
node instanceof AST_ClassPrivateProperty
|
29928
|
+
|| node instanceof AST_PrivateMethod
|
29929
|
+
|| node instanceof AST_PrivateGetter
|
29930
|
+
|| node instanceof AST_PrivateSetter
|
29931
|
+
|| node instanceof AST_DotHash
|
29932
|
+
) ; else if (node instanceof AST_ObjectKeyVal) {
|
29933
|
+
if (
|
29934
|
+
typeof node.key == "string"
|
29935
|
+
&& has_annotation(node, _MANGLEPROP)
|
29936
|
+
&& can_mangle(node.key)
|
29937
|
+
) {
|
29938
|
+
annotated_names.add(node.key);
|
29939
|
+
clear_annotation(node, _MANGLEPROP);
|
29940
|
+
}
|
29941
|
+
} else if (node instanceof AST_ObjectProperty) {
|
29942
|
+
// setter or getter, since KeyVal is handled above
|
29943
|
+
if (
|
29944
|
+
has_annotation(node, _MANGLEPROP)
|
29945
|
+
&& can_mangle(node.key.name)
|
29946
|
+
) {
|
29947
|
+
annotated_names.add(node.key.name);
|
29948
|
+
clear_annotation(node, _MANGLEPROP);
|
29949
|
+
}
|
29950
|
+
}
|
29951
|
+
});
|
29952
|
+
|
29953
|
+
// step 2: find candidates to mangle
|
29860
29954
|
ast.walk(new TreeWalker(function(node) {
|
29861
29955
|
if (
|
29862
29956
|
node instanceof AST_ClassPrivateProperty
|
@@ -29948,15 +30042,19 @@ function mangle_properties(ast, options) {
|
|
29948
30042
|
}
|
29949
30043
|
|
29950
30044
|
function should_mangle(name) {
|
29951
|
-
if (
|
30045
|
+
if (only_annotated && !annotated_names.has(name)) return false;
|
30046
|
+
if (regex && !regex.test(name)) {
|
30047
|
+
return annotated_names.has(name);
|
30048
|
+
}
|
29952
30049
|
if (reserved.has(name)) return false;
|
29953
30050
|
return cache.has(name)
|
29954
30051
|
|| names_to_mangle.has(name);
|
29955
30052
|
}
|
29956
30053
|
|
29957
30054
|
function add(name) {
|
29958
|
-
if (can_mangle(name))
|
30055
|
+
if (can_mangle(name)) {
|
29959
30056
|
names_to_mangle.add(name);
|
30057
|
+
}
|
29960
30058
|
|
29961
30059
|
if (!should_mangle(name)) {
|
29962
30060
|
unmangleable.add(name);
|
package/lib/ast.js
CHANGED
@@ -2013,6 +2013,7 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", function AST_Obj
|
|
2013
2013
|
this.value = props.value;
|
2014
2014
|
this.start = props.start;
|
2015
2015
|
this.end = props.end;
|
2016
|
+
this._annotations = props._annotations;
|
2016
2017
|
}
|
2017
2018
|
|
2018
2019
|
this.flags = 0;
|
@@ -2042,6 +2043,7 @@ var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", function AST_ObjectKeyVa
|
|
2042
2043
|
this.value = props.value;
|
2043
2044
|
this.start = props.start;
|
2044
2045
|
this.end = props.end;
|
2046
|
+
this._annotations = props._annotations;
|
2045
2047
|
}
|
2046
2048
|
|
2047
2049
|
this.flags = 0;
|
@@ -2103,6 +2105,7 @@ var AST_ObjectSetter = DEFNODE("ObjectSetter", "quote static", function AST_Obje
|
|
2103
2105
|
this.value = props.value;
|
2104
2106
|
this.start = props.start;
|
2105
2107
|
this.end = props.end;
|
2108
|
+
this._annotations = props._annotations;
|
2106
2109
|
}
|
2107
2110
|
|
2108
2111
|
this.flags = 0;
|
@@ -2125,6 +2128,7 @@ var AST_ObjectGetter = DEFNODE("ObjectGetter", "quote static", function AST_Obje
|
|
2125
2128
|
this.value = props.value;
|
2126
2129
|
this.start = props.start;
|
2127
2130
|
this.end = props.end;
|
2131
|
+
this._annotations = props._annotations;
|
2128
2132
|
}
|
2129
2133
|
|
2130
2134
|
this.flags = 0;
|
@@ -2152,6 +2156,7 @@ var AST_ConciseMethod = DEFNODE(
|
|
2152
2156
|
this.value = props.value;
|
2153
2157
|
this.start = props.start;
|
2154
2158
|
this.end = props.end;
|
2159
|
+
this._annotations = props._annotations;
|
2155
2160
|
}
|
2156
2161
|
|
2157
2162
|
this.flags = 0;
|
@@ -2274,6 +2279,7 @@ var AST_ClassProperty = DEFNODE("ClassProperty", "static quote", function AST_Cl
|
|
2274
2279
|
this.value = props.value;
|
2275
2280
|
this.start = props.start;
|
2276
2281
|
this.end = props.end;
|
2282
|
+
this._annotations = props._annotations;
|
2277
2283
|
}
|
2278
2284
|
|
2279
2285
|
this.flags = 0;
|
@@ -3177,10 +3183,11 @@ class TreeTransformer extends TreeWalker {
|
|
3177
3183
|
}
|
3178
3184
|
}
|
3179
3185
|
|
3180
|
-
const _PURE
|
3181
|
-
const _INLINE
|
3182
|
-
const _NOINLINE
|
3183
|
-
const _KEY
|
3186
|
+
const _PURE = 0b00000001;
|
3187
|
+
const _INLINE = 0b00000010;
|
3188
|
+
const _NOINLINE = 0b00000100;
|
3189
|
+
const _KEY = 0b00001000;
|
3190
|
+
const _MANGLEPROP = 0b00010000;
|
3184
3191
|
|
3185
3192
|
export {
|
3186
3193
|
AST_Accessor,
|
@@ -3326,4 +3333,5 @@ export {
|
|
3326
3333
|
_NOINLINE,
|
3327
3334
|
_PURE,
|
3328
3335
|
_KEY,
|
3336
|
+
_MANGLEPROP,
|
3329
3337
|
};
|
@@ -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,77 @@ function handle_defined_after_hoist(parent) {
|
|
542
546
|
continue;
|
543
547
|
}
|
544
548
|
|
545
|
-
//
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
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
|
+
}
|
551
556
|
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
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);
|
559
586
|
}
|
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
|
+
const index = found_symbols.indexOf(sym_id, starting_at);
|
560
596
|
|
561
|
-
// Step 2: if Step 1 occurred, find a var the defun uses
|
562
597
|
if (
|
563
|
-
|
564
|
-
&&
|
565
|
-
&&
|
566
|
-
|| is_lhs(node, info))
|
598
|
+
defun_range
|
599
|
+
&& index >= defun_range.start
|
600
|
+
&& index < defun_range.end
|
567
601
|
) {
|
568
|
-
|
569
|
-
|
602
|
+
return find(sym_id, defun_range.end, must_be_write);
|
603
|
+
} else if (
|
604
|
+
must_be_write
|
605
|
+
&& index >= 0
|
606
|
+
&& !found_symbol_writes.has(index)
|
607
|
+
) {
|
608
|
+
return find(sym_id, index + 1, must_be_write);
|
609
|
+
} else {
|
610
|
+
return index;
|
570
611
|
}
|
571
|
-
}
|
612
|
+
};
|
613
|
+
|
614
|
+
const read_defun_at = find(fname_def.id);
|
615
|
+
const wrote_def_at = find(def.id, read_defun_at + 1, true);
|
616
|
+
|
617
|
+
const wrote_def_after_reading_defun = read_defun_at != -1 && wrote_def_at != -1 && wrote_def_at > read_defun_at;
|
572
618
|
|
573
|
-
if (
|
619
|
+
if (wrote_def_after_reading_defun) {
|
574
620
|
def.fixed = false;
|
575
621
|
}
|
576
622
|
}
|
package/lib/parse.js
CHANGED
@@ -163,7 +163,8 @@ import {
|
|
163
163
|
_INLINE,
|
164
164
|
_NOINLINE,
|
165
165
|
_PURE,
|
166
|
-
_KEY
|
166
|
+
_KEY,
|
167
|
+
_MANGLEPROP,
|
167
168
|
} from "./ast.js";
|
168
169
|
|
169
170
|
var LATEST_RAW = ""; // Only used for numbers and template strings
|
@@ -2226,7 +2227,7 @@ function parse($TEXT, options) {
|
|
2226
2227
|
value : tok.value,
|
2227
2228
|
quote : tok.quote
|
2228
2229
|
});
|
2229
|
-
annotate(ret);
|
2230
|
+
annotate(ret);
|
2230
2231
|
break;
|
2231
2232
|
case "regexp":
|
2232
2233
|
const [_, source, flags] = tok.value.match(/^\/(.*)\/(\w*)$/);
|
@@ -2538,13 +2539,14 @@ function parse($TEXT, options) {
|
|
2538
2539
|
}
|
2539
2540
|
|
2540
2541
|
// Create property
|
2541
|
-
|
2542
|
+
const kv = new AST_ObjectKeyVal({
|
2542
2543
|
start: start,
|
2543
2544
|
quote: start.quote,
|
2544
2545
|
key: name instanceof AST_Node ? name : "" + name,
|
2545
2546
|
value: value,
|
2546
2547
|
end: prev()
|
2547
|
-
})
|
2548
|
+
});
|
2549
|
+
a.push(annotate(kv));
|
2548
2550
|
}
|
2549
2551
|
next();
|
2550
2552
|
return new AST_Object({ properties: a });
|
@@ -2657,26 +2659,26 @@ function parse($TEXT, options) {
|
|
2657
2659
|
: AST_ObjectSetter;
|
2658
2660
|
|
2659
2661
|
name = get_symbol_ast(name);
|
2660
|
-
return new AccessorClass({
|
2662
|
+
return annotate(new AccessorClass({
|
2661
2663
|
start,
|
2662
2664
|
static: is_static,
|
2663
2665
|
key: name,
|
2664
2666
|
quote: name instanceof AST_SymbolMethod ? property_token.quote : undefined,
|
2665
2667
|
value: create_accessor(),
|
2666
2668
|
end: prev()
|
2667
|
-
});
|
2669
|
+
}));
|
2668
2670
|
} else {
|
2669
2671
|
const AccessorClass = accessor_type === "get"
|
2670
2672
|
? AST_PrivateGetter
|
2671
2673
|
: AST_PrivateSetter;
|
2672
2674
|
|
2673
|
-
return new AccessorClass({
|
2675
|
+
return annotate(new AccessorClass({
|
2674
2676
|
start,
|
2675
2677
|
static: is_static,
|
2676
2678
|
key: get_symbol_ast(name),
|
2677
2679
|
value: create_accessor(),
|
2678
2680
|
end: prev(),
|
2679
|
-
});
|
2681
|
+
}));
|
2680
2682
|
}
|
2681
2683
|
}
|
2682
2684
|
|
@@ -2696,7 +2698,7 @@ function parse($TEXT, options) {
|
|
2696
2698
|
value : create_accessor(is_generator, is_async),
|
2697
2699
|
end : prev()
|
2698
2700
|
});
|
2699
|
-
return node;
|
2701
|
+
return annotate(node);
|
2700
2702
|
}
|
2701
2703
|
|
2702
2704
|
if (is_class) {
|
@@ -2709,14 +2711,16 @@ function parse($TEXT, options) {
|
|
2709
2711
|
: AST_ClassProperty;
|
2710
2712
|
if (is("operator", "=")) {
|
2711
2713
|
next();
|
2712
|
-
return
|
2713
|
-
|
2714
|
-
|
2715
|
-
|
2716
|
-
|
2717
|
-
|
2718
|
-
|
2719
|
-
|
2714
|
+
return annotate(
|
2715
|
+
new AST_ClassPropertyVariant({
|
2716
|
+
start,
|
2717
|
+
static: is_static,
|
2718
|
+
quote,
|
2719
|
+
key,
|
2720
|
+
value: expression(false),
|
2721
|
+
end: prev()
|
2722
|
+
})
|
2723
|
+
);
|
2720
2724
|
} else if (
|
2721
2725
|
is("name")
|
2722
2726
|
|| is("privatename")
|
@@ -2724,13 +2728,15 @@ function parse($TEXT, options) {
|
|
2724
2728
|
|| is("punc", ";")
|
2725
2729
|
|| is("punc", "}")
|
2726
2730
|
) {
|
2727
|
-
return
|
2728
|
-
|
2729
|
-
|
2730
|
-
|
2731
|
-
|
2732
|
-
|
2733
|
-
|
2731
|
+
return annotate(
|
2732
|
+
new AST_ClassPropertyVariant({
|
2733
|
+
start,
|
2734
|
+
static: is_static,
|
2735
|
+
quote,
|
2736
|
+
key,
|
2737
|
+
end: prev()
|
2738
|
+
})
|
2739
|
+
);
|
2734
2740
|
}
|
2735
2741
|
}
|
2736
2742
|
}
|
@@ -3093,10 +3099,9 @@ function parse($TEXT, options) {
|
|
3093
3099
|
}
|
3094
3100
|
|
3095
3101
|
// Annotate AST_Call, AST_Lambda or AST_New with the special comments
|
3096
|
-
function annotate(node) {
|
3097
|
-
var
|
3098
|
-
|
3099
|
-
const comments_outside_parens = outer_comments_before_counts.get(start);
|
3102
|
+
function annotate(node, before_token = node.start) {
|
3103
|
+
var comments = before_token.comments_before;
|
3104
|
+
const comments_outside_parens = outer_comments_before_counts.get(before_token);
|
3100
3105
|
var i = comments_outside_parens != null ? comments_outside_parens : comments.length;
|
3101
3106
|
while (--i >= 0) {
|
3102
3107
|
var comment = comments[i];
|
@@ -3117,8 +3122,13 @@ function parse($TEXT, options) {
|
|
3117
3122
|
set_annotation(node, _KEY);
|
3118
3123
|
break;
|
3119
3124
|
}
|
3125
|
+
if (/[@#]__MANGLE_PROP__/.test(comment.value)) {
|
3126
|
+
set_annotation(node, _MANGLEPROP);
|
3127
|
+
break;
|
3128
|
+
}
|
3120
3129
|
}
|
3121
3130
|
}
|
3131
|
+
return node;
|
3122
3132
|
}
|
3123
3133
|
|
3124
3134
|
var subscripts = function(expr, allow_calls, is_chain) {
|
package/lib/propmangle.js
CHANGED
@@ -70,6 +70,9 @@ import {
|
|
70
70
|
TreeTransformer,
|
71
71
|
TreeWalker,
|
72
72
|
_KEY,
|
73
|
+
_MANGLEPROP,
|
74
|
+
|
75
|
+
walk,
|
73
76
|
} from "./ast.js";
|
74
77
|
import { domprops } from "../tools/domprops.js";
|
75
78
|
|
@@ -188,6 +191,7 @@ function mangle_properties(ast, options) {
|
|
188
191
|
regex: null,
|
189
192
|
reserved: null,
|
190
193
|
undeclared: false,
|
194
|
+
only_annotated: false,
|
191
195
|
}, true);
|
192
196
|
|
193
197
|
var nth_identifier = options.nth_identifier;
|
@@ -206,6 +210,7 @@ function mangle_properties(ast, options) {
|
|
206
210
|
cache = new Map();
|
207
211
|
}
|
208
212
|
|
213
|
+
var only_annotated = options.only_annotated;
|
209
214
|
var regex = options.regex && new RegExp(options.regex);
|
210
215
|
|
211
216
|
// note debug is either false (disabled), or a string of the debug suffix to use (enabled).
|
@@ -217,6 +222,7 @@ function mangle_properties(ast, options) {
|
|
217
222
|
debug_name_suffix = (options.debug === true ? "" : options.debug);
|
218
223
|
}
|
219
224
|
|
225
|
+
var annotated_names = new Set();
|
220
226
|
var names_to_mangle = new Set();
|
221
227
|
var unmangleable = new Set();
|
222
228
|
// Track each already-mangled name to prevent nth_identifier from generating
|
@@ -225,7 +231,38 @@ function mangle_properties(ast, options) {
|
|
225
231
|
|
226
232
|
var keep_quoted = !!options.keep_quoted;
|
227
233
|
|
228
|
-
//
|
234
|
+
// Step 1: Find all annotated /*@__MANGLEPROP__*/
|
235
|
+
walk(ast, node => {
|
236
|
+
if (
|
237
|
+
node instanceof AST_ClassPrivateProperty
|
238
|
+
|| node instanceof AST_PrivateMethod
|
239
|
+
|| node instanceof AST_PrivateGetter
|
240
|
+
|| node instanceof AST_PrivateSetter
|
241
|
+
|| node instanceof AST_DotHash
|
242
|
+
) {
|
243
|
+
// handled by mangle_private_properties
|
244
|
+
} else if (node instanceof AST_ObjectKeyVal) {
|
245
|
+
if (
|
246
|
+
typeof node.key == "string"
|
247
|
+
&& has_annotation(node, _MANGLEPROP)
|
248
|
+
&& can_mangle(node.key)
|
249
|
+
) {
|
250
|
+
annotated_names.add(node.key);
|
251
|
+
clear_annotation(node, _MANGLEPROP);
|
252
|
+
}
|
253
|
+
} else if (node instanceof AST_ObjectProperty) {
|
254
|
+
// setter or getter, since KeyVal is handled above
|
255
|
+
if (
|
256
|
+
has_annotation(node, _MANGLEPROP)
|
257
|
+
&& can_mangle(node.key.name)
|
258
|
+
) {
|
259
|
+
annotated_names.add(node.key.name);
|
260
|
+
clear_annotation(node, _MANGLEPROP);
|
261
|
+
}
|
262
|
+
}
|
263
|
+
});
|
264
|
+
|
265
|
+
// step 2: find candidates to mangle
|
229
266
|
ast.walk(new TreeWalker(function(node) {
|
230
267
|
if (
|
231
268
|
node instanceof AST_ClassPrivateProperty
|
@@ -321,15 +358,19 @@ function mangle_properties(ast, options) {
|
|
321
358
|
}
|
322
359
|
|
323
360
|
function should_mangle(name) {
|
324
|
-
if (
|
361
|
+
if (only_annotated && !annotated_names.has(name)) return false;
|
362
|
+
if (regex && !regex.test(name)) {
|
363
|
+
return annotated_names.has(name);
|
364
|
+
}
|
325
365
|
if (reserved.has(name)) return false;
|
326
366
|
return cache.has(name)
|
327
367
|
|| names_to_mangle.has(name);
|
328
368
|
}
|
329
369
|
|
330
370
|
function add(name) {
|
331
|
-
if (can_mangle(name))
|
371
|
+
if (can_mangle(name)) {
|
332
372
|
names_to_mangle.add(name);
|
373
|
+
}
|
333
374
|
|
334
375
|
if (!should_mangle(name)) {
|
335
376
|
unmangleable.add(name);
|