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/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
- this.walk(new TreeWalker(function (node) {
901
- if (node instanceof AST_Symbol) {
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
- walk_body(this, visitor);
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
- let i = this.body.length;
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
  };
@@ -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 AST_PropAccess
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.body.some(stat => stat.has_side_effects(compressor))) {
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
- const trimmed_prop = prop.drop_side_effect_free(compressor);
169
- if (trimmed_prop)
170
- with_effects.push(trimmed_prop);
172
+ with_effects.push(trimmed_prop);
173
+ }
174
+ }
171
175
  }
176
+
172
177
  if (!with_effects.length)
173
178
  return null;
174
- return make_sequence(this, with_effects);
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
- if (node instanceof AST_DefClass) {
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) return in_list ? MAP.skip : make_node(AST_Number, node, {
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
- // Class "extends" and static blocks may have side effects
290
- const has_side_effects = !keep
291
- && node instanceof AST_Class
292
- && node.has_side_effects(compressor);
293
- if (!keep && !has_side_effects) {
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 AST_Scope) {
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();
@@ -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"