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/CHANGELOG.md CHANGED
@@ -1,13 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.0.0
4
+
5
+ - `in` operator now taken into account during property mangle.
6
+ - Fixed infinite loop in face of a reference loop in some situations.
7
+ - Kept exports and imports around even if there's something which will throw before them.
8
+ - The main exported bundle for commonjs, dist/bundle.min.js is no longer minified.
9
+
3
10
  ## v5.0.0-beta.0
4
11
 
5
- - `minify()` is now async and rejects a promise instead of returning an error.
6
- - Internal AST is no longer exposed, so that it can be improved without releasing breaking changes.
7
- - Lowest supported node version is 10
12
+ - BREAKING: `minify()` is now async and rejects a promise instead of returning an error.
13
+ - BREAKING: Internal AST is no longer exposed, so that it can be improved without releasing breaking changes.
14
+ - BREAKING: Lowest supported node version is 10
8
15
  - Module is now distributed as a dual package - You can `import` and `require()` too.
9
16
  - Inline improvements were made
10
17
 
18
+ ## v4.8.0
19
+
20
+ - Support for numeric separators (`million = 1_000_000`) was added.
21
+ - Assigning properties to a class is now assumed to be pure.
22
+ - Fixed bug where `yield` wasn't considered a valid property key in generators.
23
+
24
+ ## v4.7.0
25
+
26
+ - A bug was fixed where an arrow function would have the wrong size
27
+ - `arguments` object is now considered safe to retrieve properties from (useful for `length`, or `0`) even when `pure_getters` is not set.
28
+ - Fixed erroneous `const` declarations without value (which is invalid) in some corner cases when using `collapse_vars`.
29
+
11
30
  ## v4.6.13
12
31
 
13
32
  - Fixed issue where ES5 object properties were being turned into ES6 object properties due to more lax unicode rules.
package/README.md CHANGED
@@ -1251,6 +1251,7 @@ To allow for better optimizations, the compiler makes various assumptions:
1251
1251
  `Object.defineProperty()`, `Object.defineProperties()`, `Object.freeze()`,
1252
1252
  `Object.preventExtensions()` or `Object.seal()`).
1253
1253
  - `document.all` is not `== null`
1254
+ - Assigning properties to a class doesn't have side effects and does not throw.
1254
1255
 
1255
1256
  ### Build Tools and Adaptors using Terser
1256
1257
 
@@ -672,12 +672,14 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
672
672
  }
673
673
 
