terser 5.17.4 → 5.17.5

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 CHANGED
@@ -1,5 +1,8 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.17.5
4
+ - Take into account the non-deferred bits of a class, such as static properties, while dropping unused code.
5
+
3
6
  ## v5.17.4
4
7
 
5
8
  - Fix crash when trying to negate a class (`!class{}`)
@@ -5840,6 +5840,40 @@ var AST_Class = DEFNODE("Class", "name extends properties", function AST_Class(p
5840
5840
  if (this.extends) push(this.extends);
5841
5841
  if (this.name) push(this.name);
5842
5842
  },
5843
+ /** go through the bits that are executed instantly, not when the class is `new`'d. Doesn't walk the name. */
5844
+ visit_nondeferred_class_parts(visitor) {
5845
+ if (this.extends) {
5846
+ this.extends._walk(visitor);
5847
+ }
5848
+ this.properties.forEach((prop) => {
5849
+ if (prop instanceof AST_ClassStaticBlock) {
5850
+ prop._walk(visitor);
5851
+ return;
5852
+ }
5853
+ if (prop.computed_key()) {
5854
+ visitor.push(prop);
5855
+ prop.key._walk(visitor);
5856
+ visitor.pop();
5857
+ }
5858
+ if ((prop instanceof AST_ClassPrivateProperty || prop instanceof AST_ClassProperty) && prop.static && prop.value) {
5859
+ visitor.push(prop);
5860
+ prop.value._walk(visitor);
5861
+ visitor.pop();
5862
+ }
5863
+ });
5864
+ },
5865
+ /** go through the bits that are executed later, when the class is `new`'d or a static method is called */
5866
+ visit_deferred_class_parts(visitor) {
5867
+ this.properties.forEach((prop) => {
5868
+ if (prop instanceof AST_ConciseMethod) {
5869
+ prop.walk(visitor);
5870
+ } else if (prop instanceof AST_ClassProperty && !prop.static && prop.value) {
5871
+ visitor.push(prop);
5872
+ prop.value._walk(visitor);
5873
+ visitor.pop();
5874
+ }
5875
+ });
5876
+ },
5843
5877
  }, AST_Scope /* TODO a class might have a scope but it's not a scope */);
5844
5878
 
5845
5879
  var AST_ClassProperty = DEFNODE("ClassProperty", "static quote", function AST_ClassProperty(props) {
@@ -5956,6 +5990,7 @@ var AST_ClassStaticBlock = DEFNODE("ClassStaticBlock", "body block_scope", funct
5956
5990
  while (i--) push(this.body[i]);
5957
5991
  },
5958
5992
  clone: clone_block_scope,
5993
+ computed_key: () => false
5959
5994
  }, AST_Scope);
5960
5995
 
