terser 4.8.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cli.js ADDED
@@ -0,0 +1,487 @@
1
+ import { minify, _default_options } from "../main.js";
2
+ import { parse } from "./parse.js";
3
+ import {
4
+ AST_Assign,
5
+ AST_Array,
6
+ AST_Constant,
7
+ AST_Node,
8
+ AST_PropAccess,
9
+ AST_RegExp,
10
+ AST_Sequence,
11
+ AST_Symbol,
12
+ AST_Token,
13
+ walk
14
+ } from "./ast.js";
15
+ import { OutputStream } from "./output.js";
16
+
17
+ export async function run_cli({ program, packageJson, fs, path }) {
18
+ const skip_keys = new Set([ "cname", "parent_scope", "scope", "uses_eval", "uses_with" ]);
19
+ var files = {};
20
+ var options = {
21
+ compress: false,
22
+ mangle: false
23
+ };
24
+ const default_options = await _default_options();
25
+ program.version(packageJson.name + " " + packageJson.version);
26
+ program.parseArgv = program.parse;
27
+ program.parse = undefined;
28
+
29
+ if (process.argv.includes("ast")) program.helpInformation = describe_ast;
30
+ else if (process.argv.includes("options")) program.helpInformation = function() {
31
+ var text = [];
32
+ for (var option in default_options) {
33
+ text.push("--" + (option === "sourceMap" ? "source-map" : option) + " options:");
34
+ text.push(format_object(default_options[option]));
35
+ text.push("");
36
+ }
37
+ return text.join("\n");
38
+ };
39
+
40
+ program.option("-p, --parse <options>", "Specify parser options.", parse_js());
41
+ program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js());
42
+ program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js());
43
+ program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js());
44
+ program.option("-f, --format [options]", "Format options.", parse_js());
45
+ program.option("-b, --beautify [options]", "Alias for --format beautify=true.", parse_js());
46
+ program.option("-o, --output <file>", "Output file (default STDOUT).");
47
+ program.option("--comments [filter]", "Preserve copyright comments in the output.");
48
+ program.option("--config-file <file>", "Read minify() options from JSON file.");
49
+ program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("define"));
50
+ program.option("--ecma <version>", "Specify ECMAScript release: 5, 2015, 2016 or 2017...");
51
+ program.option("-e, --enclose [arg[,...][:value[,...]]]", "Embed output in a big function with configurable arguments and values.");
52
+ program.option("--ie8", "Support non-standard Internet Explorer 8.");
53
+ program.option("--keep-classnames", "Do not mangle/drop class names.");
54
+ program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.");
55
+ program.option("--module", "Input is an ES6 module");
56
+ program.option("--name-cache <file>", "File to hold mangled name mappings.");
57
+ program.option("--rename", "Force symbol expansion.");
58
+ program.option("--no-rename", "Disable symbol expansion.");
59
+ program.option("--safari10", "Support non-standard Safari 10.");
60
+ program.option("--source-map [options]", "Enable source map/specify source map options.", parse_js());
61
+ program.option("--timings", "Display operations run time on STDERR.");
62
+ program.option("--toplevel", "Compress and/or mangle variables in toplevel scope.");
63
+ program.option("--wrap <name>", "Embed everything as a function with “exports” corresponding to “name” globally.");
64
+ program.arguments("[files...]").parseArgv(process.argv);
65
+ if (program.configFile) {
66
+ options = JSON.parse(read_file(program.configFile));
67
+ }
68
+ if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
69
+ fatal("ERROR: cannot write source map to STDOUT");
70
+ }
71
+
72
+ [
73
+ "compress",
74
+ "enclose",
75
+ "ie8",
76
+ "mangle",
77
+ "module",
78
+ "safari10",
79
+ "sourceMap",
80
+ "toplevel",
81
+ "wrap"
82
+ ].forEach(function(name) {
83
+ if (name in program) {
84
+ options[name] = program[name];
85
+ }
86
+ });
87
+
88
+ if ("ecma" in program) {
89
+ if (program.ecma != (program.ecma | 0)) fatal("ERROR: ecma must be an integer");
90
+ const ecma = program.ecma | 0;
91
+ if (ecma > 5 && ecma < 2015)
92
+ options.ecma = ecma + 2009;
93
+ else
94
+ options.ecma = ecma;
95
+ }
96
+ if (program.beautify || program.format) {
97
+ if (program.beautify && program.format) {
98
+ fatal("Please only specify one of --beautify or --format");
99
+ }
100
+ if (program.beautify) {
101
+ options.format = typeof program.beautify == "object" ? program.beautify : {};
102
+ if (!("beautify" in options.format)) {
103
+ options.format.beautify = true;
104
+ }
105
+ }
106
+ if (program.format) {
107
+ options.format = typeof program.format == "object" ? program.format : {};
108
+ }
109
+ }
110
+ if (program.comments) {
111
+ if (typeof options.format != "object") options.format = {};
112
+ options.format.comments = typeof program.comments == "string" ? (program.comments == "false" ? false : program.comments) : "some";
113
+ }
114
+ if (program.define) {
115
+ if (typeof options.compress != "object") options.compress = {};
116
+ if (typeof options.compress.global_defs != "object") options.compress.global_defs = {};
117
+ for (var expr in program.define) {
118
+ options.compress.global_defs[expr] = program.define[expr];
119
+ }
120
+ }
121
+ if (program.keepClassnames) {
122
+ options.keep_classnames = true;
123
+ }
124
+ if (program.keepFnames) {
125
+ options.keep_fnames = true;
126
+ }
127
+ if (program.mangleProps) {
128
+ if (program.mangleProps.domprops) {
129
+ delete program.mangleProps.domprops;
130
+ } else {
131
+ if (typeof program.mangleProps != "object") program.mangleProps = {};
132
+ if (!Array.isArray(program.mangleProps.reserved)) program.mangleProps.reserved = [];
133
+ }
134
+ if (typeof options.mangle != "object") options.mangle = {};
135
+ options.mangle.properties = program.mangleProps;
136
+ }
137
+ if (program.nameCache) {
138
+ options.nameCache = JSON.parse(read_file(program.nameCache, "{}"));
139
+ }
140
+ if (program.output == "ast") {
141
+ options.format = {
142
+ ast: true,
143
+ code: false
144
+ };
145
+ }
146
+ if (program.parse) {
147
+ if (!program.parse.acorn && !program.parse.spidermonkey) {
148
+ options.parse = program.parse;
149
+ } else if (program.sourceMap && program.sourceMap.content == "inline") {
150
+ fatal("ERROR: inline source map only works with built-in parser");
151
+ }
152
+ }
153
+ if (~program.rawArgs.indexOf("--rename")) {
154
+ options.rename = true;
155
+ } else if (!program.rename) {
156
+ options.rename = false;
157
+ }
158
+
159
+ let convert_path = name => name;
160
+ if (typeof program.sourceMap == "object" && "base" in program.sourceMap) {
161
+ convert_path = function() {
162
+ var base = program.sourceMap.base;
163
+ delete options.sourceMap.base;
164
+ return function(name) {
165
+ return path.relative(base, name);
166
+ };
167
+ }();
168
+ }
169
+
170
+ let filesList;
171
+ if (options.files && options.files.length) {
172
+ filesList = options.files;
173
+
174
+ delete options.files;
175
+ } else if (program.args.length) {
176
+ filesList = program.args;
177
+ }
178
+
179
+ if (filesList) {
180
+ simple_glob(filesList).forEach(function(name) {
181
+ files[convert_path(name)] = read_file(name);
182
+ });
183
+ } else {
184
+ await new Promise((resolve) => {
185
+ var chunks = [];
186
+ process.stdin.setEncoding("utf8");
187
+ process.stdin.on("data", function(chunk) {
188
+ chunks.push(chunk);
189
+ }).on("end", function() {
190
+ files = [ chunks.join("") ];
191
+ resolve();
192
+ });
193
+ process.stdin.resume();
194
+ });
195
+ }
196
+
197
+ await run_cli();
198
+
199
+ function convert_ast(fn) {
200
+ return AST_Node.from_mozilla_ast(Object.keys(files).reduce(fn, null));
201
+ }
202
+
203
+ async function run_cli() {
204
+ var content = program.sourceMap && program.sourceMap.content;
205
+ if (content && content !== "inline") {
206
+ options.sourceMap.content = read_file(content, content);
207
+ }
208
+ if (program.timings) options.timings = true;
209
+
210
+ try {
211
+ if (program.parse) {
212
+ if (program.parse.acorn) {
213
+ files = convert_ast(function(toplevel, name) {
214
+ return require("acorn").parse(files[name], {
215
+ ecmaVersion: 2018,
216
+ locations: true,
217
+ program: toplevel,
218
+ sourceFile: name,
219
+ sourceType: options.module || program.parse.module ? "module" : "script"
220
+ });
221
+ });
222
+ } else if (program.parse.spidermonkey) {
223
+ files = convert_ast(function(toplevel, name) {
224
+ var obj = JSON.parse(files[name]);
225
+ if (!toplevel) return obj;
226
+ toplevel.body = toplevel.body.concat(obj.body);
227
+ return toplevel;
228
+ });
229
+ }
230
+ }
231
+ } catch (ex) {
232
+ fatal(ex);
233
+ }
234
+
235
+ let result;
236
+ try {
237
+ result = await minify(files, options);
238
+ } catch (ex) {
239
+ if (ex.name == "SyntaxError") {
240
+ print_error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
241
+ var col = ex.col;
242
+ var lines = files[ex.filename].split(/\r?\n/);
243
+ var line = lines[ex.line - 1];
244
+ if (!line && !col) {
245
+ line = lines[ex.line - 2];
246
+ col = line.length;
247
+ }
248
+ if (line) {
249
+ var limit = 70;
250
+ if (col > limit) {
251
+ line = line.slice(col - limit);
252
+ col = limit;
253
+ }
254
+ print_error(line.slice(0, 80));
255
+ print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
256
+ }
257
+ }
258
+ if (ex.defs) {
259
+ print_error("Supported options:");
260
+ print_error(format_object(ex.defs));
261
+ }
262
+ fatal(ex);
263
+ return;
264
+ }
265
+
266
+ if (program.output == "ast") {
267
+ if (!options.compress && !options.mangle) {
268
+ result.ast.figure_out_scope({});
269
+ }
270
+ console.log(JSON.stringify(result.ast, function(key, value) {
271
+ if (value) switch (key) {
272
+ case "thedef":
273
+ return symdef(value);
274
+ case "enclosed":
275
+ return value.length ? value.map(symdef) : undefined;
276
+ case "variables":
277
+ case "functions":
278
+ case "globals":
279
+ return value.size ? collect_from_map(value, symdef) : undefined;
280
+ }
281
+ if (skip_keys.has(key)) return;
282
+ if (value instanceof AST_Token) return;
283
+ if (value instanceof Map) return;
284
+ if (value instanceof AST_Node) {
285
+ var result = {
286
+ _class: "AST_" + value.TYPE
287
+ };
288
+ if (value.block_scope) {
289
+ result.variables = value.block_scope.variables;
290
+ result.functions = value.block_scope.functions;
291
+ result.enclosed = value.block_scope.enclosed;
292
+ }
293
+ value.CTOR.PROPS.forEach(function(prop) {
294
+ result[prop] = value[prop];
295
+ });
296
+ return result;
297
+ }
298
+ return value;
299
+ }, 2));
300
+ } else if (program.output == "spidermonkey") {
301
+ try {
302
+ const minified = await minify(result.code, {
303
+ compress: false,
304
+ mangle: false,
305
+ format: {
306
+ ast: true,
307
+ code: false
308
+ }
309
+ });
310
+ console.log(JSON.stringify(minified.ast.to_mozilla_ast(), null, 2));
311
+ } catch (ex) {
312
+ fatal(ex);
313
+ return;
314
+ }
315
+ } else if (program.output) {
316
+ fs.writeFileSync(program.output, result.code);
317
+ if (options.sourceMap && options.sourceMap.url !== "inline" && result.map) {
318
+ fs.writeFileSync(program.output + ".map", result.map);
319
+ }
320
+ } else {
321
+ console.log(result.code);
322
+ }
323
+ if (program.nameCache) {
324
+ fs.writeFileSync(program.nameCache, JSON.stringify(options.nameCache));
325
+ }
326
+ if (result.timings) for (var phase in result.timings) {
327
+ print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
328
+ }
329
+ }
330
+
331
+ function fatal(message) {
332
+ if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:");
333
+ print_error(message);
334
+ process.exit(1);
335
+ }
336
+
337
+ // A file glob function that only supports "*" and "?" wildcards in the basename.
338
+ // Example: "foo/bar/*baz??.*.js"
339
+ // Argument `glob` may be a string or an array of strings.
340
+ // Returns an array of strings. Garbage in, garbage out.
341
+ function simple_glob(glob) {
342
+ if (Array.isArray(glob)) {
343
+ return [].concat.apply([], glob.map(simple_glob));
344
+ }
345
+ if (glob && glob.match(/[*?]/)) {
346
+ var dir = path.dirname(glob);
347
+ try {
348
+ var entries = fs.readdirSync(dir);
349
+ } catch (ex) {}
350
+ if (entries) {
351
+ var pattern = "^" + path.basename(glob)
352
+ .replace(/[.+^$[\]\\(){}]/g, "\\$&")
353
+ .replace(/\*/g, "[^/\\\\]*")
354
+ .replace(/\?/g, "[^/\\\\]") + "$";
355
+ var mod = process.platform === "win32" ? "i" : "";
356
+ var rx = new RegExp(pattern, mod);
357
+ var results = entries.filter(function(name) {
358
+ return rx.test(name);
359
+ }).map(function(name) {
360
+ return path.join(dir, name);
361
+ });
362
+ if (results.length) return results;
363
+ }
364
+ }
365
+ return [ glob ];
366
+ }
367
+
368
+ function read_file(path, default_value) {
369
+ try {
370
+ return fs.readFileSync(path, "utf8");
371
+ } catch (ex) {
372
+ if ((ex.code == "ENOENT" || ex.code == "ENAMETOOLONG") && default_value != null) return default_value;
373
+ fatal(ex);
374
+ }
375
+ }
376
+
377
+ function parse_js(flag) {
378
+ return function(value, options) {
379
+ options = options || {};
380
+ try {
381
+ walk(parse(value, { expression: true }), node => {
382
+ if (node instanceof AST_Assign) {
383
+ var name = node.left.print_to_string();
384
+ var value = node.right;
385
+ if (flag) {
386
+ options[name] = value;
387
+ } else if (value instanceof AST_Array) {
388
+ options[name] = value.elements.map(to_string);
389
+ } else if (value instanceof AST_RegExp) {
390
+ value = value.value;
391
+ options[name] = new RegExp(value.source, value.flags);
392
+ } else {
393
+ options[name] = to_string(value);
394
+ }
395
+ return true;
396
+ }
397
+ if (node instanceof AST_Symbol || node instanceof AST_PropAccess) {
398
+ var name = node.print_to_string();
399
+ options[name] = true;
400
+ return true;
401
+ }
402
+ if (!(node instanceof AST_Sequence)) throw node;
403
+
404
+ function to_string(value) {
405
+ return value instanceof AST_Constant ? value.getValue() : value.print_to_string({
406
+ quote_keys: true
407
+ });
408
+ }
409
+ });
410
+ } catch(ex) {
411
+ if (flag) {
412
+ fatal("Error parsing arguments for '" + flag + "': " + value);
413
+ } else {
414
+ options[value] = null;
415
+ }
416
+ }
417
+ return options;
418
+ };
419
+ }
420
+
421
+ function symdef(def) {
422
+ var ret = (1e6 + def.id) + " " + def.name;
423
+ if (def.mangled_name) ret += " " + def.mangled_name;
424
+ return ret;
425
+ }
426
+
427
+ function collect_from_map(map, callback) {
428
+ var result = [];
429
+ map.forEach(function (def) {
430
+ result.push(callback(def));
431
+ });
432
+ return result;
433
+ }
434
+
435
+ function format_object(obj) {
436
+ var lines = [];
437
+ var padding = "";
438
+ Object.keys(obj).map(function(name) {
439
+ if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
440
+ return [ name, JSON.stringify(obj[name]) ];
441
+ }).forEach(function(tokens) {
442
+ lines.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
443
+ });
444
+ return lines.join("\n");
445
+ }
446
+
447
+ function print_error(msg) {
448
+ process.stderr.write(msg);
449
+ process.stderr.write("\n");
450
+ }
451
+
452
+ function describe_ast() {
453
+ var out = OutputStream({ beautify: true });
454
+ function doitem(ctor) {
455
+ out.print("AST_" + ctor.TYPE);
456
+ const props = ctor.SELF_PROPS.filter(prop => !/^\$/.test(prop));
457
+
458
+ if (props.length > 0) {
459
+ out.space();
460
+ out.with_parens(function() {
461
+ props.forEach(function(prop, i) {
462
+ if (i) out.space();
463
+ out.print(prop);
464
+ });
465
+ });
466
+ }
467
+
468
+ if (ctor.documentation) {
469
+ out.space();
470
+ out.print_string(ctor.documentation);
471
+ }
472
+
473
+ if (ctor.SUBCLASSES.length > 0) {
474
+ out.space();
475
+ out.with_block(function() {
476
+ ctor.SUBCLASSES.forEach(function(ctor) {
477
+ out.indent();
478
+ doitem(ctor);
479
+ out.newline();
480
+ });
481
+ });
482
+ }
483
+ }
484
+ doitem(AST_Node);
485
+ return out + "\n";
486
+ }
487
+ }