terser 5.17.3 → 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,14 @@
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
+
6
+ ## v5.17.4
7
+
8
+ - Fix crash when trying to negate a class (`!class{}`)
9
+ - Avoid outputting comments between `yield`/`await` and its argument
10
+ - Fix detection of left-hand-side of assignment, to avoid optimizing it like any other expression in some edge cases
11
+
3
12
  ## v5.17.3
4
13
 
5
14
  - Fix issue with trimming a static class property's contents accessing the class as `this`.
@@ -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) {
@@ -9345,14 +9380,17 @@ function OutputStream(options) {
9345
9380
  if (!start) return;
9346
9381
  var printed_comments = self.printed_comments;
9347
9382
 
9348
- // There cannot be a newline between return and its value.
9349
- const return_with_value = node instanceof AST_Exit && node.value;
9383
+ // There cannot be a newline between return/yield and its value.
9384
+ const keyword_with_value =
9385
+ node instanceof AST_Exit && node.value
9386
+ || (node instanceof AST_Await || node instanceof AST_Yield)
9387
+ && node.expression;
9350
9388
 
9351
9389
  if (
9352
9390
  start.comments_before
9353
9391
  && printed_comments.has(start.comments_before)
9354
9392
  ) {
9355
- if (return_with_value) {
9393
+ if (keyword_with_value) {
9356
9394
  start.comments_before = [];
9357
9395
  } else {
9358
9396
  return;
@@ -9365,10 +9403,12 @@ function OutputStream(options) {
9365
9403
  }
9366
9404
  printed_comments.add(comments);
9367
9405
 
9368
- if (return_with_value) {
9406
+ if (keyword_with_value) {
9369
9407
  var tw = new TreeWalker(function(node) {
9370
9408
  var parent = tw.parent();
9371
9409
  if (parent instanceof AST_Exit
9410
+ || parent instanceof AST_Await
9411
+ || parent instanceof AST_Yield
9372
9412
  || parent instanceof AST_Binary && parent.left === node
9373
9413
  || parent.TYPE == "Call" && parent.expression === node
9374
9414
  || parent instanceof AST_Conditional && parent.condition === node
@@ -9387,7 +9427,7 @@ function OutputStream(options) {
9387
9427
  }
9388
9428
  });
9389
9429
  tw.push(node);
9390
- node.value.walk(tw);
9430
+ keyword_with_value.walk(tw);
9391
9431
  }
9392
9432
 
9393
9433
  if (current_pos == 0) {
@@ -14022,6 +14062,9 @@ function is_lhs(node, parent) {
14022
14062
  def_negate(AST_Function, function() {
14023
14063
  return basic_negation(this);
14024
14064
  });
14065
+ def_negate(AST_Class, function() {
14066
+ return basic_negation(this);
14067
+ });
14025
14068
  def_negate(AST_Arrow, function() {
14026
14069
  return basic_negation(this);
14027
14070
  });
@@ -14764,31 +14807,43 @@ def_drop_side_effect_free(AST_Class, function (compressor) {
14764
14807
  const trimmed_extends = this.extends && this.extends.drop_side_effect_free(compressor);
14765
14808
  if (trimmed_extends)
14766
14809
  with_effects.push(trimmed_extends);
14810
+
14767
14811
  for (const prop of this.properties) {
14768
14812
  if (prop instanceof AST_ClassStaticBlock) {
14769
- if (prop.body.some(stat => stat.has_side_effects(compressor))) {
14770
- return this;
14771
- } else {
14772
- continue;
14813
+ if (prop.has_side_effects(compressor)) {
14814
+ return this; // Be cautious about these
14773
14815
  }
14774
- }
14816
+ } else {
14817
+ const trimmed_prop = prop.drop_side_effect_free(compressor);
14818
+ if (trimmed_prop) {
14819
+ if (trimmed_prop.contains_this()) return this;
14775
14820
 
14776
- if (
14777
- prop instanceof AST_ClassProperty
14778
- && prop.static
14779
- && prop.value.has_side_effects(compressor)
14780
- && prop.contains_this()
14781
- ) {
14782
- return this;
14821
+ with_effects.push(trimmed_prop);
14822
+ }
14783
14823
  }
14784
-
14785
- const trimmed_prop = prop.drop_side_effect_free(compressor);
14786
- if (trimmed_prop)
14787
- with_effects.push(trimmed_prop);
14788
14824
  }
14825
+
14789
14826
  if (!with_effects.length)
14790
14827
  return null;
14791
- 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;
14792
14847
  });
14793
14848
 
14794
14849
  def_drop_side_effect_free(AST_Binary, function (compressor, first_in_statement) {
@@ -14894,17 +14949,6 @@ def_drop_side_effect_free(AST_ObjectProperty, function (compressor, first_in_sta
14894
14949
  return key || value;
14895
14950
  });
14896
14951
 
14897
- def_drop_side_effect_free(AST_ClassProperty, function (compressor) {
14898
- const key = this.computed_key() && this.key.drop_side_effect_free(compressor);
14899
-
14900
- const value = this.static && this.value
14901
- && this.value.drop_side_effect_free(compressor);
14902
-
14903
- if (key && value)
14904
- return make_sequence(this, [key, value]);
14905
- return key || value || null;
14906
- });
14907
-
14908
14952
  def_drop_side_effect_free(AST_ConciseMethod, function () {
14909
14953
  return this.computed_key() ? this.key : null;
14910
14954
  });
@@ -15064,6 +15108,11 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15064
15108
  });
15065
15109
  }
15066
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
+ }
15067
15116
  if (node instanceof AST_Defun || node instanceof AST_DefClass) {
15068
15117
  var node_def = node.name.definition();
15069
15118
  const in_export = tw.parent() instanceof AST_Export;
@@ -15072,23 +15121,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15072
15121
  in_use_ids.set(node_def.id, node_def);
15073
15122
  }
15074
15123
  }
15075
- if (node instanceof AST_DefClass) {
15076
- if (
15077
- node.extends
15078
- && (node.extends.has_side_effects(compressor)
15079
- || node.extends.may_throw(compressor))
15080
- ) {
15081
- node.extends.walk(tw);
15082
- }
15083
- for (const prop of node.properties) {
15084
- if (
15085
- prop.has_side_effects(compressor) ||
15086
- prop.may_throw(compressor)
15087
- ) {
15088
- prop.walk(tw);
15089
- }
15090
- }
15091
- }
15124
+
15092
15125
  map_add(initializations, node_def.id, node);
15093
15126
  return true; // don't go in nested scopes
15094
15127
  }
@@ -15153,9 +15186,9 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15153
15186
  if (!in_use || fixed_ids.has(def.id) && fixed_ids.get(def.id) !== node) {
15154
15187
  return maintain_this_binding(parent, node, node.right.transform(tt));
15155
15188
  }
15156
- } else if (!in_use) return in_list ? MAP.skip : make_node(AST_Number, node, {
15157
- value: 0
15158
- });
15189
+ } else if (!in_use) {
15190
+ return in_list ? MAP.skip : make_node(AST_Number, node, { value: 0 });
15191
+ }
15159
15192
  }
15160
15193
  }
15161
15194
  if (scope !== self) return;
@@ -15198,11 +15231,18 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15198
15231
  if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) {
15199
15232
  const def = node.name.definition();
15200
15233
  const keep = def.global && !drop_funcs || in_use_ids.has(def.id);
15201
- // Class "extends" and static blocks may have side effects
15202
- const has_side_effects = !keep
15203
- && node instanceof AST_Class
15204
- && node.has_side_effects(compressor);
15205
- 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
+ }
15206
15246
  def.eliminated++;
15207
15247
  return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
15208
15248
  }
