terser 5.29.1 → 5.30.0

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,12 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.20.0
4
+ - Improve removal of classes referring to themselves
5
+
6
+ ## v5.29.2
7
+ - Make sure 'computed_props' creates string keys
8
+ - Take into account the evaluated size when inlining
9
+
3
10
  ## v5.29.1
4
11
  - fix optimisation of all-bits mask check
5
12
 
@@ -5894,6 +5894,24 @@ var AST_Class = DEFNODE("Class", "name extends properties", function AST_Class(p
5894
5894
  }
5895
5895
  });
5896
5896
  },
5897
+ is_self_referential: function() {
5898
+ const this_id = this.name && this.name.definition().id;
5899
+ let found = false;
5900
+ let class_this = true;
5901
+ this.visit_nondeferred_class_parts(new TreeWalker((node, descend) => {
5902
+ if (found) return true;
5903
+ if (node instanceof AST_This) return (found = class_this);
5904
+ if (node instanceof AST_SymbolRef) return (found = node.definition().id === this_id);
5905
+ if (node instanceof AST_Lambda && !(node instanceof AST_Arrow)) {
5906
+ const class_this_save = class_this;
5907
+ class_this = false;
5908
+ descend();
5909
+ class_this = class_this_save;
5910
+ return true;
5911
+ }
5912
+ }));
5913
+ return found;
5914
+ },
5897
5915
  }, AST_Scope /* TODO a class might have a scope but it's not a scope */);
5898
5916
 
5899
5917
  var AST_ClassProperty = DEFNODE("ClassProperty", "static quote", function AST_ClassProperty(props) {
@@ -14919,9 +14937,13 @@ def_drop_side_effect_free(AST_Arrow, return_null);
14919
14937
 
14920
14938
  def_drop_side_effect_free(AST_Class, function (compressor) {
14921
14939
  const with_effects = [];
14940
+
14941
+ if (this.is_self_referential() && this.has_side_effects(compressor)) {
14942
+ return this;
14943
+ }
14944
+
14922
14945
  const trimmed_extends = this.extends && this.extends.drop_side_effect_free(compressor);
14923
- if (trimmed_extends)
14924
- with_effects.push(trimmed_extends);
14946
+ if (trimmed_extends) with_effects.push(trimmed_extends);
14925
14947
 
14926
14948
  for (const prop of this.properties) {
14927
14949
  if (prop instanceof AST_ClassStaticBlock) {
@@ -14930,11 +14952,7 @@ def_drop_side_effect_free(AST_Class, function (compressor) {
14930
14952
  }
14931
14953
  } else {
14932
14954
  const trimmed_prop = prop.drop_side_effect_free(compressor);
14933
- if (trimmed_prop) {
14934
- if (trimmed_prop.contains_this()) return this;
14935
-
14936
- with_effects.push(trimmed_prop);
14937
- }
14955
+ if (trimmed_prop) with_effects.push(trimmed_prop);
14938
14956
  }
14939
14957
  }
14940
14958
 
@@ -15204,7 +15222,6 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15204
15222
  return node.expression;
15205
15223
  }
15206
15224
  };
15207
- var this_def = null;
15208
15225
  var in_use_ids = new Map();
15209
15226
  var fixed_ids = new Map();
15210
15227
  if (self instanceof AST_Toplevel && compressor.top_retain) {
@@ -15216,6 +15233,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15216
15233
  }
15217
15234
  var var_defs_by_id = new Map();
15218
15235
  var initializations = new Map();
15236
+ var self_referential_classes = new Set();
15219
15237
 
15220
15238
  // pass 1: find out which symbols are directly used in
15221
15239
  // this scope (not in nested scopes).
@@ -15229,6 +15247,10 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15229
15247
  });
15230
15248
  }
15231
15249
  if (node === self) return;
15250
+ if (node instanceof AST_Class && node.has_side_effects(compressor)) {
15251
+ if (node.is_self_referential()) self_referential_classes.add(node);
15252
+ node.visit_nondeferred_class_parts(tw);
15253
+ }
15232
15254
  if (node instanceof AST_Defun || node instanceof AST_DefClass) {
15233
15255
  var node_def = node.name.definition();
15234
15256
  const in_export = tw.parent() instanceof AST_Export;
@@ -15238,22 +15260,11 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15238
15260
  }
15239
15261
  }
15240
15262
 
15241
- if (node instanceof AST_DefClass && node.has_side_effects(compressor)) {
15242
- const save_this_def = this_def;
15243
- this_def = node_def;
15244
- node.visit_nondeferred_class_parts(tw);
15245
- this_def = save_this_def;
15246
- }
15247
-
15248
15263
  map_add(initializations, node_def.id, node);
15249
15264
  return true; // don't go in nested scopes
15250
15265
  }
15251
15266
  // In the root scope, we drop things. In inner scopes, we just check for uses.
15252
15267
  const in_root_scope = scope === self;
15253
- if (node instanceof AST_This && this_def && in_root_scope) {
15254
- in_use_ids.set(this_def.id, this_def);
15255
- return true;
15256
- }
15257
15268
  if (node instanceof AST_SymbolFunarg && in_root_scope) {
15258
15269
  map_add(var_defs_by_id, node.definition().id, node);
15259
15270
  }
@@ -15302,6 +15313,9 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
15302
15313
  init.walk(tw);
15303
15314
  });
15304
15315
  });
15316
+ self_referential_classes.forEach(function (cls) {
15317
+ cls.walk(tw);
15318
+ });
15305
15319
  // pass 3: we should drop declarations not in_use
15306
15320
  var tt = new TreeTransformer(
15307
15321
  function before(node, descend, in_list) {
@@ -17971,7 +17985,7 @@ function inline_into_symbolref(self, compressor) {
17971
17985
  let overhead = 0;
17972
17986
  if (compressor.option("unused") && !compressor.exposed(def)) {
17973
17987
  overhead =
17974
- (name_length + 2 + replace_size) /
17988
+ (name_length + 2 + fixed.size(compressor)) /
17975
17989
  (def.references.length - def.assignments);
17976
17990
  }
17977
17991
 
@@ -22163,21 +22177,23 @@ function lift_key(self, compressor) {
22163
22177
  if (!(self.key instanceof AST_Constant)) return self;
22164
22178
  // allow certain acceptable props as not all AST_Constants are true constants
22165
22179
  if (self.key instanceof AST_String || self.key instanceof AST_Number) {
22166
- if (self.key.value === "__proto__") return self;
22167
- if (self.key.value == "constructor"
22180
+ const key = self.key.value.toString();
22181
+
22182
+ if (key === "__proto__") return self;
22183
+ if (key == "constructor"
22168
22184
  && compressor.parent() instanceof AST_Class) return self;
22169
22185
  if (self instanceof AST_ObjectKeyVal) {
22170
22186
  self.quote = self.key.quote;
22171
- self.key = self.key.value;
22187
+ self.key = key;
22172
22188
  } else if (self instanceof AST_ClassProperty) {
22173
22189
  self.quote = self.key.quote;
22174
22190
  self.key = make_node(AST_SymbolClassProperty, self.key, {
22175
- name: self.key.value
22191
+ name: key,
22176
22192
  });
22177
22193
  } else {
22178
22194
  self.quote = self.key.quote;
22179
22195
  self.key = make_node(AST_SymbolMethod, self.key, {
22180
- name: self.key.value
22196
+ name: key,
22181
22197
  });
22182
22198
  }
22183
22199
  }
package/lib/ast.js CHANGED
@@ -2271,6 +2271,24 @@ var AST_Class = DEFNODE("Class", "name extends properties", function AST_Class(p
2271
2271
  }
2272
2272
  });
2273
2273
  },
2274
+ is_self_referential: function() {
2275
+ const this_id = this.name && this.name.definition().id;
2276
+ let found = false;
2277
+ let class_this = true;
2278
+ this.visit_nondeferred_class_parts(new TreeWalker((node, descend) => {
2279
+ if (found) return true;
2280
+ if (node instanceof AST_This) return (found = class_this);
2281
+ if (node instanceof AST_SymbolRef) return (found = node.definition().id === this_id);
2282
+ if (node instanceof AST_Lambda && !(node instanceof AST_Arrow)) {
2283
+ const class_this_save = class_this;
2284
+ class_this = false;
2285
+ descend();
2286
+ class_this = class_this_save;
2287
+ return true;
2288
+ }
2289
+ }));
2290
+ return found;
2291
+ },
2274
2292
  }, AST_Scope /* TODO a class might have a scope but it's not a scope */);
2275
2293
 
2276
2294
  var AST_ClassProperty = DEFNODE("ClassProperty", "static quote", function AST_ClassProperty(props) {
@@ -155,9 +155,13 @@ def_drop_side_effect_free(AST_Arrow, return_null);
155
155
 
156
156
  def_drop_side_effect_free(AST_Class, function (compressor) {
157
157
  const with_effects = [];
158
+
159
+ if (this.is_self_referential() && this.has_side_effects(compressor)) {
160
+ return this;
161
+ }
162
+
158
163
  const trimmed_extends = this.extends && this.extends.drop_side_effect_free(compressor);
159
- if (trimmed_extends)
160
- with_effects.push(trimmed_extends);
164
+ if (trimmed_extends) with_effects.push(trimmed_extends);
161
165
 
162
166
  for (const prop of this.properties) {
163
167
  if (prop instanceof AST_ClassStaticBlock) {
@@ -166,11 +170,7 @@ def_drop_side_effect_free(AST_Class, function (compressor) {
166
170
  }
167
171
  } else {
168
172
  const trimmed_prop = prop.drop_side_effect_free(compressor);
169
- if (trimmed_prop) {
170
- if (trimmed_prop.contains_this()) return this;
171
-
172
- with_effects.push(trimmed_prop);
173
- }
173
+ if (trimmed_prop) with_effects.push(trimmed_prop);
174
174
  }
175
175
  }
176
176
 
@@ -70,7 +70,6 @@ import {
70
70
  AST_SymbolFunarg,
71
71
  AST_SymbolRef,
72
72
  AST_SymbolVar,
73
- AST_This,
74
73
  AST_Toplevel,
75
74
  AST_Unary,
76
75
  AST_Var,
@@ -127,7 +126,6 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
127
126
  return node.expression;
128
127
  }
129
128
  };
130
- var this_def = null;
131
129
  var in_use_ids = new Map();
132
130
  var fixed_ids = new Map();
133
131
  if (self instanceof AST_Toplevel && compressor.top_retain) {
@@ -139,6 +137,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
139
137
  }
140
138
  var var_defs_by_id = new Map();
141
139
  var initializations = new Map();
140
+ var self_referential_classes = new Set();
142
141
 
143
142
  // pass 1: find out which symbols are directly used in
144
143
  // this scope (not in nested scopes).
@@ -152,6 +151,10 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
152
151
  });
153
152
  }
154
153
  if (node === self) return;
154
+ if (node instanceof AST_Class && node.has_side_effects(compressor)) {
155
+ if (node.is_self_referential()) self_referential_classes.add(node);
156
+ node.visit_nondeferred_class_parts(tw);
157
+ }
155
158
  if (node instanceof AST_Defun || node instanceof AST_DefClass) {
156
159
  var node_def = node.name.definition();
157
160
  const in_export = tw.parent() instanceof AST_Export;
@@ -161,22 +164,11 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
161
164
  }
162
165
  }
163
166
 
164
- if (node instanceof AST_DefClass && node.has_side_effects(compressor)) {
165
- const save_this_def = this_def;
166
- this_def = node_def;
167
- node.visit_nondeferred_class_parts(tw);
168
- this_def = save_this_def;
169
- }
170
-
171
167
  map_add(initializations, node_def.id, node);
172
168
  return true; // don't go in nested scopes
173
169
  }
174
170
  // In the root scope, we drop things. In inner scopes, we just check for uses.
175
171
  const in_root_scope = scope === self;
176
- if (node instanceof AST_This && this_def && in_root_scope) {
177
- in_use_ids.set(this_def.id, this_def);
178
- return true;
179
- }
180
172
  if (node instanceof AST_SymbolFunarg && in_root_scope) {
181
173
  map_add(var_defs_by_id, node.definition().id, node);
182
174
  }
@@ -225,6 +217,9 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
225
217
  init.walk(tw);
226
218
  });
227
219
  });
220
+ self_referential_classes.forEach(function (cls) {
221
+ cls.walk(tw);
222
+ });
228
223
  // pass 3: we should drop declarations not in_use
229
224
  var tt = new TreeTransformer(
230
225
  function before(node, descend, in_list) {
@@ -3924,21 +3924,23 @@ function lift_key(self, compressor) {
3924
3924
  if (!(self.key instanceof AST_Constant)) return self;
3925
3925
  // allow certain acceptable props as not all AST_Constants are true constants
3926
3926
  if (self.key instanceof AST_String || self.key instanceof AST_Number) {
3927
- if (self.key.value === "__proto__") return self;
3928
- if (self.key.value == "constructor"
3927
+ const key = self.key.value.toString();
3928
+
3929
+ if (key === "__proto__") return self;
3930
+ if (key == "constructor"
3929
3931
  && compressor.parent() instanceof AST_Class) return self;
3930
3932
  if (self instanceof AST_ObjectKeyVal) {
3931
3933
  self.quote = self.key.quote;
3932
- self.key = self.key.value;
3934
+ self.key = key;
3933
3935
  } else if (self instanceof AST_ClassProperty) {
3934
3936
  self.quote = self.key.quote;
3935
3937
  self.key = make_node(AST_SymbolClassProperty, self.key, {
3936
- name: self.key.value
3938
+ name: key,
3937
3939
  });
3938
3940
  } else {
3939
3941
  self.quote = self.key.quote;
3940
3942
  self.key = make_node(AST_SymbolMethod, self.key, {
3941
- name: self.key.value
3943
+ name: key,
3942
3944
  });
3943
3945
  }
3944
3946
  }
@@ -302,7 +302,7 @@ export function inline_into_symbolref(self, compressor) {
302
302
  let overhead = 0;
303
303
  if (compressor.option("unused") && !compressor.exposed(def)) {
304
304
  overhead =
305
- (name_length + 2 + replace_size) /
305
+ (name_length + 2 + fixed.size(compressor)) /
306
306
  (def.references.length - def.assignments);
307
307
  }
308
308
 
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.29.1",
7
+ "version": "5.30.0",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },