terser 5.16.1 → 5.17.7
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 +80 -0
- package/README.md +19 -5
- package/dist/bundle.min.js +931 -459
- package/lib/ast.js +82 -9
- package/lib/compress/common.js +6 -3
- package/lib/compress/drop-side-effect-free.js +32 -20
- package/lib/compress/drop-unused.js +29 -30
- package/lib/compress/evaluate.js +20 -1
- package/lib/compress/index.js +63 -54
- package/lib/compress/inference.js +31 -3
- package/lib/compress/inline.js +119 -114
- package/lib/compress/native-objects.js +22 -0
- package/lib/compress/reduce-vars.js +111 -11
- package/lib/compress/tighten-body.js +61 -21
- package/lib/equivalent-to.js +1 -1
- package/lib/minify.js +23 -17
- package/lib/mozilla-ast.js +119 -41
- package/lib/output.js +98 -44
- package/lib/parse.js +78 -19
- package/lib/propmangle.js +11 -0
- package/lib/scope.js +5 -9
- package/lib/size.js +1 -3
- package/lib/transform.js +4 -10
- package/lib/utils/index.js +24 -39
- package/package.json +8 -9
- package/tools/domprops.js +3 -0
- package/tools/terser.d.ts +1 -0
- package/bin/terser.mjs +0 -21
@@ -71,11 +71,13 @@ 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,
|
77
78
|
AST_SymbolCatch,
|
78
79
|
AST_SymbolConst,
|
80
|
+
AST_SymbolDeclaration,
|
79
81
|
AST_SymbolDefun,
|
80
82
|
AST_SymbolFunarg,
|
81
83
|
AST_SymbolLambda,
|
@@ -91,6 +93,8 @@ import {
|
|
91
93
|
AST_Yield,
|
92
94
|
|
93
95
|
walk,
|
96
|
+
walk_parent,
|
97
|
+
walk_abort,
|
94
98
|
walk_body,
|
95
99
|
|
96
100
|
_INLINE,
|
@@ -99,19 +103,21 @@ import {
|
|
99
103
|
} from "../ast.js";
|
100
104
|
import { HOP, make_node, noop } from "../utils/index.js";
|
101
105
|
|
102
|
-
import { lazy_op, is_modified } from "./inference.js";
|
106
|
+
import { lazy_op, is_modified, is_lhs } from "./inference.js";
|
103
107
|
import { INLINED, clear_flag } from "./compressor-flags.js";
|
104
108
|
import { read_property, has_break_or_continue, is_recursive_ref } from "./common.js";
|
105
109
|
|
106
|
-
|
107
|
-
|
108
|
-
|
110
|
+
/**
|
111
|
+
* Define the method AST_Node#reduce_vars, which goes through the AST in
|
112
|
+
* execution order to perform basic flow analysis
|
113
|
+
*/
|
109
114
|
function def_reduce_vars(node, func) {
|
110
115
|
node.DEFMETHOD("reduce_vars", func);
|
111
116
|
}
|
112
117
|
|
113
118
|
def_reduce_vars(AST_Node, noop);
|
114
119
|
|
120
|
+
/** Clear definition properties */
|
115
121
|
function reset_def(compressor, def) {
|
116
122
|
def.assignments = 0;
|
117
123
|
def.chained = false;
|
@@ -120,7 +126,10 @@ function reset_def(compressor, def) {
|
|
120
126
|
def.recursive_refs = 0;
|
121
127
|
def.references = [];
|
122
128
|
def.single_use = undefined;
|
123
|
-
if (
|
129
|
+
if (
|
130
|
+
def.scope.pinned()
|
131
|
+
|| (def.orig[0] instanceof AST_SymbolFunarg && def.scope.uses_arguments)
|
132
|
+
) {
|
124
133
|
def.fixed = false;
|
125
134
|
} else if (def.orig[0] instanceof AST_SymbolConst || !compressor.exposed(def)) {
|
126
135
|
def.fixed = def.init;
|
@@ -444,13 +453,11 @@ function mark_lambda(tw, descend, compressor) {
|
|
444
453
|
clear_flag(this, INLINED);
|
445
454
|
push(tw);
|
446
455
|
reset_variables(tw, compressor, this);
|
447
|
-
|
448
|
-
descend();
|
449
|
-
pop(tw);
|
450
|
-
return;
|
451
|
-
}
|
456
|
+
|
452
457
|
var iife;
|
453
458
|
if (!this.name
|
459
|
+
&& !this.uses_arguments
|
460
|
+
&& !this.pinned()
|
454
461
|
&& (iife = tw.parent()) instanceof AST_Call
|
455
462
|
&& iife.expression === this
|
456
463
|
&& !iife.args.some(arg => arg instanceof AST_Expansion)
|
@@ -475,11 +482,101 @@ function mark_lambda(tw, descend, compressor) {
|
|
475
482
|
}
|
476
483
|
});
|
477
484
|
}
|
485
|
+
|
478
486
|
descend();
|
479
487
|
pop(tw);
|
488
|
+
|
489
|
+
handle_defined_after_hoist(this);
|
490
|
+
|
480
491
|
return true;
|
481
492
|
}
|
482
493
|
|
494
|
+
/**
|
495
|
+
* It's possible for a hoisted function to use something that's not defined yet. Example:
|
496
|
+
*
|
497
|
+
* hoisted();
|
498
|
+
* var defined_after = true;
|
499
|
+
* function hoisted() {
|
500
|
+
* // use defined_after
|
501
|
+
* }
|
502
|
+
*
|
503
|
+
* This function is called on the parent to handle this issue.
|
504
|
+
*/
|
505
|
+
function handle_defined_after_hoist(parent) {
|
506
|
+
const defuns = [];
|
507
|
+
walk(parent, node => {
|
508
|
+
if (node === parent) return;
|
509
|
+
if (node instanceof AST_Defun) defuns.push(node);
|
510
|
+
if (
|
511
|
+
node instanceof AST_Scope
|
512
|
+
|| node instanceof AST_SimpleStatement
|
513
|
+
) return true;
|
514
|
+
});
|
515
|
+
|
516
|
+
for (const defun of defuns) {
|
517
|
+
const fname_def = defun.name.definition();
|
518
|
+
const found_self_ref_in_other_defuns = defuns.some(
|
519
|
+
d => d !== defun && d.enclosed.indexOf(fname_def) !== -1
|
520
|
+
);
|
521
|
+
|
522
|
+
for (const def of defun.enclosed) {
|
523
|
+
if (
|
524
|
+
def.fixed === false
|
525
|
+
|| def === fname_def
|
526
|
+
|| def.scope.get_defun_scope() !== parent
|
527
|
+
) {
|
528
|
+
continue;
|
529
|
+
}
|
530
|
+
|
531
|
+
// defun is hoisted, so always safe
|
532
|
+
if (
|
533
|
+
def.assignments === 0
|
534
|
+
&& def.orig.length === 1
|
535
|
+
&& def.orig[0] instanceof AST_SymbolDefun
|
536
|
+
) {
|
537
|
+
continue;
|
538
|
+
}
|
539
|
+
|
540
|
+
if (found_self_ref_in_other_defuns) {
|
541
|
+
def.fixed = false;
|
542
|
+
continue;
|
543
|
+
}
|
544
|
+
|
545
|
+
// Detect `call_defun(); var used_in_defun = X`
|
546
|
+
// Because `used_in_defun` is not certainly X when it's defined after.
|
547
|
+
let found_defun_ref = false;
|
548
|
+
let found_def_after_defun = false;
|
549
|
+
walk_parent(parent, (node, info) => {
|
550
|
+
if (node === defun) return true;
|
551
|
+
|
552
|
+
// Step 1: find `call_defun()` or other refs to the defun
|
553
|
+
if (
|
554
|
+
!found_defun_ref
|
555
|
+
&& node.thedef === fname_def
|
556
|
+
&& node instanceof AST_Symbol
|
557
|
+
) {
|
558
|
+
found_defun_ref = true;
|
559
|
+
}
|
560
|
+
|
561
|
+
// Step 2: if Step 1 occurred, find a var the defun uses
|
562
|
+
if (
|
563
|
+
found_defun_ref
|
564
|
+
&& node.thedef === def
|
565
|
+
&& (node instanceof AST_SymbolDeclaration
|
566
|
+
|| is_lhs(node, info))
|
567
|
+
) {
|
568
|
+
found_def_after_defun = true;
|
569
|
+
return walk_abort;
|
570
|
+
}
|
571
|
+
});
|
572
|
+
|
573
|
+
if (found_def_after_defun) {
|
574
|
+
def.fixed = false;
|
575
|
+
}
|
576
|
+
}
|
577
|
+
}
|
578
|
+
}
|
579
|
+
|
483
580
|
def_reduce_vars(AST_Lambda, mark_lambda);
|
484
581
|
|
485
582
|
def_reduce_vars(AST_Do, function(tw, descend, compressor) {
|
@@ -600,12 +697,15 @@ def_reduce_vars(AST_Toplevel, function(tw, descend, compressor) {
|
|
600
697
|
reset_def(compressor, def);
|
601
698
|
});
|
602
699
|
reset_variables(tw, compressor, this);
|
700
|
+
descend();
|
701
|
+
handle_defined_after_hoist(this);
|
702
|
+
return true;
|
603
703
|
});
|
604
704
|
|
605
705
|
def_reduce_vars(AST_Try, function(tw, descend, compressor) {
|
606
706
|
reset_block_variables(compressor, this);
|
607
707
|
push(tw);
|
608
|
-
|
708
|
+
this.body.walk(tw);
|
609
709
|
pop(tw);
|
610
710
|
if (this.bcatch) {
|
611
711
|
push(tw);
|
@@ -52,7 +52,6 @@ import {
|
|
52
52
|
AST_Break,
|
53
53
|
AST_Call,
|
54
54
|
AST_Case,
|
55
|
-
AST_Catch,
|
56
55
|
AST_Chain,
|
57
56
|
AST_Class,
|
58
57
|
AST_Conditional,
|
@@ -71,7 +70,6 @@ import {
|
|
71
70
|
AST_Exit,
|
72
71
|
AST_Expansion,
|
73
72
|
AST_Export,
|
74
|
-
AST_Finally,
|
75
73
|
AST_For,
|
76
74
|
AST_ForIn,
|
77
75
|
AST_If,
|
@@ -103,6 +101,7 @@ import {
|
|
103
101
|
AST_SymbolVar,
|
104
102
|
AST_This,
|
105
103
|
AST_Try,
|
104
|
+
AST_TryBlock,
|
106
105
|
AST_Unary,
|
107
106
|
AST_UnaryPostfix,
|
108
107
|
AST_UnaryPrefix,
|
@@ -171,12 +170,31 @@ function is_lhs_read_only(lhs) {
|
|
171
170
|
return false;
|
172
171
|
}
|
173
172
|
|
174
|
-
|
173
|
+
/** var a = 1 --> var a*/
|
174
|
+
function remove_initializers(var_statement) {
|
175
|
+
var decls = [];
|
176
|
+
var_statement.definitions.forEach(function(def) {
|
177
|
+
if (def.name instanceof AST_SymbolDeclaration) {
|
178
|
+
def.value = null;
|
179
|
+
decls.push(def);
|
180
|
+
} else {
|
181
|
+
def.declarations_as_names().forEach(name => {
|
182
|
+
decls.push(make_node(AST_VarDef, def, {
|
183
|
+
name,
|
184
|
+
value: null
|
185
|
+
}));
|
186
|
+
});
|
187
|
+
}
|
188
|
+
});
|
189
|
+
return decls.length ? make_node(AST_Var, var_statement, { definitions: decls }) : null;
|
190
|
+
}
|
191
|
+
|
192
|
+
/** Called on code which we know is unreachable, to keep elements that affect outside of it. */
|
175
193
|
export function trim_unreachable_code(compressor, stat, target) {
|
176
194
|
walk(stat, node => {
|
177
195
|
if (node instanceof AST_Var) {
|
178
|
-
|
179
|
-
target.push(
|
196
|
+
const no_initializers = remove_initializers(node);
|
197
|
+
if (no_initializers) target.push(no_initializers);
|
180
198
|
return true;
|
181
199
|
}
|
182
200
|
if (
|
@@ -234,13 +252,11 @@ export function tighten_body(statements, compressor) {
|
|
234
252
|
function find_loop_scope_try() {
|
235
253
|
var node = compressor.self(), level = 0, in_loop = false, in_try = false;
|
236
254
|
do {
|
237
|
-
if (node instanceof
|
238
|
-
level++;
|
239
|
-
} else if (node instanceof AST_IterationStatement) {
|
255
|
+
if (node instanceof AST_IterationStatement) {
|
240
256
|
in_loop = true;
|
241
257
|
} else if (node instanceof AST_Scope) {
|
242
258
|
break;
|
243
|
-
} else if (node instanceof
|
259
|
+
} else if (node instanceof AST_TryBlock) {
|
244
260
|
in_try = true;
|
245
261
|
}
|
246
262
|
} while (node = compressor.parent(level++));
|
@@ -284,6 +300,9 @@ export function tighten_body(statements, compressor) {
|
|
284
300
|
&& (node.logical || node.operator != "=" && lhs.equivalent_to(node.left))
|
285
301
|
|| node instanceof AST_Await
|
286
302
|
|| node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression)
|
303
|
+
||
|
304
|
+
(node instanceof AST_Call || node instanceof AST_PropAccess)
|
305
|
+
&& node.optional
|
287
306
|
|| node instanceof AST_Debugger
|
288
307
|
|| node instanceof AST_Destructuring
|
289
308
|
|| node instanceof AST_Expansion
|
@@ -457,7 +476,11 @@ export function tighten_body(statements, compressor) {
|
|
457
476
|
var hit = funarg;
|
458
477
|
var abort = false, replaced = 0, can_replace = !args || !hit;
|
459
478
|
if (!can_replace) {
|
460
|
-
for (
|
479
|
+
for (
|
480
|
+
let j = compressor.self().argnames.lastIndexOf(candidate.name) + 1;
|
481
|
+
!abort && j < args.length;
|
482
|
+
j++
|
483
|
+
) {
|
461
484
|
args[j].transform(scanner);
|
462
485
|
}
|
463
486
|
can_replace = true;
|
@@ -979,27 +1002,34 @@ export function tighten_body(statements, compressor) {
|
|
979
1002
|
}
|
980
1003
|
|
981
1004
|
if (stat instanceof AST_If) {
|
982
|
-
|
983
|
-
|
1005
|
+
let ab, new_else;
|
1006
|
+
|
1007
|
+
ab = aborts(stat.body);
|
1008
|
+
if (
|
1009
|
+
can_merge_flow(ab)
|
1010
|
+
&& (new_else = as_statement_array_with_return(stat.body, ab))
|
1011
|
+
) {
|
984
1012
|
if (ab.label) {
|
985
1013
|
remove(ab.label.thedef.references, ab);
|
986
1014
|
}
|
987
1015
|
CHANGED = true;
|
988
1016
|
stat = stat.clone();
|
989
1017
|
stat.condition = stat.condition.negate(compressor);
|
990
|
-
var body = as_statement_array_with_return(stat.body, ab);
|
991
1018
|
stat.body = make_node(AST_BlockStatement, stat, {
|
992
1019
|
body: as_statement_array(stat.alternative).concat(extract_functions())
|
993
1020
|
});
|
994
1021
|
stat.alternative = make_node(AST_BlockStatement, stat, {
|
995
|
-
body:
|
1022
|
+
body: new_else
|
996
1023
|
});
|
997
1024
|
statements[i] = stat.transform(compressor);
|
998
1025
|
continue;
|
999
1026
|
}
|
1000
1027
|
|
1001
|
-
|
1002
|
-
if (
|
1028
|
+
ab = aborts(stat.alternative);
|
1029
|
+
if (
|
1030
|
+
can_merge_flow(ab)
|
1031
|
+
&& (new_else = as_statement_array_with_return(stat.alternative, ab))
|
1032
|
+
) {
|
1003
1033
|
if (ab.label) {
|
1004
1034
|
remove(ab.label.thedef.references, ab);
|
1005
1035
|
}
|
@@ -1008,9 +1038,8 @@ export function tighten_body(statements, compressor) {
|
|
1008
1038
|
stat.body = make_node(AST_BlockStatement, stat.body, {
|
1009
1039
|
body: as_statement_array(stat.body).concat(extract_functions())
|
1010
1040
|
});
|
1011
|
-
var body = as_statement_array_with_return(stat.alternative, ab);
|
1012
1041
|
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
|
1013
|
-
body:
|
1042
|
+
body: new_else
|
1014
1043
|
});
|
1015
1044
|
statements[i] = stat.transform(compressor);
|
1016
1045
|
continue;
|
@@ -1125,7 +1154,11 @@ export function tighten_body(statements, compressor) {
|
|
1125
1154
|
}
|
1126
1155
|
|
1127
1156
|
function as_statement_array_with_return(node, ab) {
|
1128
|
-
var body = as_statement_array(node)
|
1157
|
+
var body = as_statement_array(node);
|
1158
|
+
if (ab !== body[body.length - 1]) {
|
1159
|
+
return undefined;
|
1160
|
+
}
|
1161
|
+
body = body.slice(0, -1);
|
1129
1162
|
if (ab.value) {
|
1130
1163
|
body.push(make_node(AST_SimpleStatement, ab.value, {
|
1131
1164
|
body: ab.value.expression
|
@@ -1404,14 +1437,21 @@ export function tighten_body(statements, compressor) {
|
|
1404
1437
|
CHANGED = true;
|
1405
1438
|
stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
|
1406
1439
|
statements[++j] = stat;
|
1407
|
-
} else if (
|
1440
|
+
} else if (
|
1441
|
+
prev instanceof AST_Var
|
1442
|
+
&& (!stat.init || stat.init.TYPE == prev.TYPE)
|
1443
|
+
) {
|
1408
1444
|
if (stat.init) {
|
1409
1445
|
prev.definitions = prev.definitions.concat(stat.init.definitions);
|
1410
1446
|
}
|
1411
1447
|
stat.init = prev;
|
1412
1448
|
statements[j] = stat;
|
1413
1449
|
CHANGED = true;
|
1414
|
-
} else if (
|
1450
|
+
} else if (
|
1451
|
+
defs instanceof AST_Var
|
1452
|
+
&& stat.init instanceof AST_Var
|
1453
|
+
&& declarations_only(stat.init)
|
1454
|
+
) {
|
1415
1455
|
defs.definitions = defs.definitions.concat(stat.init.definitions);
|
1416
1456
|
stat.init = null;
|
1417
1457
|
statements[++j] = stat;
|
package/lib/equivalent-to.js
CHANGED
@@ -172,7 +172,7 @@ AST_Switch.prototype.shallow_cmp = pass_through;
|
|
172
172
|
AST_SwitchBranch.prototype.shallow_cmp = pass_through;
|
173
173
|
|
174
174
|
AST_Try.prototype.shallow_cmp = function(other) {
|
175
|
-
return (this.bcatch == null ? other.bcatch == null : this.bcatch === other.bcatch) && (this.bfinally == null ? other.bfinally == null : this.bfinally === other.bfinally);
|
175
|
+
return (this.body === other.body) && (this.bcatch == null ? other.bcatch == null : this.bcatch === other.bcatch) && (this.bfinally == null ? other.bfinally == null : this.bfinally === other.bfinally);
|
176
176
|
};
|
177
177
|
|
178
178
|
AST_Catch.prototype.shallow_cmp = function(other) {
|
package/lib/minify.js
CHANGED
@@ -19,12 +19,15 @@ import {
|
|
19
19
|
reserve_quoted_keys,
|
20
20
|
} from "./propmangle.js";
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
var
|
26
|
-
|
27
|
-
|
22
|
+
// to/from base64 functions
|
23
|
+
// Prefer built-in Buffer, if available, then use hack
|
24
|
+
// https://developer.mozilla.org/en-US/docs/Glossary/Base64#The_Unicode_Problem
|
25
|
+
var to_ascii = typeof Buffer !== "undefined"
|
26
|
+
? (b64) => Buffer.from(b64, "base64").toString()
|
27
|
+
: (b64) => decodeURIComponent(escape(atob(b64)));
|
28
|
+
var to_base64 = typeof Buffer !== "undefined"
|
29
|
+
? (str) => Buffer.from(str).toString("base64")
|
30
|
+
: (str) => btoa(unescape(encodeURIComponent(str)));
|
28
31
|
|
29
32
|
function read_source_map(code) {
|
30
33
|
var match = /(?:^|[^.])\/\/# sourceMappingURL=data:application\/json(;[\w=-]*)?;base64,([+/0-9A-Za-z]*=*)\s*$/.exec(code);
|
@@ -277,10 +280,13 @@ async function minify(files, options, _fs_module) {
|
|
277
280
|
if (options.format.spidermonkey) {
|
278
281
|
result.ast = toplevel.to_mozilla_ast();
|
279
282
|
}
|
283
|
+
let format_options;
|
280
284
|
if (!HOP(options.format, "code") || options.format.code) {
|
281
|
-
|
285
|
+
// Make a shallow copy so that we can modify without mutating the user's input.
|
286
|
+
format_options = {...options.format};
|
287
|
+
if (!format_options.ast) {
|
282
288
|
// Destroy stuff to save RAM. (unless the deprecated `ast` option is on)
|
283
|
-
|
289
|
+
format_options._destroy_ast = true;
|
284
290
|
|
285
291
|
walk(toplevel, node => {
|
286
292
|
if (node instanceof AST_Scope) {
|
@@ -300,17 +306,17 @@ async function minify(files, options, _fs_module) {
|
|
300
306
|
if (options.sourceMap.includeSources && files instanceof AST_Toplevel) {
|
301
307
|
throw new Error("original source content unavailable");
|
302
308
|
}
|
303
|
-
|
309
|
+
format_options.source_map = await SourceMap({
|
304
310
|
file: options.sourceMap.filename,
|
305
311
|
orig: options.sourceMap.content,
|
306
312
|
root: options.sourceMap.root,
|
307
313
|
files: options.sourceMap.includeSources ? files : null,
|
308
314
|
});
|
309
315
|
}
|
310
|
-
delete
|
311
|
-
delete
|
312
|
-
delete
|
313
|
-
var stream = OutputStream(
|
316
|
+
delete format_options.ast;
|
317
|
+
delete format_options.code;
|
318
|
+
delete format_options.spidermonkey;
|
319
|
+
var stream = OutputStream(format_options);
|
314
320
|
toplevel.print(stream);
|
315
321
|
result.code = stream.get();
|
316
322
|
if (options.sourceMap) {
|
@@ -318,7 +324,7 @@ async function minify(files, options, _fs_module) {
|
|
318
324
|
configurable: true,
|
319
325
|
enumerable: true,
|
320
326
|
get() {
|
321
|
-
const map =
|
327
|
+
const map = format_options.source_map.getEncoded();
|
322
328
|
return (result.map = options.sourceMap.asObject ? map : JSON.stringify(map));
|
323
329
|
},
|
324
330
|
set(value) {
|
@@ -328,7 +334,7 @@ async function minify(files, options, _fs_module) {
|
|
328
334
|
});
|
329
335
|
}
|
330
336
|
});
|
331
|
-
result.decoded_map =
|
337
|
+
result.decoded_map = format_options.source_map.getDecoded();
|
332
338
|
if (options.sourceMap.url == "inline") {
|
333
339
|
var sourceMap = typeof result.map === "object" ? JSON.stringify(result.map) : result.map;
|
334
340
|
result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(sourceMap);
|
@@ -343,8 +349,8 @@ async function minify(files, options, _fs_module) {
|
|
343
349
|
options.nameCache.props = cache_to_json(options.mangle.properties.cache);
|
344
350
|
}
|
345
351
|
}
|
346
|
-
if (
|
347
|
-
|
352
|
+
if (format_options && format_options.source_map) {
|
353
|
+
format_options.source_map.destroy();
|
348
354
|
}
|
349
355
|
if (timings) {
|
350
356
|
timings.end = Date.now();
|