674
674
  function read_num(prefix) {
675
- var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".", is_big_int = false;
675
+ var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".", is_big_int = false, numeric_separator = false;
676
676
  var num = read_while(function(ch, i) {
677
677
  if (is_big_int) return false;
678
678
 
679
679
  var code = ch.charCodeAt(0);
680
680
  switch (code) {
681
+ case 95: // _
682
+ return (numeric_separator = true);
681
683
  case 98: case 66: // bB
682
684
  return (has_x = true); // Can occur in hex sequence, don't return false yet
683
685
  case 111: case 79: // oO
@@ -705,6 +707,14 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
705
707
  if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) {
706
708
  parse_error("Legacy octal literals are not allowed in strict mode");
707
709
  }
710
+ if (numeric_separator) {
711
+ if (num.endsWith("_")) {
712
+ parse_error("Numeric separators are not allowed at the end of numeric literals");
713
+ } else if (num.includes("__")) {
714
+ parse_error("Only one underscore is allowed as numeric separator");
715
+ }
716
+ num = num.replace(/_/g, "");
717
+ }
708
718
  if (num.endsWith("n")) {
709
719
  const without_n = num.slice(0, -1);
710
720
  const allow_e = RE_HEX_NUMBER.test(without_n);
@@ -2967,15 +2977,6 @@ function parse($TEXT, options) {
2967
2977
  }
2968
2978
  /* falls through */
2969
2979
  case "name":
2970
- if (tmp.value == "yield") {
2971
- if (is_in_generator()) {
2972
- token_error(tmp, "Yield cannot be used as identifier inside generators");
2973
- } else if (!is_token(peek(), "punc", ":")
2974
- && !is_token(peek(), "punc", "(")
2975
- && S.input.has_directive("use strict")) {
2976
- token_error(tmp, "Unexpected yield identifier inside strict mode");
2977
- }
2978
- }
2979
2980
  case "string":
2980
2981
  case "num":
2981
2982
  case "big_int":
@@ -9780,7 +9781,7 @@ AST_Arrow.prototype._size = function () {
9780
9781
  args_and_arrow += 2;
9781
9782
  }
9782
9783
 
9783
- return lambda_modifiers(this) + args_and_arrow + Array.isArray(this.body) ? list_overhead(this.body) : this.body._size();
9784
+ return lambda_modifiers(this) + args_and_arrow + (Array.isArray(this.body) ? list_overhead(this.body) : this.body._size());
9784
9785
  };
9785
9786
 
9786
9787
  AST_Destructuring.prototype._size = () => 2;
@@ -11546,8 +11547,7 @@ function tighten_body(statements, compressor) {
11546
11547
  extract_candidates(expr.condition);
11547
11548
  extract_candidates(expr.consequent);
11548
11549
  extract_candidates(expr.alternative);
11549
- } else if (expr instanceof AST_Definitions
11550
- && (compressor.option("unused") || !(expr instanceof AST_Const))) {
11550
+ } else if (expr instanceof AST_Definitions) {
11551
11551
  var len = expr.definitions.length;
11552
11552
  // limit number of trailing variable definitions for consideration
11553
11553
  var i = len - 200;
@@ -11715,7 +11715,9 @@ function tighten_body(statements, compressor) {
11715
11715
  if (node === expr || node.body === expr) {
11716
11716
  found = true;
11717
11717
  if (node instanceof AST_VarDef) {
11718
- node.value = null;
11718
+ node.value = node.name instanceof AST_SymbolConst
11719
+ ? make_node(AST_Undefined, node.value) // `const` always needs value.
11720
+ : null;
11719
11721
  return node;
11720
11722
  }
11721
11723
  return in_list ? MAP.skip : null;
@@ -12031,7 +12033,7 @@ function tighten_body(statements, compressor) {
12031
12033
  statements.length = n;
12032
12034
  CHANGED = n != len;
12033
12035
  if (has_quit) has_quit.forEach(function(stat) {
12034
- extract_declarations_from_unreachable_code(compressor, stat, statements);
12036
+ trim_unreachable_code(compressor, stat, statements);
12035
12037
  });
12036
12038
  }
12037
12039
 
@@ -12289,7 +12291,7 @@ function tighten_body(statements, compressor) {
12289
12291
  }
12290
12292
  }
12291
12293
 
12292
- function extract_declarations_from_unreachable_code(compressor, stat, target) {
12294
+ function trim_unreachable_code(compressor, stat, target) {
12293
12295
  walk(stat, node => {
12294
12296
  if (node instanceof AST_Var) {
12295
12297
  node.remove_initializers();
@@ -12310,6 +12312,10 @@ function extract_declarations_from_unreachable_code(compressor, stat, target) {
12310
12312
  }));
12311
12313
  return true;
12312
12314
  }
12315
+ if (node instanceof AST_Export || node instanceof AST_Import) {
12316
+ target.push(node);
12317
+ return true;
12318
+ }
12313
12319
  if (node instanceof AST_Scope) {
12314
12320
  return true;
12315
12321
  }
@@ -12361,6 +12367,10 @@ function is_undefined(node, compressor) {
12361
12367
  if (this.properties[i]._dot_throw(compressor)) return true;
12362
12368
  return false;
12363
12369
  });
12370
+ // Do not be as strict with classes as we are with objects.
12371
+ // Hopefully the community is not going to abuse static getters and setters.
12372
+ // https://github.com/terser/terser/issues/724#issuecomment-643655656
12373
+ def_may_throw_on_access(AST_Class, return_false);
12364
12374
  def_may_throw_on_access(AST_ObjectProperty, return_false);
12365
12375
  def_may_throw_on_access(AST_ObjectGetter, return_true);
12366
12376
  def_may_throw_on_access(AST_Expansion, function(compressor) {
@@ -12393,7 +12403,8 @@ function is_undefined(node, compressor) {
12393
12403
  return this.tail_node()._dot_throw(compressor);
12394
12404
  });
12395
12405
  def_may_throw_on_access(AST_SymbolRef, function(compressor) {
12396
- if (is_undefined(this, compressor)) return true;
12406
+ if (this.name === "arguments") return false;
12407
+ if (has_flag(this, UNDEFINED)) return true;
12397
12408
  if (!is_strict(compressor)) return false;
12398
12409
  if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
12399
12410
  if (this.is_immutable()) return false;
@@ -12795,13 +12806,33 @@ var static_fns = convert_to_predicate({
12795
12806
  return this;
12796
12807
  });
12797
12808
  var non_converting_binary = makePredicate("&& || ?? === !==");
12809
+ const identity_comparison = makePredicate("== != === !==");
12810
+ const has_identity = value =>
12811
+ typeof value === "object"
12812
+ || typeof value === "function"
12813
+ || typeof value === "symbol";
12814
+
12798
12815
  def_eval(AST_Binary, function(compressor, depth) {
12799
12816
  if (!non_converting_binary.has(this.operator)) depth++;
12817
+
12800
12818
  var left = this.left._eval(compressor, depth);
12801
12819
  if (left === this.left) return this;
12802
12820
  var right = this.right._eval(compressor, depth);
12803
12821
  if (right === this.right) return this;
12804
12822
  var result;
12823
+
12824
+ if (
12825
+ left != null
12826
+ && right != null
12827
+ && identity_comparison.has(this.operator)
12828
+ && has_identity(left)
12829
+ && has_identity(right)
12830
+ && typeof left === typeof right
12831
+ ) {
12832
+ // Do not compare by reference
12833
+ return this;
12834
+ }
12835
+
12805
12836
  switch (this.operator) {
12806
12837
  case "&&" : result = left && right; break;
12807
12838
  case "||" : result = left || right; break;
@@ -12842,20 +12873,24 @@ var static_fns = convert_to_predicate({
12842
12873
  var value = node._eval(compressor, depth);
12843
12874
  return value === node ? this : value;
12844
12875
  });
12876
+
12877
+ // Set of AST_SymbolRef which are currently being evaluated.
12878
+ // Avoids infinite recursion of ._eval()
12879
+ const reentrant_ref_eval = new Set();
12845
12880
  def_eval(AST_SymbolRef, function(compressor, depth) {
12881
+ if (reentrant_ref_eval.has(this)) return this;
12882
+
12846
12883
  var fixed = this.fixed_value();
12847
12884
  if (!fixed) return this;
12848
12885
  var value;
12849
12886
  if (HOP(fixed, "_eval")) {
12850
12887
  value = fixed._eval();
12851
12888
  } else {
12852
- this._eval = return_this;
12889
+ reentrant_ref_eval.add(this);
12853
12890
  value = fixed._eval(compressor, depth);
12854
- delete this._eval;
12891
+ reentrant_ref_eval.delete(this);
12892
+
12855
12893
  if (value === fixed) return this;
12856
- fixed._eval = function() {
12857
- return value;
12858
- };
12859
12894
  }
12860
12895
  if (value && typeof value == "object") {
12861
12896
  var escaped = this.definition().escaped;
@@ -12863,13 +12898,7 @@ var static_fns = convert_to_predicate({
12863
12898
  }
12864
12899
  return value;
12865
12900
  });
12866
- var global_objs = {
12867
- Array: Array,
12868
- Math: Math,
12869
- Number: Number,
12870
- Object: Object,
12871
- String: String,
12872
- };
12901
+ var global_objs = { Array, Math, Number, Object, String };
12873
12902
  var static_values = convert_to_predicate({
12874
12903
  Math: [
12875
12904
  "E",
@@ -14395,7 +14424,7 @@ function if_break_in_loop(self, compressor) {
14395
14424
  body: self.condition
14396
14425
  }));
14397
14426
  }
14398
- extract_declarations_from_unreachable_code(compressor, self.body, body);
14427
+ trim_unreachable_code(compressor, self.body, body);
14399
14428
  return make_node(AST_BlockStatement, self, {
14400
14429
  body: body
14401
14430
  });
@@ -14466,7 +14495,7 @@ def_optimize(AST_For, function(self, compressor) {
14466
14495
  if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
14467
14496
  if (!cond) {
14468
14497
  var body = [];
14469
- extract_declarations_from_unreachable_code(compressor, self.body, body);
14498
+ trim_unreachable_code(compressor, self.body, body);
14470
14499
  if (self.init instanceof AST_Statement) {
14471
14500
  body.push(self.init);
14472
14501
  } else if (self.init) {
@@ -14502,7 +14531,7 @@ def_optimize(AST_If, function(self, compressor) {
14502
14531
  if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
14503
14532
  if (!cond) {
14504
14533
  var body = [];
14505
- extract_declarations_from_unreachable_code(compressor, self.body, body);
14534
+ trim_unreachable_code(compressor, self.body, body);
14506
14535
  body.push(make_node(AST_SimpleStatement, self.condition, {
14507
14536
  body: self.condition
14508
14537
  }));
@@ -14515,7 +14544,7 @@ def_optimize(AST_If, function(self, compressor) {
14515
14544
  }));
14516
14545
  body.push(self.body);
14517
14546
  if (self.alternative) {
14518
- extract_declarations_from_unreachable_code(compressor, self.alternative, body);
14547
+ trim_unreachable_code(compressor, self.alternative, body);
14519
14548
  }
14520
14549
  return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
14521
14550
  }
@@ -14727,7 +14756,7 @@ def_optimize(AST_Switch, function(self, compressor) {
14727
14756
  if (prev && !aborts(prev)) {
14728
14757
  prev.body = prev.body.concat(branch.body);
14729
14758
  } else {
14730
- extract_declarations_from_unreachable_code(compressor, branch, decl);
14759
+ trim_unreachable_code(compressor, branch, decl);
14731
14760
  }
14732
14761
  }
14733
14762
  });
@@ -14738,7 +14767,7 @@ def_optimize(AST_Try, function(self, compressor) {
14738
14767
  if (compressor.option("dead_code") && self.body.every(is_empty)) {
14739
14768
  var body = [];
14740
14769
  if (self.bcatch) {
14741
- extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
14770
+ trim_unreachable_code(compressor, self.bcatch, body);
14742
14771
  }
14743
14772
  if (self.bfinally) body.push(...self.bfinally.body);
14744
14773
  return make_node(AST_BlockStatement, self, {
@@ -17267,7 +17296,7 @@ function lift_key(self, compressor) {
17267
17296
  if (!compressor.option("computed_props")) return self;
17268
17297
  // save a comparison in the typical case
17269
17298
  if (!(self.key instanceof AST_Constant)) return self;
17270
- // whitelist acceptable props as not all AST_Constants are true constants
17299
+ // allow certain acceptable props as not all AST_Constants are true constants
17271
17300
  if (self.key instanceof AST_String || self.key instanceof AST_Number) {
17272
17301
  if (self.key.value === "__proto__") return self;
17273
17302
  if (self.key.value == "constructor"
@@ -23301,6 +23330,8 @@ function mangle_properties(ast, options) {
23301
23330
  } else if (node instanceof AST_Call
23302
23331
  && node.expression.print_to_string() == "Object.defineProperty") {
23303
23332
  addStrings(node.args[1], add);
23333
+ } else if (node instanceof AST_Binary && node.operator === "in") {
23334
+ addStrings(node.left, add);
23304
23335
  }
23305
23336
  }));
23306
23337
 
@@ -23325,6 +23356,8 @@ function mangle_properties(ast, options) {
23325
23356
  } else if (node instanceof AST_Call
23326
23357
  && node.expression.print_to_string() == "Object.defineProperty") {
23327
23358
  node.args[1] = mangleStrings(node.args[1]);
23359
+ } else if (node instanceof AST_Binary && node.operator === "in") {
23360
+ node.left = mangleStrings(node.left);
23328
23361
  }
23329
23362
  }));
23330
23363
 
@@ -23704,6 +23737,7 @@ async function run_cli({ program, packageJson, fs, path }) {
23704
23737
  options[name] = program[name];
23705
23738
  }
23706
23739
  });
23740
+
23707
23741
  if ("ecma" in program) {
23708
23742
  if (program.ecma != (program.ecma | 0)) fatal("ERROR: ecma must be an integer");
23709
23743
  const ecma = program.ecma | 0;
@@ -23933,7 +23967,7 @@ async function run_cli({ program, packageJson, fs, path }) {
23933
23967
  }
23934
23968
  } else if (program.output) {
23935
23969
  fs.writeFileSync(program.output, result.code);
23936
- if (options.sourceMap.url !== "inline" && result.map) {
23970
+ if (options.sourceMap && options.sourceMap.url !== "inline" && result.map) {
23937
23971
  fs.writeFileSync(program.output + ".map", result.map);
23938
23972
  }
23939
23973
  } else {