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/lib/ast.js CHANGED
@@ -864,6 +864,10 @@ var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
864
864
  },
865
865
  });
866
866
 
867
+ var AST_ImportMeta = DEFNODE("ImportMeta", null, {
868
+ $documentation: "A reference to import.meta",
869
+ });
870
+
867
871
  var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default exported_names module_name", {
868
872
  $documentation: "An `export` statement",
869
873
  $propdoc: {
@@ -1642,6 +1646,7 @@ export {
1642
1646
  AST_Hole,
1643
1647
  AST_If,
1644
1648
  AST_Import,
1649
+ AST_ImportMeta,
1645
1650
  AST_Infinity,
1646
1651
  AST_IterationStatement,
1647
1652
  AST_Jump,
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"
@@ -26,6 +26,7 @@ import {
26
26
  AST_ForOf,
27
27
  AST_If,
28
28
  AST_Import,
29
+ AST_ImportMeta,
29
30
  AST_Jump,
30
31
  AST_LabeledStatement,
31
32
  AST_Lambda,
@@ -209,6 +210,8 @@ AST_Import.prototype.shallow_cmp = mkshallow({
209
210
  imported_names: "exist"
210
211
  });
211
212
 
213
+ AST_ImportMeta.prototype.shallow_cmp = pass_through;
214
+
212
215
  AST_Export.prototype.shallow_cmp = mkshallow({
213
216
  exported_definition: "exist",
214
217
  exported_value: "exist",
@@ -89,6 +89,7 @@ import {
89
89
  AST_Hole,
90
90
  AST_If,
91
91
  AST_Import,
92
+ AST_ImportMeta,
92
93
  AST_Label,
93
94
  AST_LabeledStatement,
94
95
  AST_LabelRef,
@@ -545,6 +546,11 @@ import {
545
546
  start: my_start_token(M),
546
547
  end: my_end_token(M)
547
548
  });
549
+ } else if (M.meta.name === "import" && M.property.name === "meta") {
550
+ return new AST_ImportMeta({
551
+ start: my_start_token(M),
552
+ end: my_end_token(M)
553
+ });
548
554
  }
549
555
  },
550
556
  Identifier: function(M) {
@@ -836,6 +842,20 @@ import {
836
842
  };
837
843
  });
838
844
 
845
+ def_to_moz(AST_ImportMeta, function To_Moz_MetaProperty() {
846
+ return {
847
+ type: "MetaProperty",
848
+ meta: {
849
+ type: "Identifier",
850
+ name: "import"
851
+ },
852
+ property: {
853
+ type: "Identifier",
854
+ name: "meta"
855
+ }
856
+ };
857
+ });
858
+
839
859
  def_to_moz(AST_Sequence, function To_Moz_SequenceExpression(M) {
840
860
  return {
841
861
  type: "SequenceExpression",
package/lib/output.js CHANGED
@@ -94,6 +94,7 @@ import {
94
94
  AST_Hole,
95
95
  AST_If,
96
96
  AST_Import,
97
+ AST_ImportMeta,
97
98
  AST_Jump,
98
99
  AST_LabeledStatement,
99
100
  AST_Lambda,
@@ -1608,6 +1609,9 @@ function OutputStream(options) {
1608
1609
  self.module_name.print(output);
1609
1610
  output.semicolon();
1610
1611
  });
1612
+ DEFPRINT(AST_ImportMeta, function(self, output) {
1613
+ output.print("import.meta");
1614
+ });
1611
1615
 
1612
1616
  DEFPRINT(AST_NameMapping, function(self, output) {
1613
1617
  var is_import = output.parent() instanceof AST_Import;
package/lib/parse.js CHANGED
@@ -91,6 +91,7 @@ import {
91
91
  AST_Hole,
92
92
  AST_If,
93
93
  AST_Import,
94
+ AST_ImportMeta,
94
95
  AST_IterationStatement,
95
96
  AST_Label,
96
97
  AST_LabeledStatement,
@@ -503,12 +504,14 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
503
504
  }
504
505
 
505
506
  function read_num(prefix) {
506
- var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".", is_big_int = false;
507
+ var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".", is_big_int = false, numeric_separator = false;
507
508
  var num = read_while(function(ch, i) {
508
509
  if (is_big_int) return false;
509
510
 
510
511
  var code = ch.charCodeAt(0);
511
512
  switch (code) {
513
+ case 95: // _
514
+ return (numeric_separator = true);
512
515
  case 98: case 66: // bB
513
516
  return (has_x = true); // Can occur in hex sequence, don't return false yet
514
517
  case 111: case 79: // oO
@@ -536,6 +539,14 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
536
539
  if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) {
537
540
  parse_error("Legacy octal literals are not allowed in strict mode");
538
541
  }
542
+ if (numeric_separator) {
543
+ if (num.endsWith("_")) {
544
+ parse_error("Numeric separators are not allowed at the end of numeric literals");
545
+ } else if (num.includes("__")) {
546
+ parse_error("Only one underscore is allowed as numeric separator");
547
+ }
548
+ num = num.replace(/_/g, "");
549
+ }
539
550
  if (num.endsWith("n")) {
540
551
  const without_n = num.slice(0, -1);
541
552
  const allow_e = RE_HEX_NUMBER.test(without_n);
@@ -1159,7 +1170,7 @@ function parse($TEXT, options) {
1159
1170
  }
1160
1171
  return function_(AST_Defun, false, true, is_export_default);
1161
1172
  }
1162
- if (S.token.value == "import" && !is_token(peek(), "punc", "(")) {
1173
+ if (S.token.value == "import" && !is_token(peek(), "punc", "(") && !is_token(peek(), "punc", ".")) {
1163
1174
  next();
1164
1175
  var node = import_();
1165
1176
  semicolon();
@@ -2207,6 +2218,9 @@ function parse($TEXT, options) {
2207
2218
  if (is("operator", "new")) {
2208
2219
  return new_(allow_calls);
2209
2220
  }
2221
+ if (is("operator", "import")) {
2222
+ return import_meta();
2223
+ }
2210
2224
  var start = S.token;
2211
2225
  var peeked;
2212
2226
  var async = is("name", "async")
@@ -2584,6 +2598,7 @@ function parse($TEXT, options) {
2584
2598
 
2585
2599
  function import_() {
2586
2600
  var start = prev();
2601
+
2587
2602
  var imported_name;
2588
2603
  var imported_names;
2589
2604
  if (is("name")) {
@@ -2618,6 +2633,17 @@ function parse($TEXT, options) {
2618
2633
  });
2619
2634
  }
2620
2635
 
2636
+ function import_meta() {
2637
+ var start = S.token;
2638
+ expect_token("operator", "import");
2639
+ expect_token("punc", ".");
2640
+ expect_token("name", "meta");
2641
+ return subscripts(new AST_ImportMeta({
2642
+ start: start,
2643
+ end: prev()
2644
+ }), false);
2645
+ }
2646
+
2621
2647
  function map_name(is_import) {
2622
2648
  function make_symbol(type) {
2623
2649
  return new type({
@@ -2798,15 +2824,6 @@ function parse($TEXT, options) {
2798
2824
  }
2799
2825
  /* falls through */
2800
2826
  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
2827
  case "string":
2811
2828
  case "num":
2812
2829
  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
@@ -32,6 +32,7 @@ import {
32
32
  AST_Hole,
33
33
  AST_If,
34
34
  AST_Import,
35
+ AST_ImportMeta,
35
36
  AST_Infinity,
36
37
  AST_LabeledStatement,
37
38
  AST_Let,
@@ -159,7 +160,7 @@ AST_Arrow.prototype._size = function () {
159
160
  args_and_arrow += 2;
160
161
  }
161
162
 
162
- return lambda_modifiers(this) + args_and_arrow + Array.isArray(this.body) ? list_overhead(this.body) : this.body._size();
163
+ return lambda_modifiers(this) + args_and_arrow + (Array.isArray(this.body) ? list_overhead(this.body) : this.body._size());
163
164
  };
164
165
 
165
166
  AST_Destructuring.prototype._size = () => 2;
@@ -257,6 +258,8 @@ AST_Import.prototype._size = function () {
257
258
  return size;
258
259
  };
259
260
 
261
+ AST_ImportMeta.prototype._size = () => 11;
262
+
260
263
  AST_Export.prototype._size = function () {
261
264
  let size = 7 + (this.is_default ? 8 : 0);
262
265
 
package/main.js ADDED
@@ -0,0 +1,27 @@
1
+ import "./lib/transform.js";
2
+ import "./lib/mozilla-ast.js";
3
+ import { minify } from "./lib/minify.js";
4
+
5
+ export { minify } from "./lib/minify.js";
6
+ export { run_cli as _run_cli } from "./lib/cli.js";
7
+
8
+ export async function _default_options() {
9
+ const defs = {};
10
+
11
+ Object.keys(infer_options({ 0: 0 })).forEach((component) => {
12
+ const options = infer_options({
13
+ [component]: {0: 0}
14
+ });
15
+
16
+ if (options) defs[component] = options;
17
+ });
18
+ return defs;
19
+ }
20
+
21
+ async function infer_options(options) {
22
+ try {
23
+ await minify("", options);
24
+ } catch (error) {
25
+ return error.defs;
26
+ }
27
+ }
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.0",
7
+ "version": "5.1.0",
8
8
  "engines": {
9
9
  "node": ">=6.0.0"
10
10
  },
@@ -38,7 +38,8 @@
38
38
  "LICENSE",
39
39
  "README.md",
40
40
  "CHANGELOG.md",
41
- "PATRONS.md"
41
+ "PATRONS.md",
42
+ "main.js"
42
43
  ],
43
44
  "dependencies": {
44
45
  "commander": "^2.20.0",
@@ -46,7 +47,8 @@
46
47
  "source-map-support": "~0.5.12"
47
48
  },
48
49
  "devDependencies": {
49
- "acorn": "^7.1.1",
50
+ "@ls-lint/ls-lint": "^1.9.2",
51
+ "acorn": "^7.4.0",
50
52
  "astring": "^1.4.1",
51
53
  "eslint": "^7.0.0",
52
54
  "eslump": "^2.0.0",
@@ -63,6 +65,7 @@
63
65
  "test:mocha": "mocha test/mocha",
64
66
  "lint": "eslint lib",
65
67
  "lint-fix": "eslint --fix lib",
68
+ "ls-lint": "ls-lint",
66
69
  "build": "rimraf dist/bundle* && rollup --config --silent",
67
70
  "prepare": "npm run build",
68
71
  "postversion": "echo 'Remember to update the changelog!'"
@@ -139,6 +142,7 @@
139
142
  },
140
143
  "pre-commit": [
141
144
  "lint-fix",
145
+ "ls-lint",
142
146
  "test"
143
147
  ]
144
148
  }