terser 5.0.0-beta.0 → 5.1.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,38 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.1.0
4
+
5
+ - `import.meta` is now supported
6
+ - Typescript typings have been improved
7
+
8
+ ## v5.0.0
9
+
10
+ - `in` operator now taken into account during property mangle.
11
+ - Fixed infinite loop in face of a reference loop in some situations.
12
+ - Kept exports and imports around even if there's something which will throw before them.
13
+ - The main exported bundle for commonjs, dist/bundle.min.js is no longer minified.
14
+
15
+ ## v5.0.0-beta.0
16
+
17
+ - BREAKING: `minify()` is now async and rejects a promise instead of returning an error.
18
+ - BREAKING: Internal AST is no longer exposed, so that it can be improved without releasing breaking changes.
19
+ - BREAKING: Lowest supported node version is 10
20
+ - BREAKING: There are no more warnings being emitted
21
+ - Module is now distributed as a dual package - You can `import` and `require()` too.
22
+ - Inline improvements were made
23
+
24
+ ## v4.8.0
25
+
26
+ - Support for numeric separators (`million = 1_000_000`) was added.
27
+ - Assigning properties to a class is now assumed to be pure.
28
+ - Fixed bug where `yield` wasn't considered a valid property key in generators.
29
+
30
+ ## v4.7.0
31
+
32
+ - A bug was fixed where an arrow function would have the wrong size
33
+ - `arguments` object is now considered safe to retrieve properties from (useful for `length`, or `0`) even when `pure_getters` is not set.
34
+ - Fixed erroneous `const` declarations without value (which is invalid) in some corner cases when using `collapse_vars`.
35
+
3
36
  ## v4.6.13
4
37
 
5
38
  - 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);
@@ -1328,7 +1338,7 @@ function parse($TEXT, options) {
1328
1338
  }
1329
1339
  return function_(AST_Defun, false, true, is_export_default);
1330
1340
  }
1331
- if (S.token.value == "import" && !is_token(peek(), "punc", "(")) {
1341
+ if (S.token.value == "import" && !is_token(peek(), "punc", "(") && !is_token(peek(), "punc", ".")) {
1332
1342
  next();
1333
1343
  var node = import_();
1334
1344
  semicolon();
@@ -2376,6 +2386,9 @@ function parse($TEXT, options) {
2376
2386
  if (is("operator", "new")) {
2377
2387
  return new_(allow_calls);
2378
2388
  }
2389
+ if (is("operator", "import")) {
2390
+ return import_meta();
2391
+ }
2379
2392
  var start = S.token;
2380
2393
  var peeked;
2381
2394
  var async = is("name", "async")
@@ -2753,6 +2766,7 @@ function parse($TEXT, options) {
2753
2766
 
2754
2767
  function import_() {
2755
2768
  var start = prev();
2769
+
2756
2770
  var imported_name;
2757
2771
  var imported_names;
2758
2772
  if (is("name")) {
@@ -2787,6 +2801,17 @@ function parse($TEXT, options) {
2787
2801
  });
2788
2802
  }
2789
2803
 
2804
+ function import_meta() {
2805
+ var start = S.token;
2806
+ expect_token("operator", "import");
2807
+ expect_token("punc", ".");
2808
+ expect_token("name", "meta");
2809
+ return subscripts(new AST_ImportMeta({
2810
+ start: start,
2811
+ end: prev()
2812
+ }), false);
2813
+ }
2814
+
2790
2815
  function map_name(is_import) {
2791
2816
  function make_symbol(type) {
2792
2817
  return new type({
@@ -2967,15 +2992,6 @@ function parse($TEXT, options) {
2967
2992
  }
2968
2993
  /* falls through */
2969
2994
  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
2995
  case "string":
2980
2996
  case "num":
2981
2997
  case "big_int":
@@ -4198,6 +4214,10 @@ var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
4198
4214
  },
4199
4215
  });
4200
4216
 
4217
+ var AST_ImportMeta = DEFNODE("ImportMeta", null, {
4218
+ $documentation: "A reference to import.meta",
4219
+ });
4220
+
4201
4221
  var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default exported_names module_name", {
4202
4222
  $documentation: "An `export` statement",
4203
4223
  $propdoc: {
@@ -4977,6 +4997,7 @@ AST_Function: AST_Function,
4977
4997
  AST_Hole: AST_Hole,
4978
4998
  AST_If: AST_If,
4979
4999
  AST_Import: AST_Import,
5000
+ AST_ImportMeta: AST_ImportMeta,
4980
5001
  AST_Infinity: AST_Infinity,
4981
5002
  AST_IterationStatement: AST_IterationStatement,
4982
5003
  AST_Jump: AST_Jump,
@@ -5759,6 +5780,11 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
5759
5780
  start: my_start_token(M),
5760
5781
  end: my_end_token(M)
5761
5782
  });
5783
+ } else if (M.meta.name === "import" && M.property.name === "meta") {
5784
+ return new AST_ImportMeta({
5785
+ start: my_start_token(M),
5786
+ end: my_end_token(M)
5787
+ });
5762
5788
  }
5763
5789
  },
5764
5790
  Identifier: function(M) {
@@ -6050,6 +6076,20 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
6050
6076
  };
6051
6077
  });
