Apply module bundling
[platform/framework/web/wrtjs.git] / node_modules / terser / lib / minify.js
1 "use strict";
2 /* eslint-env browser, es6, node */
3
4 import {
5     defaults,
6     map_from_object,
7     map_to_object,
8     HOP,
9 } from "./utils/index.js";
10 import { AST_Toplevel, AST_Node, walk, AST_Scope } from "./ast.js";
11 import { parse } from "./parse.js";
12 import { OutputStream } from "./output.js";
13 import { Compressor } from "./compress/index.js";
14 import { base54 } from "./scope.js";
15 import { SourceMap } from "./sourcemap.js";
16 import {
17     mangle_properties,
18     mangle_private_properties,
19     reserve_quoted_keys,
20 } from "./propmangle.js";
21
22 var to_ascii = typeof atob == "undefined" ? function(b64) {
23     return Buffer.from(b64, "base64").toString();
24 } : atob;
25 var to_base64 = typeof btoa == "undefined" ? function(str) {
26     return Buffer.from(str).toString("base64");
27 } : btoa;
28
29 function read_source_map(code) {
30     var match = /(?:^|[^.])\/\/# sourceMappingURL=data:application\/json(;[\w=-]*)?;base64,([+/0-9A-Za-z]*=*)\s*$/.exec(code);
31     if (!match) {
32         console.warn("inline source map not found");
33         return null;
34     }
35     return to_ascii(match[2]);
36 }
37
38 function set_shorthand(name, options, keys) {
39     if (options[name]) {
40         keys.forEach(function(key) {
41             if (options[key]) {
42                 if (typeof options[key] != "object") options[key] = {};
43                 if (!(name in options[key])) options[key][name] = options[name];
44             }
45         });
46     }
47 }
48
49 function init_cache(cache) {
50     if (!cache) return;
51     if (!("props" in cache)) {
52         cache.props = new Map();
53     } else if (!(cache.props instanceof Map)) {
54         cache.props = map_from_object(cache.props);
55     }
56 }
57
58 function cache_to_json(cache) {
59     return {
60         props: map_to_object(cache.props)
61     };
62 }
63
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
112     options = defaults(options, {
113         compress: {},
114         ecma: undefined,
115         enclose: false,
116         ie8: false,
117         keep_classnames: undefined,
118         keep_fnames: false,
119         mangle: {},
120         module: false,
121         nameCache: null,
122         output: null,
123         format: null,
124         parse: {},
125         rename: undefined,
126         safari10: false,
127         sourceMap: false,
128         spidermonkey: false,
129         timings: false,
130         toplevel: false,
131         warnings: false,
132         wrap: false,
133     }, true);
134
135     var timings = options.timings && {
136         start: Date.now()
137     };
138     if (options.keep_classnames === undefined) {
139         options.keep_classnames = options.keep_fnames;
140     }
141     if (options.rename === undefined) {
142         options.rename = options.compress && options.mangle;
143     }
144     if (options.output && options.format) {
145         throw new Error("Please only specify either output or format option, preferrably format.");
146     }
147     options.format = options.format || options.output || {};
148     set_shorthand("ecma", options, [ "parse", "compress", "format" ]);
149     set_shorthand("ie8", options, [ "compress", "mangle", "format" ]);
150     set_shorthand("keep_classnames", options, [ "compress", "mangle" ]);
151     set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
152     set_shorthand("module", options, [ "parse", "compress", "mangle" ]);
153     set_shorthand("safari10", options, [ "mangle", "format" ]);
154     set_shorthand("toplevel", options, [ "compress", "mangle" ]);
155     set_shorthand("warnings", options, [ "compress" ]); // legacy
156     var quoted_props;
157     if (options.mangle) {
158         options.mangle = defaults(options.mangle, {
159             cache: options.nameCache && (options.nameCache.vars || {}),
160             eval: false,
161             ie8: false,
162             keep_classnames: false,
163             keep_fnames: false,
164             module: false,
165             nth_identifier: base54,
166             properties: false,
167             reserved: [],
168             safari10: false,
169             toplevel: false,
170         }, true);
171         if (options.mangle.properties) {
172             if (typeof options.mangle.properties != "object") {
173                 options.mangle.properties = {};
174             }
175             if (options.mangle.properties.keep_quoted) {
176                 quoted_props = options.mangle.properties.reserved;
177                 if (!Array.isArray(quoted_props)) quoted_props = [];
178                 options.mangle.properties.reserved = quoted_props;
179             }
180             if (options.nameCache && !("cache" in options.mangle.properties)) {
181                 options.mangle.properties.cache = options.nameCache.props || {};
182             }
183         }
184         init_cache(options.mangle.cache);
185         init_cache(options.mangle.properties.cache);
186     }
187     if (options.sourceMap) {
188         options.sourceMap = defaults(options.sourceMap, {
189             asObject: false,
190             content: null,
191             filename: null,
192             includeSources: false,
193             root: null,
194             url: null,
195         }, true);
196     }
197
198     // -- Parse phase --
199     if (timings) timings.parse = Date.now();
200     var toplevel;
201     if (files instanceof AST_Toplevel) {
202         toplevel = files;
203     } else {
204         if (typeof files == "string" || (options.parse.spidermonkey && !Array.isArray(files))) {
205             files = [ files ];
206         }
207         options.parse = options.parse || {};
208         options.parse.toplevel = null;
209
210         if (options.parse.spidermonkey) {
211             options.parse.toplevel = AST_Node.from_mozilla_ast(Object.keys(files).reduce(function(toplevel, name) {
212                 if (!toplevel) return files[name];
213                 toplevel.body = toplevel.body.concat(files[name].body);
214                 return toplevel;
215             }, null));
216         } else {
217             delete options.parse.spidermonkey;
218
219             for (var name in files) if (HOP(files, name)) {
220                 options.parse.filename = name;
221                 options.parse.toplevel = parse(files[name], options.parse);
222                 if (options.sourceMap && options.sourceMap.content == "inline") {
223                     if (Object.keys(files).length > 1)
224                         throw new Error("inline source map only works with singular input");
225                     options.sourceMap.content = read_source_map(files[name]);
226                 }
227             }
228         }
229
230         toplevel = options.parse.toplevel;
231     }
232     if (quoted_props && options.mangle.properties.keep_quoted !== "strict") {
233         reserve_quoted_keys(toplevel, quoted_props);
234     }
235     if (options.wrap) {
236         toplevel = toplevel.wrap_commonjs(options.wrap);
237     }
238     if (options.enclose) {
239         toplevel = toplevel.wrap_enclose(options.enclose);
240     }
241     if (timings) timings.rename = Date.now();
242     // disable rename on harmony due to expand_names bug in for-of loops
243     // https://github.com/mishoo/UglifyJS2/issues/2794
244     if (0 && options.rename) {
245         toplevel.figure_out_scope(options.mangle);
246         toplevel.expand_names(options.mangle);
247     }
248
249     // -- Compress phase --
250     if (timings) timings.compress = Date.now();
251     if (options.compress) {
252         toplevel = new Compressor(options.compress, {
253             mangle_options: options.mangle
254         }).compress(toplevel);
255     }
256
257     // -- Mangle phase --
258     if (timings) timings.scope = Date.now();
259     if (options.mangle) toplevel.figure_out_scope(options.mangle);
260     if (timings) timings.mangle = Date.now();
261     if (options.mangle) {
262         toplevel.compute_char_frequency(options.mangle);
263         toplevel.mangle_names(options.mangle);
264         toplevel = mangle_private_properties(toplevel, options.mangle);
265     }
266     if (timings) timings.properties = Date.now();
267     if (options.mangle && options.mangle.properties) {
268         toplevel = mangle_properties(toplevel, options.mangle.properties);
269     }
270
271     // Format phase
272     if (timings) timings.format = Date.now();
273     var result = {};
274     if (options.format.ast) {
275         result.ast = toplevel;
276     }
277     if (options.format.spidermonkey) {
278         result.ast = toplevel.to_mozilla_ast();
279     }
280     if (!HOP(options.format, "code") || options.format.code) {
281         if (!options.format.ast) {
282             // Destroy stuff to save RAM. (unless the deprecated `ast` option is on)
283             options.format._destroy_ast = true;
284
285             walk(toplevel, node => {
286                 if (node instanceof AST_Scope) {
287                     node.variables = undefined;
288                     node.enclosed = undefined;
289                     node.parent_scope = undefined;
290                 }
291                 if (node.block_scope) {
292                     node.block_scope.variables = undefined;
293                     node.block_scope.enclosed = undefined;
294                     node.parent_scope = undefined;
295                 }
296             });
297         }
298
299         if (options.sourceMap) {
300             if (options.sourceMap.includeSources && files instanceof AST_Toplevel) {
301                 throw new Error("original source content unavailable");
302             }
303             options.format.source_map = await SourceMap({
304                 file: options.sourceMap.filename,
305                 orig: options.sourceMap.content,
306                 root: options.sourceMap.root,
307                 files: options.sourceMap.includeSources ? files : null,
308             });
309         }
310         delete options.format.ast;
311         delete options.format.code;
312         delete options.format.spidermonkey;
313         var stream = OutputStream(options.format);
314         toplevel.print(stream);
315         result.code = stream.get();
316         if (options.sourceMap) {
317             Object.defineProperty(result, "map", {
318                 configurable: true,
319                 enumerable: true,
320                 get() {
321                     const map = options.format.source_map.getEncoded();
322                     return (result.map = options.sourceMap.asObject ? map : JSON.stringify(map));
323                 },
324                 set(value) {
325                     Object.defineProperty(result, "map", {
326                         value,
327                         writable: true,
328                     });
329                 }
330             });
331             result.decoded_map = options.format.source_map.getDecoded();
332             if (options.sourceMap.url == "inline") {
333                 var sourceMap = typeof result.map === "object" ? JSON.stringify(result.map) : result.map;
334                 result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(sourceMap);
335             } else if (options.sourceMap.url) {
336                 result.code += "\n//# sourceMappingURL=" + options.sourceMap.url;
337             }
338         }
339     }
340     if (options.nameCache && options.mangle) {
341         if (options.mangle.cache) options.nameCache.vars = cache_to_json(options.mangle.cache);
342         if (options.mangle.properties && options.mangle.properties.cache) {
343             options.nameCache.props = cache_to_json(options.mangle.properties.cache);
344         }
345     }
346     if (options.format && options.format.source_map) {
347         options.format.source_map.destroy();
348     }
349     if (timings) {
350         timings.end = Date.now();
351         result.timings = {
352             parse: 1e-3 * (timings.rename - timings.parse),
353             rename: 1e-3 * (timings.compress - timings.rename),
354             compress: 1e-3 * (timings.scope - timings.compress),
355             scope: 1e-3 * (timings.mangle - timings.scope),
356             mangle: 1e-3 * (timings.properties - timings.mangle),
357             properties: 1e-3 * (timings.format - timings.properties),
358             format: 1e-3 * (timings.end - timings.format),
359             total: 1e-3 * (timings.end - timings.start)
360         };
361     }
362     return result;
363 }
364
365 export {
366   minify,
367   to_ascii,
368 };