terser 5.10.0 → 5.12.1

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,20 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.12.1
4
+
5
+ - Fixed an issue with function definitions inside blocks (#1155)
6
+ - Fixed parens of `new` in some situations (closes #1159)
7
+
8
+ ## v5.12.0
9
+
10
+ - `TERSER_DEBUG_DIR` environment variable
11
+ - @copyright comments are now preserved with the comments="some" option (#1153)
12
+
13
+ ## v5.11.0
14
+
15
+ - Unicode code point escapes (`\u{abcde}`) are not emitted inside RegExp literals anymore (#1147)
16
+ - acorn is now a regular dependency
17
+
3
18
  ## v5.10.0
4
19
 
5
20
  - Massive optimization to max_line_len (#1109)
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![Travis Build][travis-image]][travis-url]
6
6
  [![Opencollective financial contributors][opencollective-contributors]][opencollective-url]
7
7
 
8
- A JavaScript parser and mangler/compressor toolkit for ES6+.
8
+ A JavaScript mangler/compressor toolkit for ES6+.
9
9
 
10
10
  *note*: You can support this project on patreon: <a target="_blank" rel="nofollow" href="https://www.patreon.com/fabiosantoscode"><img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" alt="patron" width="100px" height="auto"></a>. Check out [PATRONS.md](https://github.com/terser/terser/blob/master/PATRONS.md) for our first-tier patrons.
11
11
 
@@ -60,6 +60,12 @@ in sequence and apply any compression options. The files are parsed in the
60
60
  same global scope, that is, a reference from a file to some
61
61
  variable/function declared in another file will be matched properly.
62
62
 
63
+ Command line arguments that take options (like --parse, --compress, --mangle and
64
+ --format) can take in a comma-separated list of default option overrides. For
65
+ instance:
66
+
67
+ terser input.js --compress ecma=2015,computed_props=false
68
+
63
69
  If no input file is specified, Terser will read from STDIN.
64
70
 
65
71
  If you wish to pass your options before the input files, separate the two with
@@ -121,8 +127,8 @@ a double dash to prevent input files being used as option arguments:
121
127
  as JSON to STDOUT respectively.
122
128
  --comments [filter] Preserve copyright comments in the output. By
123
129
  default this works like Google Closure, keeping
124
- JSDoc-style comments that contain "@license" or
125
- "@preserve". You can optionally pass one of the
130
+ JSDoc-style comments that contain e.g. "@license",
131
+ or start with "!". You can optionally pass one of the
126
132
  following arguments to this flag:
127
133
  - "all" to keep all comments
128
134
  - `false` to omit comments in the output
@@ -983,8 +989,8 @@ as "output options".
983
989
  statement.
984
990
 
985
991
  - `comments` (default `"some"`) -- by default it keeps JSDoc-style comments
986
- that contain "@license", "@preserve" or start with `!`, pass `true` or
987
- `"all"` to preserve all comments, `false` to omit comments in the output,
992
+ that contain "@license", "@copyright", "@preserve" or start with `!`, pass `true`
993
+ or `"all"` to preserve all comments, `false` to omit comments in the output,
988
994
  a regular expression string (e.g. `/^!/`) or a function.
989
995
 
990
996
  - `ecma` (default `5`) -- set desired EcmaScript standard version for output.
@@ -1056,9 +1062,9 @@ as "output options".
1056
1062
  ### Keeping copyright notices or other comments
1057
1063
 
1058
1064
  You can pass `--comments` to retain certain comments in the output. By
1059
- default it will keep JSDoc-style comments that contain "@preserve",
1060
- "@license" or "@cc_on" (conditional compilation for IE). You can pass
1061
- `--comments all` to keep all the comments, or a valid JavaScript regexp to
1065
+ default it will keep comments starting with "!" and JSDoc-style comments that
1066
+ contain "@preserve", "@copyright", "@license" or "@cc_on" (conditional compilation for IE).
1067
+ You can pass `--comments all` to keep all the comments, or a valid JavaScript regexp to
1062
1068
  keep only comments that match this regexp. For example `--comments /^!/`
1063
1069
  will keep comments like `/*! Copyright Notice */`.
1064
1070
 
@@ -1312,6 +1318,22 @@ $ yarn
1312
1318
 
1313
1319
  In the terser CLI we use [source-map-support](https://npmjs.com/source-map-support) to produce good error stacks. In your own app, you're expected to enable source-map-support (read their docs) to have nice stack traces that will help you write good issues.
1314
1320
 
1321
+ ## Obtaining the source code given to Terser
1322
+
1323
+ Because users often don't control the call to `await minify()` or its arguments, Terser provides a `TERSER_DEBUG_DIR` environment variable to make terser output some debug logs. If you're using a bundler or a project that includes a bundler and are not sure what went wrong with your code, pass that variable like so:
1324
+
1325
+ ```
1326
+ $ TERSER_DEBUG_DIR=/path/to/logs command-that-uses-terser
1327
+ $ ls /path/to/logs
1328
+ terser-debug-123456.log
1329
+ ```
1330
+
1331
+ If you're not sure how to set an environment variable on your shell (the above example works in bash), you can try using cross-env:
1332
+
1333
+ ```
1334
+ > npx cross-env TERSER_DEBUG_DIR=/path/to/logs command-that-uses-terser
1335
+ ```
1336
+
1315
1337
  # README.md Patrons:
1316
1338
 
1317
1339
  *note*: You can support this project on patreon: <a target="_blank" rel="nofollow" href="https://www.patreon.com/fabiosantoscode"><img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" alt="patron" width="100px" height="auto"></a>. Check out [PATRONS.md](https://github.com/terser/terser/blob/master/PATRONS.md) for our first-tier patrons.
@@ -6975,7 +6975,7 @@ function is_some_comments(comment) {
6975
6975
  // multiline comment
6976
6976
  return (
6977
6977
  (comment.type === "comment2" || comment.type === "comment1")
6978
- && /@preserve|@lic|@cc_on|^\**!/i.test(comment.value)
6978
+ && /@preserve|@copyright|@lic|@cc_on|^\**!/i.test(comment.value)
6979
6979
  );
6980
6980
  }
6981
6981
 
@@ -7088,8 +7088,8 @@ function OutputStream(options) {
7088
7088
  var OUTPUT = new Rope();
7089
7089
  let printed_comments = new Set();
7090
7090
 
7091
- var to_utf8 = options.ascii_only ? function(str, identifier) {
7092
- if (options.ecma >= 2015 && !options.safari10) {
7091
+ var to_utf8 = options.ascii_only ? function(str, identifier = false, regexp = false) {
7092
+ if (options.ecma >= 2015 && !options.safari10 && !regexp) {
7093
7093
  str = str.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function(ch) {
7094
7094
  var code = get_full_char_code(ch, 0).toString(16);
7095
7095
  return "\\u{" + code + "}";
@@ -7901,7 +7901,8 @@ function OutputStream(options) {
7901
7901
  var p = output.parent();
7902
7902
  if (this.args.length === 0
7903
7903
  && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
7904
- || p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
7904
+ || p instanceof AST_Call && p.expression === this
7905
+ || p instanceof AST_PrefixedTemplateString && p.prefix === this)) // (new foo)(bar)
7905
7906
  return true;
7906
7907
  });
7907
7908
 
@@ -9016,7 +9017,7 @@ function OutputStream(options) {
9016
9017
  flags = flags ? sort_regexp_flags(flags) : "";
9017
9018
  source = source.replace(r_slash_script, slash_script_replace);
9018
9019
 
9019
- output.print(output.to_utf8(`/${source}/${flags}`));
9020
+ output.print(output.to_utf8(`/${source}/${flags}`, false, true));
9020
9021
 
9021
9022
  const parent = output.parent();
9022
9023
  if (
@@ -9440,6 +9441,11 @@ const MASK_EXPORT_WANT_MANGLE = 1 << 1;
9440
9441
 
9441
9442
  let function_defs = null;
9442
9443
  let unmangleable_names = null;
9444
+ /**
9445
+ * When defined, there is a function declaration somewhere that's inside of a block.
9446
+ * See https://tc39.es/ecma262/multipage/additional-ecmascript-features-for-web-browsers.html#sec-block-level-function-declarations-web-legacy-compatibility-semantics
9447
+ */
9448
+ let scopes_with_block_defuns = null;
9443
9449
 
9444
9450
  class SymbolDef {
9445
9451
  constructor(scope, orig, init) {
@@ -9990,6 +9996,15 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
9990
9996
  });
9991
9997
 
9992
9998
  function next_mangled(scope, options) {
9999
+ let defun_scope;
10000
+ if (
10001
+ scopes_with_block_defuns
10002
+ && (defun_scope = scope.get_defun_scope())
10003
+ && scopes_with_block_defuns.has(defun_scope)
10004
+ ) {
10005
+ scope = defun_scope;
10006
+ }
10007
+
9993
10008
  var ext = scope.enclosed;
9994
10009
  var nth_identifier = options.nth_identifier;
9995
10010
  out: while (true) {
@@ -10124,6 +10139,13 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
10124
10139
  lname = save_nesting;
10125
10140
  return true; // don't descend again in TreeWalker
10126
10141
  }
10142
+ if (
10143
+ node instanceof AST_Defun
10144
+ && !(tw.parent() instanceof AST_Scope)
10145
+ ) {
10146
+ scopes_with_block_defuns = scopes_with_block_defuns || new Set();
10147
+ scopes_with_block_defuns.add(node.parent_scope.get_defun_scope());
10148
+ }
10127
10149
  if (node instanceof AST_Scope) {
10128
10150
  node.variables.forEach(collect);
10129
10151
  return;
@@ -10172,6 +10194,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
10172
10194
 
10173
10195
  function_defs = null;
10174
10196
  unmangleable_names = null;
10197
+ scopes_with_block_defuns = null;
10175
10198
 
10176
10199
  function collect(symbol) {
10177
10200
  if (symbol.export & MASK_EXPORT_DONT_MANGLE) {
@@ -27555,7 +27578,54 @@ function cache_to_json(cache) {
27555
27578
  };
27556
27579
  }
27557
27580
 
27558
- async function minify(files, options) {
27581
+ function log_input(files, options, fs, debug_folder) {
27582
+ if (!(fs && fs.writeFileSync && fs.mkdirSync)) {
27583
+ return;
27584
+ }
27585
+
27586
+ try {
27587
+ fs.mkdirSync(debug_folder);
27588
+ } catch (e) {
27589
+ if (e.code !== "EEXIST") throw e;
27590
+ }
27591
+
27592
+ const log_path = `${debug_folder}/terser-debug-${(Math.random() * 9999999) | 0}.log`;
27593
+
27594
+ options = options || {};
27595
+
27596
+ const options_str = JSON.stringify(options, (_key, thing) => {
27597
+ if (typeof thing === "function") return "[Function " + thing.toString() + "]";
27598
+ if (thing instanceof RegExp) return "[RegExp " + thing.toString() + "]";
27599
+ return thing;
27600
+ }, 4);
27601
+
27602
+ const files_str = (file) => {
27603
+ if (typeof file === "object" && options.parse && options.parse.spidermonkey) {
27604
+ return JSON.stringify(file, null, 2);
27605
+ } else if (typeof file === "object") {
27606
+ return Object.keys(file)
27607
+ .map((key) => key + ": " + files_str(file[key]))
27608
+ .join("\n\n");
27609
+ } else if (typeof file === "string") {
27610
+ return "```\n" + file + "\n```";
27611
+ } else {
27612
+ return file; // What do?
27613
+ }
27614
+ };
27615
+
27616
+ fs.writeFileSync(log_path, "Options: \n" + options_str + "\n\nInput files:\n\n" + files_str(files) + "\n");
27617
+ }
27618
+
27619
+ async function minify(files, options, _fs_module) {
27620
+ if (
27621
+ _fs_module
27622
+ && typeof process === "object"
27623
+ && process.env
27624
+ && typeof process.env.TERSER_DEBUG_DIR === "string"
27625
+ ) {
27626
+ log_input(files, options, _fs_module, process.env.TERSER_DEBUG_DIR);
27627
+ }
27628
+
27559
27629
  options = defaults(options, {
27560
27630
  compress: {},
27561
27631
  ecma: undefined,
@@ -27578,6 +27648,7 @@ async function minify(files, options) {
27578
27648
  warnings: false,
27579
27649
  wrap: false,
27580
27650
  }, true);
27651
+
27581
27652
  var timings = options.timings && {
27582
27653
  start: Date.now()
27583
27654
  };
@@ -27979,7 +28050,7 @@ async function run_cli({ program, packageJson, fs, path }) {
27979
28050
 
27980
28051
  let result;
27981
28052
  try {
27982
- result = await minify(files, options);
28053
+ result = await minify(files, options, fs);
27983
28054
  } catch (ex) {
27984
28055
  if (ex.name == "SyntaxError") {
27985
28056
  print_error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
@@ -28042,14 +28113,18 @@ async function run_cli({ program, packageJson, fs, path }) {
28042
28113
  }, 2));
28043
28114
  } else if (program.output == "spidermonkey") {
28044
28115
  try {
28045
- const minified = await minify(result.code, {
28046
- compress: false,
28047
- mangle: false,
28048
- format: {
28049
- ast: true,
28050
- code: false
28051
- }
28052
- });
28116
+ const minified = await minify(
28117
+ result.code,
28118
+ {
28119
+ compress: false,
28120
+ mangle: false,
28121
+ format: {
28122
+ ast: true,
28123
+ code: false
28124
+ }
28125
+ },
28126
+ fs
28127
+ );
28053
28128
  console.log(JSON.stringify(minified.ast.to_mozilla_ast(), null, 2));
28054
28129
  } catch (ex) {
28055
28130
  fatal(ex);
package/lib/cli.js CHANGED
@@ -224,7 +224,7 @@ export async function run_cli({ program, packageJson, fs, path }) {
224
224
 
225
225
  let result;
226
226
  try {
227
- result = await minify(files, options);
227
+ result = await minify(files, options, fs);
228
228
  } catch (ex) {
229
229
  if (ex.name == "SyntaxError") {
230
230
  print_error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
@@ -287,14 +287,18 @@ export async function run_cli({ program, packageJson, fs, path }) {
287
287
  }, 2));
288
288
  } else if (program.output == "spidermonkey") {
289
289
  try {
290
- const minified = await minify(result.code, {
291
- compress: false,
292
- mangle: false,
293
- format: {
294
- ast: true,
295
- code: false
296
- }
297
- });
290
+ const minified = await minify(
291
+ result.code,
292
+ {
293
+ compress: false,
294
+ mangle: false,
295
+ format: {
296
+ ast: true,
297
+ code: false
298
+ }
299
+ },
300
+ fs
301
+ );
298
302
  console.log(JSON.stringify(minified.ast.to_mozilla_ast(), null, 2));
299
303
  } catch (ex) {
300
304
  fatal(ex);
package/lib/minify.js CHANGED
@@ -61,7 +61,54 @@ function cache_to_json(cache) {
61
61
  };
62
62
  }
63
63
 
64
- async function minify(files, options) {
64
+ function log_input(files, options, fs, debug_folder) {
65
+ if (!(fs && fs.writeFileSync && fs.mkdirSync)) {
66
+ return;
67
+ }
68
+
69
+ try {
70
+ fs.mkdirSync(debug_folder);
71
+ } catch (e) {
72
+ if (e.code !== "EEXIST") throw e;
73
+ }
74
+
75
+ const log_path = `${debug_folder}/terser-debug-${(Math.random() * 9999999) | 0}.log`;
76
+
77
+ options = options || {};
78
+
79
+ const options_str = JSON.stringify(options, (_key, thing) => {
80
+ if (typeof thing === "function") return "[Function " + thing.toString() + "]";
81
+ if (thing instanceof RegExp) return "[RegExp " + thing.toString() + "]";
82
+ return thing;
83
+ }, 4);
84
+
85
+ const files_str = (file) => {
86
+ if (typeof file === "object" && options.parse && options.parse.spidermonkey) {
87
+ return JSON.stringify(file, null, 2);
88
+ } else if (typeof file === "object") {
89
+ return Object.keys(file)
90
+ .map((key) => key + ": " + files_str(file[key]))
91
+ .join("\n\n");
92
+ } else if (typeof file === "string") {
93
+ return "```\n" + file + "\n```";
94
+ } else {
95
+ return file; // What do?
96
+ }
97
+ };
98
+
99
+ fs.writeFileSync(log_path, "Options: \n" + options_str + "\n\nInput files:\n\n" + files_str(files) + "\n");
100
+ }
101
+
102
+ async function minify(files, options, _fs_module) {
103
+ if (
104
+ _fs_module
105
+ && typeof process === "object"
106
+ && process.env
107
+ && typeof process.env.TERSER_DEBUG_DIR === "string"
108
+ ) {
109
+ log_input(files, options, _fs_module, process.env.TERSER_DEBUG_DIR);
110
+ }
111
+
65
112
  options = defaults(options, {
66
113
  compress: {},
67
114
  ecma: undefined,
@@ -84,6 +131,7 @@ async function minify(files, options) {
84
131
  warnings: false,
85
132
  wrap: false,
86
133
  }, true);
134
+
87
135
  var timings = options.timings && {
88
136
  start: Date.now()
89
137
  };
package/lib/output.js CHANGED
@@ -172,7 +172,7 @@ function is_some_comments(comment) {
172
172
  // multiline comment
173
173
  return (
174
174
  (comment.type === "comment2" || comment.type === "comment1")
175
- && /@preserve|@lic|@cc_on|^\**!/i.test(comment.value)
175
+ && /@preserve|@copyright|@lic|@cc_on|^\**!/i.test(comment.value)
176
176
  );
177
177
  }
178
178
 
@@ -285,8 +285,8 @@ function OutputStream(options) {
285
285
  var OUTPUT = new Rope();
286
286
  let printed_comments = new Set();
287
287
 
288
- var to_utf8 = options.ascii_only ? function(str, identifier) {
289
- if (options.ecma >= 2015 && !options.safari10) {
288
+ var to_utf8 = options.ascii_only ? function(str, identifier = false, regexp = false) {
289
+ if (options.ecma >= 2015 && !options.safari10 && !regexp) {
290
290
  str = str.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function(ch) {
291
291
  var code = get_full_char_code(ch, 0).toString(16);
292
292
  return "\\u{" + code + "}";
@@ -1098,7 +1098,8 @@ function OutputStream(options) {
1098
1098
  var p = output.parent();
1099
1099
  if (this.args.length === 0
1100
1100
  && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
1101
- || p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
1101
+ || p instanceof AST_Call && p.expression === this
1102
+ || p instanceof AST_PrefixedTemplateString && p.prefix === this)) // (new foo)(bar)
1102
1103
  return true;
1103
1104
  });
1104
1105
 
@@ -2213,7 +2214,7 @@ function OutputStream(options) {
2213
2214
  flags = flags ? sort_regexp_flags(flags) : "";
2214
2215
  source = source.replace(r_slash_script, slash_script_replace);
2215
2216
 
2216
- output.print(output.to_utf8(`/${source}/${flags}`));
2217
+ output.print(output.to_utf8(`/${source}/${flags}`, false, true));
2217
2218
 
2218
2219
  const parent = output.parent();
2219
2220
  if (
package/lib/scope.js CHANGED
@@ -116,6 +116,11 @@ const MASK_EXPORT_WANT_MANGLE = 1 << 1;
116
116
 
117
117
  let function_defs = null;
118
118
  let unmangleable_names = null;
119
+ /**
120
+ * When defined, there is a function declaration somewhere that's inside of a block.
121
+ * See https://tc39.es/ecma262/multipage/additional-ecmascript-features-for-web-browsers.html#sec-block-level-function-declarations-web-legacy-compatibility-semantics
122
+ */
123
+ let scopes_with_block_defuns = null;
119
124
 
120
125
  class SymbolDef {
121
126
  constructor(scope, orig, init) {
@@ -666,6 +671,15 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
666
671
  });
667
672
 
668
673
  function next_mangled(scope, options) {
674
+ let defun_scope;
675
+ if (
676
+ scopes_with_block_defuns
677
+ && (defun_scope = scope.get_defun_scope())
678
+ && scopes_with_block_defuns.has(defun_scope)
679
+ ) {
680
+ scope = defun_scope;
681
+ }
682
+
669
683
  var ext = scope.enclosed;
670
684
  var nth_identifier = options.nth_identifier;
671
685
  out: while (true) {
@@ -800,6 +814,13 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
800
814
  lname = save_nesting;
801
815
  return true; // don't descend again in TreeWalker
802
816
  }
817
+ if (
818
+ node instanceof AST_Defun
819
+ && !(tw.parent() instanceof AST_Scope)
820
+ ) {
821
+ scopes_with_block_defuns = scopes_with_block_defuns || new Set();
822
+ scopes_with_block_defuns.add(node.parent_scope.get_defun_scope());
823
+ }
803
824
  if (node instanceof AST_Scope) {
804
825
  node.variables.forEach(collect);
805
826
  return;
@@ -848,6 +869,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
848
869
 
849
870
  function_defs = null;
850
871
  unmangleable_names = null;
872
+ scopes_with_block_defuns = null;
851
873
 
852
874
  function collect(symbol) {
853
875
  if (symbol.export & MASK_EXPORT_DONT_MANGLE) {
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.10.0",
7
+ "version": "5.12.1",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },
@@ -43,6 +43,7 @@
43
43
  "main.js"
44
44
  ],
45
45
  "dependencies": {
46
+ "acorn": "^8.5.0",
46
47
  "commander": "^2.20.0",
47
48
  "source-map": "~0.7.2",
48
49
  "source-map-support": "~0.5.20"
@@ -53,20 +54,12 @@
53
54
  "eslint": "^7.32.0",
54
55
  "eslump": "^3.0.0",
55
56
  "esm": "^3.2.25",
56
- "mocha": "^9.1.1",
57
+ "mocha": "^9.2.0",
57
58
  "pre-commit": "^1.2.2",
58
59
  "rimraf": "^3.0.2",
59
60
  "rollup": "2.56.3",
60
61
  "semver": "^7.3.4"
61
62
  },
62
- "peerDependencies": {
63
- "acorn": "^8.5.0"
64
- },
65
- "peerDependenciesMeta": {
66
- "acorn": {
67
- "optional": true
68
- }
69
- },
70
63
  "scripts": {
71
64
  "test": "node test/compress.js && mocha test/mocha",
72
65
  "test:compress": "node test/compress.js",
@@ -111,6 +104,8 @@
111
104
  "describe": false,
112
105
  "it": false,
113
106
  "require": false,
107
+ "before": false,
108
+ "after": false,
114
109
  "global": false,
115
110
  "process": false
116
111
  },
package/bin/terser.mjs DELETED
@@ -1,19 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- "use strict";
4
-
5
- import '../tools/exit.cjs'
6
- import fs from 'fs'
7
- import path from 'path'
8
- import program from 'commander'
9
-
10
- import { _run_cli as run_cli } from '../main.js'
11
-
12
- const packageJson = {
13
- name: 'terser',
14
- version: 'development-cli'
15
- }
16
- run_cli({ program, packageJson, fs, path }).catch((error) => {
17
- console.error(error);
18
- process.exitCode = 1;
19
- });