terser 5.32.0 → 5.34.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 +10 -0
- package/dist/bundle.min.js +202 -128
- package/lib/compress/common.js +19 -2
- package/lib/compress/drop-unused.js +1 -40
- package/lib/compress/index.js +26 -14
- package/lib/compress/inference.js +49 -1
- package/lib/compress/reduce-vars.js +110 -75
- package/lib/minify.js +1 -1
- package/lib/propmangle.js +3 -1
- package/lib/scope.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v5.34.0
|
4
|
+
|
5
|
+
- internal: stop assigning properties to objects they don't belong in
|
6
|
+
- internal: run compress tests in parallel
|
7
|
+
- `drop_console`: emit an empty function if the return value of `console.METHOD(...)` may be called.
|
8
|
+
|
9
|
+
## v5.33.0
|
10
|
+
|
11
|
+
- `reduce_vars` improved when dealing with hoisted function definitions (#1544)
|
12
|
+
|
3
13
|
## v5.32.0
|
4
14
|
|
5
15
|
- `import("module")` can now be input and output from ESTree AST (#1557)
|
package/dist/bundle.min.js
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@jridgewell/source-map')) :
|
3
3
|
typeof define === 'function' && define.amd ? define(['exports', '@jridgewell/source-map'], factory) :
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Terser = {}, global.sourceMap));
|
5
|
-
}(this, (function (exports, sourceMap) { 'use strict';
|
5
|
+
})(this, (function (exports, sourceMap) { 'use strict';
|
6
6
|
|
7
7
|
/***********************************************************************
|
8
8
|
|
@@ -11674,7 +11674,7 @@ function redefined_catch_def(def) {
|
|
11674
11674
|
}
|
11675
11675
|
}
|
11676
11676
|
|
11677
|
-
AST_Scope.DEFMETHOD("figure_out_scope", function(options, { parent_scope =
|
11677
|
+
AST_Scope.DEFMETHOD("figure_out_scope", function(options, { parent_scope = undefined, toplevel = this } = {}) {
|
11678
11678
|
options = defaults(options, {
|
11679
11679
|
cache: null,
|
11680
11680
|
ie8: false,
|
@@ -11998,7 +11998,7 @@ AST_Scope.DEFMETHOD("add_child_scope", function (scope) {
|
|
11998
11998
|
scope.parent_scope = this;
|
11999
11999
|
|
12000
12000
|
// Propagate to this.uses_arguments from arrow functions
|
12001
|
-
if ((scope instanceof AST_Arrow) && !this.uses_arguments) {
|
12001
|
+
if ((scope instanceof AST_Arrow) && (this instanceof AST_Lambda && !this.uses_arguments)) {
|
12002
12002
|
this.uses_arguments = walk(scope, node => {
|
12003
12003
|
if (
|
12004
12004
|
node instanceof AST_SymbolRef
|
@@ -13065,6 +13065,23 @@ function make_sequence(orig, expressions) {
|
|
13065
13065
|
});
|
13066
13066
|
}
|
13067
13067
|
|
13068
|
+
function make_empty_function(self) {
|
13069
|
+
return make_node(AST_Function, self, {
|
13070
|
+
uses_arguments: false,
|
13071
|
+
argnames: [],
|
13072
|
+
body: [],
|
13073
|
+
is_generator: false,
|
13074
|
+
async: false,
|
13075
|
+
variables: new Map(),
|
13076
|
+
uses_with: false,
|
13077
|
+
uses_eval: false,
|
13078
|
+
parent_scope: null,
|
13079
|
+
enclosed: [],
|
13080
|
+
cname: 0,
|
13081
|
+
block_scope: undefined,
|
13082
|
+
});
|
13083
|
+
}
|
13084
|
+
|
13068
13085
|
function make_node_from_constant(val, orig) {
|
13069
13086
|
switch (typeof val) {
|
13070
13087
|
case "string":
|
@@ -13291,9 +13308,9 @@ function is_reachable(scope_node, defs) {
|
|
13291
13308
|
}
|
13292
13309
|
|
13293
13310
|
/** Check if a ref refers to the name of a function/class it's defined within */
|
13294
|
-
function is_recursive_ref(
|
13311
|
+
function is_recursive_ref(tw, def) {
|
13295
13312
|
var node;
|
13296
|
-
for (var i = 0; node =
|
13313
|
+
for (var i = 0; node = tw.parent(i); i++) {
|
13297
13314
|
if (node instanceof AST_Lambda || node instanceof AST_Class) {
|
13298
13315
|
var name = node.name;
|
13299
13316
|
if (name && name.definition() === def) {
|
@@ -14418,6 +14435,54 @@ function is_modified(compressor, tw, node, value, level, immutable) {
|
|
14418
14435
|
}
|
14419
14436
|
}
|
14420
14437
|
|
14438
|
+
/**
|
14439
|
+
* Check if a node may be used by the expression it's in
|
14440
|
+
* void (0, 1, {node}, 2) -> false
|
14441
|
+
* console.log(0, {node}) -> true
|
14442
|
+
*/
|
14443
|
+
function is_used_in_expression(tw) {
|
14444
|
+
for (let p = -1, node, parent; node = tw.parent(p), parent = tw.parent(p + 1); p++) {
|
14445
|
+
if (parent instanceof AST_Sequence) {
|
14446
|
+
const nth_expression = parent.expressions.indexOf(node);
|
14447
|
+
if (nth_expression !== parent.expressions.length - 1) {
|
14448
|
+
// Detect (0, x.noThis)() constructs
|
14449
|
+
const grandparent = tw.parent(p + 2);
|
14450
|
+
if (
|
14451
|
+
parent.expressions.length > 2
|
14452
|
+
|| parent.expressions.length === 1
|
14453
|
+
|| !requires_sequence_to_maintain_binding(grandparent, parent, parent.expressions[1])
|
14454
|
+
) {
|
14455
|
+
return false;
|
14456
|
+
}
|
14457
|
+
return true;
|
14458
|
+
} else {
|
14459
|
+
continue;
|
14460
|
+
}
|
14461
|
+
}
|
14462
|
+
if (parent instanceof AST_Unary) {
|
14463
|
+
const op = parent.operator;
|
14464
|
+
if (op === "void") {
|
14465
|
+
return false;
|
14466
|
+
}
|
14467
|
+
if (op === "typeof" || op === "+" || op === "-" || op === "!" || op === "~") {
|
14468
|
+
continue;
|
14469
|
+
}
|
14470
|
+
}
|
14471
|
+
if (
|
14472
|
+
parent instanceof AST_SimpleStatement
|
14473
|
+
|| parent instanceof AST_LabeledStatement
|
14474
|
+
) {
|
14475
|
+
return false;
|
14476
|
+
}
|
14477
|
+
if (parent instanceof AST_Scope) {
|
14478
|
+
return false;
|
14479
|
+
}
|
14480
|
+
return true;
|
14481
|
+
}
|
14482
|
+
|
14483
|
+
return true;
|
14484
|
+
}
|
14485
|
+
|
14421
14486
|
/***********************************************************************
|
14422
14487
|
|
14423
14488
|
A JavaScript tokenizer / parser / beautifier / compressor.
|
@@ -15672,45 +15737,6 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
15672
15737
|
}
|
15673
15738
|
});
|
15674
15739
|
|
15675
|
-
/**
|
15676
|
-
* Check if a node may be used by the expression it's in
|
15677
|
-
* void (0, 1, {node}, 2) -> false
|
15678
|
-
* console.log(0, {node}) -> true
|
15679
|
-
*/
|
15680
|
-
function is_used_in_expression(tw) {
|
15681
|
-
for (let p = -1, node, parent; node = tw.parent(p), parent = tw.parent(p + 1); p++) {
|
15682
|
-
if (parent instanceof AST_Sequence) {
|
15683
|
-
const nth_expression = parent.expressions.indexOf(node);
|
15684
|
-
if (nth_expression !== parent.expressions.length - 1) {
|
15685
|
-
// Detect (0, x.noThis)() constructs
|
15686
|
-
const grandparent = tw.parent(p + 2);
|
15687
|
-
if (
|
15688
|
-
parent.expressions.length > 2
|
15689
|
-
|| parent.expressions.length === 1
|
15690
|
-
|| !requires_sequence_to_maintain_binding(grandparent, parent, parent.expressions[1])
|
15691
|
-
) {
|
15692
|
-
return false;
|
15693
|
-
}
|
15694
|
-
return true;
|
15695
|
-
} else {
|
15696
|
-
continue;
|
15697
|
-
}
|
15698
|
-
}
|
15699
|
-
if (parent instanceof AST_Unary) {
|
15700
|
-
const op = parent.operator;
|
15701
|
-
if (op === "void") {
|
15702
|
-
return false;
|
15703
|
-
}
|
15704
|
-
if (op === "typeof" || op === "+" || op === "-" || op === "!" || op === "~") {
|
15705
|
-
continue;
|
15706
|
-
}
|
15707
|
-
}
|
15708
|
-
return true;
|
15709
|
-
}
|
15710
|
-
|
15711
|
-
return true;
|
15712
|
-
}
|
15713
|
-
|
15714
15740
|
/***********************************************************************
|
15715
15741
|
|
15716
15742
|
A JavaScript tokenizer / parser / beautifier / compressor.
|
@@ -16147,28 +16173,50 @@ function mark_lambda(tw, descend, compressor) {
|
|
16147
16173
|
* // use defined_after
|
16148
16174
|
* }
|
16149
16175
|
*
|
16150
|
-
*
|
16176
|
+
* Or even indirectly:
|
16177
|
+
*
|
16178
|
+
* B();
|
16179
|
+
* var defined_after = true;
|
16180
|
+
* function A() {
|
16181
|
+
* // use defined_after
|
16182
|
+
* }
|
16183
|
+
* function B() {
|
16184
|
+
* A();
|
16185
|
+
* }
|
16186
|
+
*
|
16187
|
+
* Access a variable before declaration will either throw a ReferenceError
|
16188
|
+
* (if the variable is declared with `let` or `const`),
|
16189
|
+
* or get an `undefined` (if the variable is declared with `var`).
|
16190
|
+
*
|
16191
|
+
* If the variable is inlined into the function, the behavior will change.
|
16192
|
+
*
|
16193
|
+
* This function is called on the parent to disallow inlining of such variables,
|
16151
16194
|
*/
|
16152
16195
|
function handle_defined_after_hoist(parent) {
|
16153
16196
|
const defuns = [];
|
16154
16197
|
walk(parent, node => {
|
16155
16198
|
if (node === parent) return;
|
16156
|
-
if (node instanceof AST_Defun)
|
16199
|
+
if (node instanceof AST_Defun) {
|
16200
|
+
defuns.push(node);
|
16201
|
+
return true;
|
16202
|
+
}
|
16157
16203
|
if (
|
16158
16204
|
node instanceof AST_Scope
|
16159
16205
|
|| node instanceof AST_SimpleStatement
|
16160
16206
|
) return true;
|
16161
16207
|
});
|
16162
16208
|
|
16209
|
+
// `defun` id to array of `defun` it uses
|
16210
|
+
const defun_dependencies_map = new Map();
|
16211
|
+
// `defun` id to array of enclosing `def` that are used by the function
|
16212
|
+
const dependencies_map = new Map();
|
16213
|
+
// all symbol ids that will be tracked for read/write
|
16163
16214
|
const symbols_of_interest = new Set();
|
16164
16215
|
const defuns_of_interest = new Set();
|
16165
|
-
const potential_conflicts = [];
|
16166
16216
|
|
16167
16217
|
for (const defun of defuns) {
|
16168
16218
|
const fname_def = defun.name.definition();
|
16169
|
-
const
|
16170
|
-
d => d !== defun && d.enclosed.indexOf(fname_def) !== -1
|
16171
|
-
);
|
16219
|
+
const enclosing_defs = [];
|
16172
16220
|
|
16173
16221
|
for (const def of defun.enclosed) {
|
16174
16222
|
if (
|
@@ -16179,93 +16227,107 @@ function handle_defined_after_hoist(parent) {
|
|
16179
16227
|
continue;
|
16180
16228
|
}
|
16181
16229
|
|
16182
|
-
|
16230
|
+
symbols_of_interest.add(def.id);
|
16231
|
+
|
16232
|
+
// found a reference to another function
|
16183
16233
|
if (
|
16184
16234
|
def.assignments === 0
|
16185
16235
|
&& def.orig.length === 1
|
16186
16236
|
&& def.orig[0] instanceof AST_SymbolDefun
|
16187
16237
|
) {
|
16188
|
-
|
16189
|
-
|
16238
|
+
defuns_of_interest.add(def.id);
|
16239
|
+
symbols_of_interest.add(def.id);
|
16240
|
+
|
16241
|
+
defuns_of_interest.add(fname_def.id);
|
16242
|
+
symbols_of_interest.add(fname_def.id);
|
16243
|
+
|
16244
|
+
if (!defun_dependencies_map.has(fname_def.id)) {
|
16245
|
+
defun_dependencies_map.set(fname_def.id, []);
|
16246
|
+
}
|
16247
|
+
defun_dependencies_map.get(fname_def.id).push(def.id);
|
16190
16248
|
|
16191
|
-
if (found_self_ref_in_other_defuns) {
|
16192
|
-
def.fixed = false;
|
16193
16249
|
continue;
|
16194
16250
|
}
|
16195
16251
|
|
16196
|
-
|
16197
|
-
|
16198
|
-
|
16252
|
+
enclosing_defs.push(def);
|
16253
|
+
}
|
16254
|
+
|
16255
|
+
if (enclosing_defs.length) {
|
16256
|
+
dependencies_map.set(fname_def.id, enclosing_defs);
|
16257
|
+
defuns_of_interest.add(fname_def.id);
|
16199
16258
|
symbols_of_interest.add(fname_def.id);
|
16200
|
-
defuns_of_interest.add(defun);
|
16201
16259
|
}
|
16202
16260
|
}
|
16203
16261
|
|
16204
|
-
//
|
16205
|
-
if (
|
16206
|
-
|
16207
|
-
|
16208
|
-
const found_symbols = [];
|
16209
|
-
// Indices of `found_symbols` which are writes
|
16210
|
-
const found_symbol_writes = new Set();
|
16211
|
-
// Defun ranges are recorded because we don't care if a function uses the def internally
|
16212
|
-
const defun_ranges = new Map();
|
16262
|
+
// No defuns use outside constants
|
16263
|
+
if (!dependencies_map.size) {
|
16264
|
+
return;
|
16265
|
+
}
|
16213
16266
|
|
16214
|
-
|
16215
|
-
|
16216
|
-
|
16217
|
-
|
16218
|
-
|
16219
|
-
|
16267
|
+
// Increment to count "symbols of interest" (defuns or defs) that we found.
|
16268
|
+
// These are tracked in AST order so we can check which is after which.
|
16269
|
+
let symbol_index = 1;
|
16270
|
+
// Map a defun ID to its first read (a `symbol_index`)
|
16271
|
+
const defun_first_read_map = new Map();
|
16272
|
+
// Map a symbol ID to its last write (a `symbol_index`)
|
16273
|
+
const symbol_last_write_map = new Map();
|
16220
16274
|
|
16221
|
-
|
16222
|
-
|
16223
|
-
|
16224
|
-
// if we found a defun on the list, mark IN_DEFUN=id and descend
|
16275
|
+
walk_parent(parent, (node, walk_info) => {
|
16276
|
+
if (node instanceof AST_Symbol && node.thedef) {
|
16277
|
+
const id = node.definition().id;
|
16225
16278
|
|
16226
|
-
|
16227
|
-
|
16228
|
-
|
16229
|
-
|
16230
|
-
|
16231
|
-
|
16232
|
-
found_symbols.push(id);
|
16279
|
+
symbol_index++;
|
16280
|
+
|
16281
|
+
// Track last-writes to symbols
|
16282
|
+
if (symbols_of_interest.has(id)) {
|
16283
|
+
if (node instanceof AST_SymbolDeclaration || is_lhs(node, walk_info.parent())) {
|
16284
|
+
symbol_last_write_map.set(id, symbol_index);
|
16233
16285
|
}
|
16234
16286
|
}
|
16235
|
-
})));
|
16236
16287
|
|
16237
|
-
|
16238
|
-
|
16288
|
+
// Track first-reads of defuns (refined later)
|
16289
|
+
if (defuns_of_interest.has(id)) {
|
16290
|
+
if (!defun_first_read_map.has(id) && !is_recursive_ref(walk_info, id)) {
|
16291
|
+
defun_first_read_map.set(id, symbol_index);
|
16292
|
+
}
|
16293
|
+
}
|
16294
|
+
}
|
16295
|
+
});
|
16239
16296
|
|
16240
|
-
|
16241
|
-
|
16242
|
-
|
16297
|
+
// Refine `defun_first_read_map` to be as high as possible
|
16298
|
+
for (const [defun, defun_first_read] of defun_first_read_map) {
|
16299
|
+
// Update all depdencies of `defun`
|
16300
|
+
const queue = new Set(defun_dependencies_map.get(defun));
|
16301
|
+
for (const enclosed_defun of queue) {
|
16302
|
+
let enclosed_defun_first_read = defun_first_read_map.get(enclosed_defun);
|
16303
|
+
if (enclosed_defun_first_read != null && enclosed_defun_first_read < defun_first_read) {
|
16304
|
+
continue;
|
16305
|
+
}
|
16243
16306
|
|
16244
|
-
|
16245
|
-
index = found_symbols.indexOf(sym_id, index);
|
16307
|
+
defun_first_read_map.set(enclosed_defun, defun_first_read);
|
16246
16308
|
|
16247
|
-
|
16248
|
-
|
16249
|
-
|
16250
|
-
|
16251
|
-
|
16252
|
-
} else if (must_be_write && !found_symbol_writes.has(index)) {
|
16253
|
-
index++;
|
16254
|
-
continue;
|
16255
|
-
} else {
|
16256
|
-
break;
|
16257
|
-
}
|
16258
|
-
}
|
16309
|
+
for (const enclosed_enclosed_defun of defun_dependencies_map.get(enclosed_defun) || []) {
|
16310
|
+
queue.add(enclosed_enclosed_defun);
|
16311
|
+
}
|
16312
|
+
}
|
16313
|
+
}
|
16259
16314
|
|
16260
|
-
|
16261
|
-
|
16315
|
+
// ensure write-then-read order, otherwise clear `fixed`
|
16316
|
+
// 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.
|
16317
|
+
for (const [defun, defs] of dependencies_map) {
|
16318
|
+
const defun_first_read = defun_first_read_map.get(defun);
|
16319
|
+
if (defun_first_read === undefined) {
|
16320
|
+
continue;
|
16321
|
+
}
|
16262
16322
|
|
16263
|
-
|
16264
|
-
|
16323
|
+
for (const def of defs) {
|
16324
|
+
if (def.fixed === false) {
|
16325
|
+
continue;
|
16326
|
+
}
|
16265
16327
|
|
16266
|
-
|
16328
|
+
let def_last_write = symbol_last_write_map.get(def.id) || 0;
|
16267
16329
|
|
16268
|
-
if (
|
16330
|
+
if (defun_first_read < def_last_write) {
|
16269
16331
|
def.fixed = false;
|
16270
16332
|
}
|
16271
16333
|
}
|
@@ -18899,9 +18961,8 @@ def_optimize(AST_Node, function(self) {
|
|
18899
18961
|
});
|
18900
18962
|
|
18901
18963
|
AST_Toplevel.DEFMETHOD("drop_console", function(options) {
|
18902
|
-
|
18903
|
-
|
18904
|
-
return this.transform(new TreeTransformer(function(self) {
|
18964
|
+
const isArray = Array.isArray(options);
|
18965
|
+
const tt = new TreeTransformer(function(self) {
|
18905
18966
|
if (self.TYPE !== "Call") {
|
18906
18967
|
return;
|
18907
18968
|
}
|
@@ -18912,18 +18973,35 @@ AST_Toplevel.DEFMETHOD("drop_console", function(options) {
|
|
18912
18973
|
return;
|
18913
18974
|
}
|
18914
18975
|
|
18915
|
-
if (isArray && options.
|
18976
|
+
if (isArray && !options.includes(exp.property)) {
|
18916
18977
|
return;
|
18917
18978
|
}
|
18918
18979
|
|
18919
18980
|
var name = exp.expression;
|
18981
|
+
var depth = 2;
|
18920
18982
|
while (name.expression) {
|
18921
18983
|
name = name.expression;
|
18984
|
+
depth++;
|
18922
18985
|
}
|
18986
|
+
|
18923
18987
|
if (is_undeclared_ref(name) && name.name == "console") {
|
18924
|
-
|
18988
|
+
if (
|
18989
|
+
depth === 3
|
18990
|
+
&& !["call", "apply"].includes(exp.property)
|
18991
|
+
&& is_used_in_expression(tt)
|
18992
|
+
) {
|
18993
|
+
// a (used) call to Function.prototype methods (eg: console.log.bind(console))
|
18994
|
+
// but not .call and .apply which would also return undefined.
|
18995
|
+
exp.expression = make_empty_function(self);
|
18996
|
+
set_flag(exp.expression, SQUEEZED);
|
18997
|
+
self.args = [];
|
18998
|
+
} else {
|
18999
|
+
return make_node(AST_Undefined, self);
|
19000
|
+
}
|
18925
19001
|
}
|
18926
|
-
})
|
19002
|
+
});
|
19003
|
+
|
19004
|
+
return this.transform(tt);
|
18927
19005
|
});
|
18928
19006
|
|
18929
19007
|
AST_Node.DEFMETHOD("equivalent_to", function(node) {
|
@@ -20300,10 +20378,7 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
20300
20378
|
&& is_undeclared_ref(exp)
|
20301
20379
|
&& exp.name == "Function") {
|
20302
20380
|
// new Function() => function(){}
|
20303
|
-
if (self.args.length == 0) return
|
20304
|
-
argnames: [],
|
20305
|
-
body: []
|
20306
|
-
}).optimize(compressor);
|
20381
|
+
if (self.args.length == 0) return make_empty_function(self).optimize(compressor);
|
20307
20382
|
if (self.args.every((x) => x instanceof AST_String)) {
|
20308
20383
|
// quite a corner-case, but we can handle it:
|
20309
20384
|
// https://github.com/mishoo/UglifyJS2/issues/203
|
@@ -22063,10 +22138,7 @@ def_optimize(AST_Dot, function(self, compressor) {
|
|
22063
22138
|
});
|
22064
22139
|
break;
|
22065
22140
|
case "Function":
|
22066
|
-
self.expression =
|
22067
|
-
argnames: [],
|
22068
|
-
body: []
|
22069
|
-
});
|
22141
|
+
self.expression = make_empty_function(self.expression);
|
22070
22142
|
break;
|
22071
22143
|
case "Number":
|
22072
22144
|
self.expression = make_node(AST_Number, self.expression, {
|
@@ -31611,7 +31683,9 @@ function mangle_properties(ast, options, annotated_props = find_annotated_props(
|
|
31611
31683
|
} else if (node instanceof AST_ObjectProperty) {
|
31612
31684
|
// setter, getter, method or class field
|
31613
31685
|
if (!keep_quoted || !node.quote) {
|
31614
|
-
|
31686
|
+
if (!node.computed_key()) {
|
31687
|
+
node.key.name = mangle(node.key.name);
|
31688
|
+
}
|
31615
31689
|
}
|
31616
31690
|
} else if (node instanceof AST_Dot) {
|
31617
31691
|
if (!keep_quoted || !node.quote) {
|
@@ -31988,7 +32062,7 @@ function* minify_sync_or_async(files, options, _fs_module) {
|
|
31988
32062
|
if (node.block_scope) {
|
31989
32063
|
node.block_scope.variables = undefined;
|
31990
32064
|
node.block_scope.enclosed = undefined;
|
31991
|
-
node.parent_scope = undefined;
|
32065
|
+
node.block_scope.parent_scope = undefined;
|
31992
32066
|
}
|
31993
32067
|
});
|
31994
32068
|
}
|
@@ -32580,4 +32654,4 @@ exports._run_cli = run_cli;
|
|
32580
32654
|
exports.minify = minify;
|
32581
32655
|
exports.minify_sync = minify_sync;
|
32582
32656
|
|
32583
|
-
}))
|
32657
|
+
}));
|
package/lib/compress/common.js
CHANGED
@@ -107,6 +107,23 @@ export function make_sequence(orig, expressions) {
|
|
107
107
|
});
|
108
108
|
}
|
109
109
|
|
110
|
+
export function make_empty_function(self) {
|
111
|
+
return make_node(AST_Function, self, {
|
112
|
+
uses_arguments: false,
|
113
|
+
argnames: [],
|
114
|
+
body: [],
|
115
|
+
is_generator: false,
|
116
|
+
async: false,
|
117
|
+
variables: new Map(),
|
118
|
+
uses_with: false,
|
119
|
+
uses_eval: false,
|
120
|
+
parent_scope: null,
|
121
|
+
enclosed: [],
|
122
|
+
cname: 0,
|
123
|
+
block_scope: undefined,
|
124
|
+
});
|
125
|
+
}
|
126
|
+
|
110
127
|
export function make_node_from_constant(val, orig) {
|
111
128
|
switch (typeof val) {
|
112
129
|
case "string":
|
@@ -333,9 +350,9 @@ export function is_reachable(scope_node, defs) {
|
|
333
350
|
}
|
334
351
|
|
335
352
|
/** Check if a ref refers to the name of a function/class it's defined within */
|
336
|
-
export function is_recursive_ref(
|
353
|
+
export function is_recursive_ref(tw, def) {
|
337
354
|
var node;
|
338
|
-
for (var i = 0; node =
|
355
|
+
for (var i = 0; node = tw.parent(i); i++) {
|
339
356
|
if (node instanceof AST_Lambda || node instanceof AST_Class) {
|
340
357
|
var name = node.name;
|
341
358
|
if (name && name.definition() === def) {
|
@@ -103,8 +103,8 @@ import {
|
|
103
103
|
is_empty,
|
104
104
|
is_ref_of,
|
105
105
|
can_be_evicted_from_block,
|
106
|
-
requires_sequence_to_maintain_binding,
|
107
106
|
} from "./common.js";
|
107
|
+
import { is_used_in_expression } from "./inference.js";
|
108
108
|
|
109
109
|
const r_keep_assign = /keep_assign/;
|
110
110
|
|
@@ -503,42 +503,3 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
503
503
|
}
|
504
504
|
}
|
505
505
|
});
|
506
|
-
|
507
|
-
/**
|
508
|
-
* Check if a node may be used by the expression it's in
|
509
|
-
* void (0, 1, {node}, 2) -> false
|
510
|
-
* console.log(0, {node}) -> true
|
511
|
-
*/
|
512
|
-
function is_used_in_expression(tw) {
|
513
|
-
for (let p = -1, node, parent; node = tw.parent(p), parent = tw.parent(p + 1); p++) {
|
514
|
-
if (parent instanceof AST_Sequence) {
|
515
|
-
const nth_expression = parent.expressions.indexOf(node);
|
516
|
-
if (nth_expression !== parent.expressions.length - 1) {
|
517
|
-
// Detect (0, x.noThis)() constructs
|
518
|
-
const grandparent = tw.parent(p + 2);
|
519
|
-
if (
|
520
|
-
parent.expressions.length > 2
|
521
|
-
|| parent.expressions.length === 1
|
522
|
-
|| !requires_sequence_to_maintain_binding(grandparent, parent, parent.expressions[1])
|
523
|
-
) {
|
524
|
-
return false;
|
525
|
-
}
|
526
|
-
return true;
|
527
|
-
} else {
|
528
|
-
continue;
|
529
|
-
}
|
530
|
-
}
|
531
|
-
if (parent instanceof AST_Unary) {
|
532
|
-
const op = parent.operator;
|
533
|
-
if (op === "void") {
|
534
|
-
return false;
|
535
|
-
}
|
536
|
-
if (op === "typeof" || op === "+" || op === "-" || op === "!" || op === "~") {
|
537
|
-
continue;
|
538
|
-
}
|
539
|
-
}
|
540
|
-
return true;
|
541
|
-
}
|
542
|
-
|
543
|
-
return true;
|
544
|
-
}
|
package/lib/compress/index.js
CHANGED
@@ -176,6 +176,7 @@ import {
|
|
176
176
|
is_undefined,
|
177
177
|
is_lhs,
|
178
178
|
aborts,
|
179
|
+
is_used_in_expression,
|
179
180
|
} from "./inference.js";
|
180
181
|
import {
|
181
182
|
SQUEEZED,
|
@@ -195,6 +196,7 @@ import {
|
|
195
196
|
make_sequence,
|
196
197
|
best_of,
|
197
198
|
best_of_expression,
|
199
|
+
make_empty_function,
|
198
200
|
make_node_from_constant,
|
199
201
|
merge_sequence,
|
200
202
|
get_simple_key,
|
@@ -510,9 +512,8 @@ def_optimize(AST_Node, function(self) {
|
|
510
512
|
});
|
511
513
|
|
512
514
|
AST_Toplevel.DEFMETHOD("drop_console", function(options) {
|
513
|
-
|
514
|
-
|
515
|
-
return this.transform(new TreeTransformer(function(self) {
|
515
|
+
const isArray = Array.isArray(options);
|
516
|
+
const tt = new TreeTransformer(function(self) {
|
516
517
|
if (self.TYPE !== "Call") {
|
517
518
|
return;
|
518
519
|
}
|
@@ -523,18 +524,35 @@ AST_Toplevel.DEFMETHOD("drop_console", function(options) {
|
|
523
524
|
return;
|
524
525
|
}
|
525
526
|
|
526
|
-
if (isArray && options.
|
527
|
+
if (isArray && !options.includes(exp.property)) {
|
527
528
|
return;
|
528
529
|
}
|
529
530
|
|
530
531
|
var name = exp.expression;
|
532
|
+
var depth = 2;
|
531
533
|
while (name.expression) {
|
532
534
|
name = name.expression;
|
535
|
+
depth++;
|
533
536
|
}
|
537
|
+
|
534
538
|
if (is_undeclared_ref(name) && name.name == "console") {
|
535
|
-
|
539
|
+
if (
|
540
|
+
depth === 3
|
541
|
+
&& !["call", "apply"].includes(exp.property)
|
542
|
+
&& is_used_in_expression(tt)
|
543
|
+
) {
|
544
|
+
// a (used) call to Function.prototype methods (eg: console.log.bind(console))
|
545
|
+
// but not .call and .apply which would also return undefined.
|
546
|
+
exp.expression = make_empty_function(self);
|
547
|
+
set_flag(exp.expression, SQUEEZED);
|
548
|
+
self.args = [];
|
549
|
+
} else {
|
550
|
+
return make_node(AST_Undefined, self);
|
551
|
+
}
|
536
552
|
}
|
537
|
-
})
|
553
|
+
});
|
554
|
+
|
555
|
+
return this.transform(tt);
|
538
556
|
});
|
539
557
|
|
540
558
|
AST_Node.DEFMETHOD("equivalent_to", function(node) {
|
@@ -1911,10 +1929,7 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
1911
1929
|
&& is_undeclared_ref(exp)
|
1912
1930
|
&& exp.name == "Function") {
|
1913
1931
|
// new Function() => function(){}
|
1914
|
-
if (self.args.length == 0) return
|
1915
|
-
argnames: [],
|
1916
|
-
body: []
|
1917
|
-
}).optimize(compressor);
|
1932
|
+
if (self.args.length == 0) return make_empty_function(self).optimize(compressor);
|
1918
1933
|
if (self.args.every((x) => x instanceof AST_String)) {
|
1919
1934
|
// quite a corner-case, but we can handle it:
|
1920
1935
|
// https://github.com/mishoo/UglifyJS2/issues/203
|
@@ -3674,10 +3689,7 @@ def_optimize(AST_Dot, function(self, compressor) {
|
|
3674
3689
|
});
|
3675
3690
|
break;
|
3676
3691
|
case "Function":
|
3677
|
-
self.expression =
|
3678
|
-
argnames: [],
|
3679
|
-
body: []
|
3680
|
-
});
|
3692
|
+
self.expression = make_empty_function(self.expression);
|
3681
3693
|
break;
|
3682
3694
|
case "Number":
|
3683
3695
|
self.expression = make_node(AST_Number, self.expression, {
|
@@ -121,7 +121,7 @@ import {
|
|
121
121
|
member,
|
122
122
|
has_annotation,
|
123
123
|
} from "../utils/index.js";
|
124
|
-
import { make_sequence, best_of_expression, read_property } from "./common.js";
|
124
|
+
import { make_sequence, best_of_expression, read_property, requires_sequence_to_maintain_binding } from "./common.js";
|
125
125
|
|
126
126
|
import { INLINED, UNDEFINED, has_flag } from "./compressor-flags.js";
|
127
127
|
import { pure_prop_access_globals, is_pure_native_fn, is_pure_native_method } from "./native-objects.js";
|
@@ -982,3 +982,51 @@ export function is_modified(compressor, tw, node, value, level, immutable) {
|
|
982
982
|
return !immutable && is_modified(compressor, tw, parent, prop, level + 1);
|
983
983
|
}
|
984
984
|
}
|
985
|
+
|
986
|
+
/**
|
987
|
+
* Check if a node may be used by the expression it's in
|
988
|
+
* void (0, 1, {node}, 2) -> false
|
989
|
+
* console.log(0, {node}) -> true
|
990
|
+
*/
|
991
|
+
export function is_used_in_expression(tw) {
|
992
|
+
for (let p = -1, node, parent; node = tw.parent(p), parent = tw.parent(p + 1); p++) {
|
993
|
+
if (parent instanceof AST_Sequence) {
|
994
|
+
const nth_expression = parent.expressions.indexOf(node);
|
995
|
+
if (nth_expression !== parent.expressions.length - 1) {
|
996
|
+
// Detect (0, x.noThis)() constructs
|
997
|
+
const grandparent = tw.parent(p + 2);
|
998
|
+
if (
|
999
|
+
parent.expressions.length > 2
|
1000
|
+
|| parent.expressions.length === 1
|
1001
|
+
|| !requires_sequence_to_maintain_binding(grandparent, parent, parent.expressions[1])
|
1002
|
+
) {
|
1003
|
+
return false;
|
1004
|
+
}
|
1005
|
+
return true;
|
1006
|
+
} else {
|
1007
|
+
continue;
|
1008
|
+
}
|
1009
|
+
}
|
1010
|
+
if (parent instanceof AST_Unary) {
|
1011
|
+
const op = parent.operator;
|
1012
|
+
if (op === "void") {
|
1013
|
+
return false;
|
1014
|
+
}
|
1015
|
+
if (op === "typeof" || op === "+" || op === "-" || op === "!" || op === "~") {
|
1016
|
+
continue;
|
1017
|
+
}
|
1018
|
+
}
|
1019
|
+
if (
|
1020
|
+
parent instanceof AST_SimpleStatement
|
1021
|
+
|| parent instanceof AST_LabeledStatement
|
1022
|
+
) {
|
1023
|
+
return false;
|
1024
|
+
}
|
1025
|
+
if (parent instanceof AST_Scope) {
|
1026
|
+
return false;
|
1027
|
+
}
|
1028
|
+
return true;
|
1029
|
+
}
|
1030
|
+
|
1031
|
+
return true;
|
1032
|
+
}
|
@@ -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
|
-
*
|
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)
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
546
|
-
|
547
|
-
|
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
|
-
//
|
554
|
-
if (
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
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
|
-
|
587
|
-
|
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
|
-
|
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
|
613
|
-
|
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
|
-
|
650
|
+
let def_last_write = symbol_last_write_map.get(def.id) || 0;
|
616
651
|
|
617
|
-
if (
|
652
|
+
if (defun_first_read < def_last_write) {
|
618
653
|
def.fixed = false;
|
619
654
|
}
|
620
655
|
}
|
package/lib/minify.js
CHANGED
@@ -305,7 +305,7 @@ function* minify_sync_or_async(files, options, _fs_module) {
|
|
305
305
|
if (node.block_scope) {
|
306
306
|
node.block_scope.variables = undefined;
|
307
307
|
node.block_scope.enclosed = undefined;
|
308
|
-
node.parent_scope = undefined;
|
308
|
+
node.block_scope.parent_scope = undefined;
|
309
309
|
}
|
310
310
|
});
|
311
311
|
}
|
package/lib/propmangle.js
CHANGED
@@ -326,7 +326,9 @@ function mangle_properties(ast, options, annotated_props = find_annotated_props(
|
|
326
326
|
} else if (node instanceof AST_ObjectProperty) {
|
327
327
|
// setter, getter, method or class field
|
328
328
|
if (!keep_quoted || !node.quote) {
|
329
|
-
|
329
|
+
if (!node.computed_key()) {
|
330
|
+
node.key.name = mangle(node.key.name);
|
331
|
+
}
|
330
332
|
}
|
331
333
|
} else if (node instanceof AST_Dot) {
|
332
334
|
if (!keep_quoted || !node.quote) {
|
package/lib/scope.js
CHANGED
@@ -200,7 +200,7 @@ function redefined_catch_def(def) {
|
|
200
200
|
}
|
201
201
|
}
|
202
202
|
|
203
|
-
AST_Scope.DEFMETHOD("figure_out_scope", function(options, { parent_scope =
|
203
|
+
AST_Scope.DEFMETHOD("figure_out_scope", function(options, { parent_scope = undefined, toplevel = this } = {}) {
|
204
204
|
options = defaults(options, {
|
205
205
|
cache: null,
|
206
206
|
ie8: false,
|
@@ -524,7 +524,7 @@ AST_Scope.DEFMETHOD("add_child_scope", function (scope) {
|
|
524
524
|
scope.parent_scope = this;
|
525
525
|
|
526
526
|
// Propagate to this.uses_arguments from arrow functions
|
527
|
-
if ((scope instanceof AST_Arrow) && !this.uses_arguments) {
|
527
|
+
if ((scope instanceof AST_Arrow) && (this instanceof AST_Lambda && !this.uses_arguments)) {
|
528
528
|
this.uses_arguments = walk(scope, node => {
|
529
529
|
if (
|
530
530
|
node instanceof AST_SymbolRef
|