5961
5996
  var AST_ClassExpression = DEFNODE("ClassExpression", null, function AST_ClassExpression(props) {
@@ -14772,31 +14807,43 @@ def_drop_side_effect_free(AST_Class, function (compressor) {
14772
14807
  const trimmed_extends = this.extends && this.extends.drop_side_effect_free(compressor);
14773
14808
  if (trimmed_extends)
14774
14809
  with_effects.push(trimmed_extends);
14810
+
14775
14811
  for (const prop of this.properties) {
14776
14812
  if (prop instanceof AST_ClassStaticBlock) {
14777
- if (prop.body.some(stat => stat.has_side_effects(compressor))) {
14778
- return this;
14779
- } else {
14780
- continue;
14813
+ if (prop.has_side_effects(compressor)) {
14814
+ return this; // Be cautious about these
14781
14815
  }
14782
- }
14816
+ } else {
14817
+ const trimmed_prop = prop.drop_side_effect_free(compressor);
14818
+ if (trimmed_prop) {
14819
+ if (trimmed_prop.contains_this()) return this;
14783
14820
 
14784
- if (
14785
- prop instanceof AST_ClassProperty
14786
- && prop.static
14787
- && prop.value.has_side_effects(compressor)
14788
- && prop.contains_this()
14789
- ) {
14790
- return this;
14821
+ with_effects.push(trimmed_prop);
14822
+ }
14791
14823
  }
14792
-
14793
- const trimmed_prop = prop.drop_side_effect_free(compressor);
14794
- if (trimmed_prop)
14795
- with_effects.push(trimmed_prop);
14796
14824
  }
14825
+
14797
14826
  if (!with_effects.length)
14798
14827
  return null;
14799
- return make_sequence(this, with_effects);
14828
+
14829
+ const exprs = make_sequence(this, with_effects);
14830
+ if (this instanceof AST_DefClass) {
14831
+ // We want a statement
14832
+ return make_node(AST_SimpleStatement, this, { body: exprs });
14833
+ } else {
14834
+ return exprs;
14835
+ }
14836
+ });
14837
+
14838
+ def_drop_side_effect_free(AST_ClassProperty, function (compressor) {
14839
+ const key = this.computed_key() && this.key.drop_side_effect_free(compressor);
14840
+
14841
+ const value = this.static && this.value
14842
+ && this.value.drop_side_effect_free(compressor);
14843
+
14844
+ if (key && value)
14845
+ return make_sequence(this, [key, value]);
14846
+ return key || value || null;
14800
14847
  });
14801
14848
 
14802
14849
  def_drop_side_effect_free(AST_Binary, function (compressor, first_in_statement) {
@@ -14902,17 +14949,6 @@ def_drop_side_effect_free(AST_ObjectProperty, function (compressor, first_in_sta
14902
14949
  return key || value;
14903
14950
  });
14904
14951
 
14905
- def_drop_side_effect_free(AST_ClassProperty, function (compressor) {
14906
- const key = this.computed_key() && this.key.drop_side_effect_free(compressor);
14907
-
14908
- const value = this.static && this.value
14909
- && this.value.drop_side_effect_free(compressor);
14910
-
14911
- if (key && value)
14912
- return make_sequence(this, [key, value]);
14913
- return key || value || null;
14914
- });
14915
-
14916
14952
  def_drop_side_effect_free(AST_ConciseMethod, function () {
14917
14953
  return this.computed_key() ? this.key : null;
14918
14954
  });
@@ -15072,6 +15108,11 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15072
15108
  });
15073
15109
  }
15074
15110
  if (node === self) return;
15111
+ if (node instanceof AST_Class) {
15112
+ if (node.has_side_effects(compressor)) {
15113
+ node.visit_nondeferred_class_parts(tw);
15114
+ }
15115
+ }
15075
15116
  if (node instanceof AST_Defun || node instanceof AST_DefClass) {
15076
15117
  var node_def = node.name.definition();
15077
15118
  const in_export = tw.parent() instanceof AST_Export;
@@ -15080,23 +15121,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15080
15121
  in_use_ids.set(node_def.id, node_def);
15081
15122
  }
15082
15123
  }
15083
- if (node instanceof AST_DefClass) {
15084
- if (
15085
- node.extends
15086
- && (node.extends.has_side_effects(compressor)
15087
- || node.extends.may_throw(compressor))
15088
- ) {
15089
- node.extends.walk(tw);
15090
- }
15091
- for (const prop of node.properties) {
15092
- if (
15093
- prop.has_side_effects(compressor) ||
15094
- prop.may_throw(compressor)
15095
- ) {
15096
- prop.walk(tw);
15097
- }
15098
- }
15099
- }
15124
+
15100
15125
  map_add(initializations, node_def.id, node);
15101
15126
  return true; // don't go in nested scopes
15102
15127
  }
@@ -15161,9 +15186,9 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15161
15186
  if (!in_use || fixed_ids.has(def.id) && fixed_ids.get(def.id) !== node) {
15162
15187
  return maintain_this_binding(parent, node, node.right.transform(tt));
15163
15188
  }
15164
- } else if (!in_use) return in_list ? MAP.skip : make_node(AST_Number, node, {
15165
- value: 0
15166
- });
15189
+ } else if (!in_use) {
15190
+ return in_list ? MAP.skip : make_node(AST_Number, node, { value: 0 });
15191
+ }
15167
15192
  }
15168
15193
  }
15169
15194
  if (scope !== self) return;
@@ -15206,11 +15231,18 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15206
15231
  if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) {
15207
15232
  const def = node.name.definition();
15208
15233
  const keep = def.global && !drop_funcs || in_use_ids.has(def.id);
15209
- // Class "extends" and static blocks may have side effects
15210
- const has_side_effects = !keep
15211
- && node instanceof AST_Class
15212
- && node.has_side_effects(compressor);
15213
- if (!keep && !has_side_effects) {
15234
+ if (!keep) {
15235
+ // Class "extends" and static blocks may have side effects
15236
+ if (node instanceof AST_Class) {
15237
+ const kept = node.drop_side_effect_free(compressor);
15238
+ if (kept !== node) {
15239
+ def.eliminated++;
15240
+ if (kept) return kept;
15241
+ return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
15242
+ } else {
15243
+ return kept;
15244
+ }
15245
+ }
15214
15246
  def.eliminated++;
15215
15247
  return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
15216
15248
  }
@@ -15306,9 +15338,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15306
15338
  case 1:
15307
15339
  return body[0];
15308
15340
  default:
15309
- return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
15310
- body: body
15311
- });
15341
+ return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, { body });
15312
15342
  }