@@ -15298,9 +15338,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15298
15338
  case 1:
15299
15339
  return body[0];
15300
15340
  default:
15301
- return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
15302
- body: body
15303
- });
15341
+ return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, { body });
15304
15342
  }
15305
15343
  }
15306
15344
  // certain combination of unused name + side effect leads to:
@@ -15343,7 +15381,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15343
15381
  }
15344
15382
  return node;
15345
15383
  }
15346
- if (node instanceof AST_Scope) {
15384
+ if (node instanceof AST_Scope && !(node instanceof AST_ClassStaticBlock)) {
15347
15385
  const save_scope = scope;
15348
15386
  scope = node;
15349
15387
  descend(node, this);
@@ -15382,7 +15420,11 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15382
15420
  }
15383
15421
  return true;
15384
15422
  }
15385
- 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)) {
15386
15428
  var save_scope = scope;
15387
15429
  scope = node;
15388
15430
  descend();
@@ -18335,6 +18377,13 @@ class Compressor extends TreeWalker {
18335
18377
  if (opt === node) set_flag(opt, SQUEEZED);
18336
18378
  return opt;
18337
18379
  }
18380
+
18381
+ /** Alternative to plain is_lhs() which doesn't work within .optimize() */
18382
+ is_lhs() {
18383
+ const self = this.stack[this.stack.length - 1];
18384
+ const parent = this.stack[this.stack.length - 2];
18385
+ return is_lhs(self, parent);
18386
+ }
18338
18387
  }
