terser 5.6.1 → 5.7.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,15 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.7.0
4
+
5
+ - Several compile-time evaluation and inlining fixes
6
+ - Allow `reduce_funcs` to be disabled again.
7
+ - Add `spidermonkey` options to parse and format (#974)
8
+ - Accept `{get = "default val"}` and `{set = "default val"}` in destructuring arguments.
9
+ - Change package.json export map to help require.resolve (#971)
10
+ - Improve docs
11
+ - Fix `export default` of an anonymous class with `extends`
12
+
3
13
  ## v5.6.1
4
14
 
5
15
  - Mark assignments to the `.prototype` of a class as pure
package/README.md CHANGED
@@ -21,8 +21,8 @@ Find the changelog in [CHANGELOG.md](https://github.com/terser/terser/blob/maste
21
21
  [npm-url]: https://npmjs.org/package/terser
22
22
  [downloads-image]: https://img.shields.io/npm/dm/terser.svg
23
23
  [downloads-url]: https://npmjs.org/package/terser
24
- [travis-image]: https://img.shields.io/travis/terser/terser/master.svg
25
- [travis-url]: https://travis-ci.org/terser/terser
24
+ [travis-image]: https://travis-ci.com/terser/terser.svg?branch=master
25
+ [travis-url]: https://travis-ci.com/terser/terser
26
26
  [opencollective-contributors]: https://opencollective.com/terser/tiers/badge.svg
27
27
  [opencollective-url]: https://opencollective.com/terser
28
28
 
@@ -416,7 +416,7 @@ Browser loading is also supported:
416
416
 
417
417
  There is a single async high level function, **`async minify(code, options)`**,
418
418
  which will perform all minification [phases](#minify-options) in a configurable
419
- manner. There is no synchronous function, but this functionality can be achieved with a package like [deasync](https://github.com/abbr/deasync). By default `minify()` will enable the options [`compress`](#compress-options)
419
+ manner. By default `minify()` will enable [`compress`](#compress-options)
420
420
  and [`mangle`](#mangle-options). Example:
421
421
  ```javascript
422
422
  var code = "function add(first, second) { return first + second; }";
@@ -608,7 +608,7 @@ try {
608
608
  module: false,
609
609
  nameCache: null, // or specify a name cache object
610
610
  safari10: false,
611
- toplevel: false,
611
+ toplevel: false
612
612
  }
613
613
  ```
614
614
 
@@ -669,6 +669,8 @@ If you happen to need the source map as a raw object, set `sourceMap.asObject` t
669
669
 
670
670
  - `shebang` (default `true`) -- support `#!command` as the first line
671
671
 
672
+ - `spidermonkey` (default `false`) -- accept a Spidermonkey (Mozilla) AST
673
+
672
674
  ## Compress options
673
675
 
674
676
  - `defaults` (default: `true`) -- Pass `false` to disable most default
@@ -795,11 +797,13 @@ If you happen to need the source map as a raw object, set `sourceMap.asObject` t
795
797
  Specify `"strict"` to treat `foo.bar` as side-effect-free only when
796
798
  `foo` is certain to not throw, i.e. not `null` or `undefined`.
797
799
 
798
- - `reduce_funcs` (legacy option, safely ignored for backwards compatibility).
799
-
800
800
  - `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and
801
801
  used as constant values.
802
802
 
803
+ - `reduce_funcs` (default: `true`) -- Inline single-use functions when
804
+ possible. Depends on `reduce_vars` being enabled. Disabling this option
805
+ sometimes improves performance of the output code.
806
+
803
807
  - `sequences` (default: `true`) -- join consecutive simple statements using the
804
808
  comma operator. May be set to a positive integer to specify the maximum number
805
809
  of consecutive comma sequences that will be generated. If this option is set to
@@ -848,7 +852,7 @@ If you happen to need the source map as a raw object, set `sourceMap.asObject` t
848
852
  - `unsafe_math` (default: `false`) -- optimize numerical expressions like
849
853
  `2 * x * 3` into `6 * x`, which may give imprecise floating point results.
850
854
 
851
- - `unsafe_symbols` (default: `false`) -- removes keys from native Symbol
855
+ - `unsafe_symbols` (default: `false`) -- removes keys from native Symbol
852
856
  declarations, e.g `Symbol("kDog")` becomes `Symbol()`.
853
857
 
854
858
  - `unsafe_methods` (default: false) -- Converts `{ m: function(){} }` to
@@ -929,11 +933,10 @@ await minify(code, { mangle: { toplevel: true } }).code;
929
933
  - `debug` (default: `false`) — Mangle names with the original name still present.
930
934
  Pass an empty string `""` to enable, or a non-empty string to set the debug suffix.
931
935
 
932
- - `keep_quoted` (default: `false`) — Only mangle unquoted property names.
933
- - `true` -- Quoted property names are automatically reserved and any unquoted
934
- property names will not be mangled.
935
- - `"strict"` -- Advanced, all unquoted property names are mangled unless
936
- explicitly reserved.
936
+ - `keep_quoted` (default: `false`) — How quoting properties (`{"prop": ...}` and `obj["prop"]`) controls what gets mangled.
937
+ - `"strict"` (recommended) -- `obj.prop` is mangled.
938
+ - `false` -- `obj["prop"]` is mangled.
939
+ - `true` -- `obj.prop` is mangled unless there is `obj["prop"]` elsewhere in the code.
937
940
 
938
941
  - `regex` (default: `null`) — Pass a [RegExp literal or pattern string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) to only mangle property matching the regular expression.
939
942
 
@@ -954,19 +957,17 @@ as "output options".
954
957
  - `ascii_only` (default `false`) -- escape Unicode characters in strings and
955
958
  regexps (affects directives with non-ascii characters becoming invalid)
956
959
 
957
- - `beautify` (default `false`) -- whether to actually beautify the output.
958
- Passing `-b` will set this to true, but you might need to pass `-b` even
959
- when you want to generate minified code, in order to specify additional
960
- arguments, so you can use `-b beautify=false` to override it.
960
+ - `beautify` (default `false`) -- (DEPRECATED) whether to beautify the output.
961
+ When using the legacy `-b` CLI flag, this is set to true by default.
961
962
 
962
963
  - `braces` (default `false`) -- always insert braces in `if`, `for`,
963
964
  `do`, `while` or `with` statements, even if their body is a single
964
965
  statement.
965
966
 
966
967
  - `comments` (default `"some"`) -- by default it keeps JSDoc-style comments
967
- that contain "@license" or "@preserve", pass `true` or `"all"` to preserve all
968
- comments, `false` to omit comments in the output, a regular expression string
969
- (e.g. `/^!/`) or a function.
968
+ that contain "@license", "@preserve" or start with `!`, pass `true` or
969
+ `"all"` to preserve all comments, `false` to omit comments in the output,
970
+ a regular expression string (e.g. `/^!/`) or a function.
970
971
 
971
972
  - `ecma` (default `5`) -- set desired EcmaScript standard version for output.
972
973
  Set `ecma` to `2015` or greater to emit shorthand object properties - i.e.:
@@ -1019,6 +1020,8 @@ as "output options".
1019
1020
 
1020
1021
  - `shebang` (default `true`) -- preserve shebang `#!` in preamble (bash scripts)
1021
1022
 
1023
+ - `spidermonkey` (default `false`) -- produce a Spidermonkey (Mozilla) AST
1024
+
1022
1025
  - `webkit` (default `false`) -- enable workarounds for WebKit bugs.
1023
1026
  PhantomJS users should set this option to `true`.
1024
1027
 
@@ -1190,6 +1193,9 @@ JavaScript, but JS code described in SpiderMonkey AST in JSON. Therefore we
1190
1193
  don't use our own parser in this case, but just transform that AST into our
1191
1194
  internal AST.
1192
1195
 
1196
+ `spidermonkey` is also available in `minify` as `parse` and `format` options to
1197
+ accept and/or produce a spidermonkey AST.
1198
+
1193
1199
  ### Use Acorn for parsing
1194
1200
 
1195
1201
  More for fun, I added the `-p acorn` option which will use Acorn to do all
@@ -204,7 +204,7 @@ function mergeSort(array, cmp) {
204
204
  function makePredicate(words) {
205
205
  if (!Array.isArray(words)) words = words.split(" ");
206
206
 
207
- return new Set(words);
207
+ return new Set(words.sort());
208
208
  }
209
209
 
210
210
  function map_add(map, key, value) {
@@ -1459,7 +1459,7 @@ function parse($TEXT, options) {
1459
1459
  if (is_if_body) {
1460
1460
  croak("classes are not allowed as the body of an if");
1461
1461
  }
1462
- return class_(AST_DefClass);
1462
+ return class_(AST_DefClass, is_export_default);
1463
1463
 
1464
1464
  case "function":
1465
1465
  next();
@@ -2654,7 +2654,7 @@ function parse($TEXT, options) {
2654
2654
  return new AST_Object({ properties: a });
2655
2655
  });
2656
2656
 
2657
- function class_(KindOfClass) {
2657
+ function class_(KindOfClass, is_export_default) {
2658
2658
  var start, method, class_name, extends_, a = [];
2659
2659
 
2660
2660
  S.input.push_directives_stack(); // Push directive stack, but not scope stack
@@ -2665,7 +2665,11 @@ function parse($TEXT, options) {
2665
2665
  }
2666
2666
 
2667
2667
  if (KindOfClass === AST_DefClass && !class_name) {
2668
- unexpected();
2668
+ if (is_export_default) {
2669
+ KindOfClass = AST_ClassExpression;
2670
+ } else {
2671
+ unexpected();
2672
+ }
2669
2673
  }
2670
2674
 
2671
2675
  if (S.token.value == "extends") {
@@ -2698,9 +2702,9 @@ function parse($TEXT, options) {
2698
2702
  }
2699
2703
 
2700
2704
  function concise_method_or_getset(name, start, is_class) {
2701
- var get_method_name_ast = function(name, start) {
2705
+ const get_symbol_ast = (name, SymbolClass = AST_SymbolMethod) => {
2702
2706
  if (typeof name === "string" || typeof name === "number") {
2703
- return new AST_SymbolMethod({
2707
+ return new SymbolClass({
2704
2708
  start,
2705
2709
  name: "" + name,
2706
2710
  end: prev()
@@ -2710,47 +2714,71 @@ function parse($TEXT, options) {
2710
2714
  }
2711
2715
  return name;
2712
2716
  };
2713
- const get_class_property_key_ast = (name) => {
2714
- if (typeof name === "string" || typeof name === "number") {
2715
- return new AST_SymbolClassProperty({
2716
- start: property_token,
2717
- end: property_token,
2718
- name: "" + name
2719
- });
2720
- } else if (name === null) {
2721
- unexpected();
2722
- }
2723
- return name;
2724
- };
2725
- var privatename = start.type == "privatename";
2717
+
2718
+ const is_not_method_start = () =>
2719
+ !is("punc", "(") && !is("punc", ",") && !is("punc", "}") && !is("operator", "=");
2720
+
2726
2721
  var is_async = false;
2727
2722
  var is_static = false;
2728
2723
  var is_generator = false;
2729
- var property_token = start;
2730
- if (is_class && name === "static" && !is("punc", "(")) {
2724
+ var is_private = false;
2725
+ var accessor_type = null;
2726
+
2727
+ if (is_class && name === "static" && is_not_method_start()) {
2731
2728
  is_static = true;
2732
- property_token = S.token;
2733
- privatename = property_token.type == "privatename";
2734
2729
  name = as_property_name();
2735
2730
  }
2736
- if (name === "async" && !is("punc", "(") && !is("punc", ",") && !is("punc", "}") && !is("operator", "=")) {
2731
+ if (name === "async" && is_not_method_start()) {
2737
2732
  is_async = true;
2738
- property_token = S.token;
2739
- privatename = property_token.type == "privatename";
2740
2733
  name = as_property_name();
2741
2734
  }
2742
- if (name === null) {
2735
+ if (prev().type === "operator" && prev().value === "*") {
2743
2736
  is_generator = true;
2744
- property_token = S.token;
2745
- privatename = property_token.type == "privatename";
2746
2737
  name = as_property_name();
2747
- if (name === null) {
2748
- unexpected();
2738
+ }
2739
+ if ((name === "get" || name === "set") && is_not_method_start()) {
2740
+ accessor_type = name;
2741
+ name = as_property_name();
2742
+ }
2743
+ if (prev().type === "privatename") {
2744
+ is_private = true;
2745
+ }
2746
+
2747
+ const property_token = prev();
2748
+
2749
+ if (accessor_type != null) {
2750
+ if (!is_private) {
2751
+ const AccessorClass = accessor_type === "get"
2752
+ ? AST_ObjectGetter
2753
+ : AST_ObjectSetter;
2754
+
2755
+ name = get_symbol_ast(name);
2756
+ return new AccessorClass({
2757
+ start,
2758
+ static: is_static,
2759
+ key: name,
2760
+ quote: name instanceof AST_SymbolMethod ? property_token.quote : undefined,
2761
+ value: create_accessor(),
2762
+ end: prev()
2763
+ });
2764
+ } else {
2765
+ const AccessorClass = accessor_type === "get"
2766
+ ? AST_PrivateGetter
2767
+ : AST_PrivateSetter;
2768
+
2769
+ return new AccessorClass({
2770
+ start,
2771
+ static: is_static,
2772
+ key: get_symbol_ast(name),
2773
+ value: create_accessor(),
2774
+ end: prev(),
2775
+ });
2749
2776
  }
2750
2777
  }
2778
+
2751
2779
  if (is("punc", "(")) {
2752
- name = get_method_name_ast(name, start);
2753
- const AST_MethodVariant = privatename
2780
+ name = get_symbol_ast(name);
2781
+ const AST_MethodVariant = is_private
2754
2782
  ? AST_PrivateMethod
2755
2783
  : AST_ConciseMethod;
2756
2784
  var node = new AST_MethodVariant({
@@ -2766,57 +2794,13 @@ function parse($TEXT, options) {
2766
2794
  });
2767
2795
  return node;
2768
2796
  }
2769
- const setter_token = S.token;
2770
- if ((name === "get" || name === "set") && setter_token.type === "privatename") {
2771
- next();
2772
-
2773
- const AST_AccessorVariant =
2774
- name === "get"
2775
- ? AST_PrivateGetter
2776
- : AST_PrivateSetter;
2777
-
2778
- return new AST_AccessorVariant({
2779
- start,
2780
- static: is_static,
2781
- key: get_method_name_ast(setter_token.value, start),
2782
- value: create_accessor(),
2783
- end: prev(),
2784
- });
2785
- }
2786
2797
 
2787
- if (name == "get") {
2788
- if (!is("punc") || is("punc", "[")) {
2789
- name = get_method_name_ast(as_property_name(), start);
2790
- return new AST_ObjectGetter({
2791
- start : start,
2792
- static: is_static,
2793
- key : name,
2794
- quote : name instanceof AST_SymbolMethod ?
2795
- setter_token.quote : undefined,
2796
- value : create_accessor(),
2797
- end : prev()
2798
- });
2799
- }
2800
- } else if (name == "set") {
2801
- if (!is("punc") || is("punc", "[")) {
2802
- name = get_method_name_ast(as_property_name(), start);
2803
- return new AST_ObjectSetter({
2804
- start : start,
2805
- static: is_static,
2806
- key : name,
2807
- quote : name instanceof AST_SymbolMethod ?
2808
- setter_token.quote : undefined,
2809
- value : create_accessor(),
2810
- end : prev()
2811
- });
2812
- }
2813
- }
2814
2798
  if (is_class) {
2815
- const key = get_class_property_key_ast(name);
2799
+ const key = get_symbol_ast(name, AST_SymbolClassProperty);
2816
2800
  const quote = key instanceof AST_SymbolClassProperty
2817
2801
  ? property_token.quote
2818
2802
  : undefined;
2819
- const AST_ClassPropertyVariant = privatename
2803
+ const AST_ClassPropertyVariant = is_private
2820
2804
  ? AST_ClassPrivateProperty
2821
2805
  : AST_ClassProperty;
2822
2806
  if (is("operator", "=")) {
@@ -3038,8 +3022,17 @@ function parse($TEXT, options) {
3038
3022
  semicolon();
3039
3023
  } else if ((node = statement(is_default)) instanceof AST_Definitions && is_default) {
3040
3024
  unexpected(node.start);
3041
- } else if (node instanceof AST_Definitions || node instanceof AST_Lambda || node instanceof AST_DefClass) {
3025
+ } else if (
3026
+ node instanceof AST_Definitions
3027
+ || node instanceof AST_Defun
3028
+ || node instanceof AST_DefClass
3029
+ ) {
3042
3030
  exported_definition = node;
3031
+ } else if (
3032
+ node instanceof AST_ClassExpression
3033
+ || node instanceof AST_Function
3034
+ ) {
3035
+ exported_value = node;
3043
3036
  } else if (node instanceof AST_SimpleStatement) {
3044
3037
  exported_value = node.body;
3045
3038
  } else {
@@ -4018,6 +4011,18 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator as
4018
4011
  },
4019
4012
  is_braceless() {
4020
4013
  return this.body[0] instanceof AST_Return && this.body[0].value;
4014
+ },
4015
+ // Default args and expansion don't count, so .argnames.length doesn't cut it
4016
+ length_property() {
4017
+ let length = 0;
4018
+
4019
+ for (const arg of this.argnames) {
4020
+ if (arg instanceof AST_SymbolFunarg || arg instanceof AST_Destructuring) {
4021
+ length++;
4022
+ }
4023
+ }
4024
+
4025
+ return length;
4021
4026
  }
4022
4027
  }, AST_Scope);
4023
4028
 
@@ -10647,11 +10652,8 @@ const TOP = 0b0000010000000000;
10647
10652
 
10648
10653
  const CLEAR_BETWEEN_PASSES = SQUEEZED | OPTIMIZED | TOP;
10649
10654
 
10650
- /*@__INLINE__*/
10651
10655
  const has_flag = (node, flag) => node.flags & flag;
10652
- /*@__INLINE__*/
10653
10656
  const set_flag = (node, flag) => { node.flags |= flag; };
10654
- /*@__INLINE__*/
10655
10657
  const clear_flag = (node, flag) => { node.flags &= ~flag; };
10656
10658
 
10657
10659
  class Compressor extends TreeWalker {
@@ -10694,7 +10696,7 @@ class Compressor extends TreeWalker {
10694
10696
  properties : !false_by_default,
10695
10697
  pure_getters : !false_by_default && "strict",
10696
10698
  pure_funcs : null,
10697
- reduce_funcs : null, // legacy
10699
+ reduce_funcs : !false_by_default,
10698
10700
  reduce_vars : !false_by_default,
10699
10701
  sequences : !false_by_default,
10700
10702
  side_effects : !false_by_default,
@@ -10994,7 +10996,7 @@ function is_modified(compressor, tw, node, value, level, immutable) {
10994
10996
  && parent.expression === node
10995
10997
  && !(value instanceof AST_Arrow)
10996
10998
  && !(value instanceof AST_Class)
10997
- && !parent.is_expr_pure(compressor)
10999
+ && !parent.is_callee_pure(compressor)
10998
11000
  && (!(value instanceof AST_Function)
10999
11001
  || !(parent instanceof AST_New) && value.contains_this())) {
11000
11002
  return true;
@@ -11299,9 +11301,6 @@ function is_modified(compressor, tw, node, value, level, immutable) {
11299
11301
  return true;
11300
11302
  });
11301
11303
  def_reduce_vars(AST_Call, function (tw) {
11302
- // TODO this block should just be { return } but
11303
- // for some reason the _walk function of AST_Call walks the callee last
11304
-
11305
11304
  this.expression.walk(tw);
11306
11305
 
11307
11306
  if (this.optional) {
@@ -13375,9 +13374,7 @@ var static_fns = convert_to_predicate({
13375
13374
  if (compressor.option("unsafe")) {
13376
13375
  var fn = function() {};
13377
13376
  fn.node = this;
13378
- fn.toString = function() {
13379
- return this.node.print_to_string();
13380
- };
13377
+ fn.toString = () => this.print_to_string();
13381
13378
  return fn;
13382
13379
  }
13383
13380
  return this;
@@ -13558,6 +13555,14 @@ var static_fns = convert_to_predicate({
13558
13555
  "POSITIVE_INFINITY",
13559
13556
  ],
13560
13557
  });
13558
+ const regexp_flags = new Set([
13559
+ "dotAll",
13560
+ "global",
13561
+ "ignoreCase",
13562
+ "multiline",
13563
+ "sticky",
13564
+ "unicode",
13565
+ ]);
13561
13566
  def_eval(AST_PropAccess, function(compressor, depth) {
13562
13567
  if (this.optional) {
13563
13568
  const obj = this.expression._eval(compressor, depth);
@@ -13590,12 +13595,19 @@ var static_fns = convert_to_predicate({
13590
13595
  val = global_objs[exp.name];
13591
13596
  } else {
13592
13597
  val = exp._eval(compressor, depth + 1);
13598
+ if (val instanceof RegExp) {
13599
+ if (key == "source") {
13600
+ return regexp_source_fix(val.source);
13601
+ } else if (key == "flags" || regexp_flags.has(key)) {
13602
+ return val[key];
13603
+ }
13604
+ }
13593
13605
  if (!val || val === exp || !HOP(val, key)) return this;
13594
13606
  if (typeof val == "function") switch (key) {
13595
13607
  case "name":
13596
13608
  return val.node.name ? val.node.name.name : "";
13597
13609
  case "length":
13598
- return val.node.argnames.length;
13610
+ return val.node.length_property();
13599
13611
  default:
13600
13612
  return this;
13601
13613
  }
@@ -13647,6 +13659,7 @@ var static_fns = convert_to_predicate({
13647
13659
  var arg = this.args[i];
13648
13660
  var value = arg._eval(compressor, depth);
13649
13661
  if (arg === value) return this;
13662
+ if (arg instanceof AST_Lambda) return this;
13650
13663
  args.push(value);
13651
13664
  }
13652
13665
  try {
@@ -13746,7 +13759,7 @@ var static_fns = convert_to_predicate({
13746
13759
  });
13747
13760
 
13748
13761
  var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError");
13749
- AST_Call.DEFMETHOD("is_expr_pure", function(compressor) {
13762
+ AST_Call.DEFMETHOD("is_callee_pure", function(compressor) {
13750
13763
  if (compressor.option("unsafe")) {
13751
13764
  var expr = this.expression;
13752
13765
  var first_arg = (this.args && this.args[0] && this.args[0].evaluate(compressor));
@@ -13816,7 +13829,7 @@ const pure_prop_access_globals = new Set([
13816
13829
  });
13817
13830
  def_has_side_effects(AST_Call, function(compressor) {
13818
13831
  if (
13819
- !this.is_expr_pure(compressor)
13832
+ !this.is_callee_pure(compressor)
13820
13833
  && (!this.expression.is_call_pure(compressor)
13821
13834
  || this.expression.has_side_effects(compressor))
13822
13835
  ) {
@@ -13978,7 +13991,7 @@ const pure_prop_access_globals = new Set([
13978
13991
  def_may_throw(AST_Call, function(compressor) {
13979
13992
  if (this.optional && is_nullish(this.expression)) return false;
13980
13993
  if (any(this.args, compressor)) return true;
13981
- if (this.is_expr_pure(compressor)) return false;
13994
+ if (this.is_callee_pure(compressor)) return false;
13982
13995
  if (this.expression.may_throw(compressor)) return true;
13983
13996
  return !(this.expression instanceof AST_Lambda)
13984
13997
  || any(this.expression.body, compressor);
@@ -14856,7 +14869,7 @@ AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
14856
14869
  return make_node(AST_Undefined, this);
14857
14870
  }
14858
14871
 
14859
- if (!this.is_expr_pure(compressor)) {
14872
+ if (!this.is_callee_pure(compressor)) {
14860
14873
  if (this.expression.is_call_pure(compressor)) {
14861
14874
  var exprs = this.args.slice();
14862
14875
  exprs.unshift(this.expression.expression);
@@ -15876,7 +15889,7 @@ def_optimize(AST_Call, function(self, compressor) {
15876
15889
 
15877
15890
  var stat = is_func && fn.body[0];
15878
15891
  var is_regular_func = is_func && !fn.is_generator && !fn.async;
15879
- var can_inline = is_regular_func && compressor.option("inline") && !self.is_expr_pure(compressor);
15892
+ var can_inline = is_regular_func && compressor.option("inline") && !self.is_callee_pure(compressor);
15880
15893
  if (can_inline && stat instanceof AST_Return) {
15881
15894
  let returned = stat.value;
15882
15895
  if (!returned || returned.is_constant_expression()) {
@@ -16902,19 +16915,26 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
16902
16915
  let fixed = self.fixed_value();
16903
16916
  let single_use = def.single_use
16904
16917
  && !(parent instanceof AST_Call
16905
- && (parent.is_expr_pure(compressor))
16918
+ && (parent.is_callee_pure(compressor))
16906
16919
  || has_annotation(parent, _NOINLINE))
16907
16920
  && !(parent instanceof AST_Export
16908
16921
  && fixed instanceof AST_Lambda
16909
16922
  && fixed.name);
16910
16923
 
16924
+ if (single_use && fixed instanceof AST_Node) {
16925
+ single_use =
16926
+ !fixed.has_side_effects(compressor)
16927
+ && !fixed.may_throw(compressor);
16928
+ }
16929
+
16911
16930
  if (single_use && (fixed instanceof AST_Lambda || fixed instanceof AST_Class)) {
16912
16931
  if (retain_top_func(fixed, compressor)) {
16913
16932
  single_use = false;
16914
16933
  } else if (def.scope !== self.scope
16915
16934
  && (def.escaped == 1
16916
16935
  || has_flag(fixed, INLINED)
16917
- || within_array_or_object_literal(compressor))) {
16936
+ || within_array_or_object_literal(compressor)
16937
+ || !compressor.option("reduce_funcs"))) {
16918
16938
  single_use = false;
16919
16939
  } else if (recursive_ref(compressor, def)) {
16920
16940
  single_use = false;
@@ -16930,6 +16950,7 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
16930
16950
  }
16931
16951
  }
16932
16952
  }
16953
+
16933
16954
  if (single_use && fixed instanceof AST_Lambda) {
16934
16955
  single_use =
16935
16956
  def.scope === self.scope
@@ -16939,15 +16960,6 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
16939
16960
  && !scope_encloses_variables_in_this_scope(nearest_scope, fixed)
16940
16961
  && !(fixed.name && fixed.name.definition().recursive_refs > 0);
16941
16962
  }
16942
- if (single_use && fixed instanceof AST_Class) {
16943
- const extends_inert = !fixed.extends
16944
- || !fixed.extends.may_throw(compressor)
16945
- && !fixed.extends.has_side_effects(compressor);
16946
- single_use = extends_inert
16947
- && !fixed.properties.some(prop =>
16948
- prop.may_throw(compressor) || prop.has_side_effects(compressor)
16949
- );
16950
- }
16951
16963
 
16952
16964
  if (single_use && fixed) {
16953
16965
  if (fixed instanceof AST_DefClass) {
@@ -17024,6 +17036,7 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
17024
17036
  }
17025
17037
  }
17026
17038
  }
17039
+
17027
17040
  return self;
17028
17041
  });
17029
17042
 
@@ -17610,27 +17623,40 @@ function safe_to_flatten(value, compressor) {
17610
17623
 
17611
17624
  AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
17612
17625
  if (!compressor.option("properties")) return;
17626
+ if (key === "__proto__") return;
17627
+
17613
17628
  var arrows = compressor.option("unsafe_arrows") && compressor.option("ecma") >= 2015;
17614
17629
  var expr = this.expression;
17615
17630
  if (expr instanceof AST_Object) {
17616
17631
  var props = expr.properties;
17632
+
17617
17633
  for (var i = props.length; --i >= 0;) {
17618
17634
  var prop = props[i];
17635
+
17619
17636
  if ("" + (prop instanceof AST_ConciseMethod ? prop.key.name : prop.key) == key) {
17620
- if (!props.every((prop) => {
17621
- return prop instanceof AST_ObjectKeyVal
17622
- || arrows && prop instanceof AST_ConciseMethod && !prop.is_generator;
17623
- })) break;
17624
- if (!safe_to_flatten(prop.value, compressor)) break;
17637
+ const all_props_flattenable = props.every((p) =>
17638
+ (p instanceof AST_ObjectKeyVal
17639
+ || arrows && p instanceof AST_ConciseMethod && !p.is_generator
17640
+ )
17641
+ && !p.computed_key()
17642
+ );
17643
+
17644
+ if (!all_props_flattenable) return;
17645
+ if (!safe_to_flatten(prop.value, compressor)) return;
17646
+
17625
17647
  return make_node(AST_Sub, this, {
17626
17648
  expression: make_node(AST_Array, expr, {
17627
17649
  elements: props.map(function(prop) {
17628
17650
  var v = prop.value;
17629
- if (v instanceof AST_Accessor) v = make_node(AST_Function, v, v);
17651
+ if (v instanceof AST_Accessor) {
17652
+ v = make_node(AST_Function, v, v);
17653
+ }
17654
+
17630
17655
  var k = prop.key;
17631
17656
  if (k instanceof AST_Node && !(k instanceof AST_SymbolMethod)) {
17632
17657
  return make_sequence(prop, [ k, v ]);
17633
17658
  }
17659
+
17634
17660
  return v;
17635
17661
  })
17636
17662
  }),
@@ -26430,6 +26456,7 @@ async function minify(files, options) {
26430
26456
  rename: undefined,
26431
26457
  safari10: false,
26432
26458
  sourceMap: false,
26459
+ spidermonkey: false,
26433
26460
  timings: false,
26434
26461
  toplevel: false,
26435
26462
  warnings: false,
@@ -26501,20 +26528,32 @@ async function minify(files, options) {
26501
26528
  if (files instanceof AST_Toplevel) {
26502
26529
  toplevel = files;
26503
26530
  } else {
26504
- if (typeof files == "string") {
26531
+ if (typeof files == "string" || (options.parse.spidermonkey && !Array.isArray(files))) {
26505
26532
  files = [ files ];
26506
26533
  }
26507
26534
  options.parse = options.parse || {};
26508
26535
  options.parse.toplevel = null;
26509
- for (var name in files) if (HOP(files, name)) {
26510
- options.parse.filename = name;
26511
- options.parse.toplevel = parse(files[name], options.parse);
26512
- if (options.sourceMap && options.sourceMap.content == "inline") {
26513
- if (Object.keys(files).length > 1)
26514
- throw new Error("inline source map only works with singular input");
26515
- options.sourceMap.content = read_source_map(files[name]);
26536
+
26537
+ if (options.parse.spidermonkey) {
26538
+ options.parse.toplevel = AST_Node.from_mozilla_ast(Object.keys(files).reduce(function(toplevel, name) {
26539
+ if (!toplevel) return files[name];
26540
+ toplevel.body = toplevel.body.concat(files[name].body);
26541
+ return toplevel;
26542
+ }, null));
26543
+ } else {
26544
+ delete options.parse.spidermonkey;
26545
+
26546
+ for (var name in files) if (HOP(files, name)) {
26547
+ options.parse.filename = name;
26548
+ options.parse.toplevel = parse(files[name], options.parse);
26549
+ if (options.sourceMap && options.sourceMap.content == "inline") {
26550
+ if (Object.keys(files).length > 1)
26551
+ throw new Error("inline source map only works with singular input");
26552
+ options.sourceMap.content = read_source_map(files[name]);
26553
+ }
26516
26554
  }
26517
26555
  }
26556
+
26518
26557
  toplevel = options.parse.toplevel;
26519
26558
  }
26520
26559
  if (quoted_props && options.mangle.properties.keep_quoted !== "strict") {
@@ -26550,6 +26589,9 @@ async function minify(files, options) {
26550
26589
  if (options.format.ast) {
26551
26590
  result.ast = toplevel;
26552
26591
  }
26592
+ if (options.format.spidermonkey) {
26593
+ result.ast = toplevel.to_mozilla_ast();
26594
+ }
26553
26595
  if (!HOP(options.format, "code") || options.format.code) {
26554
26596
  if (options.sourceMap) {
26555
26597
  options.format.source_map = await SourceMap({
@@ -26567,6 +26609,7 @@ async function minify(files, options) {
26567
26609
  }
26568
26610
  delete options.format.ast;
26569
26611
  delete options.format.code;
26612
+ delete options.format.spidermonkey;
26570
26613
  var stream = OutputStream(options.format);
26571
26614
  toplevel.print(stream);
26572
26615
  result.code = stream.get();
package/lib/ast.js CHANGED
@@ -524,6 +524,18 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator as
524
524
  },
525
525
  is_braceless() {
526
526
  return this.body[0] instanceof AST_Return && this.body[0].value;
527
+ },
528
+ // Default args and expansion don't count, so .argnames.length doesn't cut it
529
+ length_property() {
530
+ let length = 0;
531
+
532
+ for (const arg of this.argnames) {
533
+ if (arg instanceof AST_SymbolFunarg || arg instanceof AST_Destructuring) {
534
+ length++;
535
+ }
536
+ }
537
+
538
+ return length;
527
539
  }
528
540
  }, AST_Scope);
529
541
 
@@ -209,11 +209,8 @@ const TOP = 0b0000010000000000;
209
209
 
210
210
  const CLEAR_BETWEEN_PASSES = SQUEEZED | OPTIMIZED | TOP;
211
211
 
212
- /*@__INLINE__*/
213
212
  const has_flag = (node, flag) => node.flags & flag;
214
- /*@__INLINE__*/
215
213
  const set_flag = (node, flag) => { node.flags |= flag; };
216
- /*@__INLINE__*/
217
214
  const clear_flag = (node, flag) => { node.flags &= ~flag; };
218
215
 
219
216
  class Compressor extends TreeWalker {
@@ -256,7 +253,7 @@ class Compressor extends TreeWalker {
256
253
  properties : !false_by_default,
257
254
  pure_getters : !false_by_default && "strict",
258
255
  pure_funcs : null,
259
- reduce_funcs : null, // legacy
256
+ reduce_funcs : !false_by_default,
260
257
  reduce_vars : !false_by_default,
261
258
  sequences : !false_by_default,
262
259
  side_effects : !false_by_default,
@@ -556,7 +553,7 @@ function is_modified(compressor, tw, node, value, level, immutable) {
556
553
  && parent.expression === node
557
554
  && !(value instanceof AST_Arrow)
558
555
  && !(value instanceof AST_Class)
559
- && !parent.is_expr_pure(compressor)
556
+ && !parent.is_callee_pure(compressor)
560
557
  && (!(value instanceof AST_Function)
561
558
  || !(parent instanceof AST_New) && value.contains_this())) {
562
559
  return true;
@@ -861,9 +858,6 @@ function is_modified(compressor, tw, node, value, level, immutable) {
861
858
  return true;
862
859
  });
863
860
  def_reduce_vars(AST_Call, function (tw) {
864
- // TODO this block should just be { return } but
865
- // for some reason the _walk function of AST_Call walks the callee last
866
-
867
861
  this.expression.walk(tw);
868
862
 
869
863
  if (this.optional) {
@@ -2937,9 +2931,7 @@ var static_fns = convert_to_predicate({
2937
2931
  if (compressor.option("unsafe")) {
2938
2932
  var fn = function() {};
2939
2933
  fn.node = this;
2940
- fn.toString = function() {
2941
- return this.node.print_to_string();
2942
- };
2934
+ fn.toString = () => this.print_to_string();
2943
2935
  return fn;
2944
2936
  }
2945
2937
  return this;
@@ -3120,6 +3112,14 @@ var static_fns = convert_to_predicate({
3120
3112
  "POSITIVE_INFINITY",
3121
3113
  ],
3122
3114
  });
3115
+ const regexp_flags = new Set([
3116
+ "dotAll",
3117
+ "global",
3118
+ "ignoreCase",
3119
+ "multiline",
3120
+ "sticky",
3121
+ "unicode",
3122
+ ]);
3123
3123
  def_eval(AST_PropAccess, function(compressor, depth) {
3124
3124
  if (this.optional) {
3125
3125
  const obj = this.expression._eval(compressor, depth);
@@ -3152,12 +3152,19 @@ var static_fns = convert_to_predicate({
3152
3152
  val = global_objs[exp.name];
3153
3153
  } else {
3154
3154
  val = exp._eval(compressor, depth + 1);
3155
+ if (val instanceof RegExp) {
3156
+ if (key == "source") {
3157
+ return regexp_source_fix(val.source);
3158
+ } else if (key == "flags" || regexp_flags.has(key)) {
3159
+ return val[key];
3160
+ }
3161
+ }
3155
3162
  if (!val || val === exp || !HOP(val, key)) return this;
3156
3163
  if (typeof val == "function") switch (key) {
3157
3164
  case "name":
3158
3165
  return val.node.name ? val.node.name.name : "";
3159
3166
  case "length":
3160
- return val.node.argnames.length;
3167
+ return val.node.length_property();
3161
3168
  default:
3162
3169
  return this;
3163
3170
  }
@@ -3209,6 +3216,7 @@ var static_fns = convert_to_predicate({
3209
3216
  var arg = this.args[i];
3210
3217
  var value = arg._eval(compressor, depth);
3211
3218
  if (arg === value) return this;
3219
+ if (arg instanceof AST_Lambda) return this;
3212
3220
  args.push(value);
3213
3221
  }
3214
3222
  try {
@@ -3308,7 +3316,7 @@ var static_fns = convert_to_predicate({
3308
3316
  });
3309
3317
 
3310
3318
  var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError");
3311
- AST_Call.DEFMETHOD("is_expr_pure", function(compressor) {
3319
+ AST_Call.DEFMETHOD("is_callee_pure", function(compressor) {
3312
3320
  if (compressor.option("unsafe")) {
3313
3321
  var expr = this.expression;
3314
3322
  var first_arg = (this.args && this.args[0] && this.args[0].evaluate(compressor));
@@ -3378,7 +3386,7 @@ const pure_prop_access_globals = new Set([
3378
3386
  });
3379
3387
  def_has_side_effects(AST_Call, function(compressor) {
3380
3388
  if (
3381
- !this.is_expr_pure(compressor)
3389
+ !this.is_callee_pure(compressor)
3382
3390
  && (!this.expression.is_call_pure(compressor)
3383
3391
  || this.expression.has_side_effects(compressor))
3384
3392
  ) {
@@ -3540,7 +3548,7 @@ const pure_prop_access_globals = new Set([
3540
3548
  def_may_throw(AST_Call, function(compressor) {
3541
3549
  if (this.optional && is_nullish(this.expression)) return false;
3542
3550
  if (any(this.args, compressor)) return true;
3543
- if (this.is_expr_pure(compressor)) return false;
3551
+ if (this.is_callee_pure(compressor)) return false;
3544
3552
  if (this.expression.may_throw(compressor)) return true;
3545
3553
  return !(this.expression instanceof AST_Lambda)
3546
3554
  || any(this.expression.body, compressor);
@@ -4418,7 +4426,7 @@ AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
4418
4426
  return make_node(AST_Undefined, this);
4419
4427
  }
4420
4428
 
4421
- if (!this.is_expr_pure(compressor)) {
4429
+ if (!this.is_callee_pure(compressor)) {
4422
4430
  if (this.expression.is_call_pure(compressor)) {
4423
4431
  var exprs = this.args.slice();
4424
4432
  exprs.unshift(this.expression.expression);
@@ -5438,7 +5446,7 @@ def_optimize(AST_Call, function(self, compressor) {
5438
5446
 
5439
5447
  var stat = is_func && fn.body[0];
5440
5448
  var is_regular_func = is_func && !fn.is_generator && !fn.async;
5441
- var can_inline = is_regular_func && compressor.option("inline") && !self.is_expr_pure(compressor);
5449
+ var can_inline = is_regular_func && compressor.option("inline") && !self.is_callee_pure(compressor);
5442
5450
  if (can_inline && stat instanceof AST_Return) {
5443
5451
  let returned = stat.value;
5444
5452
  if (!returned || returned.is_constant_expression()) {
@@ -6464,19 +6472,26 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
6464
6472
  let fixed = self.fixed_value();
6465
6473
  let single_use = def.single_use
6466
6474
  && !(parent instanceof AST_Call
6467
- && (parent.is_expr_pure(compressor))
6475
+ && (parent.is_callee_pure(compressor))
6468
6476
  || has_annotation(parent, _NOINLINE))
6469
6477
  && !(parent instanceof AST_Export
6470
6478
  && fixed instanceof AST_Lambda
6471
6479
  && fixed.name);
6472
6480
 
6481
+ if (single_use && fixed instanceof AST_Node) {
6482
+ single_use =
6483
+ !fixed.has_side_effects(compressor)
6484
+ && !fixed.may_throw(compressor);
6485
+ }
6486
+
6473
6487
  if (single_use && (fixed instanceof AST_Lambda || fixed instanceof AST_Class)) {
6474
6488
  if (retain_top_func(fixed, compressor)) {
6475
6489
  single_use = false;
6476
6490
  } else if (def.scope !== self.scope
6477
6491
  && (def.escaped == 1
6478
6492
  || has_flag(fixed, INLINED)
6479
- || within_array_or_object_literal(compressor))) {
6493
+ || within_array_or_object_literal(compressor)
6494
+ || !compressor.option("reduce_funcs"))) {
6480
6495
  single_use = false;
6481
6496
  } else if (recursive_ref(compressor, def)) {
6482
6497
  single_use = false;
@@ -6492,6 +6507,7 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
6492
6507
  }
6493
6508
  }
6494
6509
  }
6510
+
6495
6511
  if (single_use && fixed instanceof AST_Lambda) {
6496
6512
  single_use =
6497
6513
  def.scope === self.scope
@@ -6501,15 +6517,6 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
6501
6517
  && !scope_encloses_variables_in_this_scope(nearest_scope, fixed)
6502
6518
  && !(fixed.name && fixed.name.definition().recursive_refs > 0);
6503
6519
  }
6504
- if (single_use && fixed instanceof AST_Class) {
6505
- const extends_inert = !fixed.extends
6506
- || !fixed.extends.may_throw(compressor)
6507
- && !fixed.extends.has_side_effects(compressor);
6508
- single_use = extends_inert
6509
- && !fixed.properties.some(prop =>
6510
- prop.may_throw(compressor) || prop.has_side_effects(compressor)
6511
- );
6512
- }
6513
6520
 
6514
6521
  if (single_use && fixed) {
6515
6522
  if (fixed instanceof AST_DefClass) {
@@ -6586,6 +6593,7 @@ def_optimize(AST_SymbolRef, function(self, compressor) {
6586
6593
  }
6587
6594
  }
6588
6595
  }
6596
+
6589
6597
  return self;
6590
6598
  });
6591
6599
 
@@ -7172,27 +7180,40 @@ function safe_to_flatten(value, compressor) {
7172
7180
 
7173
7181
  AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
7174
7182
  if (!compressor.option("properties")) return;
7183
+ if (key === "__proto__") return;
7184
+
7175
7185
  var arrows = compressor.option("unsafe_arrows") && compressor.option("ecma") >= 2015;
7176
7186
  var expr = this.expression;
7177
7187
  if (expr instanceof AST_Object) {
7178
7188
  var props = expr.properties;
7189
+
7179
7190
  for (var i = props.length; --i >= 0;) {
7180
7191
  var prop = props[i];
7192
+
7181
7193
  if ("" + (prop instanceof AST_ConciseMethod ? prop.key.name : prop.key) == key) {
7182
- if (!props.every((prop) => {
7183
- return prop instanceof AST_ObjectKeyVal
7184
- || arrows && prop instanceof AST_ConciseMethod && !prop.is_generator;
7185
- })) break;
7186
- if (!safe_to_flatten(prop.value, compressor)) break;
7194
+ const all_props_flattenable = props.every((p) =>
7195
+ (p instanceof AST_ObjectKeyVal
7196
+ || arrows && p instanceof AST_ConciseMethod && !p.is_generator
7197
+ )
7198
+ && !p.computed_key()
7199
+ );
7200
+
7201
+ if (!all_props_flattenable) return;
7202
+ if (!safe_to_flatten(prop.value, compressor)) return;
7203
+
7187
7204
  return make_node(AST_Sub, this, {
7188
7205
  expression: make_node(AST_Array, expr, {
7189
7206
  elements: props.map(function(prop) {
7190
7207
  var v = prop.value;
7191
- if (v instanceof AST_Accessor) v = make_node(AST_Function, v, v);
7208
+ if (v instanceof AST_Accessor) {
7209
+ v = make_node(AST_Function, v, v);
7210
+ }
7211
+
7192
7212
  var k = prop.key;
7193
7213
  if (k instanceof AST_Node && !(k instanceof AST_SymbolMethod)) {
7194
7214
  return make_sequence(prop, [ k, v ]);
7195
7215
  }
7216
+
7196
7217
  return v;
7197
7218
  })
7198
7219
  }),
package/lib/minify.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  map_to_object,
8
8
  HOP,
9
9
  } from "./utils/index.js";
10
- import { AST_Toplevel } from "./ast.js";
10
+ import { AST_Toplevel, AST_Node } from "./ast.js";
11
11
  import { parse } from "./parse.js";
12
12
  import { OutputStream } from "./output.js";
13
13
  import { Compressor } from "./compress/index.js";
@@ -77,6 +77,7 @@ async function minify(files, options) {
77
77
  rename: undefined,
78
78
  safari10: false,
79
79
  sourceMap: false,
80
+ spidermonkey: false,
80
81
  timings: false,
81
82
  toplevel: false,
82
83
  warnings: false,
@@ -148,20 +149,32 @@ async function minify(files, options) {
148
149
  if (files instanceof AST_Toplevel) {
149
150
  toplevel = files;
150
151
  } else {
151
- if (typeof files == "string") {
152
+ if (typeof files == "string" || (options.parse.spidermonkey && !Array.isArray(files))) {
152
153
  files = [ files ];
153
154
  }
154
155
  options.parse = options.parse || {};
155
156
  options.parse.toplevel = null;
156
- for (var name in files) if (HOP(files, name)) {
157
- options.parse.filename = name;
158
- options.parse.toplevel = parse(files[name], options.parse);
159
- if (options.sourceMap && options.sourceMap.content == "inline") {
160
- if (Object.keys(files).length > 1)
161
- throw new Error("inline source map only works with singular input");
162
- options.sourceMap.content = read_source_map(files[name]);
157
+
158
+ if (options.parse.spidermonkey) {
159
+ options.parse.toplevel = AST_Node.from_mozilla_ast(Object.keys(files).reduce(function(toplevel, name) {
160
+ if (!toplevel) return files[name];
161
+ toplevel.body = toplevel.body.concat(files[name].body);
162
+ return toplevel;
163
+ }, null));
164
+ } else {
165
+ delete options.parse.spidermonkey;
166
+
167
+ for (var name in files) if (HOP(files, name)) {
168
+ options.parse.filename = name;
169
+ options.parse.toplevel = parse(files[name], options.parse);
170
+ if (options.sourceMap && options.sourceMap.content == "inline") {
171
+ if (Object.keys(files).length > 1)
172
+ throw new Error("inline source map only works with singular input");
173
+ options.sourceMap.content = read_source_map(files[name]);
174
+ }
163
175
  }
164
176
  }
177
+
165
178
  toplevel = options.parse.toplevel;
166
179
  }
167
180
  if (quoted_props && options.mangle.properties.keep_quoted !== "strict") {
@@ -203,6 +216,9 @@ async function minify(files, options) {
203
216
  if (options.format.ast) {
204
217
  result.ast = toplevel;
205
218
  }
219
+ if (options.format.spidermonkey) {
220
+ result.ast = toplevel.to_mozilla_ast();
221
+ }
206
222
  if (!HOP(options.format, "code") || options.format.code) {
207
223
  if (options.sourceMap) {
208
224
  options.format.source_map = await SourceMap({
@@ -220,6 +236,7 @@ async function minify(files, options) {
220
236
  }
221
237
  delete options.format.ast;
222
238
  delete options.format.code;
239
+ delete options.format.spidermonkey;
223
240
  var stream = OutputStream(options.format);
224
241
  toplevel.print(stream);
225
242
  result.code = stream.get();
package/lib/parse.js CHANGED
@@ -102,7 +102,6 @@ import {
102
102
  AST_Label,
103
103
  AST_LabeledStatement,
104
104
  AST_LabelRef,
105
- AST_Lambda,
106
105
  AST_Let,
107
106
  AST_NameMapping,
108
107
  AST_New,
@@ -1293,7 +1292,7 @@ function parse($TEXT, options) {
1293
1292
  if (is_if_body) {
1294
1293
  croak("classes are not allowed as the body of an if");
1295
1294
  }
1296
- return class_(AST_DefClass);
1295
+ return class_(AST_DefClass, is_export_default);
1297
1296
 
1298
1297
  case "function":
1299
1298
  next();
@@ -2488,7 +2487,7 @@ function parse($TEXT, options) {
2488
2487
  return new AST_Object({ properties: a });
2489
2488
  });
2490
2489
 
2491
- function class_(KindOfClass) {
2490
+ function class_(KindOfClass, is_export_default) {
2492
2491
  var start, method, class_name, extends_, a = [];
2493
2492
 
2494
2493
  S.input.push_directives_stack(); // Push directive stack, but not scope stack
@@ -2499,7 +2498,11 @@ function parse($TEXT, options) {
2499
2498
  }
2500
2499
 
2501
2500
  if (KindOfClass === AST_DefClass && !class_name) {
2502
- unexpected();
2501
+ if (is_export_default) {
2502
+ KindOfClass = AST_ClassExpression;
2503
+ } else {
2504
+ unexpected();
2505
+ }
2503
2506
  }
2504
2507
 
2505
2508
  if (S.token.value == "extends") {
@@ -2532,9 +2535,9 @@ function parse($TEXT, options) {
2532
2535
  }
2533
2536
 
2534
2537
  function concise_method_or_getset(name, start, is_class) {
2535
- var get_method_name_ast = function(name, start) {
2538
+ const get_symbol_ast = (name, SymbolClass = AST_SymbolMethod) => {
2536
2539
  if (typeof name === "string" || typeof name === "number") {
2537
- return new AST_SymbolMethod({
2540
+ return new SymbolClass({
2538
2541
  start,
2539
2542
  name: "" + name,
2540
2543
  end: prev()
@@ -2544,47 +2547,71 @@ function parse($TEXT, options) {
2544
2547
  }
2545
2548
  return name;
2546
2549
  };
2547
- const get_class_property_key_ast = (name) => {
2548
- if (typeof name === "string" || typeof name === "number") {
2549
- return new AST_SymbolClassProperty({
2550
- start: property_token,
2551
- end: property_token,
2552
- name: "" + name
2553
- });
2554
- } else if (name === null) {
2555
- unexpected();
2556
- }
2557
- return name;
2558
- };
2559
- var privatename = start.type == "privatename";
2550
+
2551
+ const is_not_method_start = () =>
2552
+ !is("punc", "(") && !is("punc", ",") && !is("punc", "}") && !is("operator", "=");
2553
+
2560
2554
  var is_async = false;
2561
2555
  var is_static = false;
2562
2556
  var is_generator = false;
2563
- var property_token = start;
2564
- if (is_class && name === "static" && !is("punc", "(")) {
2557
+ var is_private = false;
2558
+ var accessor_type = null;
2559
+
2560
+ if (is_class && name === "static" && is_not_method_start()) {
2565
2561
  is_static = true;
2566
- property_token = S.token;
2567
- privatename = property_token.type == "privatename";
2568
2562
  name = as_property_name();
2569
2563
  }
2570
- if (name === "async" && !is("punc", "(") && !is("punc", ",") && !is("punc", "}") && !is("operator", "=")) {
2564
+ if (name === "async" && is_not_method_start()) {
2571
2565
  is_async = true;
2572
- property_token = S.token;
2573
- privatename = property_token.type == "privatename";
2574
2566
  name = as_property_name();
2575
2567
  }
2576
- if (name === null) {
2568
+ if (prev().type === "operator" && prev().value === "*") {
2577
2569
  is_generator = true;
2578
- property_token = S.token;
2579
- privatename = property_token.type == "privatename";
2580
2570
  name = as_property_name();
2581
- if (name === null) {
2582
- unexpected();
2571
+ }
2572
+ if ((name === "get" || name === "set") && is_not_method_start()) {
2573
+ accessor_type = name;
2574
+ name = as_property_name();
2575
+ }
2576
+ if (prev().type === "privatename") {
2577
+ is_private = true;
2578
+ }
2579
+
2580
+ const property_token = prev();
2581
+
2582
+ if (accessor_type != null) {
2583
+ if (!is_private) {
2584
+ const AccessorClass = accessor_type === "get"
2585
+ ? AST_ObjectGetter
2586
+ : AST_ObjectSetter;
2587
+
2588
+ name = get_symbol_ast(name);
2589
+ return new AccessorClass({
2590
+ start,
2591
+ static: is_static,
2592
+ key: name,
2593
+ quote: name instanceof AST_SymbolMethod ? property_token.quote : undefined,
2594
+ value: create_accessor(),
2595
+ end: prev()
2596
+ });
2597
+ } else {
2598
+ const AccessorClass = accessor_type === "get"
2599
+ ? AST_PrivateGetter
2600
+ : AST_PrivateSetter;
2601
+
2602
+ return new AccessorClass({
2603
+ start,
2604
+ static: is_static,
2605
+ key: get_symbol_ast(name),
2606
+ value: create_accessor(),
2607
+ end: prev(),
2608
+ });
2583
2609
  }
2584
2610
  }
2611
+
2585
2612
  if (is("punc", "(")) {
2586
- name = get_method_name_ast(name, start);
2587
- const AST_MethodVariant = privatename
2613
+ name = get_symbol_ast(name);
2614
+ const AST_MethodVariant = is_private
2588
2615
  ? AST_PrivateMethod
2589
2616
  : AST_ConciseMethod;
2590
2617
  var node = new AST_MethodVariant({
@@ -2600,57 +2627,13 @@ function parse($TEXT, options) {
2600
2627
  });
2601
2628
  return node;
2602
2629
  }
2603
- const setter_token = S.token;
2604
- if ((name === "get" || name === "set") && setter_token.type === "privatename") {
2605
- next();
2606
-
2607
- const AST_AccessorVariant =
2608
- name === "get"
2609
- ? AST_PrivateGetter
2610
- : AST_PrivateSetter;
2611
-
2612
- return new AST_AccessorVariant({
2613
- start,
2614
- static: is_static,
2615
- key: get_method_name_ast(setter_token.value, start),
2616
- value: create_accessor(),
2617
- end: prev(),
2618
- });
2619
- }
2620
2630
 
2621
- if (name == "get") {
2622
- if (!is("punc") || is("punc", "[")) {
2623
- name = get_method_name_ast(as_property_name(), start);
2624
- return new AST_ObjectGetter({
2625
- start : start,
2626
- static: is_static,
2627
- key : name,
2628
- quote : name instanceof AST_SymbolMethod ?
2629
- setter_token.quote : undefined,
2630
- value : create_accessor(),
2631
- end : prev()
2632
- });
2633
- }
2634
- } else if (name == "set") {
2635
- if (!is("punc") || is("punc", "[")) {
2636
- name = get_method_name_ast(as_property_name(), start);
2637
- return new AST_ObjectSetter({
2638
- start : start,
2639
- static: is_static,
2640
- key : name,
2641
- quote : name instanceof AST_SymbolMethod ?
2642
- setter_token.quote : undefined,
2643
- value : create_accessor(),
2644
- end : prev()
2645
- });
2646
- }
2647
- }
2648
2631
  if (is_class) {
2649
- const key = get_class_property_key_ast(name, property_token);
2632
+ const key = get_symbol_ast(name, AST_SymbolClassProperty);
2650
2633
  const quote = key instanceof AST_SymbolClassProperty
2651
2634
  ? property_token.quote
2652
2635
  : undefined;
2653
- const AST_ClassPropertyVariant = privatename
2636
+ const AST_ClassPropertyVariant = is_private
2654
2637
  ? AST_ClassPrivateProperty
2655
2638
  : AST_ClassProperty;
2656
2639
  if (is("operator", "=")) {
@@ -2872,8 +2855,17 @@ function parse($TEXT, options) {
2872
2855
  semicolon();
2873
2856
  } else if ((node = statement(is_default)) instanceof AST_Definitions && is_default) {
2874
2857
  unexpected(node.start);
2875
- } else if (node instanceof AST_Definitions || node instanceof AST_Lambda || node instanceof AST_DefClass) {
2858
+ } else if (
2859
+ node instanceof AST_Definitions
2860
+ || node instanceof AST_Defun
2861
+ || node instanceof AST_DefClass
2862
+ ) {
2876
2863
  exported_definition = node;
2864
+ } else if (
2865
+ node instanceof AST_ClassExpression
2866
+ || node instanceof AST_Function
2867
+ ) {
2868
+ exported_value = node;
2877
2869
  } else if (node instanceof AST_SimpleStatement) {
2878
2870
  exported_value = node.body;
2879
2871
  } else {
@@ -196,7 +196,7 @@ function mergeSort(array, cmp) {
196
196
  function makePredicate(words) {
197
197
  if (!Array.isArray(words)) words = words.split(" ");
198
198
 
199
- return new Set(words);
199
+ return new Set(words.sort());
200
200
  }
201
201
 
202
202
  function map_add(map, key, value) {
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.6.1",
7
+ "version": "5.7.0",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },
@@ -24,7 +24,8 @@
24
24
  "./dist/bundle.min.js"
25
25
  ],
26
26
  "./package": "./package.json",
27
- "./package.json": "./package.json"
27
+ "./package.json": "./package.json",
28
+ "./bin/terser": "./bin/terser"
28
29
  },
29
30
  "types": "tools/terser.d.ts",
30
31
  "bin": {
@@ -124,7 +125,7 @@
124
125
  "no-unused-vars": [
125
126
  "error",
126
127
  {
127
- "varsIgnorePattern": "^_$"
128
+ "varsIgnorePattern": "^_"
128
129
  }
129
130
  ],
130
131
  "no-tabs": "error",