terser 5.16.9 → 5.17.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,10 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.17.0
4
+ - Drop vestigial `= undefined` default argument in IIFE calls (#1366)
5
+ - Evaluate known arrays' `.length` property when statically determinable
6
+ - Add `@__KEY__` annotation to mangle string literals (#1365)
7
+
3
8
  ## v5.16.9
4
9
  - Fix parentheses in output of optional chains (`a?.b`) (#1374)
5
10
  - More documentation on source maps (#1368)
package/README.md CHANGED
@@ -1188,6 +1188,7 @@ Annotations in Terser are a way to tell it to treat a certain function call diff
1188
1188
  * `/*@__INLINE__*/` - forces a function to be inlined somewhere.
1189
1189
  * `/*@__NOINLINE__*/` - Makes sure the called function is not inlined into the call site.
1190
1190
  * `/*@__PURE__*/` - Marks a function call as pure. That means, it can safely be dropped.
1191
+ * `/*@__KEY__*/` - Marks a string literal as a property to also mangle it when mangling properties.
1191
1192
 
1192
1193
  You can use either a `@` sign at the start, or a `#`.
1193
1194
 
@@ -1201,6 +1202,9 @@ function_always_inlined_here()
1201
1202
  function_cant_be_inlined_into_here()
1202
1203
 
1203
1204
  const x = /*#__PURE__*/i_am_dropped_if_x_is_not_used()
1205
+
1206
+ function lookup(object, key) { return object[key]; }
1207
+ lookup({ i_will_be_mangled_too: "bar" }, /*@__KEY__*/ "i_will_be_mangled_too");
1204
1208
  ```
1205
1209
 
1206
1210
  ### ESTree / SpiderMonkey AST
@@ -264,6 +264,10 @@ function set_annotation(node, annotation) {
264
264
  node._annotations |= annotation;
265
265
  }
266
266
 
267
+ function clear_annotation(node, annotation) {
268
+ node._annotations &= ~annotation;
269
+ }
270
+
267
271
  /***********************************************************************
268
272
 
269
273
  A JavaScript tokenizer / parser / beautifier / compressor.
@@ -2368,6 +2372,7 @@ function parse($TEXT, options) {
2368
2372
  value : tok.value,
2369
2373
  quote : tok.quote
2370
2374
  });
2375
+ annotate(ret);
2371
2376
  break;
2372
2377
  case "regexp":
2373
2378
  const [_, source, flags] = tok.value.match(/^\/(.*)\/(\w*)$/);
@@ -3254,6 +3259,10 @@ function parse($TEXT, options) {
3254
3259
  set_annotation(node, _NOINLINE);
3255
3260
  break;
3256
3261
  }
3262
+ if (/[@#]__KEY__/.test(comment.value)) {
3263
+ set_annotation(node, _KEY);
3264
+ break;
3265
+ }
3257
3266
  }
3258
3267
  }
3259
3268
  }
@@ -6363,6 +6372,7 @@ var AST_String = DEFNODE("String", "value quote", function AST_String(props) {
6363
6372
  this.quote = props.quote;
6364
6373
  this.start = props.start;
6365
6374
  this.end = props.end;
6375
+ this._annotations = props._annotations;
6366
6376
  }
6367
6377
 
6368
6378
  this.flags = 0;
@@ -6735,6 +6745,7 @@ class TreeTransformer extends TreeWalker {
6735
6745
  const _PURE = 0b00000001;
6736
6746
  const _INLINE = 0b00000010;
6737
6747
  const _NOINLINE = 0b00000100;
6748
+ const _KEY = 0b00001000;
6738
6749
 
6739
6750
  /***********************************************************************
6740
6751
 
@@ -14478,6 +14489,33 @@ const regexp_flags = new Set([
14478
14489
  def_eval(AST_PropAccess, function (compressor, depth) {
14479
14490
  let obj = this.expression._eval(compressor, depth + 1);
14480
14491
  if (obj === nullish || (this.optional && obj == null)) return nullish;
14492
+
14493
+ // `.length` of strings and arrays is always safe
14494
+ if (this.property === "length") {
14495
+ if (typeof obj === "string") {
14496
+ return obj.length;
14497
+ }
14498
+
14499
+ const is_spreadless_array = obj =>
14500
+ obj instanceof AST_Array
14501
+ && obj.elements.every(el => !(el instanceof AST_Expansion));
14502
+
14503
+ if (
14504
+ is_spreadless_array(obj)
14505
+ && obj.elements.every(el => !el.has_side_effects(compressor))
14506
+ ) {
14507
+ return obj.elements.length;
14508
+ }
14509
+
14510
+ let fixed;
14511
+ if (
14512
+ obj instanceof AST_SymbolRef
14513
+ && is_spreadless_array((fixed = obj.definition().fixed_value()))
14514
+ ) {
14515
+ return fixed.elements.length;
14516
+ }
14517
+ }
14518
+
14481
14519
  if (compressor.option("unsafe")) {
14482
14520
  var key = this.property;
14483
14521
  if (key instanceof AST_Node) {
@@ -14485,9 +14523,9 @@ def_eval(AST_PropAccess, function (compressor, depth) {
14485
14523
  if (key === this.property)
14486
14524
  return this;
14487
14525
  }
14526
+
14488
14527
  var exp = this.expression;
14489
14528
  if (is_undeclared_ref(exp)) {
14490
-
14491
14529
  var aa;
14492
14530
  var first_arg = exp.name === "hasOwnProperty"
14493
14531
  && key === "call"
@@ -20572,13 +20610,20 @@ def_optimize(AST_DefaultAssign, function(self, compressor) {
20572
20610
 
20573
20611
  // `[x = undefined] = foo` ---> `[x] = foo`
20574
20612
  // `(arg = undefined) => ...` ---> `(arg) => ...` (unless `keep_fargs`)
20575
- if (
20576
- evaluateRight === undefined &&
20577
- (compressor.parent() instanceof AST_Lambda
20578
- ? compressor.option("keep_fargs") === false
20579
- : true)
20580
- ) {
20581
- self = self.left;
20613
+ // `((arg = undefined) => ...)()` ---> `((arg) => ...)()`
20614
+ let lambda, iife;
20615
+ if (evaluateRight === undefined) {
20616
+ if (
20617
+ (lambda = compressor.parent()) instanceof AST_Lambda
20618
+ ? (
20619
+ compressor.option("keep_fargs") === false
20620
+ || (iife = compressor.parent(1)).TYPE === "Call"
20621
+ && iife.expression === lambda
20622
+ )
20623
+ : true
20624
+ ) {
20625
+ self = self.left;
20626
+ }
20582
20627
  } else if (evaluateRight !== self.right) {
20583
20628
  evaluateRight = make_node_from_constant(evaluateRight, self.right);
20584
20629
  self.right = best_of_expression(evaluateRight, self.right);
@@ -29731,6 +29776,8 @@ function mangle_properties(ast, options) {
29731
29776
  addStrings(node.args[1], add);
29732
29777
  } else if (node instanceof AST_Binary && node.operator === "in") {
29733
29778
  addStrings(node.left, add);
29779
+ } else if (node instanceof AST_String && has_annotation(node, _KEY)) {
29780
+ add(node.value);
29734
29781
  }
29735
29782
  }));
29736
29783
 
@@ -29762,6 +29809,10 @@ function mangle_properties(ast, options) {
29762
29809
  node.args[1] = mangleStrings(node.args[1]);
29763
29810
  } else if (node instanceof AST_Binary && node.operator === "in") {
29764
29811
  node.left = mangleStrings(node.left);
29812
+ } else if (node instanceof AST_String && has_annotation(node, _KEY)) {
29813
+ // Clear _KEY annotation to prevent double mangling
29814
+ clear_annotation(node, _KEY);
29815
+ node.value = mangle(node.value);
29765
29816
  }
29766
29817
  }));
29767
29818
 
@@ -29827,6 +29878,8 @@ function mangle_properties(ast, options) {
29827
29878
  var last = node.expressions.length - 1;
29828
29879
  node.expressions[last] = mangleStrings(node.expressions[last]);
29829
29880
  } else if (node instanceof AST_String) {
29881
+ // Clear _KEY annotation to prevent double mangling
29882
+ clear_annotation(node, _KEY);
29830
29883
  node.value = mangle(node.value);
29831
29884
  } else if (node instanceof AST_Conditional) {
29832
29885
  node.consequent = mangleStrings(node.consequent);
package/lib/ast.js CHANGED
@@ -2762,6 +2762,7 @@ var AST_String = DEFNODE("String", "value quote", function AST_String(props) {
2762
2762
  this.quote = props.quote;
2763
2763
  this.start = props.start;
2764
2764
  this.end = props.end;
2765
+ this._annotations = props._annotations;
2765
2766
  }
2766
2767
 
2767
2768
  this.flags = 0;
@@ -3134,6 +3135,7 @@ class TreeTransformer extends TreeWalker {
3134
3135
  const _PURE = 0b00000001;
3135
3136
  const _INLINE = 0b00000010;
3136
3137
  const _NOINLINE = 0b00000100;
3138
+ const _KEY = 0b00001000;
3137
3139
 
3138
3140
  export {
3139
3141
  AST_Accessor,
@@ -3278,4 +3280,5 @@ export {
3278
3280
  _INLINE,
3279
3281
  _NOINLINE,
3280
3282
  _PURE,
3283
+ _KEY,
3281
3284
  };
@@ -352,6 +352,33 @@ 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 = obj =>
363
+ obj instanceof AST_Array
364
+ && obj.elements.every(el => !(el instanceof AST_Expansion));
365
+
366
+ if (
367
+ is_spreadless_array(obj)
368
+ && obj.elements.every(el => !el.has_side_effects(compressor))
369
+ ) {
370
+ return obj.elements.length;
371
+ }
372
+
373
+ let fixed;
374
+ if (
375
+ obj instanceof AST_SymbolRef
376
+ && is_spreadless_array((fixed = obj.definition().fixed_value()))
377
+ ) {
378
+ return fixed.elements.length;
379
+ }
380
+ }
381
+
355
382
  if (compressor.option("unsafe")) {
356
383
  var key = this.property;
357
384
  if (key instanceof AST_Node) {
@@ -359,9 +386,9 @@ def_eval(AST_PropAccess, function (compressor, depth) {
359
386
  if (key === this.property)
360
387
  return this;
361
388
  }
389
+
362
390
  var exp = this.expression;
363
391
  if (is_undeclared_ref(exp)) {
364
-
365
392
  var aa;
366
393
  var first_arg = exp.name === "hasOwnProperty"
367
394
  && key === "call"
@@ -2782,13 +2782,20 @@ def_optimize(AST_DefaultAssign, function(self, compressor) {
2782
2782
 
2783
2783
  // `[x = undefined] = foo` ---> `[x] = foo`
2784
2784
  // `(arg = undefined) => ...` ---> `(arg) => ...` (unless `keep_fargs`)
2785
- if (
2786
- evaluateRight === undefined &&
2787
- (compressor.parent() instanceof AST_Lambda
2788
- ? compressor.option("keep_fargs") === false
2789
- : true)
2790
- ) {
2791
- self = self.left;
2785
+ // `((arg = undefined) => ...)()` ---> `((arg) => ...)()`
2786
+ let lambda, iife;
2787
+ if (evaluateRight === undefined) {
2788
+ if (
2789
+ (lambda = compressor.parent()) instanceof AST_Lambda
2790
+ ? (
2791
+ compressor.option("keep_fargs") === false
2792
+ || (iife = compressor.parent(1)).TYPE === "Call"
2793
+ && iife.expression === lambda
2794
+ )
2795
+ : true
2796
+ ) {
2797
+ self = self.left;
2798
+ }
2792
2799
  } else if (evaluateRight !== self.right) {
2793
2800
  evaluateRight = make_node_from_constant(evaluateRight, self.right);
2794
2801
  self.right = best_of_expression(evaluateRight, self.right);
package/lib/parse.js CHANGED
@@ -162,7 +162,8 @@ import {
162
162
  AST_Yield,
163
163
  _INLINE,
164
164
  _NOINLINE,
165
- _PURE
165
+ _PURE,
166
+ _KEY
166
167
  } from "./ast.js";
167
168
 
168
169
  var LATEST_RAW = ""; // Only used for numbers and template strings
@@ -2225,6 +2226,7 @@ function parse($TEXT, options) {
2225
2226
  value : tok.value,
2226
2227
  quote : tok.quote
2227
2228
  });
2229
+ annotate(ret);
2228
2230
  break;
2229
2231
  case "regexp":
2230
2232
  const [_, source, flags] = tok.value.match(/^\/(.*)\/(\w*)$/);
@@ -3111,6 +3113,10 @@ function parse($TEXT, options) {
3111
3113
  set_annotation(node, _NOINLINE);
3112
3114
  break;
3113
3115
  }
3116
+ if (/[@#]__KEY__/.test(comment.value)) {
3117
+ set_annotation(node, _KEY);
3118
+ break;
3119
+ }
3114
3120
  }
3115
3121
  }
3116
3122
  }
package/lib/propmangle.js CHANGED
@@ -47,6 +47,8 @@
47
47
  import {
48
48
  defaults,
49
49
  push_uniq,
50
+ has_annotation,
51
+ clear_annotation,
50
52
  } from "./utils/index.js";
51
53
  import { base54 } from "./scope.js";
52
54
  import {
@@ -67,6 +69,7 @@ import {
67
69
  AST_Sub,
68
70
  TreeTransformer,
69
71
  TreeWalker,
72
+ _KEY,
70
73
  } from "./ast.js";
71
74
  import { domprops } from "../tools/domprops.js";
72
75
 
@@ -263,6 +266,8 @@ function mangle_properties(ast, options) {
263
266
  addStrings(node.args[1], add);
264
267
  } else if (node instanceof AST_Binary && node.operator === "in") {
265
268
  addStrings(node.left, add);
269
+ } else if (node instanceof AST_String && has_annotation(node, _KEY)) {
270
+ add(node.value);
266
271
  }
267
272
  }));
268
273
 
@@ -296,6 +301,10 @@ function mangle_properties(ast, options) {
296
301
  node.args[1] = mangleStrings(node.args[1]);
297
302
  } else if (node instanceof AST_Binary && node.operator === "in") {
298
303
  node.left = mangleStrings(node.left);
304
+ } else if (node instanceof AST_String && has_annotation(node, _KEY)) {
305
+ // Clear _KEY annotation to prevent double mangling
306
+ clear_annotation(node, _KEY);
307
+ node.value = mangle(node.value);
299
308
  }
300
309
  }));
301
310
 
@@ -361,6 +370,8 @@ function mangle_properties(ast, options) {
361
370
  var last = node.expressions.length - 1;
362
371
  node.expressions[last] = mangleStrings(node.expressions[last]);
363
372
  } else if (node instanceof AST_String) {
373
+ // Clear _KEY annotation to prevent double mangling
374
+ clear_annotation(node, _KEY);
364
375
  node.value = mangle(node.value);
365
376
  } else if (node instanceof AST_Conditional) {
366
377
  node.consequent = mangleStrings(node.consequent);
@@ -262,6 +262,10 @@ function set_annotation(node, annotation) {
262
262
  node._annotations |= annotation;
263
263
  }
264
264
 
265
+ function clear_annotation(node, annotation) {
266
+ node._annotations &= ~annotation;
267
+ }
268
+
265
269
  export {
266
270
  characters,
267
271
  defaults,
@@ -286,5 +290,6 @@ export {
286
290
  sort_regexp_flags,
287
291
  string_template,
288
292
  has_annotation,
289
- set_annotation
293
+ set_annotation,
294
+ clear_annotation,
290
295
  };
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.16.9",
7
+ "version": "5.17.0",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },