terser 5.7.1 → 5.10.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/lib/propmangle.js CHANGED
@@ -59,6 +59,8 @@ import {
59
59
  AST_ObjectKeyVal,
60
60
  AST_ObjectProperty,
61
61
  AST_PrivateMethod,
62
+ AST_PrivateGetter,
63
+ AST_PrivateSetter,
62
64
  AST_Sequence,
63
65
  AST_String,
64
66
  AST_Sub,
@@ -140,33 +142,61 @@ function addStrings(node, add) {
140
142
  }));
141
143
  }
142
144
 
145
+ function mangle_private_properties(ast, options) {
146
+ var cprivate = -1;
147
+ var private_cache = new Map();
148
+ var nth_identifier = options.nth_identifier || base54;
149
+
150
+ ast = ast.transform(new TreeTransformer(function(node) {
151
+ if (
152
+ node instanceof AST_ClassPrivateProperty
153
+ || node instanceof AST_PrivateMethod
154
+ || node instanceof AST_PrivateGetter
155
+ || node instanceof AST_PrivateSetter
156
+ ) {
157
+ node.key.name = mangle_private(node.key.name);
158
+ } else if (node instanceof AST_DotHash) {
159
+ node.property = mangle_private(node.property);
160
+ }
161
+ }));
162
+ return ast;
163
+
164
+ function mangle_private(name) {
165
+ let mangled = private_cache.get(name);
166
+ if (!mangled) {
167
+ mangled = nth_identifier.get(++cprivate);
168
+ private_cache.set(name, mangled);
169
+ }
170
+
171
+ return mangled;
172
+ }
173
+ }
174
+
143
175
  function mangle_properties(ast, options) {
144
176
  options = defaults(options, {
145
177
  builtins: false,
146
178
  cache: null,
147
179
  debug: false,
148
180
  keep_quoted: false,
181
+ nth_identifier: base54,
149
182
  only_cache: false,
150
183
  regex: null,
151
184
  reserved: null,
152
185
  undeclared: false,
153
186
  }, true);
154
187
 
188
+ var nth_identifier = options.nth_identifier;
189
+
155
190
  var reserved_option = options.reserved;
156
191
  if (!Array.isArray(reserved_option)) reserved_option = [reserved_option];
157
192
  var reserved = new Set(reserved_option);
158
193
  if (!options.builtins) find_builtins(reserved);
159
194
 
160
195
  var cname = -1;
161
- var cprivate = -1;
162
196
 
163
197
  var cache;
164
- var private_cache = new Map();
165
198
  if (options.cache) {
166
199
  cache = options.cache.props;
167
- cache.forEach(function(mangled_name) {
168
- reserved.add(mangled_name);
169
- });
170
200
  } else {
171
201
  cache = new Map();
172
202
  }
@@ -184,27 +214,29 @@ function mangle_properties(ast, options) {
184
214
 
185
215
  var names_to_mangle = new Set();
186
216
  var unmangleable = new Set();
187
- var private_properties = new Set();
217
+ // Track each already-mangled name to prevent nth_identifier from generating
218
+ // the same name.
219
+ cache.forEach((mangled_name) => unmangleable.add(mangled_name));
188
220
 
189
- var keep_quoted_strict = options.keep_quoted === "strict";
221
+ var keep_quoted = !!options.keep_quoted;
190
222
 
191
223
  // step 1: find candidates to mangle
192
224
  ast.walk(new TreeWalker(function(node) {
193
225
  if (
194
226
  node instanceof AST_ClassPrivateProperty
195
227
  || node instanceof AST_PrivateMethod
228
+ || node instanceof AST_PrivateGetter
229
+ || node instanceof AST_PrivateSetter
230
+ || node instanceof AST_DotHash
196
231
  ) {
197
- private_properties.add(node.key.name);
198
- } else if (node instanceof AST_DotHash) {
199
- private_properties.add(node.property);
232
+ // handled by mangle_private_properties
200
233
  } else if (node instanceof AST_ObjectKeyVal) {
201
- if (typeof node.key == "string" &&
202
- (!keep_quoted_strict || !node.quote)) {
234
+ if (typeof node.key == "string" && (!keep_quoted || !node.quote)) {
203
235
  add(node.key);
204
236
  }
205
237
  } else if (node instanceof AST_ObjectProperty) {
206
238
  // setter or getter, since KeyVal is handled above
207
- if (!keep_quoted_strict || !node.key.end.quote) {
239
+ if (!keep_quoted || !node.quote) {
208
240
  add(node.key.name);
209
241
  }
210
242
  } else if (node instanceof AST_Dot) {
@@ -217,11 +249,11 @@ function mangle_properties(ast, options) {
217
249
  declared = !(root.thedef && root.thedef.undeclared);
218
250
  }
219
251
  if (declared &&
220
- (!keep_quoted_strict || !node.quote)) {
252
+ (!keep_quoted || !node.quote)) {
221
253
  add(node.property);
222
254
  }
223
255
  } else if (node instanceof AST_Sub) {
224
- if (!keep_quoted_strict) {
256
+ if (!keep_quoted) {
225
257
  addStrings(node.property, add);
226
258
  }
227
259
  } else if (node instanceof AST_Call
@@ -237,25 +269,25 @@ function mangle_properties(ast, options) {
237
269
  if (
238
270
  node instanceof AST_ClassPrivateProperty
239
271
  || node instanceof AST_PrivateMethod
272
+ || node instanceof AST_PrivateGetter
273
+ || node instanceof AST_PrivateSetter
274
+ || node instanceof AST_DotHash
240
275
  ) {
241
- node.key.name = mangle_private(node.key.name);
242
- } else if (node instanceof AST_DotHash) {
243
- node.property = mangle_private(node.property);
276
+ // handled by mangle_private_properties
244
277
  } else if (node instanceof AST_ObjectKeyVal) {
245
- if (typeof node.key == "string" &&
246
- (!keep_quoted_strict || !node.quote)) {
278
+ if (typeof node.key == "string" && (!keep_quoted || !node.quote)) {
247
279
  node.key = mangle(node.key);
248
280
  }
249
281
  } else if (node instanceof AST_ObjectProperty) {
250
282
  // setter, getter, method or class field
251
- if (!keep_quoted_strict || !node.key.end.quote) {
283
+ if (!keep_quoted || !node.quote) {
252
284
  node.key.name = mangle(node.key.name);
253
285
  }
254
286
  } else if (node instanceof AST_Dot) {
255
- if (!keep_quoted_strict || !node.quote) {
287
+ if (!keep_quoted || !node.quote) {
256
288
  node.property = mangle(node.property);
257
289
  }
258
- } else if (!options.keep_quoted && node instanceof AST_Sub) {
290
+ } else if (!keep_quoted && node instanceof AST_Sub) {
259
291
  node.property = mangleStrings(node.property);
260
292
  } else if (node instanceof AST_Call
261
293
  && node.expression.print_to_string() == "Object.defineProperty") {
@@ -312,7 +344,7 @@ function mangle_properties(ast, options) {
312
344
  // either debug mode is off, or it is on and we could not use the mangled name
313
345
  if (!mangled) {
314
346
  do {
315
- mangled = base54(++cname);
347
+ mangled = nth_identifier.get(++cname);
316
348
  } while (!can_mangle(mangled));
317
349
  }
318
350
 
@@ -321,16 +353,6 @@ function mangle_properties(ast, options) {
321
353
  return mangled;
322
354
  }
323
355
 
324
- function mangle_private(name) {
325
- let mangled = private_cache.get(name);
326
- if (!mangled) {
327
- mangled = base54(++cprivate);
328
- private_cache.set(name, mangled);
329
- }
330
-
331
- return mangled;
332
- }
333
-
334
356
  function mangleStrings(node) {
335
357
  return node.transform(new TreeTransformer(function(node) {
336
358
  if (node instanceof AST_Sequence) {
@@ -350,4 +372,5 @@ function mangle_properties(ast, options) {
350
372
  export {
351
373
  reserve_quoted_keys,
352
374
  mangle_properties,
375
+ mangle_private_properties,
353
376
  };
package/lib/scope.js CHANGED
@@ -107,7 +107,7 @@ import {
107
107
  walk
108
108
  } from "./ast.js";
109
109
  import {
110
- RESERVED_WORDS,
110
+ ALL_RESERVED_WORDS,
111
111
  js_error,
112
112
  } from "./parse.js";
113
113
 
@@ -667,9 +667,10 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
667
667
 
668
668
  function next_mangled(scope, options) {
669
669
  var ext = scope.enclosed;
670
+ var nth_identifier = options.nth_identifier;
670
671
  out: while (true) {
671
- var m = base54(++scope.cname);
672
- if (RESERVED_WORDS.has(m)) continue; // skip over "do"
672
+ var m = nth_identifier.get(++scope.cname);
673
+ if (ALL_RESERVED_WORDS.has(m)) continue; // skip over "do"
673
674
 
674
675
  // https://github.com/mishoo/UglifyJS2/issues/242 -- do not
675
676
  // shadow a name reserved from mangling.
@@ -744,6 +745,7 @@ AST_Symbol.DEFMETHOD("global", function() {
744
745
  AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
745
746
  options = defaults(options, {
746
747
  eval : false,
748
+ nth_identifier : base54,
747
749
  ie8 : false,
748
750
  keep_classnames: false,
749
751
  keep_fnames : false,
@@ -765,6 +767,7 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
765
767
 
766
768
  AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
767
769
  options = this._default_mangler_options(options);
770
+ var nth_identifier = options.nth_identifier;
768
771
 
769
772
  // We only need to mangle declaration nodes. Special logic wired
770
773
  // into the code generator will display the mangled name if it's
@@ -778,6 +781,8 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
778
781
  }
779
782
 
780
783
  const mangled_names = this.mangled_names = new Set();
784
+ unmangleable_names = new Set();
785
+
781
786
  if (options.cache) {
782
787
  this.globals.forEach(collect);
783
788
  if (options.cache.props) {
@@ -816,8 +821,8 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
816
821
  if (node instanceof AST_Label) {
817
822
  let name;
818
823
  do {
819
- name = base54(++lname);
820
- } while (RESERVED_WORDS.has(name));
824
+ name = nth_identifier.get(++lname);
825
+ } while (ALL_RESERVED_WORDS.has(name));
821
826
  node.mangled_name = name;
822
827
  return true;
823
828
  }
@@ -830,7 +835,6 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
830
835
  this.walk(tw);
831
836
 
832
837
  if (options.keep_fnames || options.keep_classnames) {
833
- unmangleable_names = new Set();
834
838
  // Collect a set of short names which are unmangleable,
835
839
  // for use in avoiding collisions in next_mangled.
836
840
  to_mangle.forEach(def => {
@@ -846,9 +850,9 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
846
850
  unmangleable_names = null;
847
851
 
848
852
  function collect(symbol) {
849
- const should_mangle = !options.reserved.has(symbol.name)
850
- && !(symbol.export & MASK_EXPORT_DONT_MANGLE);
851
- if (should_mangle) {
853
+ if (symbol.export & MASK_EXPORT_DONT_MANGLE) {
854
+ unmangleable_names.add(symbol.name);
855
+ } else if (!options.reserved.has(symbol.name)) {
852
856
  to_mangle.push(symbol);
853
857
  }
854
858
  }
@@ -878,9 +882,12 @@ AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
878
882
  });
879
883
 
880
884
  AST_Toplevel.DEFMETHOD("expand_names", function(options) {
881
- base54.reset();
882
- base54.sort();
883
885
  options = this._default_mangler_options(options);
886
+ var nth_identifier = options.nth_identifier;
887
+ if (nth_identifier.reset && nth_identifier.sort) {
888
+ nth_identifier.reset();
889
+ nth_identifier.sort();
890
+ }
884
891
  var avoid = this.find_colliding_names(options);
885
892
  var cname = 0;
886
893
  this.globals.forEach(rename);
@@ -892,8 +899,8 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
892
899
  function next_name() {
893
900
  var name;
894
901
  do {
895
- name = base54(cname++);
896
- } while (avoid.has(name) || RESERVED_WORDS.has(name));
902
+ name = nth_identifier.get(cname++);
903
+ } while (avoid.has(name) || ALL_RESERVED_WORDS.has(name));
897
904
  return name;
898
905
  }
899
906
 
@@ -919,30 +926,37 @@ AST_Sequence.DEFMETHOD("tail_node", function() {
919
926
 
920
927
  AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
921
928
  options = this._default_mangler_options(options);
929
+ var nth_identifier = options.nth_identifier;
930
+ if (!nth_identifier.reset || !nth_identifier.consider || !nth_identifier.sort) {
931
+ // If the identifier mangler is invariant, skip computing character frequency.
932
+ return;
933
+ }
934
+ nth_identifier.reset();
935
+
922
936
  try {
923
937
  AST_Node.prototype.print = function(stream, force_parens) {
924
938
  this._print(stream, force_parens);
925
939
  if (this instanceof AST_Symbol && !this.unmangleable(options)) {
926
- base54.consider(this.name, -1);
940
+ nth_identifier.consider(this.name, -1);
927
941
  } else if (options.properties) {
928
942
  if (this instanceof AST_DotHash) {
929
- base54.consider("#" + this.property, -1);
943
+ nth_identifier.consider("#" + this.property, -1);
930
944
  } else if (this instanceof AST_Dot) {
931
- base54.consider(this.property, -1);
945
+ nth_identifier.consider(this.property, -1);
932
946
  } else if (this instanceof AST_Sub) {
933
947
  skip_string(this.property);
934
948
  }
935
949
  }
936
950
  };
937
- base54.consider(this.print_to_string(), 1);
951
+ nth_identifier.consider(this.print_to_string(), 1);
938
952
  } finally {
939
953
  AST_Node.prototype.print = AST_Node.prototype._print;
940
954
  }
941
- base54.sort();
955
+ nth_identifier.sort();
942
956
 
943
957
  function skip_string(node) {
944
958
  if (node instanceof AST_String) {
945
- base54.consider(node.value, -1);
959
+ nth_identifier.consider(node.value, -1);
946
960
  } else if (node instanceof AST_Conditional) {
947
961
  skip_string(node.consequent);
948
962
  skip_string(node.alternative);
@@ -966,19 +980,20 @@ const base54 = (() => {
966
980
  frequency.set(ch, 0);
967
981
  });
968
982
  }
969
- base54.consider = function(str, delta) {
983
+ function consider(str, delta) {
970
984
  for (var i = str.length; --i >= 0;) {
971
985
  frequency.set(str[i], frequency.get(str[i]) + delta);
972
986
  }
973
- };
987
+ }
974
988
  function compare(a, b) {
975
989
  return frequency.get(b) - frequency.get(a);
976
990
  }
977
- base54.sort = function() {
991
+ function sort() {
978
992
  chars = mergeSort(leading, compare).concat(mergeSort(digits, compare));
979
- };
980
- base54.reset = reset;
993
+ }
994
+ // Ensure this is in a usable initial state.
981
995
  reset();
996
+ sort();
982
997
  function base54(num) {
983
998
  var ret = "", base = 54;
984
999
  num++;
@@ -990,7 +1005,13 @@ const base54 = (() => {
990
1005
  } while (num > 0);
991
1006
  return ret;
992
1007
  }
993
- return base54;
1008
+
1009
+ return {
1010
+ get: base54,
1011
+ consider,
1012
+ reset,
1013
+ sort
1014
+ };
994
1015
  })();
995
1016
 
996
1017
  export {
package/lib/transform.js CHANGED
@@ -57,7 +57,6 @@ import {
57
57
  AST_Definitions,
58
58
  AST_Destructuring,
59
59
  AST_Do,
60
- AST_Dot,
61
60
  AST_Exit,
62
61
  AST_Expansion,
63
62
  AST_Export,
@@ -74,6 +73,7 @@ import {
74
73
  AST_Object,
75
74
  AST_ObjectProperty,
76
75
  AST_PrefixedTemplateString,
76
+ AST_PropAccess,
77
77
  AST_Sequence,
78
78
  AST_SimpleStatement,
79
79
  AST_Sub,
@@ -228,7 +228,7 @@ def_transform(AST_Sequence, function(self, tw) {
228
228
  : [new AST_Number({ value: 0 })];
229
229
  });
230
230
 
231
- def_transform(AST_Dot, function(self, tw) {
231
+ def_transform(AST_PropAccess, function(self, tw) {
232
232
  self.expression = self.expression.transform(tw);
233
233
  });
234
234
 
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.7.1",
7
+ "version": "5.10.0",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },
@@ -45,21 +45,28 @@
45
45
  "dependencies": {
46
46
  "commander": "^2.20.0",
47
47
  "source-map": "~0.7.2",
48
- "source-map-support": "~0.5.19"
48
+ "source-map-support": "~0.5.20"
49
49
  },
50
50
  "devDependencies": {
51
- "@ls-lint/ls-lint": "^1.9.2",
52
- "acorn": "^8.0.5",
53
- "astring": "^1.6.2",
54
- "eslint": "^7.19.0",
55
- "eslump": "^2.0.0",
51
+ "@ls-lint/ls-lint": "^1.10.0",
52
+ "astring": "^1.7.5",
53
+ "eslint": "^7.32.0",
54
+ "eslump": "^3.0.0",
56
55
  "esm": "^3.2.25",
57
- "mocha": "^8.2.1",
56
+ "mocha": "^9.1.1",
58
57
  "pre-commit": "^1.2.2",
59
58
  "rimraf": "^3.0.2",
60
- "rollup": "2.38.4",
59
+ "rollup": "2.56.3",
61
60
  "semver": "^7.3.4"
62
61
  },
62
+ "peerDependencies": {
63
+ "acorn": "^8.5.0"
64
+ },
65
+ "peerDependenciesMeta": {
66
+ "acorn": {
67
+ "optional": true
68
+ }
69
+ },
63
70
  "scripts": {
64
71
  "test": "node test/compress.js && mocha test/mocha",
65
72
  "test:compress": "node test/compress.js",
@@ -93,7 +100,7 @@
93
100
  "eslintConfig": {
94
101
  "parserOptions": {
95
102
  "sourceType": "module",
96
- "ecmaVersion": "2020"
103
+ "ecmaVersion": 2020
97
104
  },
98
105
  "env": {
99
106
  "node": true,
package/tools/terser.d.ts CHANGED
@@ -80,16 +80,52 @@ export interface MangleOptions {
80
80
  keep_classnames?: boolean | RegExp;
81
81
  keep_fnames?: boolean | RegExp;
82
82
  module?: boolean;
83
+ nth_identifier?: SimpleIdentifierMangler | WeightedIdentifierMangler;
83
84
  properties?: boolean | ManglePropertiesOptions;
84
85
  reserved?: string[];
85
86
  safari10?: boolean;
86
87
  toplevel?: boolean;
87
88
  }
88
89
 
90
+ /**
91
+ * An identifier mangler for which the output is invariant with respect to the source code.
92
+ */
93
+ export interface SimpleIdentifierMangler {
94
+ /**
95
+ * Obtains the nth most favored (usually shortest) identifier to rename a variable to.
96
+ * The mangler will increment n and retry until the return value is not in use in scope, and is not a reserved word.
97
+ * This function is expected to be stable; Evaluating get(n) === get(n) should always return true.
98
+ * @param n The ordinal of the identifier.
99
+ */
100
+ get(n: number): string;
101
+ }
102
+
103
+ /**
104
+ * An identifier mangler that leverages character frequency analysis to determine identifier precedence.
105
+ */
106
+ export interface WeightedIdentifierMangler extends SimpleIdentifierMangler {
107
+ /**
108
+ * Modifies the internal weighting of the input characters by the specified delta.
109
+ * Will be invoked on the entire printed AST, and then deduct mangleable identifiers.
110
+ * @param chars The characters to modify the weighting of.
111
+ * @param delta The numeric weight to add to the characters.
112
+ */
113
+ consider(chars: string, delta: number): number;
114
+ /**
115
+ * Resets character weights.
116
+ */
117
+ reset(): void;
118
+ /**
119
+ * Sorts identifiers by character frequency, in preparation for calls to get(n).
120
+ */
121
+ sort(): void;
122
+ }
123
+
89
124
  export interface ManglePropertiesOptions {
90
125
  builtins?: boolean;
91
126
  debug?: boolean;
92
127
  keep_quoted?: boolean | 'strict';
128
+ nth_identifier?: SimpleIdentifierMangler | WeightedIdentifierMangler;
93
129
  regex?: RegExp | string;
94
130
  reserved?: string[];
95
131
  }
@@ -138,6 +174,7 @@ export enum OutputQuoteStyle {
138
174
  export interface MinifyOptions {
139
175
  compress?: boolean | CompressOptions;
140
176
  ecma?: ECMA;
177
+ enclose?: boolean | string;
141
178
  ie8?: boolean;
142
179
  keep_classnames?: boolean | RegExp;
143
180
  keep_fnames?: boolean | RegExp;