6052
6078
 
6079
+ def_to_moz(AST_ImportMeta, function To_Moz_MetaProperty() {
6080
+ return {
6081
+ type: "MetaProperty",
6082
+ meta: {
6083
+ type: "Identifier",
6084
+ name: "import"
6085
+ },
6086
+ property: {
6087
+ type: "Identifier",
6088
+ name: "meta"
6089
+ }
6090
+ };
6091
+ });
6092
+
6053
6093
  def_to_moz(AST_Sequence, function To_Moz_SequenceExpression(M) {
6054
6094
  return {
6055
6095
  type: "SequenceExpression",
@@ -7989,6 +8029,9 @@ function OutputStream(options) {
7989
8029
  self.module_name.print(output);
7990
8030
  output.semicolon();
7991
8031
  });
8032
+ DEFPRINT(AST_ImportMeta, function(self, output) {
8033
+ output.print("import.meta");
8034
+ });
7992
8035
 
7993
8036
  DEFPRINT(AST_NameMapping, function(self, output) {
7994
8037
  var is_import = output.parent() instanceof AST_Import;
@@ -8716,6 +8759,8 @@ AST_Import.prototype.shallow_cmp = mkshallow({
8716
8759
  imported_names: "exist"
8717
8760
  });
8718
8761
 
8762
+ AST_ImportMeta.prototype.shallow_cmp = pass_through;
8763
+
8719
8764
  AST_Export.prototype.shallow_cmp = mkshallow({
8720
8765
  exported_definition: "exist",
8721
8766
  exported_value: "exist",
@@ -9780,7 +9825,7 @@ AST_Arrow.prototype._size = function () {
9780
9825
  args_and_arrow += 2;
9781
9826
  }
9782
9827
 
9783
- return lambda_modifiers(this) + args_and_arrow + Array.isArray(this.body) ? list_overhead(this.body) : this.body._size();
9828
+ return lambda_modifiers(this) + args_and_arrow + (Array.isArray(this.body) ? list_overhead(this.body) : this.body._size());
9784
9829
  };
9785
9830
 
9786
9831
  AST_Destructuring.prototype._size = () => 2;
@@ -9878,6 +9923,8 @@ AST_Import.prototype._size = function () {
9878
9923
  return size;
9879
9924
  };
9880
9925
 
9926
+ AST_ImportMeta.prototype._size = () => 11;
9927
+
9881
9928
  AST_Export.prototype._size = function () {
9882
9929
  let size = 7 + (this.is_default ? 8 : 0);
9883
9930
 
@@ -11546,8 +11593,7 @@ function tighten_body(statements, compressor) {
11546
11593
  extract_candidates(expr.condition);
11547
11594
  extract_candidates(expr.consequent);
11548
11595
  extract_candidates(expr.alternative);
11549
- } else if (expr instanceof AST_Definitions
11550
- && (compressor.option("unused") || !(expr instanceof AST_Const))) {
11596
+ } else if (expr instanceof AST_Definitions) {
11551
11597
  var len = expr.definitions.length;
11552
11598
  // limit number of trailing variable definitions for consideration
11553
11599
  var i = len - 200;
@@ -11715,7 +11761,9 @@ function tighten_body(statements, compressor) {
11715
11761
  if (node === expr || node.body === expr) {
11716
11762
  found = true;
11717
11763
  if (node instanceof AST_VarDef) {
11718
- node.value = null;
11764
+ node.value = node.name instanceof AST_SymbolConst
11765
+ ? make_node(AST_Undefined, node.value) // `const` always needs value.
11766
+ : null;
11719
11767
  return node;
11720
11768
  }
11721
11769
  return in_list ? MAP.skip : null;
@@ -12031,7 +12079,7 @@ function tighten_body(statements, compressor) {
12031
12079
  statements.length = n;
12032
12080
  CHANGED = n != len;
12033
12081
  if (has_quit) has_quit.forEach(function(stat) {
12034
- extract_declarations_from_unreachable_code(compressor, stat, statements);
12082
+ trim_unreachable_code(compressor, stat, statements);
12035
12083
  });
12036
12084
  }
12037
12085
 
@@ -12289,7 +12337,7 @@ function tighten_body(statements, compressor) {
12289
12337
  }
12290
12338
  }
12291
12339
 
12292
- function extract_declarations_from_unreachable_code(compressor, stat, target) {
12340
+ function trim_unreachable_code(compressor, stat, target) {
12293
12341
  walk(stat, node => {
12294
12342
  if (node instanceof AST_Var) {
12295
12343
  node.remove_initializers();
@@ -12310,6 +12358,10 @@ function extract_declarations_from_unreachable_code(compressor, stat, target) {
12310
12358
  }));
12311
12359
  return true;
12312
12360
  }
12361
+ if (node instanceof AST_Export || node instanceof AST_Import) {
12362
+ target.push(node);
12363
+ return true;
12364
+ }
12313
12365
  if (node instanceof AST_Scope) {
12314
12366
  return true;
12315
12367
  }
@@ -12361,6 +12413,10 @@ function is_undefined(node, compressor) {
12361
12413
  if (this.properties[i]._dot_throw(compressor)) return true;
12362
12414
  return false;
12363
12415
  });
12416
+ // Do not be as strict with classes as we are with objects.
12417
+ // Hopefully the community is not going to abuse static getters and setters.
12418
+ // https://github.com/terser/terser/issues/724#issuecomment-643655656
12419
+ def_may_throw_on_access(AST_Class, return_false);
12364
12420
  def_may_throw_on_access(AST_ObjectProperty, return_false);
12365
12421
  def_may_throw_on_access(AST_ObjectGetter, return_true);
12366
12422
  def_may_throw_on_access(AST_Expansion, function(compressor) {
@@ -12393,7 +12449,8 @@ function is_undefined(node, compressor) {
12393
12449
  return this.tail_node()._dot_throw(compressor);
12394
12450
  });
12395
12451
  def_may_throw_on_access(AST_SymbolRef, function(compressor) {
12396
- if (is_undefined(this, compressor)) return true;
12452
+ if (this.name === "arguments") return false;
12453
+ if (has_flag(this, UNDEFINED)) return true;
12397
12454
  if (!is_strict(compressor)) return false;
12398
12455
  if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
12399
12456
  if (this.is_immutable()) return false;
@@ -12795,13 +12852,33 @@ var static_fns = convert_to_predicate({
12795
12852
  return this;
12796
12853
  });
12797
12854
  var non_converting_binary = makePredicate("&& || ?? === !==");
12855
+ const identity_comparison = makePredicate("== != === !==");
12856
+ const has_identity = value =>
12857
+ typeof value === "object"
12858
+ || typeof value === "function"
12859
+ || typeof value === "symbol";
12860
+
12798
12861
  def_eval(AST_Binary, function(compressor, depth) {
12799
12862
  if (!non_converting_binary.has(this.operator)) depth++;
12863
+
12800
12864
  var left = this.left._eval(compressor, depth);
12801
12865
  if (left === this.left) return this;
12802
12866
  var right = this.right._eval(compressor, depth);
12803
12867
  if (right === this.right) return this;
12804
12868
  var result;
12869
+
12870
+ if (
12871
+ left != null
12872
+ && right != null
12873
+ && identity_comparison.has(this.operator)
12874
+ && has_identity(left)
12875
+ && has_identity(right)
12876
+ && typeof left === typeof right
12877
+ ) {
12878
+ // Do not compare by reference
12879
+ return this;
12880
+ }
12881
+
12805
12882
  switch (this.operator) {
12806
12883
  case "&&" : result = left && right; break;
12807
12884
  case "||" : result = left || right; break;
@@ -12842,20 +12919,24 @@ var static_fns = convert_to_predicate({
12842
12919
  var value = node._eval(compressor, depth);
12843
12920
  return value === node ? this : value;
12844
12921
  });
12922
+
12923
+ // Set of AST_SymbolRef which are currently being evaluated.
12924
+ // Avoids infinite recursion of ._eval()
12925
+ const reentrant_ref_eval = new Set();
12845
12926
  def_eval(AST_SymbolRef, function(compressor, depth) {
12927
+ if (reentrant_ref_eval.has(this)) return this;
12928
+
12846
12929
  var fixed = this.fixed_value();
12847
12930
  if (!fixed) return this;
12848
12931
  var value;
12849
12932
  if (HOP(fixed, "_eval")) {
12850
12933
  value = fixed._eval();
12851
12934
  } else {
12852
- this._eval = return_this;
12935
+ reentrant_ref_eval.add(this);
12853
12936
  value = fixed._eval(compressor, depth);
12854
- delete this._eval;
12937
+ reentrant_ref_eval.delete(this);
12938
+
12855
12939
  if (value === fixed) return this;
12856
- fixed._eval = function() {
12857
- return value;
12858
- };
12859
12940
  }
12860
12941
  if (value && typeof value == "object") {
12861
12942
  var escaped = this.definition().escaped;
@@ -12863,13 +12944,7 @@ var static_fns = convert_to_predicate({
12863
12944
  }
12864
12945
  return value;
12865
12946
  });
12866
- var global_objs = {
12867
- Array: Array,
12868
- Math: Math,
12869
- Number: Number,
12870
- Object: Object,
12871
- String: String,
12872
- };
12947
+ var global_objs = { Array, Math, Number, Object, String };
12873
12948
  var static_values = convert_to_predicate({
12874
12949
  Math: [
12875
12950
  "E",
@@ -14395,7 +14470,7 @@ function if_break_in_loop(self, compressor) {
14395
14470
  body: self.condition
14396
14471
  }));
14397
14472
  }
14398
- extract_declarations_from_unreachable_code(compressor, self.body, body);
14473
+ trim_unreachable_code(compressor, self.body, body);
14399
14474
  return make_node(AST_BlockStatement, self, {
14400
14475
  body: body
14401
14476
  });
@@ -14466,7 +14541,7 @@ def_optimize(AST_For, function(self, compressor) {
14466
14541
  if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
14467
14542
  if (!cond) {
14468
14543
  var body = [];
14469
- extract_declarations_from_unreachable_code(compressor, self.body, body);
14544
+ trim_unreachable_code(compressor, self.body, body);
14470
14545
  if (self.init instanceof AST_Statement) {
14471
14546
  body.push(self.init);
14472
14547
  } else if (self.init) {
@@ -14502,7 +14577,7 @@ def_optimize(AST_If, function(self, compressor) {
14502
14577
  if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
14503
14578
  if (!cond) {
14504
14579
  var body = [];
14505
- extract_declarations_from_unreachable_code(compressor, self.body, body);
14580
+ trim_unreachable_code(compressor, self.body, body);
14506
14581
  body.push(make_node(AST_SimpleStatement, self.condition, {
14507
14582
  body: self.condition
14508
14583
  }));
@@ -14515,7 +14590,7 @@ def_optimize(AST_If, function(self, compressor) {
14515
14590
  }));
14516
14591
  body.push(self.body);
14517
14592
  if (self.alternative) {
14518
- extract_declarations_from_unreachable_code(compressor, self.alternative, body);
14593
+ trim_unreachable_code(compressor, self.alternative, body);
14519
14594
  }
14520
14595
  return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
14521
14596
  }
@@ -14727,7 +14802,7 @@ def_optimize(AST_Switch, function(self, compressor) {
14727
14802
  if (prev && !aborts(prev)) {
14728
14803
  prev.body = prev.body.concat(branch.body);
14729
14804
  } else {
14730
- extract_declarations_from_unreachable_code(compressor, branch, decl);
14805
+ trim_unreachable_code(compressor, branch, decl);
14731
14806
  }
14732
14807
  }
14733
14808
  });
@@ -14738,7 +14813,7 @@ def_optimize(AST_Try, function(self, compressor) {
14738
14813
  if (compressor.option("dead_code") && self.body.every(is_empty)) {
14739
14814
  var body = [];
14740
14815
  if (self.bcatch) {
14741
- extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
14816
+ trim_unreachable_code(compressor, self.bcatch, body);
14742
14817
  }
14743
14818
  if (self.bfinally) body.push(...self.bfinally.body);
14744
14819
  return make_node(AST_BlockStatement, self, {
@@ -17267,7 +17342,7 @@ function lift_key(self, compressor) {
17267
17342
  if (!compressor.option("computed_props")) return self;
17268
17343
  // save a comparison in the typical case
17269
17344
  if (!(self.key instanceof AST_Constant)) return self;
17270
- // whitelist acceptable props as not all AST_Constants are true constants
17345
+ // allow certain acceptable props as not all AST_Constants are true constants
17271
17346
  if (self.key instanceof AST_String || self.key instanceof AST_Number) {
17272
17347
  if (self.key.value === "__proto__") return self;
17273
17348
  if (self.key.value == "constructor"
@@ -23301,6 +23376,8 @@ function mangle_properties(ast, options) {
23301
23376
  } else if (node instanceof AST_Call
23302
23377
  && node.expression.print_to_string() == "Object.defineProperty") {
23303
23378
  addStrings(node.args[1], add);
23379
+ } else if (node instanceof AST_Binary && node.operator === "in") {
23380
+ addStrings(node.left, add);
23304
23381
  }
23305
23382
  }));
23306
23383
 
@@ -23325,6 +23402,8 @@ function mangle_properties(ast, options) {
23325
23402
  } else if (node instanceof AST_Call
23326
23403
  && node.expression.print_to_string() == "Object.defineProperty") {
23327
23404
  node.args[1] = mangleStrings(node.args[1]);
23405
+ } else if (node instanceof AST_Binary && node.operator === "in") {
23406
+ node.left = mangleStrings(node.left);
23328
23407
  }
23329
23408
  }));
23330
23409
 
@@ -23704,6 +23783,7 @@ async function run_cli({ program, packageJson, fs, path }) {
23704
23783
  options[name] = program[name];
23705
23784
  }
23706
23785
  });
23786
+
23707
23787
  if ("ecma" in program) {
23708
23788
  if (program.ecma != (program.ecma | 0)) fatal("ERROR: ecma must be an integer");
23709
23789
  const ecma = program.ecma | 0;
@@ -23933,7 +24013,7 @@ async function run_cli({ program, packageJson, fs, path }) {
23933
24013
  }
23934
24014
  } else if (program.output) {
23935
24015
  fs.writeFileSync(program.output, result.code);
23936
- if (options.sourceMap.url !== "inline" && result.map) {
24016
+ if (options.sourceMap && options.sourceMap.url !== "inline" && result.map) {
23937
24017
  fs.writeFileSync(program.output + ".map", result.map);
23938
24018
  }
23939
24019
  } else {