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
package/lib/ast.js
CHANGED
@@ -111,6 +111,14 @@ class AST_Token {
|
|
111
111
|
Object.seal(this);
|
112
112
|
}
|
113
113
|
|
114
|
+
// Return a string summary of the token for node.js console.log
|
115
|
+
[Symbol.for("nodejs.util.inspect.custom")](_depth, options) {
|
116
|
+
const special = str => options.stylize(str, "special");
|
117
|
+
const quote = typeof this.value === "string" && this.value.includes("`") ? "'" : "`";
|
118
|
+
const value = `${quote}${this.value}${quote}`;
|
119
|
+
return `${special("[AST_Token")} ${value} at ${this.line}:${this.col}${special("]")}`;
|
120
|
+
}
|
121
|
+
|
114
122
|
get nlb() {
|
115
123
|
return has_tok_flag(this, TOK_FLAG_NLB);
|
116
124
|
}
|
@@ -897,11 +905,14 @@ var AST_Destructuring = DEFNODE("Destructuring", "names is_array", function AST_
|
|
897
905
|
},
|
898
906
|
all_symbols: function() {
|
899
907
|
var out = [];
|
900
|
-
|
901
|
-
if (node instanceof
|
908
|
+
walk(this, node => {
|
909
|
+
if (node instanceof AST_SymbolDeclaration) {
|
902
910
|
out.push(node);
|
903
911
|
}
|
904
|
-
|
912
|
+
if (node instanceof AST_Lambda) {
|
913
|
+
return true;
|
914
|
+
}
|
915
|
+
});
|
905
916
|
return out;
|
906
917
|
}
|
907
918
|
});
|
@@ -994,6 +1005,7 @@ var AST_Jump = DEFNODE("Jump", null, function AST_Jump(props) {
|
|
994
1005
|
$documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)"
|
995
1006
|
}, AST_Statement);
|
996
1007
|
|
1008
|
+
/** Base class for “exits” (`return` and `throw`) */
|
997
1009
|
var AST_Exit = DEFNODE("Exit", "value", function AST_Exit(props) {
|
998
1010
|
if (props) {
|
999
1011
|
this.value = props.value;
|
@@ -1256,12 +1268,11 @@ var AST_Case = DEFNODE("Case", "expression", function AST_Case(props) {
|
|
1256
1268
|
|
1257
1269
|
/* -----[ EXCEPTIONS ]----- */
|
1258
1270
|
|
1259
|
-
var AST_Try = DEFNODE("Try", "bcatch bfinally", function AST_Try(props) {
|
1271
|
+
var AST_Try = DEFNODE("Try", "body bcatch bfinally", function AST_Try(props) {
|
1260
1272
|
if (props) {
|
1273
|
+
this.body = props.body;
|
1261
1274
|
this.bcatch = props.bcatch;
|
1262
1275
|
this.bfinally = props.bfinally;
|
1263
|
-
this.body = props.body;
|
1264
|
-
this.block_scope = props.block_scope;
|
1265
1276
|
this.start = props.start;
|
1266
1277
|
this.end = props.end;
|
1267
1278
|
}
|
@@ -1270,12 +1281,13 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", function AST_Try(props) {
|
|
1270
1281
|
}, {
|
1271
1282
|
$documentation: "A `try` statement",
|
1272
1283
|
$propdoc: {
|
1284
|
+
body: "[AST_TryBlock] the try block",
|
1273
1285
|
bcatch: "[AST_Catch?] the catch block, or null if not present",
|
1274
1286
|
bfinally: "[AST_Finally?] the finally block, or null if not present"
|
1275
1287
|
},
|
1276
1288
|
_walk: function(visitor) {
|
1277
1289
|
return visitor._visit(this, function() {
|
1278
|
-
|
1290
|
+
this.body._walk(visitor);
|
1279
1291
|
if (this.bcatch) this.bcatch._walk(visitor);
|
1280
1292
|
if (this.bfinally) this.bfinally._walk(visitor);
|
1281
1293
|
});
|
@@ -1283,9 +1295,21 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", function AST_Try(props) {
|
|
1283
1295
|
_children_backwards(push) {
|
1284
1296
|
if (this.bfinally) push(this.bfinally);
|
1285
1297
|
if (this.bcatch) push(this.bcatch);
|
1286
|
-
|
1287
|
-
while (i--) push(this.body[i]);
|
1298
|
+
push(this.body);
|
1288
1299
|
},
|
1300
|
+
}, AST_Statement);
|
1301
|
+
|
1302
|
+
var AST_TryBlock = DEFNODE("TryBlock", null, function AST_TryBlock(props) {
|
1303
|
+
if (props) {
|
1304
|
+
this.body = props.body;
|
1305
|
+
this.block_scope = props.block_scope;
|
1306
|
+
this.start = props.start;
|
1307
|
+
this.end = props.end;
|
1308
|
+
}
|
1309
|
+
|
1310
|
+
this.flags = 0;
|
1311
|
+
}, {
|
1312
|
+
$documentation: "The `try` block of a try statement"
|
1289
1313
|
}, AST_Block);
|
1290
1314
|
|
1291
1315
|
var AST_Catch = DEFNODE("Catch", "argname", function AST_Catch(props) {
|
@@ -1419,6 +1443,13 @@ var AST_VarDef = DEFNODE("VarDef", "name value", function AST_VarDef(props) {
|
|
1419
1443
|
if (this.value) push(this.value);
|
1420
1444
|
push(this.name);
|
1421
1445
|
},
|
1446
|
+
declarations_as_names() {
|
1447
|
+
if (this.name instanceof AST_SymbolDeclaration) {
|
1448
|
+
return [this];
|
1449
|
+
} else {
|
1450
|
+
return this.name.all_symbols();
|
1451
|
+
}
|
1452
|
+
}
|
1422
1453
|
});
|
1423
1454
|
|
1424
1455
|
var AST_NameMapping = DEFNODE("NameMapping", "foreign_name name", function AST_NameMapping(props) {
|
@@ -2199,6 +2230,40 @@ var AST_Class = DEFNODE("Class", "name extends properties", function AST_Class(p
|
|
2199
2230
|
if (this.extends) push(this.extends);
|
2200
2231
|
if (this.name) push(this.name);
|
2201
2232
|
},
|
2233
|
+
/** go through the bits that are executed instantly, not when the class is `new`'d. Doesn't walk the name. */
|
2234
|
+
visit_nondeferred_class_parts(visitor) {
|
2235
|
+
if (this.extends) {
|
2236
|
+
this.extends._walk(visitor);
|
2237
|
+
}
|
2238
|
+
this.properties.forEach((prop) => {
|
2239
|
+
if (prop instanceof AST_ClassStaticBlock) {
|
2240
|
+
prop._walk(visitor);
|
2241
|
+
return;
|
2242
|
+
}
|
2243
|
+
if (prop.computed_key()) {
|
2244
|
+
visitor.push(prop);
|
2245
|
+
prop.key._walk(visitor);
|
2246
|
+
visitor.pop();
|
2247
|
+
}
|
2248
|
+
if ((prop instanceof AST_ClassPrivateProperty || prop instanceof AST_ClassProperty) && prop.static && prop.value) {
|
2249
|
+
visitor.push(prop);
|
2250
|
+
prop.value._walk(visitor);
|
2251
|
+
visitor.pop();
|
2252
|
+
}
|
2253
|
+
});
|
2254
|
+
},
|
2255
|
+
/** go through the bits that are executed later, when the class is `new`'d or a static method is called */
|
2256
|
+
visit_deferred_class_parts(visitor) {
|
2257
|
+
this.properties.forEach((prop) => {
|
2258
|
+
if (prop instanceof AST_ConciseMethod) {
|
2259
|
+
prop.walk(visitor);
|
2260
|
+
} else if (prop instanceof AST_ClassProperty && !prop.static && prop.value) {
|
2261
|
+
visitor.push(prop);
|
2262
|
+
prop.value._walk(visitor);
|
2263
|
+
visitor.pop();
|
2264
|
+
}
|
2265
|
+
});
|
2266
|
+
},
|
2202
2267
|
}, AST_Scope /* TODO a class might have a scope but it's not a scope */);
|
2203
2268
|
|
2204
2269
|
var AST_ClassProperty = DEFNODE("ClassProperty", "static quote", function AST_ClassProperty(props) {
|
@@ -2315,6 +2380,7 @@ var AST_ClassStaticBlock = DEFNODE("ClassStaticBlock", "body block_scope", funct
|
|
2315
2380
|
while (i--) push(this.body[i]);
|
2316
2381
|
},
|
2317
2382
|
clone: clone_block_scope,
|
2383
|
+
computed_key: () => false
|
2318
2384
|
}, AST_Scope);
|
2319
2385
|
|
2320
2386
|
var AST_ClassExpression = DEFNODE("ClassExpression", null, function AST_ClassExpression(props) {
|
@@ -2588,6 +2654,7 @@ var AST_SymbolImportForeign = DEFNODE("SymbolImportForeign", null, function AST_
|
|
2588
2654
|
this.scope = props.scope;
|
2589
2655
|
this.name = props.name;
|
2590
2656
|
this.thedef = props.thedef;
|
2657
|
+
this.quote = props.quote;
|
2591
2658
|
this.start = props.start;
|
2592
2659
|
this.end = props.end;
|
2593
2660
|
}
|
@@ -2639,6 +2706,7 @@ var AST_SymbolExport = DEFNODE("SymbolExport", null, function AST_SymbolExport(p
|
|
2639
2706
|
this.scope = props.scope;
|
2640
2707
|
this.name = props.name;
|
2641
2708
|
this.thedef = props.thedef;
|
2709
|
+
this.quote = props.quote;
|
2642
2710
|
this.start = props.start;
|
2643
2711
|
this.end = props.end;
|
2644
2712
|
}
|
@@ -2653,6 +2721,7 @@ var AST_SymbolExportForeign = DEFNODE("SymbolExportForeign", null, function AST_
|
|
2653
2721
|
this.scope = props.scope;
|
2654
2722
|
this.name = props.name;
|
2655
2723
|
this.thedef = props.thedef;
|
2724
|
+
this.quote = props.quote;
|
2656
2725
|
this.start = props.start;
|
2657
2726
|
this.end = props.end;
|
2658
2727
|
}
|
@@ -2738,6 +2807,7 @@ var AST_String = DEFNODE("String", "value quote", function AST_String(props) {
|
|
2738
2807
|
this.quote = props.quote;
|
2739
2808
|
this.start = props.start;
|
2740
2809
|
this.end = props.end;
|
2810
|
+
this._annotations = props._annotations;
|
2741
2811
|
}
|
2742
2812
|
|
2743
2813
|
this.flags = 0;
|
@@ -3110,6 +3180,7 @@ class TreeTransformer extends TreeWalker {
|
|
3110
3180
|
const _PURE = 0b00000001;
|
3111
3181
|
const _INLINE = 0b00000010;
|
3112
3182
|
const _NOINLINE = 0b00000100;
|
3183
|
+
const _KEY = 0b00001000;
|
3113
3184
|
|
3114
3185
|
export {
|
3115
3186
|
AST_Accessor,
|
@@ -3231,6 +3302,7 @@ export {
|
|
3231
3302
|
AST_Toplevel,
|
3232
3303
|
AST_True,
|
3233
3304
|
AST_Try,
|
3305
|
+
AST_TryBlock,
|
3234
3306
|
AST_Unary,
|
3235
3307
|
AST_UnaryPostfix,
|
3236
3308
|
AST_UnaryPrefix,
|
@@ -3253,4 +3325,5 @@ export {
|
|
3253
3325
|
_INLINE,
|
3254
3326
|
_NOINLINE,
|
3255
3327
|
_PURE,
|
3328
|
+
_KEY,
|
3256
3329
|
};
|
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
|
) {
|
@@ -241,9 +243,10 @@ export function is_func_expr(node) {
|
|
241
243
|
return node instanceof AST_Arrow || node instanceof AST_Function;
|
242
244
|
}
|
243
245
|
|
246
|
+
/**
|
247
|
+
* Used to determine whether the node can benefit from negation.
|
248
|
+
* Not the case with arrow functions (you need an extra set of parens). */
|
244
249
|
export function is_iife_call(node) {
|
245
|
-
// Used to determine whether the node can benefit from negation.
|
246
|
-
// Not the case with arrow functions (you need an extra set of parens).
|
247
250
|
if (node.TYPE != "Call") return false;
|
248
251
|
return node.expression instanceof AST_Function || is_iife_call(node.expression);
|
249
252
|
}
|
@@ -55,6 +55,7 @@ import {
|
|
55
55
|
AST_ConciseMethod,
|
56
56
|
AST_Conditional,
|
57
57
|
AST_Constant,
|
58
|
+
AST_DefClass,
|
58
59
|
AST_Dot,
|
59
60
|
AST_Expansion,
|
60
61
|
AST_Function,
|
@@ -68,6 +69,7 @@ import {
|
|
68
69
|
AST_PropAccess,
|
69
70
|
AST_Scope,
|
70
71
|
AST_Sequence,
|
72
|
+
AST_SimpleStatement,
|
71
73
|
AST_Sub,
|
72
74
|
AST_SymbolRef,
|
73
75
|
AST_TemplateSegment,
|
@@ -156,22 +158,43 @@ def_drop_side_effect_free(AST_Class, function (compressor) {
|
|
156
158
|
const trimmed_extends = this.extends && this.extends.drop_side_effect_free(compressor);
|
157
159
|
if (trimmed_extends)
|
158
160
|
with_effects.push(trimmed_extends);
|
161
|
+
|
159
162
|
for (const prop of this.properties) {
|
160
163
|
if (prop instanceof AST_ClassStaticBlock) {
|
161
|
-
if (prop.
|
162
|
-
return this;
|
163
|
-
} else {
|
164
|
-
continue;
|
164
|
+
if (prop.has_side_effects(compressor)) {
|
165
|
+
return this; // Be cautious about these
|
165
166
|
}
|
166
|
-
}
|
167
|
+
} else {
|
168
|
+
const trimmed_prop = prop.drop_side_effect_free(compressor);
|
169
|
+
if (trimmed_prop) {
|
170
|
+
if (trimmed_prop.contains_this()) return this;
|
167
171
|
|
168
|
-
|
169
|
-
|
170
|
-
|
172
|
+
with_effects.push(trimmed_prop);
|
173
|
+
}
|
174
|
+
}
|
171
175
|
}
|
176
|
+
|
172
177
|
if (!with_effects.length)
|
173
178
|
return null;
|
174
|
-
|
179
|
+
|
180
|
+
const exprs = make_sequence(this, with_effects);
|
181
|
+
if (this instanceof AST_DefClass) {
|
182
|
+
// We want a statement
|
183
|
+
return make_node(AST_SimpleStatement, this, { body: exprs });
|
184
|
+
} else {
|
185
|
+
return exprs;
|
186
|
+
}
|
187
|
+
});
|
188
|
+
|
189
|
+
def_drop_side_effect_free(AST_ClassProperty, function (compressor) {
|
190
|
+
const key = this.computed_key() && this.key.drop_side_effect_free(compressor);
|
191
|
+
|
192
|
+
const value = this.static && this.value
|
193
|
+
&& this.value.drop_side_effect_free(compressor);
|
194
|
+
|
195
|
+
if (key && value)
|
196
|
+
return make_sequence(this, [key, value]);
|
197
|
+
return key || value || null;
|
175
198
|
});
|
176
199
|
|
177
200
|
def_drop_side_effect_free(AST_Binary, function (compressor, first_in_statement) {
|
@@ -277,17 +300,6 @@ def_drop_side_effect_free(AST_ObjectProperty, function (compressor, first_in_sta
|
|
277
300
|
return key || value;
|
278
301
|
});
|
279
302
|
|
280
|
-
def_drop_side_effect_free(AST_ClassProperty, function (compressor) {
|
281
|
-
const key = this.computed_key() && this.key.drop_side_effect_free(compressor);
|
282
|
-
|
283
|
-
const value = this.static && this.value
|
284
|
-
&& this.value.drop_side_effect_free(compressor);
|
285
|
-
|
286
|
-
if (key && value)
|
287
|
-
return make_sequence(this, [key, value]);
|
288
|
-
return key || value || null;
|
289
|
-
});
|
290
|
-
|
291
303
|
def_drop_side_effect_free(AST_ConciseMethod, function () {
|
292
304
|
return this.computed_key() ? this.key : null;
|
293
305
|
});
|
@@ -47,6 +47,7 @@ import {
|
|
47
47
|
AST_BlockStatement,
|
48
48
|
AST_Class,
|
49
49
|
AST_ClassExpression,
|
50
|
+
AST_ClassStaticBlock,
|
50
51
|
AST_DefaultAssign,
|
51
52
|
AST_DefClass,
|
52
53
|
AST_Definitions,
|
@@ -152,6 +153,11 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
152
153
|
});
|
153
154
|
}
|
154
155
|
if (node === self) return;
|
156
|
+
if (node instanceof AST_Class) {
|
157
|
+
if (node.has_side_effects(compressor)) {
|
158
|
+
node.visit_nondeferred_class_parts(tw);
|
159
|
+
}
|
160
|
+
}
|
155
161
|
if (node instanceof AST_Defun || node instanceof AST_DefClass) {
|
156
162
|
var node_def = node.name.definition();
|
157
163
|
const in_export = tw.parent() instanceof AST_Export;
|
@@ -160,23 +166,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
160
166
|
in_use_ids.set(node_def.id, node_def);
|
161
167
|
}
|
162
168
|
}
|
163
|
-
|
164
|
-
if (
|
165
|
-
node.extends
|
166
|
-
&& (node.extends.has_side_effects(compressor)
|
167
|
-
|| node.extends.may_throw(compressor))
|
168
|
-
) {
|
169
|
-
node.extends.walk(tw);
|
170
|
-
}
|
171
|
-
for (const prop of node.properties) {
|
172
|
-
if (
|
173
|
-
prop.has_side_effects(compressor) ||
|
174
|
-
prop.may_throw(compressor)
|
175
|
-
) {
|
176
|
-
prop.walk(tw);
|
177
|
-
}
|
178
|
-
}
|
179
|
-
}
|
169
|
+
|
180
170
|
map_add(initializations, node_def.id, node);
|
181
171
|
return true; // don't go in nested scopes
|
182
172
|
}
|
@@ -241,9 +231,9 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
241
231
|
if (!in_use || fixed_ids.has(def.id) && fixed_ids.get(def.id) !== node) {
|
242
232
|
return maintain_this_binding(parent, node, node.right.transform(tt));
|
243
233
|
}
|
244
|
-
} else if (!in_use)
|
245
|
-
value: 0
|
246
|
-
}
|
234
|
+
} else if (!in_use) {
|
235
|
+
return in_list ? MAP.skip : make_node(AST_Number, node, { value: 0 });
|
236
|
+
}
|
247
237
|
}
|
248
238
|
}
|
249
239
|
if (scope !== self) return;
|
@@ -286,11 +276,18 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
286
276
|
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) {
|
287
277
|
const def = node.name.definition();
|
288
278
|
const keep = def.global && !drop_funcs || in_use_ids.has(def.id);
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
279
|
+
if (!keep) {
|
280
|
+
// Class "extends" and static blocks may have side effects
|
281
|
+
if (node instanceof AST_Class) {
|
282
|
+
const kept = node.drop_side_effect_free(compressor);
|
283
|
+
if (kept !== node) {
|
284
|
+
def.eliminated++;
|
285
|
+
if (kept) return kept;
|
286
|
+
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
|
287
|
+
} else {
|
288
|
+
return kept;
|
289
|
+
}
|
290
|
+
}
|
294
291
|
def.eliminated++;
|
295
292
|
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
|
296
293
|
}
|
@@ -386,9 +383,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
386
383
|
case 1:
|
387
384
|
return body[0];
|
388
385
|
default:
|
389
|
-
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
|
390
|
-
body: body
|
391
|
-
});
|
386
|
+
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, { body });
|
392
387
|
}
|
393
388
|
}
|
394
389
|
// certain combination of unused name + side effect leads to:
|
@@ -431,7 +426,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
431
426
|
}
|
432
427
|
return node;
|
433
428
|
}
|
434
|
-
if (node instanceof AST_Scope) {
|
429
|
+
if (node instanceof AST_Scope && !(node instanceof AST_ClassStaticBlock)) {
|
435
430
|
const save_scope = scope;
|
436
431
|
scope = node;
|
437
432
|
descend(node, this);
|
@@ -470,7 +465,11 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
|
|
470
465
|
}
|
471
466
|
return true;
|
472
467
|
}
|
473
|
-
if (node instanceof
|
468
|
+
if (node instanceof AST_Class) {
|
469
|
+
descend();
|
470
|
+
return true;
|
471
|
+
}
|
472
|
+
if (node instanceof AST_Scope && !(node instanceof AST_ClassStaticBlock)) {
|
474
473
|
var save_scope = scope;
|
475
474
|
scope = node;
|
476
475
|
descend();
|
package/lib/compress/evaluate.js
CHANGED
@@ -352,6 +352,25 @@ const regexp_flags = new Set([
|
|
352
352
|
def_eval(AST_PropAccess, function (compressor, depth) {
|
353
353
|
let obj = this.expression._eval(compressor, depth + 1);
|
354
354
|
if (obj === nullish || (this.optional && obj == null)) return nullish;
|
355
|
+
|
356
|
+
// `.length` of strings and arrays is always safe
|
357
|
+
if (this.property === "length") {
|
358
|
+
if (typeof obj === "string") {
|
359
|
+
return obj.length;
|
360
|
+
}
|
361
|
+
|
362
|
+
const is_spreadless_array =
|
363
|
+
obj instanceof AST_Array
|
364
|
+
&& obj.elements.every(el => !(el instanceof AST_Expansion));
|
365
|
+
|
366
|
+
if (
|
367
|
+
is_spreadless_array
|
368
|
+
&& obj.elements.every(el => !el.has_side_effects(compressor))
|
369
|
+
) {
|
370
|
+
return obj.elements.length;
|
371
|
+
}
|
372
|
+
}
|
373
|
+
|
355
374
|
if (compressor.option("unsafe")) {
|
356
375
|
var key = this.property;
|
357
376
|
if (key instanceof AST_Node) {
|
@@ -359,9 +378,9 @@ def_eval(AST_PropAccess, function (compressor, depth) {
|
|
359
378
|
if (key === this.property)
|
360
379
|
return this;
|
361
380
|
}
|
381
|
+
|
362
382
|
var exp = this.expression;
|
363
383
|
if (is_undeclared_ref(exp)) {
|
364
|
-
|
365
384
|
var aa;
|
366
385
|
var first_arg = exp.name === "hasOwnProperty"
|
367
386
|
&& key === "call"
|