18339
18388
 
18340
18389
  function def_optimize(node, optimizer) {
@@ -18541,8 +18590,6 @@ def_optimize(AST_Lambda, opt_AST_Lambda);
18541
18590
  AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
18542
18591
  var self = this;
18543
18592
  if (compressor.has_directive("use asm")) return self;
18544
- // Hoisting makes no sense in an arrow func
18545
- if (!Array.isArray(self.body)) return self;
18546
18593
 
18547
18594
  var hoist_funs = compressor.option("hoist_funs");
18548
18595
  var hoist_vars = compressor.option("hoist_vars");
@@ -20492,8 +20539,7 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
20492
20539
  }
20493
20540
  }
20494
20541
 
20495
- const parent = compressor.parent();
20496
- if (compressor.option("reduce_vars") && is_lhs(self, parent) !== self) {
20542
+ if (compressor.option("reduce_vars") && !compressor.is_lhs()) {
20497
20543
  return inline_into_symbolref(self, compressor);
20498
20544
  } else {
20499
20545
  return self;
@@ -20517,7 +20563,7 @@ def_optimize(AST_Undefined, function(self, compressor) {
20517
20563
  return ref;
20518
20564
  }
20519
20565
  }
20520
- var lhs = is_lhs(compressor.self(), compressor.parent());
20566
+ var lhs = compressor.is_lhs();
20521
20567
  if (lhs && is_atomic(lhs, self)) return self;
20522
20568
  return make_node(AST_UnaryPrefix, self, {
20523
20569
  operator: "void",
@@ -20528,7 +20574,7 @@ def_optimize(AST_Undefined, function(self, compressor) {
20528
20574
  });
20529
20575
 
20530
20576
  def_optimize(AST_Infinity, function(self, compressor) {
20531
- var lhs = is_lhs(compressor.self(), compressor.parent());
20577
+ var lhs = compressor.is_lhs();
20532
20578
  if (lhs && is_atomic(lhs, self)) return self;
20533
20579
  if (
20534
20580
  compressor.option("keep_infinity")
@@ -20549,7 +20595,7 @@ def_optimize(AST_Infinity, function(self, compressor) {
20549
20595
  });
20550
20596
 
20551
20597
  def_optimize(AST_NaN, function(self, compressor) {
20552
- var lhs = is_lhs(compressor.self(), compressor.parent());
20598
+ var lhs = compressor.is_lhs();
20553
20599
  if (lhs && !is_atomic(lhs, self)
20554
20600
  || find_variable(compressor, "NaN")) {
20555
20601
  return make_node(AST_Binary, self, {
@@ -21125,7 +21171,10 @@ def_optimize(AST_Sub, function(self, compressor) {
21125
21171
  }
21126
21172
  }
21127
21173
  }
21128
- prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor));
21174
+ prop = self.property = best_of_expression(
21175
+ prop,
21176
+ make_node_from_constant(key, prop).transform(compressor)
21177
+ );
21129
21178
  var property = "" + key;
21130
21179
  if (is_basic_identifier_string(property)
21131
21180
  && property.length <= prop.size() + 1) {
@@ -21183,7 +21232,7 @@ def_optimize(AST_Sub, function(self, compressor) {
21183
21232
  return sym;
21184
21233
  }
21185
21234
  }
21186
- if (is_lhs(self, compressor.parent())) return self;
21235
+ if (compressor.is_lhs()) return self;
21187
21236
  if (key !== prop) {
21188
21237
  var sub = self.flatten_object(property, compressor);
21189
21238
  if (sub) {
@@ -21253,7 +21302,7 @@ def_optimize(AST_Chain, function (self, compressor) {
21253
21302
 
21254
21303
  def_optimize(AST_Dot, function(self, compressor) {
21255
21304
  const parent = compressor.parent();
21256
- if (is_lhs(self, parent)) return self;
21305
+ if (compressor.is_lhs()) return self;
21257
21306
  if (compressor.option("unsafe_proto")
21258
21307
  && self.expression instanceof AST_Dot
21259
21308
  && self.expression.property == "prototype") {
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();
@@ -442,6 +442,13 @@ class Compressor extends TreeWalker {
442
442
  if (opt === node) set_flag(opt, SQUEEZED);
443
443
  return opt;
444
444
  }
445
+
446
+ /** Alternative to plain is_lhs() which doesn't work within .optimize() */
447
+ is_lhs() {
448
+ const self = this.stack[this.stack.length - 1];
449
+ const parent = this.stack[this.stack.length - 2];
450
+ return is_lhs(self, parent);
451
+ }
445
452
  }
446
453
 
447
454
  function def_optimize(node, optimizer) {
@@ -648,8 +655,6 @@ def_optimize(AST_Lambda, opt_AST_Lambda);
648
655
  AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
649
656
  var self = this;
650
657
  if (compressor.has_directive("use asm")) return self;
651
- // Hoisting makes no sense in an arrow func
652
- if (!Array.isArray(self.body)) return self;
653
658
 
654
659
  var hoist_funs = compressor.option("hoist_funs");
655
660
  var hoist_vars = compressor.option("hoist_vars");
@@ -2599,8 +2604,7 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
2599
2604
  }
2600
2605
  }
2601
2606
 
2602
- const parent = compressor.parent();
2603
- if (compressor.option("reduce_vars") && is_lhs(self, parent) !== self) {
2607
+ if (compressor.option("reduce_vars") && !compressor.is_lhs()) {
2604
2608
  return inline_into_symbolref(self, compressor);
2605
2609
  } else {
2606
2610
  return self;
@@ -2624,7 +2628,7 @@ def_optimize(AST_Undefined, function(self, compressor) {
2624
2628
  return ref;
2625
2629
  }
2626
2630
  }
2627
- var lhs = is_lhs(compressor.self(), compressor.parent());
2631
+ var lhs = compressor.is_lhs();
2628
2632
  if (lhs && is_atomic(lhs, self)) return self;
2629
2633
  return make_node(AST_UnaryPrefix, self, {
2630
2634
  operator: "void",
@@ -2635,7 +2639,7 @@ def_optimize(AST_Undefined, function(self, compressor) {
2635
2639
  });
2636
2640
 
2637
2641
  def_optimize(AST_Infinity, function(self, compressor) {
2638
- var lhs = is_lhs(compressor.self(), compressor.parent());
2642
+ var lhs = compressor.is_lhs();
2639
2643
  if (lhs && is_atomic(lhs, self)) return self;
2640
2644
  if (
2641
2645
  compressor.option("keep_infinity")
@@ -2656,7 +2660,7 @@ def_optimize(AST_Infinity, function(self, compressor) {
2656
2660
  });
2657
2661
 
2658
2662
  def_optimize(AST_NaN, function(self, compressor) {
2659
- var lhs = is_lhs(compressor.self(), compressor.parent());
2663
+ var lhs = compressor.is_lhs();
2660
2664
  if (lhs && !is_atomic(lhs, self)
2661
2665
  || find_variable(compressor, "NaN")) {
2662
2666
  return make_node(AST_Binary, self, {
@@ -3232,7 +3236,10 @@ def_optimize(AST_Sub, function(self, compressor) {
3232
3236
  }
3233
3237
  }
3234
3238
  }
3235
- prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor));
3239
+ prop = self.property = best_of_expression(
3240
+ prop,
3241
+ make_node_from_constant(key, prop).transform(compressor)
3242
+ );
3236
3243
  var property = "" + key;
3237
3244
  if (is_basic_identifier_string(property)
3238
3245
  && property.length <= prop.size() + 1) {
@@ -3290,7 +3297,7 @@ def_optimize(AST_Sub, function(self, compressor) {
3290
3297
  return sym;
3291
3298
  }
3292
3299
  }
3293
- if (is_lhs(self, compressor.parent())) return self;
3300
+ if (compressor.is_lhs()) return self;
3294
3301
  if (key !== prop) {
3295
3302
  var sub = self.flatten_object(property, compressor);
3296
3303
  if (sub) {
@@ -3360,7 +3367,7 @@ def_optimize(AST_Chain, function (self, compressor) {
3360
3367
 
3361
3368
  def_optimize(AST_Dot, function(self, compressor) {
3362
3369
  const parent = compressor.parent();
3363
- if (is_lhs(self, parent)) return self;
3370
+ if (compressor.is_lhs()) return self;
3364
3371
  if (compressor.option("unsafe_proto")
3365
3372
  && self.expression instanceof AST_Dot
3366
3373
  && self.expression.property == "prototype") {
@@ -818,6 +818,9 @@ export function is_lhs(node, parent) {
818
818
  def_negate(AST_Function, function() {
819
819
  return basic_negation(this);
820
820
  });
821
+ def_negate(AST_Class, function() {
822
+ return basic_negation(this);
823
+ });
821
824
  def_negate(AST_Arrow, function() {
822
825
  return basic_negation(this);
823
826
  });
package/lib/output.js CHANGED
@@ -662,14 +662,17 @@ function OutputStream(options) {
662
662
  if (!start) return;
663
663
  var printed_comments = self.printed_comments;
664
664
 
665
- // There cannot be a newline between return and its value.
666
- const return_with_value = node instanceof AST_Exit && node.value;
665
+ // There cannot be a newline between return/yield and its value.
666
+ const keyword_with_value =
667
+ node instanceof AST_Exit && node.value
668
+ || (node instanceof AST_Await || node instanceof AST_Yield)
669
+ && node.expression;
667
670
 
668
671
  if (
669
672
  start.comments_before
670
673
  && printed_comments.has(start.comments_before)
671
674
  ) {
672
- if (return_with_value) {
675
+ if (keyword_with_value) {
673
676
  start.comments_before = [];
674
677
  } else {
675
678
  return;
@@ -682,10 +685,12 @@ function OutputStream(options) {
682
685
  }
683
686
  printed_comments.add(comments);
684
687
 
685
- if (return_with_value) {
688
+ if (keyword_with_value) {
686
689
  var tw = new TreeWalker(function(node) {
687
690
  var parent = tw.parent();
688
691
  if (parent instanceof AST_Exit
692
+ || parent instanceof AST_Await
693
+ || parent instanceof AST_Yield
689
694
  || parent instanceof AST_Binary && parent.left === node
690
695
  || parent.TYPE == "Call" && parent.expression === node
691
696
  || parent instanceof AST_Conditional && parent.condition === node
@@ -704,7 +709,7 @@ function OutputStream(options) {
704
709
  }
705
710
  });
706
711
  tw.push(node);
707
- node.value.walk(tw);
712
+ keyword_with_value.walk(tw);
708
713
  }
709
714
 
710
715
  if (current_pos == 0) {
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.3",
7
+ "version": "5.17.5",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },