terser 5.16.8 → 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,11 +1,21 @@
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
+
8
+ ## v5.16.9
9
+ - Fix parentheses in output of optional chains (`a?.b`) (#1374)
10
+ - More documentation on source maps (#1368)
11
+ - New `lhs_constants` option, allowing to stop Terser from swapping comparison operands (#1361)
12
+
3
13
  ## v5.16.8
4
14
 
5
15
  - Become even less conservative around function definitions for `reduce_vars`
6
16
  - Fix parsing context of `import.meta` expressions such that method calls are allowed
7
17
 
8
- ## v5.16.7
18
+ ## v5.16.6
9
19
 
10
20
  - Become less conservative with analyzing function definitions for `reduce_vars`
11
21
  - Parse `import.meta` as a real AST node and not an `object.property`
package/README.md CHANGED
@@ -708,7 +708,8 @@ If you happen to need the source map as a raw object, set `sourceMap.asObject` t
708
708
 
709
709
  - `comparisons` (default: `true`) -- apply certain optimizations to binary nodes,
710
710
  e.g. `!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary
711
- nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
711
+ nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc. Note: `comparisons`
712
+ works best with `lhs_constants` enabled.
712
713
 
713
714
  - `computed_props` (default: `true`) -- Transforms constant computed properties
714
715
  into regular ones: `{["computed"]: 1}` is converted to `{computed: 1}`.
@@ -775,6 +776,9 @@ If you happen to need the source map as a raw object, set `sourceMap.asObject` t
775
776
  - `keep_infinity` (default: `false`) -- Pass `true` to prevent `Infinity` from
776
777
  being compressed into `1/0`, which may cause performance issues on Chrome.
777
778
 
779
+ - `lhs_constants` (default: `true`) -- Moves constant values to the left-hand side
780
+ of binary nodes. `foo == 42 → 42 == foo`
781
+
778
782
  - `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops
779
783
  when we can statically determine the condition.
780
784
 
@@ -1184,6 +1188,7 @@ Annotations in Terser are a way to tell it to treat a certain function call diff
1184
1188
  * `/*@__INLINE__*/` - forces a function to be inlined somewhere.
1185
1189
  * `/*@__NOINLINE__*/` - Makes sure the called function is not inlined into the call site.
1186
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.
1187
1192
 
1188
1193
  You can use either a `@` sign at the start, or a `#`.
1189
1194
 
@@ -1197,6 +1202,9 @@ function_always_inlined_here()
1197
1202
  function_cant_be_inlined_into_here()
1198
1203
 
1199
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");
1200
1208
  ```
1201
1209
 
1202
1210
  ### ESTree / SpiderMonkey AST
@@ -1267,6 +1275,12 @@ expected as code is optimized and mappings are often simply not possible as
1267
1275
  some code no longer exists. For highest fidelity in source map debugging
1268
1276
  disable the `compress` option and just use `mangle`.
1269
1277
 
1278
+ When debugging, make sure you enable the **"map scopes"** feature to map mangled variable names back to their original names.
1279
+ Without this, all variable values will be `undefined`. See https://github.com/terser/terser/issues/1367 for more details.
1280
+ <br/><br/>
1281
+
1282
+ ![image](https://user-images.githubusercontent.com/27283110/230441652-ac5cf6b0-5dc5-4ffc-9d8b-bd02875484f4.png)
1283
+
1270
1284
  ### Compiler assumptions
1271
1285
 
1272
1286
  To allow for better optimizations, the compiler makes various assumptions:
@@ -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
 
@@ -9742,6 +9753,12 @@ function OutputStream(options) {
9742
9753
  return true;
9743
9754
  });
9744
9755
 
9756
+ PARENS(AST_Chain, function(output) {
9757
+ var p = output.parent();
9758
+ if (!(p instanceof AST_Call || p instanceof AST_PropAccess)) return false;
9759
+ return p.expression === this;
9760
+ });
9761
+
9745
9762
  PARENS(AST_PropAccess, function(output) {
9746
9763
  var p = output.parent();
9747
9764
  if (p instanceof AST_New && p.expression === this) {
@@ -12939,7 +12956,8 @@ function maintain_this_binding(parent, orig, val) {
12939
12956
  parent instanceof AST_UnaryPrefix && parent.operator == "delete"
12940
12957
  || parent instanceof AST_Call && parent.expression === orig
12941
12958
  && (
12942
- val instanceof AST_PropAccess
12959
+ val instanceof AST_Chain
12960
+ || val instanceof AST_PropAccess
12943
12961
  || val instanceof AST_SymbolRef && val.name == "eval"
12944
12962
  )
12945
12963
  ) {
@@ -14471,6 +14489,33 @@ const regexp_flags = new Set([
14471
14489
  def_eval(AST_PropAccess, function (compressor, depth) {
14472
14490
  let obj = this.expression._eval(compressor, depth + 1);
14473
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
+
14474
14519
  if (compressor.option("unsafe")) {
14475
14520
  var key = this.property;
14476
14521
  if (key instanceof AST_Node) {
@@ -14478,9 +14523,9 @@ def_eval(AST_PropAccess, function (compressor, depth) {
14478
14523
  if (key === this.property)
14479
14524
  return this;
14480
14525
  }
14526
+
14481
14527
  var exp = this.expression;
14482
14528
  if (is_undeclared_ref(exp)) {
14483
-
14484
14529
  var aa;
14485
14530
  var first_arg = exp.name === "hasOwnProperty"
14486
14531
  && key === "call"
@@ -18028,6 +18073,7 @@ class Compressor extends TreeWalker {
18028
18073
  keep_fargs : true,
18029
18074
  keep_fnames : false,
18030
18075
  keep_infinity : false,
18076
+ lhs_constants : !false_by_default,
18031
18077
  loops : !false_by_default,
18032
18078
  module : false,
18033
18079
  negate_iife : !false_by_default,
@@ -19914,7 +19960,7 @@ def_optimize(AST_Binary, function(self, compressor) {
19914
19960
  self.right = tmp;
19915
19961
  }
19916
19962
  }
19917
- if (commutativeOperators.has(self.operator)) {
19963
+ if (compressor.option("lhs_constants") && commutativeOperators.has(self.operator)) {
19918
19964
  if (self.right.is_constant()
19919
19965
  && !self.left.is_constant()) {
19920
19966
  // if right is a constant, whatever side effects the
@@ -19944,6 +19990,9 @@ def_optimize(AST_Binary, function(self, compressor) {
19944
19990
  // void 0 == x => null == x
19945
19991
  if (!is_strict_comparison && is_undefined(self.left, compressor)) {
19946
19992
  self.left = make_node(AST_Null, self.left);
19993
+ // x == void 0 => x == null
19994
+ } else if (!is_strict_comparison && is_undefined(self.right, compressor)) {
19995
+ self.right = make_node(AST_Null, self.right);
19947
19996
  } else if (compressor.option("typeofs")
19948
19997
  // "undefined" == typeof x => undefined === x
19949
19998
  && self.left instanceof AST_String
@@ -19957,6 +20006,19 @@ def_optimize(AST_Binary, function(self, compressor) {
19957
20006
  self.left = make_node(AST_Undefined, self.left).optimize(compressor);
19958
20007
  if (self.operator.length == 2) self.operator += "=";
19959
20008
  }
20009
+ } else if (compressor.option("typeofs")
20010
+ // typeof x === "undefined" => x === undefined
20011
+ && self.left instanceof AST_UnaryPrefix
20012
+ && self.left.operator == "typeof"
20013
+ && self.right instanceof AST_String
20014
+ && self.right.value == "undefined") {
20015
+ var expr = self.left.expression;
20016
+ if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
20017
+ : !(expr instanceof AST_PropAccess && compressor.option("ie8"))) {
20018
+ self.left = expr;
20019
+ self.right = make_node(AST_Undefined, self.right).optimize(compressor);
20020
+ if (self.operator.length == 2) self.operator += "=";
20021
+ }
19960
20022
  } else if (self.left instanceof AST_SymbolRef
19961
20023
  // obj !== obj => false
19962
20024
  && self.right instanceof AST_SymbolRef
@@ -20548,13 +20610,20 @@ def_optimize(AST_DefaultAssign, function(self, compressor) {
20548
20610
 
20549
20611
  // `[x = undefined] = foo` ---> `[x] = foo`
20550
20612
  // `(arg = undefined) => ...` ---> `(arg) => ...` (unless `keep_fargs`)
20551
- if (
20552
- evaluateRight === undefined &&
20553
- (compressor.parent() instanceof AST_Lambda
20554
- ? compressor.option("keep_fargs") === false
20555
- : true)
20556
- ) {
20557
- 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
+ }
20558
20627
  } else if (evaluateRight !== self.right) {
20559
20628
  evaluateRight = make_node_from_constant(evaluateRight, self.right);
20560
20629
  self.right = best_of_expression(evaluateRight, self.right);
@@ -29707,6 +29776,8 @@ function mangle_properties(ast, options) {
29707
29776
  addStrings(node.args[1], add);
29708
29777
  } else if (node instanceof AST_Binary && node.operator === "in") {
29709
29778
  addStrings(node.left, add);
29779
+ } else if (node instanceof AST_String && has_annotation(node, _KEY)) {
29780
+ add(node.value);
29710
29781
  }
29711
29782
  }));
29712
29783
 
@@ -29738,6 +29809,10 @@ function mangle_properties(ast, options) {
29738
29809
  node.args[1] = mangleStrings(node.args[1]);
29739
29810
  } else if (node instanceof AST_Binary && node.operator === "in") {
29740
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);
29741
29816
  }
29742
29817
  }));
29743
29818
 
@@ -29803,6 +29878,8 @@ function mangle_properties(ast, options) {
29803
29878
  var last = node.expressions.length - 1;
29804
29879
  node.expressions[last] = mangleStrings(node.expressions[last]);
29805
29880
  } else if (node instanceof AST_String) {
29881
+ // Clear _KEY annotation to prevent double mangling
29882
+ clear_annotation(node, _KEY);
29806
29883
  node.value = mangle(node.value);
29807
29884
  } else if (node instanceof AST_Conditional) {
29808
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
  };
@@ -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
  ) {
@@ -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"
@@ -245,6 +245,7 @@ class Compressor extends TreeWalker {
245
245
  keep_fargs : true,
246
246
  keep_fnames : false,
247
247
  keep_infinity : false,
248
+ lhs_constants : !false_by_default,
248
249
  loops : !false_by_default,
249
250
  module : false,
250
251
  negate_iife : !false_by_default,
@@ -2131,7 +2132,7 @@ def_optimize(AST_Binary, function(self, compressor) {
2131
2132
  self.right = tmp;
2132
2133
  }
2133
2134
  }
2134
- if (commutativeOperators.has(self.operator)) {
2135
+ if (compressor.option("lhs_constants") && commutativeOperators.has(self.operator)) {
2135
2136
  if (self.right.is_constant()
2136
2137
  && !self.left.is_constant()) {
2137
2138
  // if right is a constant, whatever side effects the
@@ -2161,6 +2162,9 @@ def_optimize(AST_Binary, function(self, compressor) {
2161
2162
  // void 0 == x => null == x
2162
2163
  if (!is_strict_comparison && is_undefined(self.left, compressor)) {
2163
2164
  self.left = make_node(AST_Null, self.left);
2165
+ // x == void 0 => x == null
2166
+ } else if (!is_strict_comparison && is_undefined(self.right, compressor)) {
2167
+ self.right = make_node(AST_Null, self.right);
2164
2168
  } else if (compressor.option("typeofs")
2165
2169
  // "undefined" == typeof x => undefined === x
2166
2170
  && self.left instanceof AST_String
@@ -2174,6 +2178,19 @@ def_optimize(AST_Binary, function(self, compressor) {
2174
2178
  self.left = make_node(AST_Undefined, self.left).optimize(compressor);
2175
2179
  if (self.operator.length == 2) self.operator += "=";
2176
2180
  }
2181
+ } else if (compressor.option("typeofs")
2182
+ // typeof x === "undefined" => x === undefined
2183
+ && self.left instanceof AST_UnaryPrefix
2184
+ && self.left.operator == "typeof"
2185
+ && self.right instanceof AST_String
2186
+ && self.right.value == "undefined") {
2187
+ var expr = self.left.expression;
2188
+ if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
2189
+ : !(expr instanceof AST_PropAccess && compressor.option("ie8"))) {
2190
+ self.left = expr;
2191
+ self.right = make_node(AST_Undefined, self.right).optimize(compressor);
2192
+ if (self.operator.length == 2) self.operator += "=";
2193
+ }
2177
2194
  } else if (self.left instanceof AST_SymbolRef
2178
2195
  // obj !== obj => false
2179
2196
  && self.right instanceof AST_SymbolRef
@@ -2765,13 +2782,20 @@ def_optimize(AST_DefaultAssign, function(self, compressor) {
2765
2782
 
2766
2783
  // `[x = undefined] = foo` ---> `[x] = foo`
2767
2784
  // `(arg = undefined) => ...` ---> `(arg) => ...` (unless `keep_fargs`)
2768
- if (
2769
- evaluateRight === undefined &&
2770
- (compressor.parent() instanceof AST_Lambda
2771
- ? compressor.option("keep_fargs") === false
2772
- : true)
2773
- ) {
2774
- 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
+ }
2775
2799
  } else if (evaluateRight !== self.right) {
2776
2800
  evaluateRight = make_node_from_constant(evaluateRight, self.right);
2777
2801
  self.right = best_of_expression(evaluateRight, self.right);
package/lib/output.js CHANGED
@@ -1080,6 +1080,12 @@ function OutputStream(options) {
1080
1080
  return true;
1081
1081
  });
1082
1082
 
1083
+ PARENS(AST_Chain, function(output) {
1084
+ var p = output.parent();
1085
+ if (!(p instanceof AST_Call || p instanceof AST_PropAccess)) return false;
1086
+ return p.expression === this;
1087
+ });
1088
+
1083
1089
  PARENS(AST_PropAccess, function(output) {
1084
1090
  var p = output.parent();
1085
1091
  if (p instanceof AST_New && p.expression === this) {
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.8",
7
+ "version": "5.17.0",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },
package/bin/terser.mjs DELETED
@@ -1,21 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- "use strict";
4
-
5
- import "../tools/exit.cjs";
6
-
7
- import fs from "fs";
8
- import path from "path";
9
- import program from "commander";
10
-
11
- import { run_cli } from "../lib/cli.js";
12
-
13
- const packageJson = {
14
- name: "terser",
15
- version: "experimental module CLI"
16
- };
17
-
18
- run_cli({ program, packageJson, fs, path }).catch((error) => {
19
- console.error(error);
20
- process.exitCode = 1;
21
- });