terser 5.0.0-beta.2 → 5.0.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/cli.js CHANGED
@@ -84,6 +84,7 @@ export async function run_cli({ program, packageJson, fs, path }) {
84
84
  options[name] = program[name];
85
85
  }
86
86
  });
87
+
87
88
  if ("ecma" in program) {
88
89
  if (program.ecma != (program.ecma | 0)) fatal("ERROR: ecma must be an integer");
89
90
  const ecma = program.ecma | 0;
@@ -313,7 +314,7 @@ export async function run_cli({ program, packageJson, fs, path }) {
313
314
  }
314
315
  } else if (program.output) {
315
316
  fs.writeFileSync(program.output, result.code);
316
- if (options.sourceMap.url !== "inline" && result.map) {
317
+ if (options.sourceMap && options.sourceMap.url !== "inline" && result.map) {
317
318
  fs.writeFileSync(program.output + ".map", result.map);
318
319
  }
319
320
  } else {
@@ -1628,8 +1628,7 @@ function tighten_body(statements, compressor) {
1628
1628
  extract_candidates(expr.condition);
1629
1629
  extract_candidates(expr.consequent);
1630
1630
  extract_candidates(expr.alternative);
1631
- } else if (expr instanceof AST_Definitions
1632
- && (compressor.option("unused") || !(expr instanceof AST_Const))) {
1631
+ } else if (expr instanceof AST_Definitions) {
1633
1632
  var len = expr.definitions.length;
1634
1633
  // limit number of trailing variable definitions for consideration
1635
1634
  var i = len - 200;
@@ -1797,7 +1796,9 @@ function tighten_body(statements, compressor) {
1797
1796
  if (node === expr || node.body === expr) {
1798
1797
  found = true;
1799
1798
  if (node instanceof AST_VarDef) {
1800
- node.value = null;
1799
+ node.value = node.name instanceof AST_SymbolConst
1800
+ ? make_node(AST_Undefined, node.value) // `const` always needs value.
1801
+ : null;
1801
1802
  return node;
1802
1803
  }
1803
1804
  return in_list ? MAP.skip : null;
@@ -2113,7 +2114,7 @@ function tighten_body(statements, compressor) {
2113
2114
  statements.length = n;
2114
2115
  CHANGED = n != len;
2115
2116
  if (has_quit) has_quit.forEach(function(stat) {
2116
- extract_declarations_from_unreachable_code(compressor, stat, statements);
2117
+ trim_unreachable_code(compressor, stat, statements);
2117
2118
  });
2118
2119
  }
2119
2120
 
@@ -2371,7 +2372,7 @@ function tighten_body(statements, compressor) {
2371
2372
  }
2372
2373
  }
2373
2374
 
2374
- function extract_declarations_from_unreachable_code(compressor, stat, target) {
2375
+ function trim_unreachable_code(compressor, stat, target) {
2375
2376
  walk(stat, node => {
2376
2377
  if (node instanceof AST_Var) {
2377
2378
  node.remove_initializers();
@@ -2392,6 +2393,10 @@ function extract_declarations_from_unreachable_code(compressor, stat, target) {
2392
2393
  }));
2393
2394
  return true;
2394
2395
  }
2396
+ if (node instanceof AST_Export || node instanceof AST_Import) {
2397
+ target.push(node);
2398
+ return true;
2399
+ }
2395
2400
  if (node instanceof AST_Scope) {
2396
2401
  return true;
2397
2402
  }
@@ -2443,6 +2448,10 @@ function is_undefined(node, compressor) {
2443
2448
  if (this.properties[i]._dot_throw(compressor)) return true;
2444
2449
  return false;
2445
2450
  });
2451
+ // Do not be as strict with classes as we are with objects.
2452
+ // Hopefully the community is not going to abuse static getters and setters.
2453
+ // https://github.com/terser/terser/issues/724#issuecomment-643655656
2454
+ def_may_throw_on_access(AST_Class, return_false);
2446
2455
  def_may_throw_on_access(AST_ObjectProperty, return_false);
2447
2456
  def_may_throw_on_access(AST_ObjectGetter, return_true);
2448
2457
  def_may_throw_on_access(AST_Expansion, function(compressor) {
@@ -2475,7 +2484,8 @@ function is_undefined(node, compressor) {
2475
2484
  return this.tail_node()._dot_throw(compressor);
2476
2485
  });
2477
2486
  def_may_throw_on_access(AST_SymbolRef, function(compressor) {
2478
- if (is_undefined(this, compressor)) return true;
2487
+ if (this.name === "arguments") return false;
2488
+ if (has_flag(this, UNDEFINED)) return true;
2479
2489
  if (!is_strict(compressor)) return false;
2480
2490
  if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
2481
2491
  if (this.is_immutable()) return false;
@@ -2877,13 +2887,33 @@ var static_fns = convert_to_predicate({
2877
2887
  return this;
2878
2888
  });
2879
2889
  var non_converting_binary = makePredicate("&& || ?? === !==");
2890
+ const identity_comparison = makePredicate("== != === !==");
2891
+ const has_identity = value =>
2892
+ typeof value === "object"
2893
+ || typeof value === "function"
2894
+ || typeof value === "symbol";
2895
+
2880
2896
  def_eval(AST_Binary, function(compressor, depth) {
2881
2897
  if (!non_converting_binary.has(this.operator)) depth++;
2898
+
2882
2899
  var left = this.left._eval(compressor, depth);
2883
2900
  if (left === this.left) return this;
2884
2901
  var right = this.right._eval(compressor, depth);
2885
2902
  if (right === this.right) return this;
2886
2903
  var result;
2904
+
2905
+ if (
2906
+ left != null
2907
+ && right != null
2908
+ && identity_comparison.has(this.operator)
2909
+ && has_identity(left)
2910
+ && has_identity(right)
2911
+ && typeof left === typeof right
2912
+ ) {
2913
+ // Do not compare by reference
2914
+ return this;
2915
+ }
2916
+
2887
2917
  switch (this.operator) {
2888
2918
  case "&&" : result = left && right; break;
2889
2919
  case "||" : result = left || right; break;
@@ -2924,20 +2954,24 @@ var static_fns = convert_to_predicate({
2924
2954
  var value = node._eval(compressor, depth);
2925
2955
  return value === node ? this : value;
2926
2956
  });
2957
+
2958
+ // Set of AST_SymbolRef which are currently being evaluated.
2959
+ // Avoids infinite recursion of ._eval()
2960
+ const reentrant_ref_eval = new Set();
2927
2961
  def_eval(AST_SymbolRef, function(compressor, depth) {
2962
+ if (reentrant_ref_eval.has(this)) return this;
2963
+
2928
2964
  var fixed = this.fixed_value();
2929
2965
  if (!fixed) return this;
2930
2966
  var value;
2931
2967
  if (HOP(fixed, "_eval")) {
2932
2968
  value = fixed._eval();
2933
2969
  } else {
2934
- this._eval = return_this;
2970
+ reentrant_ref_eval.add(this);
2935
2971
  value = fixed._eval(compressor, depth);
2936
- delete this._eval;
2972
+ reentrant_ref_eval.delete(this);
2973
+
2937
2974
  if (value === fixed) return this;
2938
- fixed._eval = function() {
2939
- return value;
2940
- };
2941
2975
  }
2942
2976
  if (value && typeof value == "object") {
2943
2977
  var escaped = this.definition().escaped;
@@ -2945,13 +2979,7 @@ var static_fns = convert_to_predicate({
2945
2979
  }
2946
2980
  return value;
2947
2981
  });
2948
- var global_objs = {
2949
- Array: Array,
2950
- Math: Math,
2951
- Number: Number,
2952
- Object: Object,
2953
- String: String,
2954
- };
2982
+ var global_objs = { Array, Math, Number, Object, String };
2955
2983
  var static_values = convert_to_predicate({
2956
2984
  Math: [
2957
2985
  "E",
@@ -4477,7 +4505,7 @@ function if_break_in_loop(self, compressor) {
4477
4505
  body: self.condition
4478
4506
  }));
4479
4507
  }
4480
- extract_declarations_from_unreachable_code(compressor, self.body, body);
4508
+ trim_unreachable_code(compressor, self.body, body);
4481
4509
  return make_node(AST_BlockStatement, self, {
4482
4510
  body: body
4483
4511
  });
@@ -4548,7 +4576,7 @@ def_optimize(AST_For, function(self, compressor) {
4548
4576
  if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
4549
4577
  if (!cond) {
4550
4578
  var body = [];
4551
- extract_declarations_from_unreachable_code(compressor, self.body, body);
4579
+ trim_unreachable_code(compressor, self.body, body);
4552
4580
  if (self.init instanceof AST_Statement) {
4553
4581
  body.push(self.init);
4554
4582
  } else if (self.init) {
@@ -4584,7 +4612,7 @@ def_optimize(AST_If, function(self, compressor) {
4584
4612
  if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
4585
4613
  if (!cond) {
4586
4614
  var body = [];
4587
- extract_declarations_from_unreachable_code(compressor, self.body, body);
4615
+ trim_unreachable_code(compressor, self.body, body);
4588
4616
  body.push(make_node(AST_SimpleStatement, self.condition, {
4589
4617
  body: self.condition
4590
4618
  }));
@@ -4597,7 +4625,7 @@ def_optimize(AST_If, function(self, compressor) {
4597
4625
  }));
4598
4626
  body.push(self.body);
4599
4627
  if (self.alternative) {
4600
- extract_declarations_from_unreachable_code(compressor, self.alternative, body);
4628
+ trim_unreachable_code(compressor, self.alternative, body);
4601
4629
  }
4602
4630
  return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
4603
4631
  }
@@ -4809,7 +4837,7 @@ def_optimize(AST_Switch, function(self, compressor) {
4809
4837
  if (prev && !aborts(prev)) {
4810
4838
  prev.body = prev.body.concat(branch.body);
4811
4839
  } else {
4812
- extract_declarations_from_unreachable_code(compressor, branch, decl);
4840
+ trim_unreachable_code(compressor, branch, decl);
4813
4841
  }
4814
4842
  }
4815
4843
  });
@@ -4820,7 +4848,7 @@ def_optimize(AST_Try, function(self, compressor) {
4820
4848
  if (compressor.option("dead_code") && self.body.every(is_empty)) {
4821
4849
  var body = [];
4822
4850
  if (self.bcatch) {
4823
- extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
4851
+ trim_unreachable_code(compressor, self.bcatch, body);
4824
4852
  }
4825
4853
  if (self.bfinally) body.push(...self.bfinally.body);
4826
4854
  return make_node(AST_BlockStatement, self, {
@@ -7349,7 +7377,7 @@ function lift_key(self, compressor) {
7349
7377
  if (!compressor.option("computed_props")) return self;
7350
7378
  // save a comparison in the typical case
7351
7379
  if (!(self.key instanceof AST_Constant)) return self;
7352
- // whitelist acceptable props as not all AST_Constants are true constants
7380
+ // allow certain acceptable props as not all AST_Constants are true constants
7353
7381
  if (self.key instanceof AST_String || self.key instanceof AST_Number) {
7354
7382
  if (self.key.value === "__proto__") return self;
7355
7383
  if (self.key.value == "constructor"
package/lib/parse.js CHANGED
@@ -503,12 +503,14 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
503
503
  }
504
504
 
505
505
  function read_num(prefix) {
506
- var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".", is_big_int = false;
506
+ var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".", is_big_int = false, numeric_separator = false;
507
507
  var num = read_while(function(ch, i) {
508
508
  if (is_big_int) return false;
509
509
 
510
510
  var code = ch.charCodeAt(0);
511
511
  switch (code) {
512
+ case 95: // _
513
+ return (numeric_separator = true);
512
514
  case 98: case 66: // bB
513
515
  return (has_x = true); // Can occur in hex sequence, don't return false yet
514
516
  case 111: case 79: // oO
@@ -536,6 +538,14 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
536
538
  if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) {
537
539
  parse_error("Legacy octal literals are not allowed in strict mode");
538
540
  }
541
+ if (numeric_separator) {
542
+ if (num.endsWith("_")) {
543
+ parse_error("Numeric separators are not allowed at the end of numeric literals");
544
+ } else if (num.includes("__")) {
545
+ parse_error("Only one underscore is allowed as numeric separator");
546
+ }
547
+ num = num.replace(/_/g, "");
548
+ }
539
549
  if (num.endsWith("n")) {
540
550
  const without_n = num.slice(0, -1);
541
551
  const allow_e = RE_HEX_NUMBER.test(without_n);
@@ -2798,15 +2808,6 @@ function parse($TEXT, options) {
2798
2808
  }
2799
2809
  /* falls through */
2800
2810
  case "name":
2801
- if (tmp.value == "yield") {
2802
- if (is_in_generator()) {
2803
- token_error(tmp, "Yield cannot be used as identifier inside generators");
2804
- } else if (!is_token(peek(), "punc", ":")
2805
- && !is_token(peek(), "punc", "(")
2806
- && S.input.has_directive("use strict")) {
2807
- token_error(tmp, "Unexpected yield identifier inside strict mode");
2808
- }
2809
- }
2810
2811
  case "string":
2811
2812
  case "num":
2812
2813
  case "big_int":
package/lib/propmangle.js CHANGED
@@ -50,6 +50,7 @@ import {
50
50
  } from "./utils/index.js";
51
51
  import { base54 } from "./scope.js";
52
52
  import {
53
+ AST_Binary,
53
54
  AST_Call,
54
55
  AST_Conditional,
55
56
  AST_Dot,
@@ -212,6 +213,8 @@ function mangle_properties(ast, options) {
212
213
  } else if (node instanceof AST_Call
213
214
  && node.expression.print_to_string() == "Object.defineProperty") {
214
215
  addStrings(node.args[1], add);
216
+ } else if (node instanceof AST_Binary && node.operator === "in") {
217
+ addStrings(node.left, add);
215
218
  }
216
219
  }));
217
220
 
@@ -236,6 +239,8 @@ function mangle_properties(ast, options) {
236
239
  } else if (node instanceof AST_Call
237
240
  && node.expression.print_to_string() == "Object.defineProperty") {
238
241
  node.args[1] = mangleStrings(node.args[1]);
242
+ } else if (node instanceof AST_Binary && node.operator === "in") {
243
+ node.left = mangleStrings(node.left);
239
244
  }
240
245
  }));
241
246
 
package/lib/size.js CHANGED
@@ -159,7 +159,7 @@ AST_Arrow.prototype._size = function () {
159
159
  args_and_arrow += 2;
160
160
  }
161
161
 
162
- return lambda_modifiers(this) + args_and_arrow + Array.isArray(this.body) ? list_overhead(this.body) : this.body._size();
162
+ return lambda_modifiers(this) + args_and_arrow + (Array.isArray(this.body) ? list_overhead(this.body) : this.body._size());
163
163
  };
164
164
 
165
165
  AST_Destructuring.prototype._size = () => 2;
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.0.0-beta.2",
7
+ "version": "5.0.0",
8
8
  "engines": {
9
9
  "node": ">=6.0.0"
10
10
  },
@@ -47,6 +47,7 @@
47
47
  "source-map-support": "~0.5.12"
48
48
  },
49
49
  "devDependencies": {
50
+ "@ls-lint/ls-lint": "^1.9.2",
50
51
  "acorn": "^7.1.1",
51
52
  "astring": "^1.4.1",
52
53
  "eslint": "^7.0.0",
@@ -64,6 +65,7 @@
64
65
  "test:mocha": "mocha test/mocha",
65
66
  "lint": "eslint lib",
66
67
  "lint-fix": "eslint --fix lib",
68
+ "ls-lint": "ls-lint",
67
69
  "build": "rimraf dist/bundle* && rollup --config --silent",
68
70
  "prepare": "npm run build",
69
71
  "postversion": "echo 'Remember to update the changelog!'"
@@ -140,6 +142,7 @@
140
142
  },
141
143
  "pre-commit": [
142
144
  "lint-fix",
145
+ "ls-lint",
143
146
  "test"
144
147
  ]
145
148
  }