15313
15343
  }
15314
15344
  // certain combination of unused name + side effect leads to:
@@ -15351,7 +15381,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15351
15381
  }
15352
15382
  return node;
15353
15383
  }
15354
- if (node instanceof AST_Scope) {
15384
+ if (node instanceof AST_Scope && !(node instanceof AST_ClassStaticBlock)) {
15355
15385
  const save_scope = scope;
15356
15386
  scope = node;
15357
15387
  descend(node, this);
@@ -15390,7 +15420,11 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15390
15420
  }
15391
15421
  return true;
15392
15422
  }
15393
- if (node instanceof AST_Scope) {
15423
+ if (node instanceof AST_Class) {
15424
+ descend();
15425
+ return true;
15426
+ }
15427
+ if (node instanceof AST_Scope && !(node instanceof AST_ClassStaticBlock)) {
15394
15428
  var save_scope = scope;
15395
15429
  scope = node;
15396
15430
  descend();
@@ -18556,8 +18590,6 @@ def_optimize(AST_Lambda, opt_AST_Lambda);
18556
18590
  AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
18557
18591
  var self = this;
18558
18592
  if (compressor.has_directive("use asm")) return self;
18559
- // Hoisting makes no sense in an arrow func
18560
- if (!Array.isArray(self.body)) return self;
18561
18593
 
18562
18594
  var hoist_funs = compressor.option("hoist_funs");
18563
18595
  var hoist_vars = compressor.option("hoist_vars");
package/lib/ast.js CHANGED
@@ -2230,6 +2230,40 @@ var AST_Class = DEFNODE("Class", "name extends properties", function AST_Class(p
2230
2230
  if (this.extends) push(this.extends);
2231
2231
  if (this.name) push(this.name);
2232
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
+ },
2233
2267
  }, AST_Scope /* TODO a class might have a scope but it's not a scope */);
2234
2268
 
2235
2269
  var AST_ClassProperty = DEFNODE("ClassProperty", "static quote", function AST_ClassProperty(props) {
@@ -2346,6 +2380,7 @@ var AST_ClassStaticBlock = DEFNODE("ClassStaticBlock", "body block_scope", funct
2346
2380
  while (i--) push(this.body[i]);
2347
2381
  },
2348
2382
  clone: clone_block_scope,
2383
+ computed_key: () => false
2349
2384
  }, AST_Scope);
2350
2385
 
2351
2386
  var AST_ClassExpression = DEFNODE("ClassExpression", null, function AST_ClassExpression(props) {
@@ -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,31 +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
- if (
169
- prop instanceof AST_ClassProperty
170
- && prop.static
171
- && prop.value.has_side_effects(compressor)
172
- && prop.contains_this()
173
- ) {
174
- return this;
172
+ with_effects.push(trimmed_prop);
173
+ }
175
174
  }
176
-
177
- const trimmed_prop = prop.drop_side_effect_free(compressor);
178
- if (trimmed_prop)
179
- with_effects.push(trimmed_prop);
180
175
  }
176
+
181
177
  if (!with_effects.length)
182
178
  return null;
183
- 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;
184
198
  });
185
199
 
186
200
  def_drop_side_effect_free(AST_Binary, function (compressor, first_in_statement) {
@@ -286,17 +300,6 @@ def_drop_side_effect_free(AST_ObjectProperty, function (compressor, first_in_sta
286
300
  return key || value;
287
301
  });
288
302
 
289
- def_drop_side_effect_free(AST_ClassProperty, function (compressor) {
290
- const key = this.computed_key() && this.key.drop_side_effect_free(compressor);
291
-
292
- const value = this.static && this.value
293
- && this.value.drop_side_effect_free(compressor);
294
-
295
- if (key && value)
296
- return make_sequence(this, [key, value]);
297
- return key || value || null;
298
- });
299
-
300
303
  def_drop_side_effect_free(AST_ConciseMethod, function () {
301
304
  return this.computed_key() ? this.key : null;
302
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();
@@ -655,8 +655,6 @@ def_optimize(AST_Lambda, opt_AST_Lambda);
655
655
  AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
656
656
  var self = this;
657
657
  if (compressor.has_directive("use asm")) return self;
658
- // Hoisting makes no sense in an arrow func
659
- if (!Array.isArray(self.body)) return self;
660
658
 
661
659
  var hoist_funs = compressor.option("hoist_funs");
662
660
  var hoist_vars = compressor.option("hoist_vars");
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "homepage": "https://terser.org",
5
5
  "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
6
6
  "license": "BSD-2-Clause",
7
- "version": "5.17.4",
7
+ "version": "5.17.5",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },