terser 5.16.6 → 5.16.9
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 +11 -1
- package/README.md +11 -1
- package/dist/bundle.min.js +113 -25
- package/lib/ast.js +3 -17
- package/lib/compress/common.js +3 -1
- package/lib/compress/index.js +19 -2
- package/lib/compress/reduce-vars.js +82 -2
- package/lib/output.js +6 -0
- package/lib/parse.js +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## v5.16.
|
3
|
+
## v5.16.9
|
4
|
+
- Fix parentheses in output of optional chains (`a?.b`) (#1374)
|
5
|
+
- More documentation on source maps (#1368)
|
6
|
+
- New `lhs_constants` option, allowing to stop Terser from swapping comparison operands (#1361)
|
7
|
+
|
8
|
+
## v5.16.8
|
9
|
+
|
10
|
+
- Become even less conservative around function definitions for `reduce_vars`
|
11
|
+
- Fix parsing context of `import.meta` expressions such that method calls are allowed
|
12
|
+
|
13
|
+
## v5.16.6
|
4
14
|
|
5
15
|
- Become less conservative with analyzing function definitions for `reduce_vars`
|
6
16
|
- Parse `import.meta` as a real AST node and not an `object.property`
|
package/README.md
CHANGED
@@ -708,7 +708,8 @@ If you happen to need the source map as a raw object, set `sourceMap.asObject` t
|
|
708
708
|
|
709
709
|
- `comparisons` (default: `true`) -- apply certain optimizations to binary nodes,
|
710
710
|
e.g. `!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary
|
711
|
-
nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
|
711
|
+
nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc. Note: `comparisons`
|
712
|
+
works best with `lhs_constants` enabled.
|
712
713
|
|
713
714
|
- `computed_props` (default: `true`) -- Transforms constant computed properties
|
714
715
|
into regular ones: `{["computed"]: 1}` is converted to `{computed: 1}`.
|
@@ -775,6 +776,9 @@ If you happen to need the source map as a raw object, set `sourceMap.asObject` t
|
|
775
776
|
- `keep_infinity` (default: `false`) -- Pass `true` to prevent `Infinity` from
|
776
777
|
being compressed into `1/0`, which may cause performance issues on Chrome.
|
777
778
|
|
779
|
+
- `lhs_constants` (default: `true`) -- Moves constant values to the left-hand side
|
780
|
+
of binary nodes. `foo == 42 → 42 == foo`
|
781
|
+
|
778
782
|
- `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops
|
779
783
|
when we can statically determine the condition.
|
780
784
|
|
@@ -1267,6 +1271,12 @@ expected as code is optimized and mappings are often simply not possible as
|
|
1267
1271
|
some code no longer exists. For highest fidelity in source map debugging
|
1268
1272
|
disable the `compress` option and just use `mangle`.
|
1269
1273
|
|
1274
|
+
When debugging, make sure you enable the **"map scopes"** feature to map mangled variable names back to their original names.
|
1275
|
+
Without this, all variable values will be `undefined`. See https://github.com/terser/terser/issues/1367 for more details.
|
1276
|
+
<br/><br/>
|
1277
|
+
|
1278
|
+

|
1279
|
+
|
1270
1280
|
### Compiler assumptions
|
1271
1281
|
|
1272
1282
|
To allow for better optimizations, the compiler makes various assumptions:
|
package/dist/bundle.min.js
CHANGED
@@ -2451,7 +2451,7 @@ function parse($TEXT, options) {
|
|
2451
2451
|
return new_(allow_calls);
|
2452
2452
|
}
|
2453
2453
|
if (is("name", "import") && is_token(peek(), "punc", ".")) {
|
2454
|
-
return import_meta();
|
2454
|
+
return import_meta(allow_calls);
|
2455
2455
|
}
|
2456
2456
|
var start = S.token;
|
2457
2457
|
var peeked;
|
@@ -2944,7 +2944,7 @@ function parse($TEXT, options) {
|
|
2944
2944
|
});
|
2945
2945
|
}
|
2946
2946
|
|
2947
|
-
function import_meta() {
|
2947
|
+
function import_meta(allow_calls) {
|
2948
2948
|
var start = S.token;
|
2949
2949
|
expect_token("name", "import");
|
2950
2950
|
expect_token("punc", ".");
|
@@ -2952,7 +2952,7 @@ function parse($TEXT, options) {
|
|
2952
2952
|
return subscripts(new AST_ImportMeta({
|
2953
2953
|
start: start,
|
2954
2954
|
end: prev()
|
2955
|
-
}),
|
2955
|
+
}), allow_calls);
|
2956
2956
|
}
|
2957
2957
|
|
2958
2958
|
function map_name(is_import) {
|
@@ -3850,21 +3850,8 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", function AST_Simple
|
|
3850
3850
|
|
3851
3851
|
function walk_body(node, visitor) {
|
3852
3852
|
const body = node.body;
|
3853
|
-
|
3854
|
-
|
3855
|
-
if (body[i] instanceof AST_Defun) {
|
3856
|
-
body[i]._walk(visitor);
|
3857
|
-
}
|
3858
|
-
}
|
3859
|
-
for (var i = 0, len = body.length; i < len; i++) {
|
3860
|
-
if (!(body[i] instanceof AST_Defun)) {
|
3861
|
-
body[i]._walk(visitor);
|
3862
|
-
}
|
3863
|
-
}
|
3864
|
-
} else {
|
3865
|
-
for (var i = 0, len = body.length; i < len; i++) {
|
3866
|
-
body[i]._walk(visitor);
|
3867
|
-
}
|
3853
|
+
for (var i = 0, len = body.length; i < len; i++) {
|
3854
|
+
body[i]._walk(visitor);
|
3868
3855
|
}
|
3869
3856
|
}
|
3870
3857
|
|
@@ -6643,11 +6630,10 @@ const walk_abort = Symbol("abort walk");
|
|
6643
6630
|
/* -----[ TreeWalker ]----- */
|
6644
6631
|
|
6645
6632
|
class TreeWalker {
|
6646
|
-
constructor(callback
|
6633
|
+
constructor(callback) {
|
6647
6634
|
this.visit = callback;
|
6648
6635
|
this.stack = [];
|
6649
6636
|
this.directives = Object.create(null);
|
6650
|
-
this.walk_defun_first = walk_defun_first;
|
6651
6637
|
}
|
6652
6638
|
|
6653
6639
|
_visit(node, descend) {
|
@@ -9756,6 +9742,12 @@ function OutputStream(options) {
|
|
9756
9742
|
return true;
|
9757
9743
|
});
|
9758
9744
|
|
9745
|
+
PARENS(AST_Chain, function(output) {
|
9746
|
+
var p = output.parent();
|
9747
|
+
if (!(p instanceof AST_Call || p instanceof AST_PropAccess)) return false;
|
9748
|
+
return p.expression === this;
|
9749
|
+
});
|
9750
|
+
|
9759
9751
|
PARENS(AST_PropAccess, function(output) {
|
9760
9752
|
var p = output.parent();
|
9761
9753
|
if (p instanceof AST_New && p.expression === this) {
|
@@ -12953,7 +12945,8 @@ function maintain_this_binding(parent, orig, val) {
|
|
12953
12945
|
parent instanceof AST_UnaryPrefix && parent.operator == "delete"
|
12954
12946
|
|| parent instanceof AST_Call && parent.expression === orig
|
12955
12947
|
&& (
|
12956
|
-
val instanceof
|
12948
|
+
val instanceof AST_Chain
|
12949
|
+
|| val instanceof AST_PropAccess
|
12957
12950
|
|| val instanceof AST_SymbolRef && val.name == "eval"
|
12958
12951
|
)
|
12959
12952
|
) {
|
@@ -15723,9 +15716,7 @@ def_reduce_vars(AST_Default, function(tw, descend) {
|
|
15723
15716
|
|
15724
15717
|
function mark_lambda(tw, descend, compressor) {
|
15725
15718
|
clear_flag(this, INLINED);
|
15726
|
-
|
15727
15719
|
push(tw);
|
15728
|
-
|
15729
15720
|
reset_variables(tw, compressor, this);
|
15730
15721
|
|
15731
15722
|
var iife;
|
@@ -15760,9 +15751,86 @@ function mark_lambda(tw, descend, compressor) {
|
|
15760
15751
|
descend();
|
15761
15752
|
pop(tw);
|
15762
15753
|
|
15754
|
+
handle_defined_after_hoist(this);
|
15755
|
+
|
15763
15756
|
return true;
|
15764
15757
|
}
|
15765
15758
|
|
15759
|
+
/**
|
15760
|
+
* It's possible for a hoisted function to use something that's not defined yet. Example:
|
15761
|
+
*
|
15762
|
+
* hoisted();
|
15763
|
+
* var defined_after = true;
|
15764
|
+
* function hoisted() {
|
15765
|
+
* // use defined_after
|
15766
|
+
* }
|
15767
|
+
*
|
15768
|
+
* This function is called on the parent to handle this issue.
|
15769
|
+
*/
|
15770
|
+
function handle_defined_after_hoist(parent) {
|
15771
|
+
const defuns = [];
|
15772
|
+
walk(parent, node => {
|
15773
|
+
if (node === parent) return;
|
15774
|
+
if (node instanceof AST_Defun) defuns.push(node);
|
15775
|
+
if (
|
15776
|
+
node instanceof AST_Scope
|
15777
|
+
|| node instanceof AST_SimpleStatement
|
15778
|
+
) return true;
|
15779
|
+
});
|
15780
|
+
|
15781
|
+
for (const defun of defuns) {
|
15782
|
+
const fname_def = defun.name.definition();
|
15783
|
+
const found_self_ref_in_other_defuns = defuns.some(
|
15784
|
+
d => d !== defun && d.enclosed.indexOf(fname_def) !== -1
|
15785
|
+
);
|
15786
|
+
|
15787
|
+
for (const def of defun.enclosed) {
|
15788
|
+
if (
|
15789
|
+
def.fixed === false
|
15790
|
+
|| def === fname_def
|
15791
|
+
|| def.scope.get_defun_scope() !== parent
|
15792
|
+
) {
|
15793
|
+
continue;
|
15794
|
+
}
|
15795
|
+
|
15796
|
+
// defun is hoisted, so always safe
|
15797
|
+
if (
|
15798
|
+
def.assignments === 0
|
15799
|
+
&& def.orig.length === 1
|
15800
|
+
&& def.orig[0] instanceof AST_SymbolDefun
|
15801
|
+
) {
|
15802
|
+
continue;
|
15803
|
+
}
|
15804
|
+
|
15805
|
+
if (found_self_ref_in_other_defuns) {
|
15806
|
+
def.fixed = false;
|
15807
|
+
continue;
|
15808
|
+
}
|
15809
|
+
|
15810
|
+
// Detect `call_defun(); var used_in_defun = ...`
|
15811
|
+
// Because `used_in_defun` can no longer be fixed
|
15812
|
+
let found_defun = false;
|
15813
|
+
let found_def_after_defun = false;
|
15814
|
+
walk(parent, node => {
|
15815
|
+
if (node === defun) return true;
|
15816
|
+
|
15817
|
+
if (node instanceof AST_Symbol) {
|
15818
|
+
if (!found_defun && node.thedef === fname_def) {
|
15819
|
+
found_defun = true;
|
15820
|
+
} else if (found_defun && node.thedef === def) {
|
15821
|
+
found_def_after_defun = true;
|
15822
|
+
return walk_abort;
|
15823
|
+
}
|
15824
|
+
}
|
15825
|
+
});
|
15826
|
+
|
15827
|
+
if (found_def_after_defun) {
|
15828
|
+
def.fixed = false;
|
15829
|
+
}
|
15830
|
+
}
|
15831
|
+
}
|
15832
|
+
}
|
15833
|
+
|
15766
15834
|
def_reduce_vars(AST_Lambda, mark_lambda);
|
15767
15835
|
|
15768
15836
|
def_reduce_vars(AST_Do, function(tw, descend, compressor) {
|
@@ -15883,6 +15951,9 @@ def_reduce_vars(AST_Toplevel, function(tw, descend, compressor) {
|
|
15883
15951
|
reset_def(compressor, def);
|
15884
15952
|
});
|
15885
15953
|
reset_variables(tw, compressor, this);
|
15954
|
+
descend();
|
15955
|
+
handle_defined_after_hoist(this);
|
15956
|
+
return true;
|
15886
15957
|
});
|
15887
15958
|
|
15888
15959
|
def_reduce_vars(AST_Try, function(tw, descend, compressor) {
|
@@ -17964,6 +18035,7 @@ class Compressor extends TreeWalker {
|
|
17964
18035
|
keep_fargs : true,
|
17965
18036
|
keep_fnames : false,
|
17966
18037
|
keep_infinity : false,
|
18038
|
+
lhs_constants : !false_by_default,
|
17967
18039
|
loops : !false_by_default,
|
17968
18040
|
module : false,
|
17969
18041
|
negate_iife : !false_by_default,
|
@@ -18258,7 +18330,7 @@ AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
|
|
18258
18330
|
}
|
18259
18331
|
return node.reduce_vars(preparation, descend, compressor);
|
18260
18332
|
}
|
18261
|
-
}
|
18333
|
+
});
|
18262
18334
|
// Stack of look-up tables to keep track of whether a `SymbolDef` has been
|
18263
18335
|
// properly assigned before use:
|
18264
18336
|
// - `push()` & `pop()` when visiting conditional branches
|
@@ -19850,7 +19922,7 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
19850
19922
|
self.right = tmp;
|
19851
19923
|
}
|
19852
19924
|
}
|
19853
|
-
if (commutativeOperators.has(self.operator)) {
|
19925
|
+
if (compressor.option("lhs_constants") && commutativeOperators.has(self.operator)) {
|
19854
19926
|
if (self.right.is_constant()
|
19855
19927
|
&& !self.left.is_constant()) {
|
19856
19928
|
// if right is a constant, whatever side effects the
|
@@ -19880,6 +19952,9 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
19880
19952
|
// void 0 == x => null == x
|
19881
19953
|
if (!is_strict_comparison && is_undefined(self.left, compressor)) {
|
19882
19954
|
self.left = make_node(AST_Null, self.left);
|
19955
|
+
// x == void 0 => x == null
|
19956
|
+
} else if (!is_strict_comparison && is_undefined(self.right, compressor)) {
|
19957
|
+
self.right = make_node(AST_Null, self.right);
|
19883
19958
|
} else if (compressor.option("typeofs")
|
19884
19959
|
// "undefined" == typeof x => undefined === x
|
19885
19960
|
&& self.left instanceof AST_String
|
@@ -19893,6 +19968,19 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
19893
19968
|
self.left = make_node(AST_Undefined, self.left).optimize(compressor);
|
19894
19969
|
if (self.operator.length == 2) self.operator += "=";
|
19895
19970
|
}
|
19971
|
+
} else if (compressor.option("typeofs")
|
19972
|
+
// typeof x === "undefined" => x === undefined
|
19973
|
+
&& self.left instanceof AST_UnaryPrefix
|
19974
|
+
&& self.left.operator == "typeof"
|
19975
|
+
&& self.right instanceof AST_String
|
19976
|
+
&& self.right.value == "undefined") {
|
19977
|
+
var expr = self.left.expression;
|
19978
|
+
if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
|
19979
|
+
: !(expr instanceof AST_PropAccess && compressor.option("ie8"))) {
|
19980
|
+
self.left = expr;
|
19981
|
+
self.right = make_node(AST_Undefined, self.right).optimize(compressor);
|
19982
|
+
if (self.operator.length == 2) self.operator += "=";
|
19983
|
+
}
|
19896
19984
|
} else if (self.left instanceof AST_SymbolRef
|
19897
19985
|
// obj !== obj => false
|
19898
19986
|
&& self.right instanceof AST_SymbolRef
|
package/lib/ast.js
CHANGED
@@ -249,21 +249,8 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", function AST_Simple
|
|
249
249
|
|
250
250
|
function walk_body(node, visitor) {
|
251
251
|
const body = node.body;
|
252
|
-
|
253
|
-
|
254
|
-
if (body[i] instanceof AST_Defun) {
|
255
|
-
body[i]._walk(visitor);
|
256
|
-
}
|
257
|
-
}
|
258
|
-
for (var i = 0, len = body.length; i < len; i++) {
|
259
|
-
if (!(body[i] instanceof AST_Defun)) {
|
260
|
-
body[i]._walk(visitor);
|
261
|
-
}
|
262
|
-
}
|
263
|
-
} else {
|
264
|
-
for (var i = 0, len = body.length; i < len; i++) {
|
265
|
-
body[i]._walk(visitor);
|
266
|
-
}
|
252
|
+
for (var i = 0, len = body.length; i < len; i++) {
|
253
|
+
body[i]._walk(visitor);
|
267
254
|
}
|
268
255
|
}
|
269
256
|
|
@@ -3042,11 +3029,10 @@ const walk_abort = Symbol("abort walk");
|
|
3042
3029
|
/* -----[ TreeWalker ]----- */
|
3043
3030
|
|
3044
3031
|
class TreeWalker {
|
3045
|
-
constructor(callback
|
3032
|
+
constructor(callback) {
|
3046
3033
|
this.visit = callback;
|
3047
3034
|
this.stack = [];
|
3048
3035
|
this.directives = Object.create(null);
|
3049
|
-
this.walk_defun_first = walk_defun_first;
|
3050
3036
|
}
|
3051
3037
|
|
3052
3038
|
_visit(node, descend) {
|
package/lib/compress/common.js
CHANGED
@@ -46,6 +46,7 @@ import {
|
|
46
46
|
AST_Arrow,
|
47
47
|
AST_BlockStatement,
|
48
48
|
AST_Call,
|
49
|
+
AST_Chain,
|
49
50
|
AST_Class,
|
50
51
|
AST_Const,
|
51
52
|
AST_Constant,
|
@@ -226,7 +227,8 @@ export function maintain_this_binding(parent, orig, val) {
|
|
226
227
|
parent instanceof AST_UnaryPrefix && parent.operator == "delete"
|
227
228
|
|| parent instanceof AST_Call && parent.expression === orig
|
228
229
|
&& (
|
229
|
-
val instanceof
|
230
|
+
val instanceof AST_Chain
|
231
|
+
|| val instanceof AST_PropAccess
|
230
232
|
|| val instanceof AST_SymbolRef && val.name == "eval"
|
231
233
|
)
|
232
234
|
) {
|
package/lib/compress/index.js
CHANGED
@@ -245,6 +245,7 @@ class Compressor extends TreeWalker {
|
|
245
245
|
keep_fargs : true,
|
246
246
|
keep_fnames : false,
|
247
247
|
keep_infinity : false,
|
248
|
+
lhs_constants : !false_by_default,
|
248
249
|
loops : !false_by_default,
|
249
250
|
module : false,
|
250
251
|
negate_iife : !false_by_default,
|
@@ -539,7 +540,7 @@ AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
|
|
539
540
|
}
|
540
541
|
return node.reduce_vars(preparation, descend, compressor);
|
541
542
|
}
|
542
|
-
}
|
543
|
+
});
|
543
544
|
// Stack of look-up tables to keep track of whether a `SymbolDef` has been
|
544
545
|
// properly assigned before use:
|
545
546
|
// - `push()` & `pop()` when visiting conditional branches
|
@@ -2131,7 +2132,7 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
2131
2132
|
self.right = tmp;
|
2132
2133
|
}
|
2133
2134
|
}
|
2134
|
-
if (commutativeOperators.has(self.operator)) {
|
2135
|
+
if (compressor.option("lhs_constants") && commutativeOperators.has(self.operator)) {
|
2135
2136
|
if (self.right.is_constant()
|
2136
2137
|
&& !self.left.is_constant()) {
|
2137
2138
|
// if right is a constant, whatever side effects the
|
@@ -2161,6 +2162,9 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
2161
2162
|
// void 0 == x => null == x
|
2162
2163
|
if (!is_strict_comparison && is_undefined(self.left, compressor)) {
|
2163
2164
|
self.left = make_node(AST_Null, self.left);
|
2165
|
+
// x == void 0 => x == null
|
2166
|
+
} else if (!is_strict_comparison && is_undefined(self.right, compressor)) {
|
2167
|
+
self.right = make_node(AST_Null, self.right);
|
2164
2168
|
} else if (compressor.option("typeofs")
|
2165
2169
|
// "undefined" == typeof x => undefined === x
|
2166
2170
|
&& self.left instanceof AST_String
|
@@ -2174,6 +2178,19 @@ def_optimize(AST_Binary, function(self, compressor) {
|
|
2174
2178
|
self.left = make_node(AST_Undefined, self.left).optimize(compressor);
|
2175
2179
|
if (self.operator.length == 2) self.operator += "=";
|
2176
2180
|
}
|
2181
|
+
} else if (compressor.option("typeofs")
|
2182
|
+
// typeof x === "undefined" => x === undefined
|
2183
|
+
&& self.left instanceof AST_UnaryPrefix
|
2184
|
+
&& self.left.operator == "typeof"
|
2185
|
+
&& self.right instanceof AST_String
|
2186
|
+
&& self.right.value == "undefined") {
|
2187
|
+
var expr = self.left.expression;
|
2188
|
+
if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
|
2189
|
+
: !(expr instanceof AST_PropAccess && compressor.option("ie8"))) {
|
2190
|
+
self.left = expr;
|
2191
|
+
self.right = make_node(AST_Undefined, self.right).optimize(compressor);
|
2192
|
+
if (self.operator.length == 2) self.operator += "=";
|
2193
|
+
}
|
2177
2194
|
} else if (self.left instanceof AST_SymbolRef
|
2178
2195
|
// obj !== obj => false
|
2179
2196
|
&& self.right instanceof AST_SymbolRef
|
@@ -71,6 +71,7 @@ import {
|
|
71
71
|
AST_Number,
|
72
72
|
AST_ObjectKeyVal,
|
73
73
|
AST_PropAccess,
|
74
|
+
AST_Scope,
|
74
75
|
AST_Sequence,
|
75
76
|
AST_SimpleStatement,
|
76
77
|
AST_Symbol,
|
@@ -91,6 +92,7 @@ import {
|
|
91
92
|
AST_Yield,
|
92
93
|
|
93
94
|
walk,
|
95
|
+
walk_abort,
|
94
96
|
walk_body,
|
95
97
|
|
96
98
|
_INLINE,
|
@@ -447,9 +449,7 @@ def_reduce_vars(AST_Default, function(tw, descend) {
|
|
447
449
|
|
448
450
|
function mark_lambda(tw, descend, compressor) {
|
449
451
|
clear_flag(this, INLINED);
|
450
|
-
|
451
452
|
push(tw);
|
452
|
-
|
453
453
|
reset_variables(tw, compressor, this);
|
454
454
|
|
455
455
|
var iife;
|
@@ -484,9 +484,86 @@ function mark_lambda(tw, descend, compressor) {
|
|
484
484
|
descend();
|
485
485
|
pop(tw);
|
486
486
|
|
487
|
+
handle_defined_after_hoist(this);
|
488
|
+
|
487
489
|
return true;
|
488
490
|
}
|
489
491
|
|
492
|
+
/**
|
493
|
+
* It's possible for a hoisted function to use something that's not defined yet. Example:
|
494
|
+
*
|
495
|
+
* hoisted();
|
496
|
+
* var defined_after = true;
|
497
|
+
* function hoisted() {
|
498
|
+
* // use defined_after
|
499
|
+
* }
|
500
|
+
*
|
501
|
+
* This function is called on the parent to handle this issue.
|
502
|
+
*/
|
503
|
+
function handle_defined_after_hoist(parent) {
|
504
|
+
const defuns = [];
|
505
|
+
walk(parent, node => {
|
506
|
+
if (node === parent) return;
|
507
|
+
if (node instanceof AST_Defun) defuns.push(node);
|
508
|
+
if (
|
509
|
+
node instanceof AST_Scope
|
510
|
+
|| node instanceof AST_SimpleStatement
|
511
|
+
) return true;
|
512
|
+
});
|
513
|
+
|
514
|
+
for (const defun of defuns) {
|
515
|
+
const fname_def = defun.name.definition();
|
516
|
+
const found_self_ref_in_other_defuns = defuns.some(
|
517
|
+
d => d !== defun && d.enclosed.indexOf(fname_def) !== -1
|
518
|
+
);
|
519
|
+
|
520
|
+
for (const def of defun.enclosed) {
|
521
|
+
if (
|
522
|
+
def.fixed === false
|
523
|
+
|| def === fname_def
|
524
|
+
|| def.scope.get_defun_scope() !== parent
|
525
|
+
) {
|
526
|
+
continue;
|
527
|
+
}
|
528
|
+
|
529
|
+
// defun is hoisted, so always safe
|
530
|
+
if (
|
531
|
+
def.assignments === 0
|
532
|
+
&& def.orig.length === 1
|
533
|
+
&& def.orig[0] instanceof AST_SymbolDefun
|
534
|
+
) {
|
535
|
+
continue;
|
536
|
+
}
|
537
|
+
|
538
|
+
if (found_self_ref_in_other_defuns) {
|
539
|
+
def.fixed = false;
|
540
|
+
continue;
|
541
|
+
}
|
542
|
+
|
543
|
+
// Detect `call_defun(); var used_in_defun = ...`
|
544
|
+
// Because `used_in_defun` can no longer be fixed
|
545
|
+
let found_defun = false;
|
546
|
+
let found_def_after_defun = false;
|
547
|
+
walk(parent, node => {
|
548
|
+
if (node === defun) return true;
|
549
|
+
|
550
|
+
if (node instanceof AST_Symbol) {
|
551
|
+
if (!found_defun && node.thedef === fname_def) {
|
552
|
+
found_defun = true;
|
553
|
+
} else if (found_defun && node.thedef === def) {
|
554
|
+
found_def_after_defun = true;
|
555
|
+
return walk_abort;
|
556
|
+
}
|
557
|
+
}
|
558
|
+
});
|
559
|
+
|
560
|
+
if (found_def_after_defun) {
|
561
|
+
def.fixed = false;
|
562
|
+
}
|
563
|
+
}
|
564
|
+
}
|
565
|
+
}
|
566
|
+
|
490
567
|
def_reduce_vars(AST_Lambda, mark_lambda);
|
491
568
|
|
492
569
|
def_reduce_vars(AST_Do, function(tw, descend, compressor) {
|
@@ -607,6 +684,9 @@ def_reduce_vars(AST_Toplevel, function(tw, descend, compressor) {
|
|
607
684
|
reset_def(compressor, def);
|
608
685
|
});
|
609
686
|
reset_variables(tw, compressor, this);
|
687
|
+
descend();
|
688
|
+
handle_defined_after_hoist(this);
|
689
|
+
return true;
|
610
690
|
});
|
611
691
|
|
612
692
|
def_reduce_vars(AST_Try, function(tw, descend, compressor) {
|
package/lib/output.js
CHANGED
@@ -1080,6 +1080,12 @@ function OutputStream(options) {
|
|
1080
1080
|
return true;
|
1081
1081
|
});
|
1082
1082
|
|
1083
|
+
PARENS(AST_Chain, function(output) {
|
1084
|
+
var p = output.parent();
|
1085
|
+
if (!(p instanceof AST_Call || p instanceof AST_PropAccess)) return false;
|
1086
|
+
return p.expression === this;
|
1087
|
+
});
|
1088
|
+
|
1083
1089
|
PARENS(AST_PropAccess, function(output) {
|
1084
1090
|
var p = output.parent();
|
1085
1091
|
if (p instanceof AST_New && p.expression === this) {
|
package/lib/parse.js
CHANGED
@@ -2308,7 +2308,7 @@ function parse($TEXT, options) {
|
|
2308
2308
|
return new_(allow_calls);
|
2309
2309
|
}
|
2310
2310
|
if (is("name", "import") && is_token(peek(), "punc", ".")) {
|
2311
|
-
return import_meta();
|
2311
|
+
return import_meta(allow_calls);
|
2312
2312
|
}
|
2313
2313
|
var start = S.token;
|
2314
2314
|
var peeked;
|
@@ -2801,7 +2801,7 @@ function parse($TEXT, options) {
|
|
2801
2801
|
});
|
2802
2802
|
}
|
2803
2803
|
|
2804
|
-
function import_meta() {
|
2804
|
+
function import_meta(allow_calls) {
|
2805
2805
|
var start = S.token;
|
2806
2806
|
expect_token("name", "import");
|
2807
2807
|
expect_token("punc", ".");
|
@@ -2809,7 +2809,7 @@ function parse($TEXT, options) {
|
|
2809
2809
|
return subscripts(new AST_ImportMeta({
|
2810
2810
|
start: start,
|
2811
2811
|
end: prev()
|
2812
|
-
}),
|
2812
|
+
}), allow_calls);
|
2813
2813
|
}
|
2814
2814
|
|
2815
2815
|
function map_name(is_import) {
|