Compare commits
54 Commits
harmony-v3
...
harmony-v3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33ad0d258c | ||
|
|
5ea1da2d42 | ||
|
|
e77b6d525c | ||
|
|
2dde41615a | ||
|
|
8b69a3d18e | ||
|
|
d40950b741 | ||
|
|
a9eecd844f | ||
|
|
ed3032e52a | ||
|
|
7659ea1d2e | ||
|
|
52cc21d999 | ||
|
|
a938fe5e1f | ||
|
|
07a5a57336 | ||
|
|
bdeadffbf5 | ||
|
|
945db924fc | ||
|
|
087bce508a | ||
|
|
5e6f26445f | ||
|
|
fc7e33453f | ||
|
|
d052394621 | ||
|
|
4d5aeeddfb | ||
|
|
f0a99125ee | ||
|
|
1e4de2e6d3 | ||
|
|
ad139aa34d | ||
|
|
26be15f111 | ||
|
|
179f33f08a | ||
|
|
d260fe9018 | ||
|
|
96f9b8cba3 | ||
|
|
11afa816e3 | ||
|
|
8b4dcd8f3e | ||
|
|
285401ced8 | ||
|
|
9db4c42380 | ||
|
|
49f3de8397 | ||
|
|
94f93ad82d | ||
|
|
d1f085bce7 | ||
|
|
7b95b63ca1 | ||
|
|
94e5e00c03 | ||
|
|
dc6bcaa18e | ||
|
|
d58b184835 | ||
|
|
137e4c4753 | ||
|
|
b3a57ff019 | ||
|
|
3d5bc08185 | ||
|
|
0692435f01 | ||
|
|
b163b13a0b | ||
|
|
402954bdf3 | ||
|
|
f5931866e0 | ||
|
|
f67a6b0e43 | ||
|
|
471db8a717 | ||
|
|
8ba9e4e0da | ||
|
|
71556d00b5 | ||
|
|
8709753bfb | ||
|
|
db877e8729 | ||
|
|
11923e3ae8 | ||
|
|
62d1fbf645 | ||
|
|
343ea326c2 | ||
|
|
849ba79dee |
86
README.md
86
README.md
@@ -1,11 +1,11 @@
|
||||
uglify-es
|
||||
=========
|
||||
|
||||
**uglify-es** is an ECMAScript 2015 parser, minifier, compressor and beautifier toolkit.
|
||||
**uglify-es** is an ECMAScript parser, minifier, compressor and beautifier toolkit for ES6+.
|
||||
|
||||
#### Note:
|
||||
- **The `uglify-es` API and CLI is compatible with `uglify-js@3.x`.**
|
||||
- **`uglify-es` is not backwards compatible with the `uglify-js@2.x` API and CLI.**
|
||||
- **`uglify-es` is API/CLI compatible with `uglify-js@3`.**
|
||||
- **`uglify-es` is not backwards compatible with `uglify-js@2`.**
|
||||
|
||||
Install
|
||||
-------
|
||||
@@ -102,13 +102,14 @@ a double dash to prevent input files being used as option arguments:
|
||||
sequences.
|
||||
--config-file <file> Read `minify()` options from JSON file.
|
||||
-d, --define <expr>[=value] Global definitions.
|
||||
--ecma <version> Specifiy ECMAScript release: 5, 6, 7 or 8.
|
||||
--ie8 Support non-standard Internet Explorer 8.
|
||||
Equivalent to setting `ie8: true` in `minify()`
|
||||
for `compress`, `mangle` and `output` options.
|
||||
By default UglifyJS will not try to be IE-proof.
|
||||
--keep-fnames Do not mangle/drop function names. Useful for
|
||||
code relying on Function.prototype.name.
|
||||
--name-cache File to hold mangled name mappings.
|
||||
--name-cache <file> File to hold mangled name mappings.
|
||||
--self Build UglifyJS as a library (implies --wrap UglifyJS)
|
||||
--source-map [options] Enable source map/specify source map options:
|
||||
`base` Path to compute relative paths from input files.
|
||||
@@ -380,7 +381,47 @@ var code = {
|
||||
var options = { toplevel: true };
|
||||
var result = UglifyJS.minify(code, options);
|
||||
console.log(result.code);
|
||||
// console.log(function(n,o){return n+o}(3,7));
|
||||
// console.log(3+7);
|
||||
```
|
||||
|
||||
The `nameCache` option:
|
||||
```javascript
|
||||
var options = {
|
||||
mangle: {
|
||||
toplevel: true,
|
||||
},
|
||||
nameCache: {}
|
||||
};
|
||||
var result1 = UglifyJS.minify({
|
||||
"file1.js": "function add(first, second) { return first + second; }"
|
||||
}, options);
|
||||
var result2 = UglifyJS.minify({
|
||||
"file2.js": "console.log(add(1 + 2, 3 + 4));"
|
||||
}, options);
|
||||
console.log(result1.code);
|
||||
// function n(n,r){return n+r}
|
||||
console.log(result2.code);
|
||||
// console.log(n(3,7));
|
||||
```
|
||||
|
||||
You may persist the name cache to the file system in the following way:
|
||||
```javascript
|
||||
var cacheFileName = "/tmp/cache.json";
|
||||
var options = {
|
||||
mangle: {
|
||||
properties: true,
|
||||
},
|
||||
nameCache: JSON.parse(fs.readFileSync(cacheFileName, "utf8"))
|
||||
};
|
||||
fs.writeFileSync("part1.js", UglifyJS.minify({
|
||||
"file1.js": fs.readFileSync("file1.js", "utf8"),
|
||||
"file2.js": fs.readFileSync("file2.js", "utf8")
|
||||
}, options).code, "utf8");
|
||||
fs.writeFileSync("part2.js", UglifyJS.minify({
|
||||
"file3.js": fs.readFileSync("file3.js", "utf8"),
|
||||
"file4.js": fs.readFileSync("file4.js", "utf8")
|
||||
}, options).code, "utf8");
|
||||
fs.writeFileSync(cacheFileName, JSON.stringify(options.nameCache), "utf8");
|
||||
```
|
||||
|
||||
An example of a combination of `minify()` options:
|
||||
@@ -433,6 +474,9 @@ if (result.error) throw result.error;
|
||||
|
||||
## Minify options
|
||||
|
||||
- `ecma` (default `undefined`) - pass `5`, `6`, `7` or `8` to override `parse`,
|
||||
`compress` and `output` options.
|
||||
|
||||
- `warnings` (default `false`) — pass `true` to return compressor warnings
|
||||
in `result.warnings`. Use the value `"verbose"` for more detailed warnings.
|
||||
|
||||
@@ -458,13 +502,19 @@ if (result.error) throw result.error;
|
||||
- `toplevel` (default `false`) - set to `true` if you wish to enable top level
|
||||
variable and function name mangling and to drop unused variables and functions.
|
||||
|
||||
- `nameCache` (default `null`) - pass an empty object `{}` or a previously
|
||||
used `nameCache` object if you wish to cache mangled variable and
|
||||
property names across multiple invocations of `minify()`. Note: this is
|
||||
a read/write property. `minify()` will read the name cache state of this
|
||||
object and update it during minification so that it may be
|
||||
reused or externally persisted by the user.
|
||||
|
||||
- `ie8` (default `false`) - set to `true` to support IE8.
|
||||
|
||||
## Minify options structure
|
||||
|
||||
```javascript
|
||||
{
|
||||
warnings: false,
|
||||
parse: {
|
||||
// parse options
|
||||
},
|
||||
@@ -484,8 +534,11 @@ if (result.error) throw result.error;
|
||||
sourceMap: {
|
||||
// source map options
|
||||
},
|
||||
ecma: 5, // specify one of: 5, 6, 7 or 8
|
||||
nameCache: null, // or specify a name cache object
|
||||
toplevel: false,
|
||||
ie8: false,
|
||||
warnings: false,
|
||||
}
|
||||
```
|
||||
|
||||
@@ -539,6 +592,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
## Parse options
|
||||
|
||||
- `bare_returns` (default `false`) -- support top level `return` statements
|
||||
- `ecma` (default: `8`) -- specify one of `5`, `6`, `7` or `8`. Note: this setting
|
||||
is not presently enforced except for ES8 optional trailing commas in function
|
||||
parameter lists and calls with `ecma` `8`.
|
||||
- `html5_comments` (default `true`)
|
||||
- `shebang` (default `true`) -- support `#!command` as the first line
|
||||
|
||||
@@ -590,6 +646,10 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
|
||||
- `evaluate` -- attempt to evaluate constant expressions
|
||||
|
||||
- `arrows` (default `true`) -- convert ES5 style anonymous function expressions
|
||||
to arrow functions if permissible by language semantics.
|
||||
Note: `arrows` requires that the `ecma` compress option is set to `6` or greater.
|
||||
|
||||
- `booleans` -- various optimizations for boolean context, for example `!!a
|
||||
? b : c → a ? b : c`
|
||||
|
||||
@@ -678,6 +738,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
|
||||
example: `/*@__PURE__*/foo();`
|
||||
|
||||
- `ecma` -- default `5`. Pass `6` or greater to enable `compress` options that
|
||||
will transform ES5 code into smaller ES6+ equivalent forms.
|
||||
|
||||
## Mangle options
|
||||
|
||||
- `reserved` (default `[]`). Pass an array of identifiers that should be
|
||||
@@ -686,6 +749,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
- `toplevel` (default `false`). Pass `true` to mangle names declared in the
|
||||
top level scope.
|
||||
|
||||
- `keep_classnames` (default `false`). Pass `true` to not mangle class names.
|
||||
|
||||
- `keep_fnames` (default `false`). Pass `true` to not mangle function names.
|
||||
Useful for code relying on `Function.prototype.name`. See also: the `keep_fnames`
|
||||
[compress option](#compress-options).
|
||||
@@ -749,9 +814,12 @@ can pass additional arguments that control the code output:
|
||||
- `comments` (default `false`) -- pass `true` or `"all"` to preserve all
|
||||
comments, `"some"` to preserve some comments, a regular expression string
|
||||
(e.g. `/^!/`) or a function.
|
||||
- `ecma` (default `5`) -- set output printing mode. This will only change the
|
||||
output in direct control of the beautifier. Non-compatible features in the
|
||||
abstract syntax tree will still be outputted as is.
|
||||
- `ecma` (default `5`) -- set output printing mode. Set `ecma` to `6` or
|
||||
greater to emit shorthand object properties - i.e.: `{a}` instead of `{a: a}`.
|
||||
The `ecma` option will only change the output in direct control of the
|
||||
beautifier. Non-compatible features in the abstract syntax tree will still
|
||||
be output as is. For example: an `ecma` setting of `5` will **not** convert
|
||||
ES6+ code to ES5.
|
||||
- `indent_level` (default 4)
|
||||
- `indent_start` (default 0) -- prefix all lines by that many spaces
|
||||
- `inline_script` (default `false`) -- escape the slash in occurrences of
|
||||
|
||||
32
bin/uglifyjs
32
bin/uglifyjs
@@ -44,6 +44,7 @@ program.option("-o, --output <file>", "Output file (default STDOUT).");
|
||||
program.option("--comments [filter]", "Preserve copyright comments in the output.");
|
||||
program.option("--config-file <file>", "Read minify() options from JSON file.");
|
||||
program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("define"));
|
||||
program.option("--ecma <version>", "Specifiy ECMAScript release: 5, 6, 7 or 8.");
|
||||
program.option("--ie8", "Support non-standard Internet Explorer 8.");
|
||||
program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.");
|
||||
program.option("--name-cache <file>", "File to hold mangled name mappings.");
|
||||
@@ -73,6 +74,10 @@ if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
|
||||
options[name] = program[name];
|
||||
}
|
||||
});
|
||||
if ("ecma" in program) {
|
||||
if (program.ecma != (program.ecma | 0)) fatal("ERROR: ecma must be an integer");
|
||||
options.ecma = program.ecma | 0;
|
||||
}
|
||||
if (program.beautify) {
|
||||
options.output = typeof program.beautify == "object" ? program.beautify : {};
|
||||
if (!("beautify" in options.output)) {
|
||||
@@ -106,17 +111,8 @@ if (program.mangleProps) {
|
||||
if (typeof options.mangle != "object") options.mangle = {};
|
||||
options.mangle.properties = program.mangleProps;
|
||||
}
|
||||
var cache;
|
||||
if (program.nameCache) {
|
||||
cache = JSON.parse(read_file(program.nameCache, "{}"));
|
||||
if (options.mangle) {
|
||||
if (typeof options.mangle != "object") options.mangle = {};
|
||||
options.mangle.cache = to_cache("vars");
|
||||
if (options.mangle.properties) {
|
||||
if (typeof options.mangle.properties != "object") options.mangle.properties = {};
|
||||
options.mangle.properties.cache = to_cache("props");
|
||||
}
|
||||
}
|
||||
options.nameCache = JSON.parse(read_file(program.nameCache, "{}"));
|
||||
}
|
||||
if (program.output == "ast") {
|
||||
options.output = {
|
||||
@@ -266,9 +262,7 @@ function run() {
|
||||
print(result.code);
|
||||
}
|
||||
if (program.nameCache) {
|
||||
fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) {
|
||||
return value instanceof UglifyJS.Dictionary ? value.toObject() : value;
|
||||
}));
|
||||
fs.writeFileSync(program.nameCache, JSON.stringify(options.nameCache));
|
||||
}
|
||||
if (result.timings) for (var phase in result.timings) {
|
||||
print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
|
||||
@@ -381,18 +375,6 @@ function parse_source_map() {
|
||||
}
|
||||
}
|
||||
|
||||
function to_cache(key) {
|
||||
if (cache[key]) {
|
||||
cache[key].props = UglifyJS.Dictionary.fromObject(cache[key].props);
|
||||
} else {
|
||||
cache[key] = {
|
||||
cname: -1,
|
||||
props: new UglifyJS.Dictionary()
|
||||
};
|
||||
}
|
||||
return cache[key];
|
||||
}
|
||||
|
||||
function skip_key(key) {
|
||||
return skip_keys.indexOf(key) >= 0;
|
||||
}
|
||||
|
||||
36
lib/ast.js
36
lib/ast.js
@@ -345,7 +345,7 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
var AST_Expansion = DEFNODE("Expansion", "expression", {
|
||||
$documentation: "An expandible argument, such as ...rest, a splat, such as [1,2,...all], or an expansion in a variable declaration, such as var [first, ...rest] = list",
|
||||
$propdoc: {
|
||||
expression: "AST_Symbol the thing to be expanded"
|
||||
expression: "[AST_Node] the thing to be expanded"
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
var self = this;
|
||||
@@ -446,7 +446,7 @@ var AST_PrefixedTemplateString = DEFNODE("PrefixedTemplateString", "template_str
|
||||
var AST_TemplateString = DEFNODE("TemplateString", "segments", {
|
||||
$documentation: "A template string literal",
|
||||
$propdoc: {
|
||||
segments: "[AST_TemplateSegment|AST_Expression]* One or more segments, starting with AST_TemplateSegment. AST_Expression may follow AST_TemplateSegment, but each AST_Expression must be followed by AST_TemplateSegment."
|
||||
segments: "[AST_Node*] One or more segments, starting with AST_TemplateSegment. AST_Node may follow AST_TemplateSegment, but each AST_Node must be followed by AST_TemplateSegment."
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function(){
|
||||
@@ -629,11 +629,11 @@ var AST_Const = DEFNODE("Const", null, {
|
||||
$documentation: "A `const` statement"
|
||||
}, AST_Definitions);
|
||||
|
||||
var AST_NameImport = DEFNODE("NameImport", "foreign_name name", {
|
||||
$documentation: "The part of the import statement that imports names from a module.",
|
||||
var AST_NameMapping = DEFNODE("NameMapping", "foreign_name name", {
|
||||
$documentation: "The part of the export/import statement that declare names from a module.",
|
||||
$propdoc: {
|
||||
foreign_name: "[AST_SymbolImportForeign] The name being imported (as specified in the module)",
|
||||
name: "[AST_SymbolImport] The name as it becomes available to this module."
|
||||
foreign_name: "[AST_SymbolExportForeign|AST_SymbolImportForeign] The name being exported/imported (as specified in the module)",
|
||||
name: "[AST_SymbolExport|AST_SymbolImport] The name as it is visible to this module."
|
||||
},
|
||||
_walk: function (visitor) {
|
||||
return visitor._visit(this, function() {
|
||||
@@ -647,7 +647,7 @@ var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
|
||||
$documentation: "An `import` statement",
|
||||
$propdoc: {
|
||||
imported_name: "[AST_SymbolImport] The name of the variable holding the module's default export.",
|
||||
imported_names: "[AST_NameImport*] The names of non-default imported variables",
|
||||
imported_names: "[AST_NameMapping*] The names of non-default imported variables",
|
||||
module_name: "[AST_String] String literal describing where this module came from",
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
@@ -656,7 +656,7 @@ var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
|
||||
this.imported_name._walk(visitor);
|
||||
}
|
||||
if (this.imported_names) {
|
||||
this.imported_names.forEach(function (name_import) {
|
||||
this.imported_names.forEach(function(name_import) {
|
||||
name_import._walk(visitor);
|
||||
});
|
||||
}
|
||||
@@ -670,7 +670,7 @@ var AST_Export = DEFNODE("Export", "exported_definition exported_value is_defaul
|
||||
$propdoc: {
|
||||
exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition",
|
||||
exported_value: "[AST_Node?] An exported value",
|
||||
exported_names: "[AST_NameImport*?] List of exported names",
|
||||
exported_names: "[AST_NameMapping*?] List of exported names",
|
||||
module_name: "[AST_String?] Name of the file to load exports from",
|
||||
is_default: "[Boolean] Whether this is the default exported value of this module"
|
||||
},
|
||||
@@ -682,6 +682,14 @@ var AST_Export = DEFNODE("Export", "exported_definition exported_value is_defaul
|
||||
if (this.exported_value) {
|
||||
this.exported_value._walk(visitor);
|
||||
}
|
||||
if (this.exported_names) {
|
||||
this.exported_names.forEach(function(name_export) {
|
||||
name_export._walk(visitor);
|
||||
});
|
||||
}
|
||||
if (this.module_name) {
|
||||
this.module_name._walk(visitor);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, AST_Statement);
|
||||
@@ -996,7 +1004,7 @@ var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
|
||||
}, AST_SymbolBlockDeclaration);
|
||||
|
||||
var AST_SymbolImport = DEFNODE("SymbolImport", null, {
|
||||
$documentation: "Symbol refering to an imported name",
|
||||
$documentation: "Symbol referring to an imported name",
|
||||
}, AST_SymbolBlockDeclaration);
|
||||
|
||||
var AST_SymbolImportForeign = DEFNODE("SymbolImportForeign", null, {
|
||||
@@ -1018,6 +1026,14 @@ var AST_SymbolRef = DEFNODE("SymbolRef", null, {
|
||||
$documentation: "Reference to some symbol (not definition/declaration)",
|
||||
}, AST_Symbol);
|
||||
|
||||
var AST_SymbolExport = DEFNODE("SymbolExport", null, {
|
||||
$documentation: "Symbol referring to a name to export",
|
||||
}, AST_SymbolRef);
|
||||
|
||||
var AST_SymbolExportForeign = DEFNODE("SymbolExportForeign", null, {
|
||||
$documentation: "A symbol exported from this module, but it is used in the other module, and its real name is irrelevant for this module's purposes",
|
||||
}, AST_Symbol);
|
||||
|
||||
var AST_LabelRef = DEFNODE("LabelRef", null, {
|
||||
$documentation: "Reference to a label symbol",
|
||||
}, AST_Symbol);
|
||||
|
||||
425
lib/compress.js
425
lib/compress.js
@@ -48,6 +48,7 @@ function Compressor(options, false_by_default) {
|
||||
return new Compressor(options, false_by_default);
|
||||
TreeTransformer.call(this, this.before, this.after);
|
||||
this.options = defaults(options, {
|
||||
arrows : !false_by_default,
|
||||
booleans : !false_by_default,
|
||||
cascade : !false_by_default,
|
||||
collapse_vars : !false_by_default,
|
||||
@@ -93,12 +94,9 @@ function Compressor(options, false_by_default) {
|
||||
var global_defs = this.options["global_defs"];
|
||||
if (typeof global_defs == "object") for (var key in global_defs) {
|
||||
if (/^@/.test(key) && HOP(global_defs, key)) {
|
||||
var ast = parse(global_defs[key]);
|
||||
if (ast.body.length == 1 && ast.body[0] instanceof AST_SimpleStatement) {
|
||||
global_defs[key.slice(1)] = ast.body[0].body;
|
||||
} else throw new Error(string_template("Can't handle expression: {value}", {
|
||||
value: global_defs[key]
|
||||
}));
|
||||
global_defs[key.slice(1)] = parse(global_defs[key], {
|
||||
expression: true
|
||||
});
|
||||
}
|
||||
}
|
||||
var pure_funcs = this.options["pure_funcs"];
|
||||
@@ -125,13 +123,13 @@ function Compressor(options, false_by_default) {
|
||||
};
|
||||
}
|
||||
var toplevel = this.options["toplevel"];
|
||||
if (typeof toplevel == "string") {
|
||||
this.toplevel.funcs = /funcs/.test(toplevel);
|
||||
this.toplevel.vars = /vars/.test(toplevel);
|
||||
} else {
|
||||
this.toplevel = toplevel ? return_true : return_false;
|
||||
this.toplevel.funcs = this.toplevel.vars = toplevel;
|
||||
}
|
||||
this.toplevel = typeof toplevel == "string" ? {
|
||||
funcs: /funcs/.test(toplevel),
|
||||
vars: /vars/.test(toplevel)
|
||||
} : {
|
||||
funcs: toplevel,
|
||||
vars: toplevel
|
||||
};
|
||||
var sequences = this.options["sequences"];
|
||||
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
|
||||
this.warnings_produced = {};
|
||||
@@ -140,11 +138,12 @@ function Compressor(options, false_by_default) {
|
||||
Compressor.prototype = new TreeTransformer;
|
||||
merge(Compressor.prototype, {
|
||||
option: function(key) { return this.options[key] },
|
||||
toplevel: function(def) {
|
||||
for (var i = 0, len = def.orig.length; i < len; i++)
|
||||
exposed: function(def) {
|
||||
if (def.export) return true;
|
||||
if (def.global) for (var i = 0, len = def.orig.length; i < len; i++)
|
||||
if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"])
|
||||
return false;
|
||||
return true;
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
compress: function(node) {
|
||||
if (this.option("expression")) {
|
||||
@@ -280,11 +279,11 @@ merge(Compressor.prototype, {
|
||||
var reduce_vars = rescan && compressor.option("reduce_vars");
|
||||
var safe_ids = Object.create(null);
|
||||
var suppressor = new TreeWalker(function(node) {
|
||||
if (node instanceof AST_Symbol) {
|
||||
var d = node.definition();
|
||||
if (node instanceof AST_SymbolRef) d.references.push(node);
|
||||
d.fixed = false;
|
||||
}
|
||||
if (!(node instanceof AST_Symbol)) return;
|
||||
var d = node.definition();
|
||||
if (!d) return;
|
||||
if (node instanceof AST_SymbolRef) d.references.push(node);
|
||||
d.fixed = false;
|
||||
});
|
||||
var tw = new TreeWalker(function(node, descend) {
|
||||
node._squeezed = false;
|
||||
@@ -352,7 +351,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (node instanceof AST_Defun) {
|
||||
var d = node.name.definition();
|
||||
if (d.global && !compressor.toplevel(d) || safe_to_read(d)) {
|
||||
if (compressor.exposed(d) || safe_to_read(d)) {
|
||||
d.fixed = false;
|
||||
} else {
|
||||
d.fixed = node;
|
||||
@@ -364,7 +363,7 @@ merge(Compressor.prototype, {
|
||||
safe_ids = save_ids;
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Function || node instanceof AST_Arrow) {
|
||||
if (is_func_expr(node)) {
|
||||
push();
|
||||
var iife;
|
||||
if (!node.name
|
||||
@@ -527,7 +526,7 @@ merge(Compressor.prototype, {
|
||||
def.escaped = false;
|
||||
if (def.scope.uses_eval) {
|
||||
def.fixed = false;
|
||||
} else if (!def.global || def.orig[0] instanceof AST_SymbolConst || compressor.toplevel(def)) {
|
||||
} else if (def.orig[0] instanceof AST_SymbolConst || !compressor.exposed(def)) {
|
||||
def.fixed = undefined;
|
||||
} else {
|
||||
def.fixed = false;
|
||||
@@ -557,8 +556,29 @@ merge(Compressor.prototype, {
|
||||
return fixed();
|
||||
});
|
||||
|
||||
AST_SymbolRef.DEFMETHOD("is_immutable", function() {
|
||||
var orig = this.definition().orig;
|
||||
return orig.length == 1 && orig[0] instanceof AST_SymbolLambda;
|
||||
});
|
||||
|
||||
function is_func_expr(node) {
|
||||
return node instanceof AST_Arrow || node instanceof AST_Function;
|
||||
}
|
||||
|
||||
function is_lhs_read_only(lhs) {
|
||||
return lhs instanceof AST_SymbolRef && lhs.definition().orig[0] instanceof AST_SymbolLambda;
|
||||
if (lhs instanceof AST_SymbolRef) return lhs.definition().orig[0] instanceof AST_SymbolLambda;
|
||||
if (lhs instanceof AST_PropAccess) {
|
||||
lhs = lhs.expression;
|
||||
if (lhs instanceof AST_SymbolRef) {
|
||||
if (lhs.is_immutable()) return false;
|
||||
lhs = lhs.fixed_value();
|
||||
}
|
||||
if (!lhs) return true;
|
||||
if (lhs instanceof AST_RegExp) return false;
|
||||
if (lhs instanceof AST_Constant) return true;
|
||||
return is_lhs_read_only(lhs);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function is_ref_of(ref, type) {
|
||||
@@ -672,7 +692,9 @@ merge(Compressor.prototype, {
|
||||
node instanceof AST_DefClass ||
|
||||
node instanceof AST_Defun ||
|
||||
node instanceof AST_Let ||
|
||||
node instanceof AST_Const
|
||||
node instanceof AST_Const ||
|
||||
node instanceof AST_Export ||
|
||||
node instanceof AST_Import
|
||||
);
|
||||
}
|
||||
|
||||
@@ -727,15 +749,23 @@ merge(Compressor.prototype, {
|
||||
var candidates = [];
|
||||
var stat_index = statements.length;
|
||||
while (--stat_index >= 0) {
|
||||
// Treat parameters as collapsible in IIFE, i.e.
|
||||
// function(a, b){ ... }(x());
|
||||
// would be translated into equivalent assignments:
|
||||
// var a = x(), b = undefined;
|
||||
if (stat_index == 0 && compressor.option("unused")) extract_args();
|
||||
// Find collapsible assignments
|
||||
extract_candidates(statements[stat_index]);
|
||||
while (candidates.length > 0) {
|
||||
var candidate = candidates.pop();
|
||||
var lhs = get_lhs(candidate);
|
||||
if (!lhs || is_lhs_read_only(lhs)) continue;
|
||||
// Locate symbols which may execute code outside of scanning range
|
||||
var lvalues = get_lvalues(candidate);
|
||||
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
|
||||
var side_effects = value_has_side_effects(candidate);
|
||||
var hit = false, abort = false, replaced = false;
|
||||
var hit = candidate.name instanceof AST_SymbolFunarg;
|
||||
var abort = false, replaced = false;
|
||||
var tt = new TreeTransformer(function(node, descend) {
|
||||
if (abort) return node;
|
||||
// Skip nodes before `candidate` as quickly as possible
|
||||
@@ -777,7 +807,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (candidate instanceof AST_VarDef) {
|
||||
var def = candidate.name.definition();
|
||||
if (def.references.length == 1 && (!def.global || compressor.toplevel(def))) {
|
||||
if (def.references.length == 1 && !compressor.exposed(def)) {
|
||||
return maintain_this_binding(parent, node, candidate.value);
|
||||
}
|
||||
return make_node(AST_Assign, candidate, {
|
||||
@@ -818,6 +848,58 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
|
||||
function has_overlapping_symbol(fn, arg) {
|
||||
var found = false;
|
||||
arg.walk(new TreeWalker(function(node) {
|
||||
if (found) return true;
|
||||
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
|
||||
var s = node.definition().scope;
|
||||
if (s !== scope) while (s = s.parent_scope) {
|
||||
if (s === scope) return true;
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
}));
|
||||
return found;
|
||||
}
|
||||
|
||||
function extract_args() {
|
||||
var iife, fn = compressor.self();
|
||||
if (is_func_expr(fn)
|
||||
&& !fn.name
|
||||
&& !fn.uses_arguments
|
||||
&& !fn.uses_eval
|
||||
&& (iife = compressor.parent()) instanceof AST_Call
|
||||
&& iife.expression === fn
|
||||
&& all(iife.args, function(arg) {
|
||||
return !(arg instanceof AST_Expansion);
|
||||
})) {
|
||||
fn.argnames.forEach(function(sym, i) {
|
||||
if (sym instanceof AST_Expansion) {
|
||||
var elements = iife.args.slice(i);
|
||||
if (all(elements, function(arg) {
|
||||
return !has_overlapping_symbol(fn, arg);
|
||||
})) {
|
||||
candidates.push(make_node(AST_VarDef, sym, {
|
||||
name: sym.expression,
|
||||
value: make_node(AST_Array, iife, {
|
||||
elements: elements
|
||||
})
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
var arg = iife.args[i];
|
||||
if (!arg) arg = make_node(AST_Undefined, sym);
|
||||
else if (has_overlapping_symbol(fn, arg)) arg = null;
|
||||
if (arg) candidates.push(make_node(AST_VarDef, sym, {
|
||||
name: sym,
|
||||
value: arg
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function extract_candidates(expr) {
|
||||
if (expr instanceof AST_Assign && !expr.left.has_side_effects(compressor)
|
||||
|| expr instanceof AST_Unary && (expr.operator == "++" || expr.operator == "--")) {
|
||||
@@ -838,8 +920,8 @@ merge(Compressor.prototype, {
|
||||
function get_lhs(expr) {
|
||||
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
|
||||
var def = expr.name.definition();
|
||||
if (def.orig.length > 1
|
||||
|| def.references.length == 1 && (!def.global || compressor.toplevel(def))) {
|
||||
if (def.orig.length > 1 && !(expr.name instanceof AST_SymbolFunarg)
|
||||
|| def.references.length == 1 && !compressor.exposed(def)) {
|
||||
return make_node(AST_SymbolRef, expr.name, expr.name);
|
||||
}
|
||||
} else {
|
||||
@@ -881,6 +963,19 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function remove_candidate(expr) {
|
||||
if (expr.name instanceof AST_SymbolFunarg) {
|
||||
var iife = compressor.parent(), argnames = compressor.self().argnames;
|
||||
var index = argnames.indexOf(expr.name);
|
||||
if (index < 0) {
|
||||
iife.args.length = Math.min(iife.args.length, argnames.length - 1);
|
||||
} else {
|
||||
var args = iife.args;
|
||||
if (args[index]) args[index] = make_node(AST_Number, args[index], {
|
||||
value: 0
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
var found = false;
|
||||
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
|
||||
if (found) return node;
|
||||
@@ -1299,6 +1394,7 @@ merge(Compressor.prototype, {
|
||||
return false;
|
||||
});
|
||||
def(AST_Function, return_false);
|
||||
def(AST_Arrow, return_false);
|
||||
def(AST_UnaryPostfix, return_false);
|
||||
def(AST_UnaryPrefix, function() {
|
||||
return this.operator == "void";
|
||||
@@ -1328,6 +1424,7 @@ merge(Compressor.prototype, {
|
||||
def(AST_SymbolRef, function(pure_getters) {
|
||||
if (this.is_undefined) return true;
|
||||
if (!is_strict(pure_getters)) return false;
|
||||
if (this.is_immutable()) return false;
|
||||
var fixed = this.fixed_value();
|
||||
return !fixed || fixed._throw_on_access(pure_getters);
|
||||
});
|
||||
@@ -1571,9 +1668,6 @@ merge(Compressor.prototype, {
|
||||
def(AST_Lambda, function(){
|
||||
throw def;
|
||||
});
|
||||
def(AST_Arrow, function() {
|
||||
throw def;
|
||||
});
|
||||
def(AST_Class, function() {
|
||||
throw def;
|
||||
});
|
||||
@@ -1627,8 +1721,7 @@ merge(Compressor.prototype, {
|
||||
case "typeof":
|
||||
// Function would be evaluated to an array and so typeof would
|
||||
// incorrectly return 'object'. Hence making is a special case.
|
||||
if (e instanceof AST_Function ||
|
||||
e instanceof AST_Arrow) return typeof function(){};
|
||||
if (is_func_expr(e)) return typeof function(){};
|
||||
|
||||
e = ev(e, compressor);
|
||||
|
||||
@@ -1800,6 +1893,9 @@ merge(Compressor.prototype, {
|
||||
def(AST_Function, function(){
|
||||
return basic_negation(this);
|
||||
});
|
||||
def(AST_Arrow, function(){
|
||||
return basic_negation(this);
|
||||
});
|
||||
def(AST_UnaryPrefix, function(){
|
||||
if (this.operator == "!")
|
||||
return this.expression;
|
||||
@@ -2042,7 +2138,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_Block, function(self, compressor){
|
||||
if (!(self.body instanceof AST_Node)) tighten_body(self.body, compressor);
|
||||
tighten_body(self.body, compressor);
|
||||
return self;
|
||||
});
|
||||
|
||||
@@ -2081,14 +2177,14 @@ merge(Compressor.prototype, {
|
||||
var var_defs_by_id = new Dictionary();
|
||||
var initializations = new Dictionary();
|
||||
var destructuring_value = null;
|
||||
var in_definition = false;
|
||||
// pass 1: find out which symbols are directly used in
|
||||
// this scope (not in nested scopes).
|
||||
var scope = this;
|
||||
var tw = new TreeWalker(function(node, descend){
|
||||
if (node !== self) {
|
||||
if (node instanceof AST_Defun || node instanceof AST_DefClass) {
|
||||
if (!drop_funcs && scope === self) {
|
||||
var in_export = tw.parent() instanceof AST_Export;
|
||||
if (in_export || !drop_funcs && scope === self) {
|
||||
var node_def = node.name.definition();
|
||||
if (node_def.global && !(node_def.id in in_use_ids)) {
|
||||
in_use_ids[node_def.id] = true;
|
||||
@@ -2099,15 +2195,16 @@ merge(Compressor.prototype, {
|
||||
return true; // don't go in nested scopes
|
||||
}
|
||||
if (node instanceof AST_Definitions && scope === self) {
|
||||
var in_export = tw.parent() instanceof AST_Export;
|
||||
node.definitions.forEach(function(def){
|
||||
if (def.name instanceof AST_SymbolVar) {
|
||||
var_defs_by_id.add(def.name.definition().id, def);
|
||||
}
|
||||
if (!drop_vars) {
|
||||
if (in_export || !drop_vars) {
|
||||
def.name.walk(new TreeWalker(function(node) {
|
||||
if (node instanceof AST_SymbolDeclaration) {
|
||||
var def = node.definition();
|
||||
if (def.global && !(def.id in in_use_ids)) {
|
||||
if ((in_export || def.global) && !(def.id in in_use_ids)) {
|
||||
in_use_ids[def.id] = true;
|
||||
in_use.push(def);
|
||||
}
|
||||
@@ -2118,9 +2215,7 @@ merge(Compressor.prototype, {
|
||||
if (def.name instanceof AST_Destructuring) {
|
||||
var destructuring_cache = destructuring_value;
|
||||
destructuring_value = def.value;
|
||||
in_definition = true;
|
||||
def.walk(tw);
|
||||
in_definition = false;
|
||||
destructuring_value = destructuring_cache;
|
||||
} else {
|
||||
initializations.add(def.name.name, def.value);
|
||||
@@ -2156,46 +2251,8 @@ merge(Compressor.prototype, {
|
||||
scope = save_scope;
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Destructuring) {
|
||||
if (!in_definition) {
|
||||
return true;
|
||||
}
|
||||
for (var i = 0; i < node.names.length; i++) {
|
||||
if (node.names[i] instanceof AST_Destructuring) {
|
||||
node.names[i].walk(tw);
|
||||
}
|
||||
else if (node.names[i] instanceof AST_Expansion) {
|
||||
if (node.names[i].expression instanceof AST_Symbol) {
|
||||
initializations.add(node.names[i].expression.name, destructuring_value);
|
||||
} else if (node.names[i].expression instanceof AST_Destructuring) {
|
||||
node.names[i].expression.walk(tw);
|
||||
} else {
|
||||
throw new Error(string_template("Can't handle expansion of type: {type}", {
|
||||
type: Object.getPrototypeOf(node.names[i].expression).TYPE
|
||||
}));
|
||||
}
|
||||
}
|
||||
else if (node.names[i] instanceof AST_ObjectKeyVal) {
|
||||
if (typeof node.names[i].key === "string") {
|
||||
initializations.add(node.names[i].key, destructuring_value);
|
||||
}
|
||||
}
|
||||
else if (node.names[i] instanceof AST_Symbol) {
|
||||
initializations.add(node.names[i].name, destructuring_value);
|
||||
}
|
||||
else if (node.names[i] instanceof AST_DefaultAssign) {
|
||||
continue;
|
||||
}
|
||||
else if (node.names[i] instanceof AST_Hole) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
throw new Error(string_template("Unknown destructuring element of type: {type}", {
|
||||
type: Object.getPrototypeOf(node.names[i]).TYPE
|
||||
}));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
if (node.destructuring && destructuring_value) {
|
||||
initializations.add(node.name, destructuring_value);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -2224,11 +2281,8 @@ merge(Compressor.prototype, {
|
||||
// pass 3: we should drop declarations not in_use
|
||||
var tt = new TreeTransformer(
|
||||
function before(node, descend, in_list) {
|
||||
var parent = tt.parent();
|
||||
if (!compressor.option("keep_fnames")
|
||||
&& ((node instanceof AST_Function || node instanceof AST_ClassExpression) && node.name
|
||||
|| (node instanceof AST_Defun || node instanceof AST_DefClass)
|
||||
&& parent instanceof AST_Export && parent.is_default)) {
|
||||
&& node.name && (node instanceof AST_Function || node instanceof AST_ClassExpression)) {
|
||||
var def = node.name.definition();
|
||||
// any declarations with same name will overshadow
|
||||
// name of this anonymous function and can therefore
|
||||
@@ -2263,7 +2317,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && !(parent instanceof AST_Export) && node !== self) {
|
||||
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) {
|
||||
var keep = (node.name.definition().id in in_use_ids) || !drop_funcs && node.name.definition().global;
|
||||
if (!keep) {
|
||||
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
|
||||
@@ -2271,6 +2325,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return node;
|
||||
}
|
||||
var parent = tt.parent();
|
||||
if (node instanceof AST_Definitions
|
||||
&& !(parent instanceof AST_ForIn && parent.init === node)
|
||||
&& (drop_vars || !(parent instanceof AST_Toplevel) && !(node instanceof AST_Var))) {
|
||||
@@ -2587,7 +2642,7 @@ merge(Compressor.prototype, {
|
||||
def(AST_This, return_null);
|
||||
def(AST_Call, function(compressor, first_in_statement){
|
||||
if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) {
|
||||
if (this.expression instanceof AST_Function
|
||||
if (is_func_expr(this.expression)
|
||||
&& (!this.expression.name || !this.expression.name.definition().references.length)) {
|
||||
var node = this.clone();
|
||||
node.expression.process_expression(false, compressor);
|
||||
@@ -2691,6 +2746,9 @@ merge(Compressor.prototype, {
|
||||
if (expr) merge_sequence(expressions, expr);
|
||||
return make_sequence(this, expressions);
|
||||
});
|
||||
def(AST_Expansion, function(compressor, first_in_statement){
|
||||
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||
});
|
||||
})(function(node, func){
|
||||
node.DEFMETHOD("drop_side_effect_free", func);
|
||||
});
|
||||
@@ -3106,15 +3164,31 @@ merge(Compressor.prototype, {
|
||||
OPT(AST_Call, function(self, compressor){
|
||||
var exp = self.expression;
|
||||
var fn = exp;
|
||||
var simple_args = all(self.args, function(arg) {
|
||||
return !(arg instanceof AST_Expansion);
|
||||
});
|
||||
if (compressor.option("unused")
|
||||
&& (fn instanceof AST_Function
|
||||
&& simple_args
|
||||
&& (is_func_expr(fn)
|
||||
|| compressor.option("reduce_vars")
|
||||
&& fn instanceof AST_SymbolRef
|
||||
&& (fn = fn.fixed_value()) instanceof AST_Function)
|
||||
&& is_func_expr(fn = fn.fixed_value()))
|
||||
&& !fn.uses_arguments
|
||||
&& !fn.uses_eval) {
|
||||
var pos = 0, last = 0;
|
||||
for (var i = 0, len = self.args.length; i < len; i++) {
|
||||
if (fn.argnames[i] instanceof AST_Expansion) {
|
||||
if (fn.argnames[i].expression.__unused) while (i < len) {
|
||||
var node = self.args[i++].drop_side_effect_free(compressor);
|
||||
if (node) {
|
||||
self.args[pos++] = node;
|
||||
}
|
||||
} else while (i < len) {
|
||||
self.args[pos++] = self.args[i++];
|
||||
}
|
||||
last = pos;
|
||||
break;
|
||||
}
|
||||
var trim = i >= fn.argnames.length;
|
||||
if (trim || fn.argnames[i].__unused) {
|
||||
var node = self.args[i].drop_side_effect_free(compressor);
|
||||
@@ -3272,7 +3346,7 @@ merge(Compressor.prototype, {
|
||||
if (self.args.length == 0) return make_node(AST_Function, self, {
|
||||
argnames: [],
|
||||
body: []
|
||||
});
|
||||
}).optimize(compressor);
|
||||
if (all(self.args, function(x) {
|
||||
return x instanceof AST_String;
|
||||
})) {
|
||||
@@ -3280,7 +3354,7 @@ merge(Compressor.prototype, {
|
||||
// https://github.com/mishoo/UglifyJS2/issues/203
|
||||
// if the code argument is a constant, then we can minify it.
|
||||
try {
|
||||
var code = "NaN(function(" + self.args.slice(0, -1).map(function(arg) {
|
||||
var code = "n(function(" + self.args.slice(0, -1).map(function(arg) {
|
||||
return arg.value;
|
||||
}).join(",") + "){" + self.args[self.args.length - 1].value + "})";
|
||||
var ast = parse(code);
|
||||
@@ -3289,27 +3363,36 @@ merge(Compressor.prototype, {
|
||||
var comp = new Compressor(compressor.options);
|
||||
ast = ast.transform(comp);
|
||||
ast.figure_out_scope(mangle);
|
||||
ast.mangle_names();
|
||||
base54.reset();
|
||||
ast.compute_char_frequency(mangle);
|
||||
ast.mangle_names(mangle);
|
||||
var fun;
|
||||
ast.walk(new TreeWalker(function(node) {
|
||||
if (fun) return true;
|
||||
if (node instanceof AST_Lambda) {
|
||||
if (is_func_expr(node)) {
|
||||
fun = node;
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
var args = fun.argnames.map(function(arg, i) {
|
||||
return make_node(AST_String, self.args[i], {
|
||||
value: arg.print_to_string()
|
||||
});
|
||||
});
|
||||
if (fun.body instanceof AST_Node) {
|
||||
fun.body = [
|
||||
make_node(AST_Return, fun.body, {
|
||||
value: fun.body
|
||||
})
|
||||
];
|
||||
}
|
||||
var code = OutputStream();
|
||||
AST_BlockStatement.prototype._codegen.call(fun, fun, code);
|
||||
code = code.toString().replace(/^\{|\}$/g, "");
|
||||
args.push(make_node(AST_String, self.args[self.args.length - 1], {
|
||||
value: code
|
||||
}));
|
||||
self.args = args;
|
||||
self.args = [
|
||||
make_node(AST_String, self, {
|
||||
value: fun.argnames.map(function(arg) {
|
||||
return arg.print_to_string();
|
||||
}).join(",")
|
||||
}),
|
||||
make_node(AST_String, self.args[self.args.length - 1], {
|
||||
value: code.get().replace(/^\{|\}$/g, "")
|
||||
})
|
||||
];
|
||||
return self;
|
||||
} catch (ex) {
|
||||
if (ex instanceof JS_Parse_Error) {
|
||||
@@ -3321,85 +3404,67 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
}
|
||||
var stat = fn instanceof AST_Function && fn.body[0];
|
||||
var stat = is_func_expr(fn) && fn.body;
|
||||
if (stat instanceof AST_Node) {
|
||||
stat = make_node(AST_Return, stat, {
|
||||
value: stat
|
||||
});
|
||||
} else if (stat) {
|
||||
stat = stat[0];
|
||||
}
|
||||
if (compressor.option("inline") && stat instanceof AST_Return) {
|
||||
var value = stat.value;
|
||||
if (!value || value.is_constant_expression()) {
|
||||
var args = self.args.concat(value || make_node(AST_Undefined, self));
|
||||
return make_sequence(self, args).transform(compressor);
|
||||
return make_sequence(self, args).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (exp instanceof AST_Function && !exp.is_generator && !exp.async) {
|
||||
if (is_func_expr(exp) && !exp.is_generator && !exp.async) {
|
||||
if (compressor.option("inline")
|
||||
&& simple_args
|
||||
&& !exp.name
|
||||
&& exp.body.length == 1
|
||||
&& !exp.uses_arguments
|
||||
&& !exp.uses_eval
|
||||
&& (exp.body instanceof AST_Node || exp.body.length == 1)
|
||||
&& all(exp.argnames, function(arg) {
|
||||
if (arg instanceof AST_Expansion) return arg.expression.__unused;
|
||||
return arg.__unused;
|
||||
})
|
||||
&& !self.has_pure_annotation(compressor)) {
|
||||
var value;
|
||||
if (stat instanceof AST_Return) {
|
||||
value = stat.value.clone(true);
|
||||
value = stat.value;
|
||||
} else if (stat instanceof AST_SimpleStatement) {
|
||||
value = make_node(AST_UnaryPrefix, stat, {
|
||||
operator: "void",
|
||||
expression: stat.body.clone(true)
|
||||
expression: stat.body
|
||||
});
|
||||
}
|
||||
if (value) {
|
||||
var fn = exp.clone();
|
||||
fn.argnames = [];
|
||||
fn.body = [];
|
||||
if (exp.argnames.length > 0) {
|
||||
fn.body.push(make_node(AST_Var, self, {
|
||||
definitions: exp.argnames.map(function(sym, i) {
|
||||
var arg = self.args[i];
|
||||
return make_node(AST_VarDef, sym, {
|
||||
name: sym,
|
||||
value: arg ? arg.clone(true) : make_node(AST_Undefined, self)
|
||||
});
|
||||
})
|
||||
}));
|
||||
}
|
||||
if (self.args.length > exp.argnames.length) {
|
||||
fn.body.push(make_node(AST_SimpleStatement, self, {
|
||||
body: make_sequence(self, self.args.slice(exp.argnames.length).map(function(node) {
|
||||
return node.clone(true);
|
||||
}))
|
||||
}));
|
||||
}
|
||||
fn.body.push(make_node(AST_Return, self, {
|
||||
value: value
|
||||
}));
|
||||
var body = fn.transform(compressor).body;
|
||||
if (body.length == 0) return make_node(AST_Undefined, self);
|
||||
if (body.length == 1 && body[0] instanceof AST_Return) {
|
||||
value = body[0].value;
|
||||
if (!value) return make_node(AST_Undefined, self);
|
||||
var tw = new TreeWalker(function(node) {
|
||||
if (value === self) return true;
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
var ref = node.scope.find_variable(node);
|
||||
if (ref && ref.scope.parent_scope === fn.parent_scope) {
|
||||
value = self;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_This && !tw.find_parent(AST_Scope)) {
|
||||
value = self;
|
||||
var tw = new TreeWalker(function(node) {
|
||||
if (!value) return true;
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
var ref = node.scope.find_variable(node);
|
||||
if (ref && ref.scope.parent_scope === fn.parent_scope) {
|
||||
value = null;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
value.walk(tw);
|
||||
if (value !== self) value = best_of(compressor, value, self);
|
||||
} else {
|
||||
value = self;
|
||||
}
|
||||
if (value !== self) return value;
|
||||
}
|
||||
if (node instanceof AST_This && !tw.find_parent(AST_Scope)) {
|
||||
value = null;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
value.walk(tw);
|
||||
}
|
||||
if (value) {
|
||||
var args = self.args.concat(value);
|
||||
return make_sequence(self, args).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.option("side_effects") && all(exp.body, is_empty)) {
|
||||
if (compressor.option("side_effects") && !(exp.body instanceof AST_Node) && all(exp.body, is_empty)) {
|
||||
var args = self.args.concat(make_node(AST_Undefined, self));
|
||||
return make_sequence(self, args).transform(compressor);
|
||||
return make_sequence(self, args).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.option("drop_console")) {
|
||||
@@ -3532,6 +3597,8 @@ merge(Compressor.prototype, {
|
||||
|| cdr instanceof AST_PropAccess
|
||||
|| cdr instanceof AST_Unary && !unary_side_effects(cdr.operator)) {
|
||||
field = "expression";
|
||||
} else if (cdr instanceof AST_Conditional) {
|
||||
field = "condition";
|
||||
} else {
|
||||
expressions[++i] = expressions[j];
|
||||
break;
|
||||
@@ -4039,6 +4106,10 @@ merge(Compressor.prototype, {
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_SymbolExport, function(self, compressor){
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_SymbolRef, function(self, compressor){
|
||||
var def = self.resolve_defines(compressor);
|
||||
if (def) {
|
||||
@@ -4062,15 +4133,15 @@ merge(Compressor.prototype, {
|
||||
var d = self.definition();
|
||||
var fixed = self.fixed_value();
|
||||
if (fixed instanceof AST_Defun) {
|
||||
d.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
|
||||
d.fixed = fixed = make_node(AST_Function, fixed, fixed);
|
||||
}
|
||||
if (compressor.option("unused")
|
||||
&& fixed instanceof AST_Function
|
||||
&& is_func_expr(fixed)
|
||||
&& d.references.length == 1
|
||||
&& !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg)
|
||||
&& !d.scope.uses_eval
|
||||
&& compressor.find_parent(AST_Scope) === fixed.parent_scope) {
|
||||
return fixed;
|
||||
return fixed.clone(true);
|
||||
}
|
||||
if (compressor.option("evaluate") && fixed) {
|
||||
if (d.should_replace === undefined) {
|
||||
@@ -4093,7 +4164,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
var name_length = d.name.length;
|
||||
var overhead = 0;
|
||||
if (compressor.option("unused") && (!d.global || compressor.toplevel(d))) {
|
||||
if (compressor.option("unused") && !compressor.exposed(d)) {
|
||||
overhead = (name_length + 2 + value_length) / d.references.length;
|
||||
}
|
||||
d.should_replace = value_length <= name_length + overhead ? fn : false;
|
||||
@@ -4504,13 +4575,37 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_Arrow, function(self, compressor){
|
||||
if (self.body.length === 1 && self.body[0] instanceof AST_Return) {
|
||||
if (!(self.body instanceof AST_Node)) tighten_body(self.body, compressor);
|
||||
if (compressor.option("arrows")
|
||||
&& self.body.length == 1
|
||||
&& self.body[0] instanceof AST_Return) {
|
||||
var value = self.body[0].value;
|
||||
self.body = value ? value : [];
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_Function, function(self, compressor){
|
||||
tighten_body(self.body, compressor);
|
||||
if (compressor.option("arrows")
|
||||
&& compressor.option("ecma") >= 6
|
||||
&& !self.name
|
||||
&& !self.is_generator
|
||||
&& !self.uses_arguments
|
||||
&& !self.uses_eval) {
|
||||
var has_special_symbol = false;
|
||||
self.walk(new TreeWalker(function(node) {
|
||||
if (has_special_symbol) return true;
|
||||
if (node instanceof AST_Symbol && !node.definition()) {
|
||||
has_special_symbol = true;
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
if (!has_special_symbol) return make_node(AST_Arrow, self, self).optimize(compressor);
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_Class, function(self, compressor){
|
||||
// HACK to avoid compress failure.
|
||||
// AST_Class is not really an AST_Scope/AST_Block as it lacks a body.
|
||||
|
||||
@@ -27,14 +27,33 @@ function set_shorthand(name, options, keys) {
|
||||
}
|
||||
}
|
||||
|
||||
function init_cache(cache) {
|
||||
if (!cache) return;
|
||||
if (!("cname" in cache)) cache.cname = -1;
|
||||
if (!("props" in cache)) {
|
||||
cache.props = new Dictionary();
|
||||
} else if (!(cache.props instanceof Dictionary)) {
|
||||
cache.props = Dictionary.fromObject(cache.props);
|
||||
}
|
||||
}
|
||||
|
||||
function to_json(cache) {
|
||||
return {
|
||||
cname: cache.cname,
|
||||
props: cache.props.toObject()
|
||||
};
|
||||
}
|
||||
|
||||
function minify(files, options) {
|
||||
var warn_function = AST_Node.warn_function;
|
||||
try {
|
||||
options = defaults(options, {
|
||||
compress: {},
|
||||
ecma: undefined,
|
||||
ie8: false,
|
||||
keep_fnames: false,
|
||||
mangle: {},
|
||||
nameCache: null,
|
||||
output: {},
|
||||
parse: {},
|
||||
sourceMap: false,
|
||||
@@ -46,13 +65,14 @@ function minify(files, options) {
|
||||
var timings = options.timings && {
|
||||
start: Date.now()
|
||||
};
|
||||
set_shorthand("ecma", options, [ "parse", "compress", "output" ]);
|
||||
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
|
||||
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
|
||||
set_shorthand("toplevel", options, [ "compress", "mangle" ]);
|
||||
set_shorthand("warnings", options, [ "compress" ]);
|
||||
if (options.mangle) {
|
||||
options.mangle = defaults(options.mangle, {
|
||||
cache: null,
|
||||
cache: options.nameCache && (options.nameCache.vars || {}),
|
||||
eval: false,
|
||||
ie8: false,
|
||||
keep_classnames: false,
|
||||
@@ -62,6 +82,16 @@ function minify(files, options) {
|
||||
safari10: false,
|
||||
toplevel: false,
|
||||
}, true);
|
||||
if (options.nameCache && options.mangle.properties) {
|
||||
if (typeof options.mangle.properties != "object") {
|
||||
options.mangle.properties = {};
|
||||
}
|
||||
if (!("cache" in options.mangle.properties)) {
|
||||
options.mangle.properties.cache = options.nameCache.props || {};
|
||||
}
|
||||
}
|
||||
init_cache(options.mangle.cache);
|
||||
init_cache(options.mangle.properties.cache);
|
||||
}
|
||||
if (options.sourceMap) {
|
||||
options.sourceMap = defaults(options.sourceMap, {
|
||||
@@ -155,6 +185,12 @@ function minify(files, options) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options.nameCache && options.mangle) {
|
||||
if (options.mangle.cache) options.nameCache.vars = to_json(options.mangle.cache);
|
||||
if (options.mangle.properties && options.mangle.properties.cache) {
|
||||
options.nameCache.props = to_json(options.mangle.properties.cache);
|
||||
}
|
||||
}
|
||||
if (timings) {
|
||||
timings.end = Date.now();
|
||||
result.timings = {
|
||||
|
||||
@@ -533,6 +533,7 @@ function OutputStream(options) {
|
||||
use_asm = prev_use_asm;
|
||||
}
|
||||
});
|
||||
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
|
||||
|
||||
AST_Node.DEFMETHOD("print_to_string", function(options){
|
||||
var s = OutputStream(options);
|
||||
@@ -672,6 +673,12 @@ function OutputStream(options) {
|
||||
&& this.operator !== "--";
|
||||
});
|
||||
|
||||
PARENS(AST_Await, function(output){
|
||||
var p = output.parent();
|
||||
return p instanceof AST_PropAccess && p.expression === this
|
||||
|| p instanceof AST_Call && p.expression === this;
|
||||
});
|
||||
|
||||
PARENS(AST_Sequence, function(output){
|
||||
var p = output.parent();
|
||||
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
|
||||
@@ -978,12 +985,12 @@ function OutputStream(options) {
|
||||
AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){
|
||||
var self = this;
|
||||
if (!nokeyword) {
|
||||
if (this.async) {
|
||||
if (self.async) {
|
||||
output.print("async");
|
||||
output.space();
|
||||
}
|
||||
output.print("function");
|
||||
if (this.is_generator) {
|
||||
if (self.is_generator) {
|
||||
output.star();
|
||||
}
|
||||
if (self.name) {
|
||||
@@ -1039,6 +1046,10 @@ function OutputStream(options) {
|
||||
parent instanceof AST_Unary ||
|
||||
(parent instanceof AST_Call && self === parent.expression);
|
||||
if (needs_parens) { output.print("(") }
|
||||
if (self.async) {
|
||||
output.print("async");
|
||||
output.space();
|
||||
}
|
||||
if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol) {
|
||||
self.argnames[0].print(output);
|
||||
} else {
|
||||
@@ -1053,9 +1064,9 @@ function OutputStream(options) {
|
||||
output.print('=>');
|
||||
output.space();
|
||||
if (self.body instanceof AST_Node) {
|
||||
this.body.print(output);
|
||||
self.body.print(output);
|
||||
} else {
|
||||
print_bracketed(this.body, output);
|
||||
print_bracketed(self.body, output);
|
||||
}
|
||||
if (needs_parens) { output.print(")") }
|
||||
});
|
||||
@@ -1280,7 +1291,6 @@ function OutputStream(options) {
|
||||
name_import.print(output);
|
||||
if (i < self.imported_names.length - 1) {
|
||||
output.print(",");
|
||||
output.space();
|
||||
}
|
||||
});
|
||||
output.space();
|
||||
@@ -1296,17 +1306,26 @@ function OutputStream(options) {
|
||||
output.semicolon();
|
||||
});
|
||||
|
||||
DEFPRINT(AST_NameImport, function(self, output) {
|
||||
DEFPRINT(AST_NameMapping, function(self, output) {
|
||||
var is_import = output.parent() instanceof AST_Import;
|
||||
var definition = self.name.definition();
|
||||
var names_are_different =
|
||||
(definition && definition.mangled_name || self.name.name) !==
|
||||
self.foreign_name.name;
|
||||
if (names_are_different) {
|
||||
output.print(self.foreign_name.name);
|
||||
if (is_import) {
|
||||
output.print(self.foreign_name.name);
|
||||
} else {
|
||||
self.name.print(output);
|
||||
}
|
||||
output.space();
|
||||
output.print("as");
|
||||
output.space();
|
||||
self.name.print(output);
|
||||
if (is_import) {
|
||||
self.name.print(output);
|
||||
} else {
|
||||
output.print(self.foreign_name.name);
|
||||
}
|
||||
} else {
|
||||
self.name.print(output);
|
||||
}
|
||||
@@ -1320,24 +1339,20 @@ function OutputStream(options) {
|
||||
output.space();
|
||||
}
|
||||
if (self.exported_names) {
|
||||
output.space();
|
||||
|
||||
if (self.exported_names.length === 1 && self.exported_names[0].name.name === "*") {
|
||||
self.exported_names[0].print(output);
|
||||
} else {
|
||||
output.print("{");
|
||||
self.exported_names.forEach(function (name_import, i) {
|
||||
self.exported_names.forEach(function(name_export, i) {
|
||||
output.space();
|
||||
name_import.print(output);
|
||||
name_export.print(output);
|
||||
if (i < self.exported_names.length - 1) {
|
||||
output.print(",");
|
||||
output.space();
|
||||
}
|
||||
});
|
||||
output.space();
|
||||
output.print("}");
|
||||
}
|
||||
output.space();
|
||||
}
|
||||
else if (self.exported_value) {
|
||||
self.exported_value.print(output);
|
||||
|
||||
293
lib/parse.js
293
lib/parse.js
@@ -509,8 +509,11 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
}
|
||||
var content = "", raw = "", ch, tok;
|
||||
next(true, true);
|
||||
while ((ch = next(true, true)) !== "`") {
|
||||
if (ch === "$" && peek() === "{") {
|
||||
while ((ch = next(true, true)) != "`") {
|
||||
if (ch == "\r") {
|
||||
if (peek() == "\n") ++S.pos;
|
||||
ch = "\n";
|
||||
} else if (ch == "$" && peek() == "{") {
|
||||
next(true, true);
|
||||
S.brace_counter++;
|
||||
tok = token(begin ? "template_head" : "template_substitution", content);
|
||||
@@ -521,7 +524,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
}
|
||||
|
||||
raw += ch;
|
||||
if (ch === "\\") {
|
||||
if (ch == "\\") {
|
||||
var tmp = S.pos;
|
||||
ch = read_escaped_char();
|
||||
raw += S.text.substr(tmp, S.pos - tmp);
|
||||
@@ -856,6 +859,7 @@ function parse($TEXT, options) {
|
||||
|
||||
options = defaults(options, {
|
||||
bare_returns : false,
|
||||
ecma : 8,
|
||||
expression : false,
|
||||
filename : null,
|
||||
html5_comments : true,
|
||||
@@ -1229,9 +1233,12 @@ function parse($TEXT, options) {
|
||||
var is_in = is("operator", "in");
|
||||
var is_of = is("name", "of");
|
||||
if (is_in || is_of) {
|
||||
if ((init instanceof AST_Definitions) &&
|
||||
init.definitions.length > 1)
|
||||
croak("Only one variable declaration allowed in for..in loop");
|
||||
if (init instanceof AST_Definitions) {
|
||||
if (init.definitions.length > 1)
|
||||
croak("Only one variable declaration allowed in for..in loop", init.start.line, init.start.col, init.start.pos);
|
||||
} else if (!(is_assignable(init) || (init = to_destructuring(init)) instanceof AST_Destructuring)) {
|
||||
croak("Invalid left-hand side in for..in loop", init.start.line, init.start.col, init.start.pos);
|
||||
}
|
||||
next();
|
||||
if (is_in) {
|
||||
return for_in(init);
|
||||
@@ -1281,18 +1288,19 @@ function parse($TEXT, options) {
|
||||
});
|
||||
};
|
||||
|
||||
var arrow_function = function(start, argnames) {
|
||||
var arrow_function = function(start, argnames, is_async) {
|
||||
if (S.token.nlb) {
|
||||
croak("Unexpected newline before arrow (=>)");
|
||||
}
|
||||
|
||||
expect_token("arrow", "=>");
|
||||
|
||||
var body = _function_body(is("punc", "{"));
|
||||
var body = _function_body(is("punc", "{"), false, is_async);
|
||||
|
||||
return new AST_Arrow({
|
||||
start : start,
|
||||
end : body.end,
|
||||
async : is_async,
|
||||
argnames : argnames,
|
||||
body : body
|
||||
});
|
||||
@@ -1385,22 +1393,20 @@ function parse($TEXT, options) {
|
||||
|
||||
function parameters() {
|
||||
var start = S.token;
|
||||
var first = true;
|
||||
var params = [];
|
||||
var used_parameters = track_used_binding_identifiers(true, S.input.has_directive("use strict"));
|
||||
|
||||
expect("(");
|
||||
|
||||
while (!is("punc", ")")) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
expect(",");
|
||||
}
|
||||
|
||||
var param = parameter(used_parameters);
|
||||
params.push(param);
|
||||
|
||||
if (!is("punc", ")")) {
|
||||
expect(",");
|
||||
if (is("punc", ")") && options.ecma < 8) unexpected();
|
||||
}
|
||||
|
||||
if (param instanceof AST_Expansion) {
|
||||
break;
|
||||
}
|
||||
@@ -1616,25 +1622,40 @@ function parse($TEXT, options) {
|
||||
}
|
||||
}
|
||||
|
||||
function params_or_seq_() {
|
||||
var first = true;
|
||||
function params_or_seq_(allow_arrows, maybe_sequence) {
|
||||
var spread_token;
|
||||
var invalid_sequence;
|
||||
var trailing_comma;
|
||||
var a = [];
|
||||
expect("(");
|
||||
while (!is("punc", ")")) {
|
||||
if (first) first = false; else expect(",");
|
||||
if (spread_token) unexpected(spread_token);
|
||||
if (is("expand", "...")) {
|
||||
var spread_token = S.token;
|
||||
spread_token = S.token;
|
||||
if (maybe_sequence) invalid_sequence = S.token;
|
||||
next();
|
||||
a.push(new AST_Expansion({
|
||||
start: prev(),
|
||||
expression: expression(),
|
||||
end: S.token,
|
||||
}));
|
||||
if (!is("punc", ")")) {
|
||||
unexpected(spread_token);
|
||||
}
|
||||
} else {
|
||||
a.push(expression());
|
||||
}
|
||||
if (!is("punc", ")")) {
|
||||
expect(",");
|
||||
if (is("punc", ")")) {
|
||||
if (options.ecma < 8) unexpected();
|
||||
trailing_comma = prev();
|
||||
if (maybe_sequence) invalid_sequence = trailing_comma;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(")");
|
||||
if (allow_arrows && is("arrow", "=>")) {
|
||||
if (spread_token && trailing_comma) unexpected(trailing_comma);
|
||||
} else if (invalid_sequence) {
|
||||
unexpected(invalid_sequence);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
@@ -1882,7 +1903,7 @@ function parse($TEXT, options) {
|
||||
var newexp = expr_atom(false), args;
|
||||
if (is("punc", "(")) {
|
||||
next();
|
||||
args = expr_list(")");
|
||||
args = expr_list(")", options.ecma >= 8);
|
||||
} else {
|
||||
args = [];
|
||||
}
|
||||
@@ -1989,21 +2010,24 @@ function parse($TEXT, options) {
|
||||
}
|
||||
}
|
||||
|
||||
var expr_atom = function(allow_calls) {
|
||||
var expr_atom = function(allow_calls, allow_arrows) {
|
||||
if (is("operator", "new")) {
|
||||
return new_(allow_calls);
|
||||
}
|
||||
var start = S.token;
|
||||
var async = is("name", "async") && as_atom_node();
|
||||
if (is("punc")) {
|
||||
switch (start.value) {
|
||||
switch (S.token.value) {
|
||||
case "(":
|
||||
next();
|
||||
var exprs = params_or_seq_();
|
||||
expect(")");
|
||||
if (is("arrow", "=>")) {
|
||||
return arrow_function(start, exprs.map(to_fun_args));
|
||||
if (async && !allow_calls) break;
|
||||
var exprs = params_or_seq_(allow_arrows, !async);
|
||||
if (allow_arrows && is("arrow", "=>")) {
|
||||
return arrow_function(start, exprs.map(to_fun_args), !!async);
|
||||
}
|
||||
var ex = exprs.length == 1 ? exprs[0] : new AST_Sequence({
|
||||
var ex = async ? new AST_Call({
|
||||
expression: async,
|
||||
args: exprs
|
||||
}) : exprs.length == 1 ? exprs[0] : new AST_Sequence({
|
||||
expressions: exprs
|
||||
});
|
||||
ex.start = start;
|
||||
@@ -2014,23 +2038,25 @@ function parse($TEXT, options) {
|
||||
case "{":
|
||||
return subscripts(object_or_destructuring_(), allow_calls);
|
||||
}
|
||||
unexpected();
|
||||
if (!async) unexpected();
|
||||
}
|
||||
if (is("name", "async") && is_token(peek(), "keyword", "function")) {
|
||||
if (allow_arrows && is("name") && is_token(peek(), "arrow")) {
|
||||
var param = new AST_SymbolFunarg({
|
||||
name: S.token.value,
|
||||
start: start,
|
||||
end: start,
|
||||
});
|
||||
next();
|
||||
next();
|
||||
var func = function_(AST_Function, false, true);
|
||||
func.start = start;
|
||||
func.end = prev();
|
||||
return subscripts(func, allow_calls);
|
||||
return arrow_function(start, [param], !!async);
|
||||
}
|
||||
if (is("keyword", "function")) {
|
||||
next();
|
||||
var func = function_(AST_Function);
|
||||
var func = function_(AST_Function, false, !!async);
|
||||
func.start = start;
|
||||
func.end = prev();
|
||||
return subscripts(func, allow_calls);
|
||||
}
|
||||
if (async) return subscripts(async, allow_calls);
|
||||
if (is("keyword", "class")) {
|
||||
next();
|
||||
var cls = class_(AST_ClassExpression);
|
||||
@@ -2302,7 +2328,7 @@ function parse($TEXT, options) {
|
||||
next();
|
||||
}
|
||||
|
||||
imported_names = import_names(true);
|
||||
imported_names = map_names(true);
|
||||
|
||||
if (imported_names || imported_name) {
|
||||
expect_token("name", "from");
|
||||
@@ -2326,26 +2352,40 @@ function parse($TEXT, options) {
|
||||
});
|
||||
}
|
||||
|
||||
function import_name() {
|
||||
function map_name(is_import) {
|
||||
function make_symbol(type) {
|
||||
return new type({
|
||||
name: as_property_name(),
|
||||
start: prev(),
|
||||
end: prev()
|
||||
});
|
||||
}
|
||||
|
||||
var foreign_type = is_import ? AST_SymbolImportForeign : AST_SymbolExportForeign;
|
||||
var type = is_import ? AST_SymbolImport : AST_SymbolExport;
|
||||
var start = S.token;
|
||||
var foreign_name;
|
||||
var name;
|
||||
|
||||
if (peek().value === "as" && peek().type === "name") {
|
||||
foreign_name = as_symbol(AST_SymbolImportForeign);
|
||||
if (is_import) {
|
||||
foreign_name = make_symbol(foreign_type);
|
||||
} else {
|
||||
name = make_symbol(type);
|
||||
}
|
||||
if (is("name", "as")) {
|
||||
next(); // The "as" word
|
||||
}
|
||||
name = as_symbol(AST_SymbolImport);
|
||||
|
||||
if (foreign_name === undefined) {
|
||||
foreign_name = new AST_SymbolImportForeign({
|
||||
name: name.name,
|
||||
start: name.start,
|
||||
end: name.end,
|
||||
});
|
||||
if (is_import) {
|
||||
name = make_symbol(type);
|
||||
} else {
|
||||
foreign_name = make_symbol(foreign_type);
|
||||
}
|
||||
} else if (is_import) {
|
||||
name = new type(foreign_name);
|
||||
} else {
|
||||
foreign_name = new foreign_type(name);
|
||||
}
|
||||
|
||||
return new AST_NameImport({
|
||||
return new AST_NameMapping({
|
||||
start: start,
|
||||
foreign_name: foreign_name,
|
||||
name: name,
|
||||
@@ -2353,26 +2393,26 @@ function parse($TEXT, options) {
|
||||
})
|
||||
}
|
||||
|
||||
function import_nameAsterisk(name) {
|
||||
function map_nameAsterisk(is_import, name) {
|
||||
var foreign_type = is_import ? AST_SymbolImportForeign : AST_SymbolExportForeign;
|
||||
var type = is_import ? AST_SymbolImport : AST_SymbolExport;
|
||||
var start = S.token;
|
||||
var foreign_name;
|
||||
|
||||
|
||||
var end = prev();
|
||||
|
||||
name = name || new AST_SymbolImport({
|
||||
name = name || new type({
|
||||
name: '*',
|
||||
start: start,
|
||||
end: end,
|
||||
});
|
||||
|
||||
foreign_name = new AST_SymbolImportForeign({
|
||||
foreign_name = new foreign_type({
|
||||
name: '*',
|
||||
start: start,
|
||||
end: end,
|
||||
});
|
||||
|
||||
return new AST_NameImport({
|
||||
return new AST_NameMapping({
|
||||
start: start,
|
||||
foreign_name: foreign_name,
|
||||
name: name,
|
||||
@@ -2380,13 +2420,13 @@ function parse($TEXT, options) {
|
||||
})
|
||||
}
|
||||
|
||||
function import_names(allow_as) {
|
||||
function map_names(is_import) {
|
||||
var names;
|
||||
if (is("punc", "{")) {
|
||||
next();
|
||||
names = [];
|
||||
while (!is("punc", "}")) {
|
||||
names.push(import_name());
|
||||
names.push(map_name(is_import));
|
||||
if (is("punc", ",")) {
|
||||
next();
|
||||
}
|
||||
@@ -2395,11 +2435,11 @@ function parse($TEXT, options) {
|
||||
} else if (is("operator", "*")) {
|
||||
var name;
|
||||
next();
|
||||
if (allow_as && is("name", "as")) {
|
||||
if (is_import && is("name", "as")) {
|
||||
next(); // The "as" word
|
||||
name = as_symbol(AST_SymbolImportForeign);
|
||||
}
|
||||
names = [import_nameAsterisk(name)];
|
||||
names = [map_nameAsterisk(is_import, name)];
|
||||
}
|
||||
return names;
|
||||
}
|
||||
@@ -2407,72 +2447,60 @@ function parse($TEXT, options) {
|
||||
function export_() {
|
||||
var start = S.token;
|
||||
var is_default;
|
||||
var exported_value;
|
||||
var exported_definition;
|
||||
var exported_names;
|
||||
|
||||
if (is("keyword", "default")) {
|
||||
is_default = true;
|
||||
next();
|
||||
} else {
|
||||
exported_names = import_names(false);
|
||||
} else if (exported_names = map_names(false)) {
|
||||
if (is("name", "from")) {
|
||||
next();
|
||||
|
||||
if (exported_names) {
|
||||
if (is("name", "from")) {
|
||||
next();
|
||||
|
||||
var mod_str = S.token;
|
||||
if (mod_str.type !== 'string') {
|
||||
unexpected();
|
||||
}
|
||||
next();
|
||||
|
||||
return new AST_Export({
|
||||
start: start,
|
||||
is_default: is_default,
|
||||
exported_names: exported_names,
|
||||
module_name: new AST_String({
|
||||
start: mod_str,
|
||||
value: mod_str.value,
|
||||
quote: mod_str.quote,
|
||||
end: mod_str,
|
||||
}),
|
||||
end: prev(),
|
||||
});
|
||||
} else {
|
||||
return new AST_Export({
|
||||
start: start,
|
||||
is_default: is_default,
|
||||
exported_names: exported_names,
|
||||
end: prev(),
|
||||
});
|
||||
var mod_str = S.token;
|
||||
if (mod_str.type !== 'string') {
|
||||
unexpected();
|
||||
}
|
||||
next();
|
||||
|
||||
return new AST_Export({
|
||||
start: start,
|
||||
is_default: is_default,
|
||||
exported_names: exported_names,
|
||||
module_name: new AST_String({
|
||||
start: mod_str,
|
||||
value: mod_str.value,
|
||||
quote: mod_str.quote,
|
||||
end: mod_str,
|
||||
}),
|
||||
end: prev(),
|
||||
});
|
||||
} else {
|
||||
return new AST_Export({
|
||||
start: start,
|
||||
is_default: is_default,
|
||||
exported_names: exported_names,
|
||||
end: prev(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var is_definition = is("keyword", "var") || is("keyword", "let") || is("keyword", "const");
|
||||
if (is_definition) {
|
||||
if (is_default) unexpected();
|
||||
exported_definition = statement();
|
||||
} else if (is("keyword", "class")) {
|
||||
var cls = expr_atom(false);
|
||||
if (cls.name) {
|
||||
cls.name = new AST_SymbolDefClass(cls.name);
|
||||
exported_definition = new AST_DefClass(cls);
|
||||
} else {
|
||||
exported_value = cls;
|
||||
}
|
||||
} else if (is("keyword", "function")) {
|
||||
var func = expr_atom(false);
|
||||
if (func.name) {
|
||||
func.name = new AST_SymbolDefun(func.name);
|
||||
exported_definition = new AST_Defun(func);
|
||||
} else {
|
||||
exported_value = func;
|
||||
}
|
||||
} else {
|
||||
var node;
|
||||
var exported_value;
|
||||
var exported_definition;
|
||||
if (is("punc", "{")
|
||||
|| is_default
|
||||
&& (is("keyword", "class") || is("keyword", "function"))
|
||||
&& is_token(peek(), "punc")) {
|
||||
exported_value = expression(false);
|
||||
semicolon();
|
||||
} else if ((node = statement()) instanceof AST_Definitions && is_default) {
|
||||
unexpected(node.start);
|
||||
} else if (node instanceof AST_Definitions || node instanceof AST_Defun || node instanceof AST_DefClass) {
|
||||
exported_definition = node;
|
||||
} else if (node instanceof AST_SimpleStatement) {
|
||||
exported_value = node.body;
|
||||
} else {
|
||||
unexpected(node.start);
|
||||
}
|
||||
|
||||
return new AST_Export({
|
||||
@@ -2598,11 +2626,9 @@ function parse($TEXT, options) {
|
||||
return expr;
|
||||
};
|
||||
|
||||
var call_args = embed_tokens(function call_args() {
|
||||
var first = true;
|
||||
var call_args = embed_tokens(function _call_args() {
|
||||
var args = [];
|
||||
while (!is("punc", ")")) {
|
||||
if (first) first = false; else expect(",");
|
||||
if (is("expand", "...")) {
|
||||
next();
|
||||
args.push(new AST_Expansion({
|
||||
@@ -2612,12 +2638,16 @@ function parse($TEXT, options) {
|
||||
} else {
|
||||
args.push(expression(false));
|
||||
}
|
||||
if (!is("punc", ")")) {
|
||||
expect(",");
|
||||
if (is("punc", ")") && options.ecma < 8) unexpected();
|
||||
}
|
||||
}
|
||||
next();
|
||||
return args;
|
||||
});
|
||||
|
||||
var maybe_unary = function(allow_calls) {
|
||||
var maybe_unary = function(allow_calls, allow_arrows) {
|
||||
var start = S.token;
|
||||
if (start.type == "name" && start.value == "await") {
|
||||
if (is_in_async()) {
|
||||
@@ -2635,8 +2665,9 @@ function parse($TEXT, options) {
|
||||
ex.end = prev();
|
||||
return ex;
|
||||
}
|
||||
var val = expr_atom(allow_calls);
|
||||
var val = expr_atom(allow_calls, allow_arrows);
|
||||
while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) {
|
||||
if (val instanceof AST_Arrow) unexpected();
|
||||
val = make_unary(AST_UnaryPostfix, S.token, val);
|
||||
val.start = start;
|
||||
val.end = S.token;
|
||||
@@ -2684,7 +2715,7 @@ function parse($TEXT, options) {
|
||||
};
|
||||
|
||||
function expr_ops(no_in) {
|
||||
return expr_op(maybe_unary(true), 0, no_in);
|
||||
return expr_op(maybe_unary(true, true), 0, no_in);
|
||||
};
|
||||
|
||||
var maybe_conditional = function(no_in) {
|
||||
@@ -2765,22 +2796,6 @@ function parse($TEXT, options) {
|
||||
}
|
||||
}
|
||||
|
||||
if (start.type == "punc" && start.value == "(" && peek().value == ")") {
|
||||
next();
|
||||
next();
|
||||
return arrow_function(start, []);
|
||||
}
|
||||
|
||||
if (is("name") && is_token(peek(), "arrow")) {
|
||||
var param = new AST_SymbolFunarg({
|
||||
name: start.value,
|
||||
start: start,
|
||||
end: start,
|
||||
});
|
||||
next();
|
||||
return arrow_function(start, [param]);
|
||||
}
|
||||
|
||||
var left = maybe_conditional(no_in);
|
||||
var val = S.token.value;
|
||||
|
||||
|
||||
171
lib/scope.js
171
lib/scope.js
@@ -85,7 +85,7 @@ SymbolDef.prototype = {
|
||||
if (options.ie8 && sym instanceof AST_SymbolLambda)
|
||||
s = s.parent_scope;
|
||||
var def;
|
||||
if (this.defun && (def = this.defun.variables.get(this.name))) {
|
||||
if (def = this.redefined()) {
|
||||
this.mangled_name = def.mangled_name || def.name;
|
||||
} else
|
||||
this.mangled_name = s.next_mangled(options, this);
|
||||
@@ -93,6 +93,9 @@ SymbolDef.prototype = {
|
||||
cache.set(this.name, this.mangled_name);
|
||||
}
|
||||
}
|
||||
},
|
||||
redefined: function() {
|
||||
return this.defun && this.defun.variables.get(this.name);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -129,7 +132,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
scope = save_scope;
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Destructuring && node.is_array === false) {
|
||||
if (node instanceof AST_Destructuring) {
|
||||
in_destructuring = node; // These don't nest
|
||||
descend();
|
||||
in_destructuring = null;
|
||||
@@ -223,10 +226,25 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
}));
|
||||
node.thedef = sym;
|
||||
}
|
||||
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
|
||||
js_error(
|
||||
node.TYPE + " statement may only appear at top level",
|
||||
node.start.file,
|
||||
node.start.line,
|
||||
node.start.col,
|
||||
node.start.pos
|
||||
);
|
||||
}
|
||||
|
||||
function mark_export(def, level) {
|
||||
if (in_destructuring) {
|
||||
var i = 0;
|
||||
do {
|
||||
level++;
|
||||
} while (tw.parent(i++) !== in_destructuring);
|
||||
}
|
||||
var node = tw.parent(level);
|
||||
def.export = node instanceof AST_Export && !node.is_default;
|
||||
def.export = node instanceof AST_Export;
|
||||
}
|
||||
});
|
||||
self.walk(tw);
|
||||
@@ -245,8 +263,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
s.uses_eval = true;
|
||||
}
|
||||
}
|
||||
var sym = node.scope.find_variable(name);
|
||||
if (!sym) {
|
||||
var sym;
|
||||
if (tw.parent() instanceof AST_NameMapping && tw.parent(1).module_name
|
||||
|| !(sym = node.scope.find_variable(name))) {
|
||||
sym = self.def_global(node);
|
||||
} else if (sym.scope instanceof AST_Lambda && name == "arguments") {
|
||||
sym.scope.uses_arguments = true;
|
||||
@@ -255,6 +274,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
node.reference(options);
|
||||
return true;
|
||||
}
|
||||
// ensure mangling works if catch reuses a scope variable
|
||||
var def;
|
||||
if (node instanceof AST_SymbolCatch && (def = node.definition().redefined())) {
|
||||
var s = node.scope;
|
||||
while (s) {
|
||||
push_uniq(s.enclosed, def);
|
||||
if (s === def.scope) break;
|
||||
s = s.parent_scope;
|
||||
}
|
||||
}
|
||||
});
|
||||
self.walk(tw);
|
||||
|
||||
@@ -421,7 +450,7 @@ AST_Function.DEFMETHOD("next_mangled", function(options, def){
|
||||
|
||||
AST_Symbol.DEFMETHOD("unmangleable", function(options){
|
||||
var def = this.definition();
|
||||
return def && def.unmangleable(options);
|
||||
return !def || def.unmangleable(options);
|
||||
});
|
||||
|
||||
// labels are always mangleable
|
||||
@@ -526,113 +555,69 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||
|
||||
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
|
||||
options = this._default_mangler_options(options);
|
||||
var tw = new TreeWalker(function(node){
|
||||
if (node instanceof AST_Constant)
|
||||
base54.consider(node.print_to_string());
|
||||
else if (node instanceof AST_Return)
|
||||
base54.consider("return");
|
||||
else if (node instanceof AST_Throw)
|
||||
base54.consider("throw");
|
||||
else if (node instanceof AST_Continue)
|
||||
base54.consider("continue");
|
||||
else if (node instanceof AST_Break)
|
||||
base54.consider("break");
|
||||
else if (node instanceof AST_Debugger)
|
||||
base54.consider("debugger");
|
||||
else if (node instanceof AST_Directive)
|
||||
base54.consider(node.value);
|
||||
else if (node instanceof AST_While)
|
||||
base54.consider("while");
|
||||
else if (node instanceof AST_Do)
|
||||
base54.consider("do while");
|
||||
else if (node instanceof AST_If) {
|
||||
base54.consider("if");
|
||||
if (node.alternative) base54.consider("else");
|
||||
}
|
||||
else if (node instanceof AST_Var)
|
||||
base54.consider("var");
|
||||
else if (node instanceof AST_Const)
|
||||
base54.consider("const");
|
||||
else if (node instanceof AST_Lambda)
|
||||
base54.consider("function");
|
||||
else if (node instanceof AST_For)
|
||||
base54.consider("for");
|
||||
else if (node instanceof AST_ForIn)
|
||||
base54.consider("for in");
|
||||
else if (node instanceof AST_Switch)
|
||||
base54.consider("switch");
|
||||
else if (node instanceof AST_Case)
|
||||
base54.consider("case");
|
||||
else if (node instanceof AST_Default)
|
||||
base54.consider("default");
|
||||
else if (node instanceof AST_With)
|
||||
base54.consider("with");
|
||||
else if (node instanceof AST_ObjectSetter)
|
||||
base54.consider("set" + (typeof node.key === "string" ? node.key : ""));
|
||||
else if (node instanceof AST_ObjectGetter)
|
||||
base54.consider("get" + (typeof node.key === "string" ? node.key : ""));
|
||||
else if (node instanceof AST_ObjectKeyVal && typeof node.key === "string")
|
||||
base54.consider(node.key);
|
||||
else if (node instanceof AST_ConciseMethod && typeof node.key === "string")
|
||||
base54.consider(node.key);
|
||||
else if (node instanceof AST_New)
|
||||
base54.consider("new");
|
||||
else if (node instanceof AST_This)
|
||||
base54.consider("this");
|
||||
else if (node instanceof AST_Super)
|
||||
base54.consider("super");
|
||||
else if (node instanceof AST_Try)
|
||||
base54.consider("try");
|
||||
else if (node instanceof AST_Catch)
|
||||
base54.consider("catch");
|
||||
else if (node instanceof AST_Finally)
|
||||
base54.consider("finally");
|
||||
else if (node instanceof AST_Yield)
|
||||
base54.consider("yield");
|
||||
else if (node instanceof AST_Await)
|
||||
base54.consider("await");
|
||||
else if (node instanceof AST_Symbol && node.unmangleable(options))
|
||||
base54.consider(node.name);
|
||||
else if (node instanceof AST_Unary || node instanceof AST_Binary)
|
||||
base54.consider(node.operator);
|
||||
else if (node instanceof AST_Dot)
|
||||
base54.consider(node.property);
|
||||
});
|
||||
this.walk(tw);
|
||||
try {
|
||||
AST_Node.prototype.print = function(stream, force_parens) {
|
||||
this._print(stream, force_parens);
|
||||
if (this instanceof AST_Symbol && !this.unmangleable(options)) {
|
||||
base54.consider(this.name, -1);
|
||||
} else if (options.properties) {
|
||||
if (this instanceof AST_Dot) {
|
||||
base54.consider(this.property, -1);
|
||||
} else if (this instanceof AST_Sub) {
|
||||
skip_string(this.property);
|
||||
}
|
||||
}
|
||||
};
|
||||
base54.consider(this.print_to_string(), 1);
|
||||
} finally {
|
||||
AST_Node.prototype.print = AST_Node.prototype._print;
|
||||
}
|
||||
base54.sort();
|
||||
|
||||
function skip_string(node) {
|
||||
if (node instanceof AST_String) {
|
||||
base54.consider(node.value, -1);
|
||||
} else if (node instanceof AST_Conditional) {
|
||||
skip_string(node.consequent);
|
||||
skip_string(node.alternative);
|
||||
} else if (node instanceof AST_Sequence) {
|
||||
skip_string(node.expressions[node.expressions.length - 1]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var base54 = (function() {
|
||||
var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
|
||||
var leading = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_".split("");
|
||||
var digits = "0123456789".split("");
|
||||
var chars, frequency;
|
||||
function reset() {
|
||||
frequency = Object.create(null);
|
||||
chars = string.split("").map(function(ch){ return ch.charCodeAt(0) });
|
||||
chars.forEach(function(ch){ frequency[ch] = 0 });
|
||||
leading.forEach(function(ch) {
|
||||
frequency[ch] = 0;
|
||||
});
|
||||
digits.forEach(function(ch) {
|
||||
frequency[ch] = 0;
|
||||
});
|
||||
}
|
||||
base54.consider = function(str){
|
||||
base54.consider = function(str, delta) {
|
||||
for (var i = str.length; --i >= 0;) {
|
||||
var code = str.charCodeAt(i);
|
||||
if (code in frequency) ++frequency[code];
|
||||
frequency[str[i]] += delta;
|
||||
}
|
||||
};
|
||||
function compare(a, b) {
|
||||
return frequency[b] - frequency[a];
|
||||
}
|
||||
base54.sort = function() {
|
||||
chars = mergeSort(chars, function(a, b){
|
||||
if (is_digit(a) && !is_digit(b)) return 1;
|
||||
if (is_digit(b) && !is_digit(a)) return -1;
|
||||
return frequency[b] - frequency[a];
|
||||
});
|
||||
chars = mergeSort(leading, compare).concat(mergeSort(digits, compare));
|
||||
};
|
||||
base54.reset = reset;
|
||||
reset();
|
||||
base54.get = function(){ return chars };
|
||||
base54.freq = function(){ return frequency };
|
||||
function base54(num) {
|
||||
var ret = "", base = 54;
|
||||
num++;
|
||||
do {
|
||||
num--;
|
||||
ret += String.fromCharCode(chars[num % base]);
|
||||
ret += chars[num % base];
|
||||
num = Math.floor(num / base);
|
||||
base = 64;
|
||||
} while (num > 0);
|
||||
|
||||
@@ -243,9 +243,22 @@ TreeTransformer.prototype = new TreeWalker;
|
||||
self.expression = self.expression.transform(tw);
|
||||
});
|
||||
|
||||
_(AST_Export, function(self, tw){
|
||||
_(AST_NameMapping, function(self, tw) {
|
||||
self.foreign_name = self.foreign_name.transform(tw);
|
||||
self.name = self.name.transform(tw);
|
||||
});
|
||||
|
||||
_(AST_Import, function(self, tw) {
|
||||
if (self.imported_name) self.imported_name = self.imported_name.transform(tw);
|
||||
if (self.imported_names) do_list(self.imported_names, tw);
|
||||
self.module_name = self.module_name.transform(tw);
|
||||
});
|
||||
|
||||
_(AST_Export, function(self, tw) {
|
||||
if (self.exported_definition) self.exported_definition = self.exported_definition.transform(tw);
|
||||
if (self.exported_value) self.exported_value = self.exported_value.transform(tw);
|
||||
if (self.exported_names) do_list(self.exported_names, tw);
|
||||
if (self.module_name) self.module_name = self.module_name.transform(tw);
|
||||
});
|
||||
|
||||
_(AST_TemplateString, function(self, tw) {
|
||||
|
||||
17
package.json
17
package.json
@@ -4,7 +4,7 @@
|
||||
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
|
||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||
"license": "BSD-2-Clause",
|
||||
"version": "3.0.18",
|
||||
"version": "3.0.23",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
@@ -40,5 +40,18 @@
|
||||
"scripts": {
|
||||
"test": "node test/run-tests.js"
|
||||
},
|
||||
"keywords": ["uglify", "uglify-js", "uglify-es", "minify", "minifier", "es5", "es6", "es2015"]
|
||||
"keywords": [
|
||||
"uglify",
|
||||
"uglify-js",
|
||||
"uglify-es",
|
||||
"minify",
|
||||
"minifier",
|
||||
"es5",
|
||||
"es6",
|
||||
"es2015",
|
||||
"es2016",
|
||||
"es2017",
|
||||
"async",
|
||||
"await"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -202,3 +202,341 @@ arrow_unused_toplevel: {
|
||||
expect_stdout: [ "0", "1", "2", "9" ]
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
no_leading_parentheses: {
|
||||
input: {
|
||||
(x,y) => x(y);
|
||||
async (x,y) => await x(y);
|
||||
}
|
||||
expect_exact: "(x,y)=>x(y);async(x,y)=>await x(y);"
|
||||
}
|
||||
|
||||
async_identifiers: {
|
||||
options = {
|
||||
arrows: true,
|
||||
ecma: 6,
|
||||
}
|
||||
input: {
|
||||
var async = function(x){ console.log("async", x); };
|
||||
var await = function(x){ console.log("await", x); };
|
||||
async(1);
|
||||
await(2);
|
||||
}
|
||||
expect: {
|
||||
var async = x => { console.log("async", x); };
|
||||
var await = x => { console.log("await", x); };
|
||||
async(1);
|
||||
await(2);
|
||||
}
|
||||
expect_stdout: [
|
||||
"async 1",
|
||||
"await 2",
|
||||
]
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
async_function_expression: {
|
||||
options = {
|
||||
arrows: true,
|
||||
ecma: 6,
|
||||
evaluate: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
var named = async function foo() {
|
||||
await bar(1 + 0) + (2 + 0);
|
||||
}
|
||||
var anon = async function() {
|
||||
await (1 + 0) + bar(2 + 0);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var named = async function foo() {
|
||||
await bar(1);
|
||||
};
|
||||
var anon = async () => {
|
||||
await 1, bar(2);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
issue_27: {
|
||||
options = {
|
||||
arrows: true,
|
||||
collapse_vars: true,
|
||||
ecma: 6,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function(jQuery) {
|
||||
var $;
|
||||
$ = jQuery;
|
||||
$("body").addClass("foo");
|
||||
})(jQuery);
|
||||
}
|
||||
expect: {
|
||||
(jQuery => {
|
||||
jQuery("body").addClass("foo");
|
||||
})(jQuery);
|
||||
}
|
||||
}
|
||||
|
||||
issue_2105_1: {
|
||||
options = {
|
||||
arrows: true,
|
||||
collapse_vars: true,
|
||||
ecma: 6,
|
||||
inline: true,
|
||||
passes: 3,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
!function(factory) {
|
||||
factory();
|
||||
}( function() {
|
||||
return function(fn) {
|
||||
fn()().prop();
|
||||
}( function() {
|
||||
function bar() {
|
||||
var quux = function() {
|
||||
console.log("PASS");
|
||||
}, foo = function() {
|
||||
console.log;
|
||||
quux();
|
||||
};
|
||||
return { prop: foo };
|
||||
}
|
||||
return bar;
|
||||
} );
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
(() => {
|
||||
var quux = () => {
|
||||
console.log("PASS");
|
||||
};
|
||||
return {
|
||||
prop: () => {
|
||||
console.log;
|
||||
quux();
|
||||
}
|
||||
};
|
||||
})().prop();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
issue_2105_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
passes: 2,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
((factory) => {
|
||||
factory();
|
||||
})( () => {
|
||||
return ((fn) => {
|
||||
fn()().prop();
|
||||
})( () => {
|
||||
let bar = () => {
|
||||
var quux = () => {
|
||||
console.log("PASS");
|
||||
}, foo = () => {
|
||||
console.log;
|
||||
quux();
|
||||
};
|
||||
return { prop: foo };
|
||||
};
|
||||
return bar;
|
||||
} );
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
(() => {
|
||||
var quux = () => {
|
||||
console.log("PASS");
|
||||
};
|
||||
return {
|
||||
prop: () => {
|
||||
console.log;
|
||||
quux();
|
||||
}
|
||||
};
|
||||
})().prop();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_2136_2: {
|
||||
options = {
|
||||
arrows: true,
|
||||
collapse_vars: true,
|
||||
ecma: 6,
|
||||
inline: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(x) {
|
||||
console.log(x);
|
||||
}
|
||||
!function(a, ...b) {
|
||||
f(b[0]);
|
||||
}(1, 2, 3);
|
||||
}
|
||||
expect: {
|
||||
function f(x) {
|
||||
console.log(x);
|
||||
}
|
||||
f([2,3][0]);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_2136_3: {
|
||||
options = {
|
||||
arrows: true,
|
||||
collapse_vars: true,
|
||||
ecma: 6,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
passes: 3,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
unsafe: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(x) {
|
||||
console.log(x);
|
||||
}
|
||||
!function(a, ...b) {
|
||||
f(b[0]);
|
||||
}(1, 2, 3);
|
||||
}
|
||||
expect: {
|
||||
console.log(2);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
call_args: {
|
||||
options = {
|
||||
arrows: true,
|
||||
ecma: 6,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
const a = 1;
|
||||
console.log(a);
|
||||
+function(a) {
|
||||
return a;
|
||||
}(a);
|
||||
}
|
||||
expect: {
|
||||
const a = 1;
|
||||
console.log(1);
|
||||
+(1, 1);
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
call_args_drop_param: {
|
||||
options = {
|
||||
arrows: true,
|
||||
ecma: 6,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
keep_fargs: false,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
const a = 1;
|
||||
console.log(a);
|
||||
+function(a) {
|
||||
return a;
|
||||
}(a, b);
|
||||
}
|
||||
expect: {
|
||||
const a = 1;
|
||||
console.log(1);
|
||||
+(b, 1);
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_485_crashing_1530: {
|
||||
options = {
|
||||
arrows: true,
|
||||
conditionals: true,
|
||||
dead_code: true,
|
||||
ecma: 6,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
}
|
||||
input: {
|
||||
(function(a) {
|
||||
if (true) return;
|
||||
var b = 42;
|
||||
})(this);
|
||||
}
|
||||
expect: {
|
||||
this, void 0;
|
||||
}
|
||||
}
|
||||
|
||||
issue_2084: {
|
||||
options = {
|
||||
arrows: true,
|
||||
collapse_vars: true,
|
||||
conditionals: true,
|
||||
ecma: 6,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
passes: 2,
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var c = 0;
|
||||
!function() {
|
||||
!function(c) {
|
||||
c = 1 + c;
|
||||
var c = 0;
|
||||
function f14(a_1) {
|
||||
if (c = 1 + c, 0 !== 23..toString())
|
||||
c = 1 + c, a_1 && (a_1[0] = 0);
|
||||
}
|
||||
f14();
|
||||
}(-1);
|
||||
}();
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = 0;
|
||||
((c) => {
|
||||
c = 1 + c,
|
||||
c = 1 + (c = 0),
|
||||
0 !== 23..toString() && (c = 1 + c);
|
||||
})(-1),
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "0"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
@@ -6,6 +6,22 @@ await_precedence: {
|
||||
expect_exact: "async function f1(){await x+y}async function f2(){await(x+y)}"
|
||||
}
|
||||
|
||||
await_precedence_prop: {
|
||||
input: {
|
||||
async function f1(){ return (await foo()).bar; }
|
||||
async function f2(){ return (await foo().bar); }
|
||||
}
|
||||
expect_exact: "async function f1(){return(await foo()).bar}async function f2(){return await foo().bar}"
|
||||
}
|
||||
|
||||
await_precedence_call: {
|
||||
input: {
|
||||
async function f3(){ return (await foo())(); }
|
||||
async function f4(){ return await (foo()()); }
|
||||
}
|
||||
expect_exact: "async function f3(){return(await foo())()}async function f4(){return await foo()()}"
|
||||
}
|
||||
|
||||
async_function_declaration: {
|
||||
options = {
|
||||
side_effects: true,
|
||||
@@ -167,14 +183,14 @@ async_inline: {
|
||||
|
||||
async_identifiers: {
|
||||
input: {
|
||||
let async = function(x){ console.log("async", x); };
|
||||
let await = function(x){ console.log("await", x); };
|
||||
var async = function(x){ console.log("async", x); };
|
||||
var await = function(x){ console.log("await", x); };
|
||||
async(1);
|
||||
await(2);
|
||||
}
|
||||
expect: {
|
||||
let async = function(x){ console.log("async", x); };
|
||||
let await = function(x){ console.log("await", x); };
|
||||
var async = function(x){ console.log("async", x); };
|
||||
var await = function(x){ console.log("await", x); };
|
||||
async(1);
|
||||
await(2);
|
||||
}
|
||||
@@ -182,7 +198,6 @@ async_identifiers: {
|
||||
"async 1",
|
||||
"await 2",
|
||||
]
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
async_shorthand_property: {
|
||||
@@ -230,16 +245,50 @@ async_shorthand_property: {
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
/* FIXME: add test when supported by parser
|
||||
async_arrow: {
|
||||
input: {
|
||||
let a1 = async x => await foo(x);
|
||||
let a2 = async () => await bar();
|
||||
let a3 = async (x) => await baz(x);
|
||||
let a4 = async (x, y) => { await far(x, y); }
|
||||
let a5 = async ({x = [1], y: z = 2}) => { await wow(x, y); }
|
||||
let a5 = async ({x = [1], y: z = 2}) => { await wow(x, z); }
|
||||
}
|
||||
expect: {
|
||||
let a1 = async x => await foo(x);
|
||||
let a2 = async () => await bar();
|
||||
let a3 = async (x) => await baz(x);
|
||||
let a4 = async (x, y) => { await far(x, y); }
|
||||
let a5 = async ({x = [1], y: z = 2}) => { await wow(x, z); }
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
async_arrow_wait: {
|
||||
input: {
|
||||
var a = async (x, y) => await x(y);
|
||||
}
|
||||
expect_exact: "var a=async(x,y)=>await x(y);"
|
||||
}
|
||||
|
||||
async_arrow_iife: {
|
||||
input: {
|
||||
(async () => {
|
||||
await fetch({});
|
||||
})();
|
||||
}
|
||||
expect_exact: "(async()=>{await fetch({})})();"
|
||||
}
|
||||
|
||||
async_arrow_iife_negate_iife: {
|
||||
options = {
|
||||
negate_iife: true,
|
||||
}
|
||||
input: {
|
||||
(async () => {
|
||||
await fetch();
|
||||
})();
|
||||
(() => {
|
||||
plain();
|
||||
})();
|
||||
}
|
||||
expect_exact: "(async()=>{await fetch()})();(()=>{plain()})();"
|
||||
}
|
||||
|
||||
@@ -104,34 +104,36 @@ regression_block_scope_resolves: {
|
||||
};
|
||||
input: {
|
||||
(function () {
|
||||
if(1) {
|
||||
if (1) {
|
||||
let x;
|
||||
const y;
|
||||
const y = 1;
|
||||
class Zee {};
|
||||
}
|
||||
if(1) {
|
||||
if (1) {
|
||||
let ex;
|
||||
const why;
|
||||
const why = 2;
|
||||
class Zi {};
|
||||
}
|
||||
console.log(x, y, Zee, ex, why, Zi);
|
||||
console.log(typeof x, typeof y, typeof Zee, typeof ex, typeof why, typeof Zi);
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
(function () {
|
||||
if (1) {
|
||||
let o;
|
||||
const n;
|
||||
class c {};
|
||||
let e;
|
||||
const o = 1;
|
||||
class t {};
|
||||
}
|
||||
if (1) {
|
||||
let o;
|
||||
const n;
|
||||
class c {};
|
||||
let e;
|
||||
const o = 2;
|
||||
class t {};
|
||||
}
|
||||
console.log(x, y, Zee, ex, why, Zi);
|
||||
console.log(typeof x, typeof y, typeof Zee, typeof ex, typeof why, typeof Zi);
|
||||
}());
|
||||
}
|
||||
expect_stdout: "undefined undefined undefined undefined undefined undefined"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
switch_block_scope_mangler: {
|
||||
@@ -153,25 +155,37 @@ switch_block_scope_mangler: {
|
||||
console.log(cat);
|
||||
}
|
||||
};
|
||||
fn(1);
|
||||
fn(2);
|
||||
fn(3);
|
||||
}
|
||||
expect: {
|
||||
var fn = function(o) {
|
||||
switch (o) {
|
||||
var fn = function(e) {
|
||||
switch (e) {
|
||||
case 1:
|
||||
let e = o + 1
|
||||
let c = o + 4;
|
||||
console.log(e, c);
|
||||
let l = e + 1
|
||||
let o = e + 4;
|
||||
console.log(l, o);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
let l = o + 2;
|
||||
console.log(l);
|
||||
let n = e + 2;
|
||||
console.log(n);
|
||||
break;
|
||||
|
||||
default:
|
||||
let a = o + 3;
|
||||
console.log(a);
|
||||
let c = e + 3;
|
||||
console.log(c);
|
||||
}
|
||||
};
|
||||
fn(1);
|
||||
fn(2);
|
||||
fn(3);
|
||||
}
|
||||
expect_stdout: [
|
||||
"2 5",
|
||||
"4",
|
||||
"6",
|
||||
]
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
@@ -2077,10 +2077,10 @@ chained_3: {
|
||||
}
|
||||
expect: {
|
||||
console.log(function(a, b) {
|
||||
var c = a, c = b;
|
||||
var c = 1, c = b;
|
||||
b++;
|
||||
return c;
|
||||
}(1, 2));
|
||||
}(0, 2));
|
||||
}
|
||||
expect_stdout: "2"
|
||||
}
|
||||
@@ -2330,3 +2330,73 @@ reassign_const_2: {
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_2187_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var a = 1;
|
||||
!function(foo) {
|
||||
foo();
|
||||
var a = 2;
|
||||
console.log(a);
|
||||
}(function() {
|
||||
console.log(a);
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
var a = 1;
|
||||
!function(foo) {
|
||||
foo();
|
||||
var a = 2;
|
||||
console.log(a);
|
||||
}(function() {
|
||||
console.log(a);
|
||||
});
|
||||
}
|
||||
expect_stdout: [
|
||||
"1",
|
||||
"2",
|
||||
]
|
||||
}
|
||||
|
||||
issue_2187_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var b = 1;
|
||||
console.log(function(a) {
|
||||
return a && ++b;
|
||||
}(b--));
|
||||
}
|
||||
expect: {
|
||||
var b = 1;
|
||||
console.log(function(a) {
|
||||
return b-- && ++b;
|
||||
}());
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
issue_2187_3: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var b = 1;
|
||||
console.log(function(a) {
|
||||
return a && ++b;
|
||||
}(b--));
|
||||
}
|
||||
expect: {
|
||||
var b = 1;
|
||||
console.log(b-- && ++b);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
@@ -425,8 +425,8 @@ mangle_destructuring_decl: {
|
||||
expect: {
|
||||
function test(t) {
|
||||
let e = t.a || { e: 7, n: 8 };
|
||||
let {t: n, e: o, n: s, s: a = 9, o: c, r: l} = e;
|
||||
console.log(n, o, s, a, c, l);
|
||||
let {t: n, e: o, n: s, s: l = 9, o: a, r: c} = e;
|
||||
console.log(n, o, s, l, a, c);
|
||||
}
|
||||
test({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } });
|
||||
test({});
|
||||
@@ -462,15 +462,15 @@ mangle_destructuring_assign_toplevel_true: {
|
||||
test({});
|
||||
}
|
||||
expect: {
|
||||
function n(n) {
|
||||
let t, a, c;
|
||||
let l = n.a || { e: 7, n: 8 };
|
||||
({t: o, e, n: s, s: t = 9, o: a, r: c} = l);
|
||||
console.log(o, e, s, t, a, c);
|
||||
function e(e) {
|
||||
let l, s, a;
|
||||
let c = e.a || { e: 7, n: 8 };
|
||||
({t: n, e: o, n: t, s: l = 9, o: s, r: a} = c);
|
||||
console.log(n, o, t, l, s, a);
|
||||
}
|
||||
let o, e, s;
|
||||
n({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } });
|
||||
n({});
|
||||
let n, o, t;
|
||||
e({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } });
|
||||
e({});
|
||||
}
|
||||
expect_stdout: [
|
||||
"1 2 3 4 5 6",
|
||||
@@ -504,10 +504,10 @@ mangle_destructuring_assign_toplevel_false: {
|
||||
}
|
||||
expect: {
|
||||
function test(o) {
|
||||
let s, a, c;
|
||||
let l = o.a || { e: 7, n: 8 };
|
||||
({t, e, n, s = 9, o: a, r: c} = l);
|
||||
console.log(t, e, n, s, a, c);
|
||||
let s, l, a;
|
||||
let c = o.a || { e: 7, n: 8 };
|
||||
({t, e, n, s = 9, o: l, r: a} = c);
|
||||
console.log(t, e, n, s, l, a);
|
||||
}
|
||||
let t, e, n;
|
||||
test({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } });
|
||||
@@ -588,8 +588,8 @@ arrow_func_with_destructuring_args: {
|
||||
})({bar: 5 - 0}, [, 6]);
|
||||
}
|
||||
expect: {
|
||||
(({foo: o = 1, bar: n = 2}, [a = 3, b = 4]) => {
|
||||
console.log(o, n, a, b);
|
||||
(({foo: o = 1, bar: a = 2}, [b = 3, l = 4]) => {
|
||||
console.log(o, a, b, l);
|
||||
})({bar: 5}, [, 6]);
|
||||
}
|
||||
expect_stdout: "1 5 3 6"
|
||||
@@ -639,3 +639,23 @@ issue_2044_ecma_6_beautify: {
|
||||
}
|
||||
expect_exact: "({x: a = 1, y = 2 + b, z = 3 - c} = obj);"
|
||||
}
|
||||
|
||||
issue_2140: {
|
||||
options = {
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
!function() {
|
||||
var t = {};
|
||||
console.log(([t.a] = [42])[0]);
|
||||
}();
|
||||
}
|
||||
expect: {
|
||||
!function() {
|
||||
var t = {};
|
||||
console.log(([t.a] = [42])[0]);
|
||||
}();
|
||||
}
|
||||
expect_stdout: "42"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
@@ -1299,6 +1299,7 @@ issue_2105: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
passes: 3,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
@@ -1324,7 +1325,7 @@ issue_2105: {
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
!void function() {
|
||||
(function() {
|
||||
var quux = function() {
|
||||
console.log("PASS");
|
||||
};
|
||||
@@ -1334,7 +1335,95 @@ issue_2105: {
|
||||
quux();
|
||||
}
|
||||
};
|
||||
}().prop();
|
||||
})().prop();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2136_1: {
|
||||
options = {
|
||||
inline: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
!function(a, ...b) {
|
||||
console.log(b);
|
||||
}();
|
||||
}
|
||||
expect: {
|
||||
!function(a, ...b) {
|
||||
console.log(b);
|
||||
}();
|
||||
}
|
||||
expect_stdout: "[]"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_2136_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(x) {
|
||||
console.log(x);
|
||||
}
|
||||
!function(a, ...b) {
|
||||
f(b[0]);
|
||||
}(1, 2, 3);
|
||||
}
|
||||
expect: {
|
||||
function f(x) {
|
||||
console.log(x);
|
||||
}
|
||||
f([2,3][0]);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_2136_3: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
passes: 3,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
unsafe: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(x) {
|
||||
console.log(x);
|
||||
}
|
||||
!function(a, ...b) {
|
||||
f(b[0]);
|
||||
}(1, 2, 3);
|
||||
}
|
||||
expect: {
|
||||
console.log(2);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_2163: {
|
||||
options = {
|
||||
pure_funcs: [ "pure" ],
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
var c;
|
||||
/*@__PURE__*/f(...a);
|
||||
pure(b, ...c);
|
||||
}
|
||||
expect: {
|
||||
var c;
|
||||
a;
|
||||
b;
|
||||
}
|
||||
}
|
||||
|
||||
228
test/compress/export.js
Normal file
228
test/compress/export.js
Normal file
@@ -0,0 +1,228 @@
|
||||
issue_2038_1: {
|
||||
options = {
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
mangle = {
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
export var V = 1;
|
||||
export let L = 2;
|
||||
export const C = 3;
|
||||
}
|
||||
expect: {
|
||||
export var V = 1;
|
||||
export let L = 2;
|
||||
export const C = 3;
|
||||
}
|
||||
}
|
||||
|
||||
issue_2038_2: {
|
||||
options = {
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
mangle = {
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
let LET = 1;
|
||||
const CONST = 2;
|
||||
var VAR = 3;
|
||||
export { LET, CONST, VAR };
|
||||
}
|
||||
expect: {
|
||||
let t = 1;
|
||||
const e = 2;
|
||||
var o = 3;
|
||||
export { t as LET, e as CONST, o as VAR };
|
||||
}
|
||||
}
|
||||
|
||||
issue_2126: {
|
||||
mangle = {
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
import { foo as bar, cat as dog } from "stuff";
|
||||
console.log(bar, dog);
|
||||
export { bar as qux };
|
||||
export { dog };
|
||||
}
|
||||
expect: {
|
||||
import { foo as o, cat as s } from "stuff";
|
||||
console.log(o, s);
|
||||
export { o as qux };
|
||||
export { s as dog };
|
||||
}
|
||||
}
|
||||
|
||||
beautify: {
|
||||
beautify = {
|
||||
beautify: true,
|
||||
}
|
||||
input: {
|
||||
export { A as B, C as D };
|
||||
}
|
||||
expect_exact: "export { A as B, C as D };"
|
||||
}
|
||||
|
||||
issue_2131: {
|
||||
options = {
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function no() {
|
||||
console.log(42);
|
||||
}
|
||||
function go() {
|
||||
console.log(42);
|
||||
}
|
||||
var X = 1, Y = 2;
|
||||
export function main() {
|
||||
go(X);
|
||||
};
|
||||
}
|
||||
expect: {
|
||||
function go() {
|
||||
console.log(42);
|
||||
}
|
||||
var X = 1;
|
||||
export function main() {
|
||||
go(X);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
issue_2129: {
|
||||
mangle = {
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
export const { keys } = Object;
|
||||
}
|
||||
expect: {
|
||||
export const { keys } = Object;
|
||||
}
|
||||
}
|
||||
|
||||
async_func: {
|
||||
options = {
|
||||
keep_fargs: false,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
export async function Foo(x){};
|
||||
}
|
||||
expect: {
|
||||
export async function Foo(){};
|
||||
}
|
||||
}
|
||||
|
||||
issue_2134_1: {
|
||||
options = {
|
||||
keep_fargs: false,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
export function Foo(x){};
|
||||
Foo.prototype = {};
|
||||
}
|
||||
expect: {
|
||||
export function Foo(){};
|
||||
Foo.prototype = {};
|
||||
}
|
||||
}
|
||||
|
||||
issue_2134_2: {
|
||||
options = {
|
||||
keep_fargs: false,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
export async function Foo(x){};
|
||||
Foo.prototype = {};
|
||||
}
|
||||
expect: {
|
||||
export async function Foo(){};
|
||||
Foo.prototype = {};
|
||||
}
|
||||
}
|
||||
|
||||
redirection: {
|
||||
mangle = {
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
let foo = 1, bar = 2;
|
||||
export { foo as delete };
|
||||
export { bar as default };
|
||||
export { foo as var } from "module.js";
|
||||
}
|
||||
expect: {
|
||||
let e = 1, o = 2;
|
||||
export { e as delete };
|
||||
export { o as default };
|
||||
export { foo as var } from "module.js";
|
||||
}
|
||||
}
|
||||
|
||||
keyword_invalid_1: {
|
||||
input: {
|
||||
export { default };
|
||||
}
|
||||
expect: {
|
||||
export { default };
|
||||
}
|
||||
}
|
||||
|
||||
keyword_invalid_2: {
|
||||
input: {
|
||||
export { default as Alias };
|
||||
}
|
||||
expect: {
|
||||
export { default as Alias };
|
||||
}
|
||||
}
|
||||
|
||||
keyword_invalid_3: {
|
||||
input: {
|
||||
export { default as default };
|
||||
}
|
||||
expect: {
|
||||
export { default as default };
|
||||
}
|
||||
}
|
||||
|
||||
keyword_valid_1: {
|
||||
input: {
|
||||
export { default } from "module.js";
|
||||
}
|
||||
expect: {
|
||||
export { default } from "module.js";
|
||||
}
|
||||
}
|
||||
|
||||
keyword_valid_2: {
|
||||
input: {
|
||||
export { default as Alias } from "module.js";
|
||||
}
|
||||
expect: {
|
||||
export { default as Alias } from "module.js";
|
||||
}
|
||||
}
|
||||
|
||||
keyword_valid_3: {
|
||||
input: {
|
||||
export { default as default } from "module.js";
|
||||
}
|
||||
expect: {
|
||||
export { default as default } from "module.js";
|
||||
}
|
||||
}
|
||||
@@ -265,7 +265,7 @@ issue_203: {
|
||||
}
|
||||
expect: {
|
||||
var m = {};
|
||||
var fn = Function("a", "b", "b.exports=42");
|
||||
var fn = Function("n,o", "o.exports=42");
|
||||
fn(null, m, m.exports);
|
||||
console.log(m.exports);
|
||||
}
|
||||
@@ -468,11 +468,9 @@ issue_2114_1: {
|
||||
}
|
||||
expect: {
|
||||
var c = 0;
|
||||
!function() {
|
||||
0;
|
||||
}((c += 1, c = 1 + c, function() {
|
||||
c = 1 + (c += 1), function() {
|
||||
var b = void (b && (b.b += (c += 1, 0)));
|
||||
}()));
|
||||
}();
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
|
||||
@@ -174,3 +174,24 @@ issue_1986: {
|
||||
console.log(42);
|
||||
}
|
||||
}
|
||||
|
||||
issue_2167: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
dead_code: true,
|
||||
evaluate: true,
|
||||
global_defs: {
|
||||
"@isDevMode": "function(){}",
|
||||
},
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
if (isDevMode()) {
|
||||
greetOverlord();
|
||||
}
|
||||
doWork();
|
||||
}
|
||||
expect: {
|
||||
doWork();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,12 @@ typeof_arrow_functions: {
|
||||
evaluate: true
|
||||
}
|
||||
input: {
|
||||
var foo = typeof (x) => null;
|
||||
var foo = typeof (x => null);
|
||||
console.log(foo);
|
||||
}
|
||||
expect_exact: "var foo=\"function\";"
|
||||
expect_exact: "var foo=\"function\";console.log(foo);"
|
||||
expect_stdout: "function"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
classes: {
|
||||
@@ -60,15 +63,15 @@ class_name_can_be_mangled: {
|
||||
function x() {
|
||||
class Foo {
|
||||
}
|
||||
var class1 = Foo
|
||||
var class2 = class Bar {}
|
||||
var class1 = Foo;
|
||||
var class2 = class Bar {};
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function x() {
|
||||
class a { }
|
||||
var n = a
|
||||
var r = class a {}
|
||||
var s = a;
|
||||
var c = class a {};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,12 +290,12 @@ import_statement_mangling: {
|
||||
Whatever();
|
||||
}
|
||||
expect: {
|
||||
import l from "foo";
|
||||
import e, {Food as o} from "lel";
|
||||
import o from "foo";
|
||||
import m, {Food as r} from "lel";
|
||||
import {What as f} from "lel";
|
||||
l();
|
||||
e();
|
||||
o();
|
||||
m();
|
||||
r();
|
||||
f();
|
||||
}
|
||||
}
|
||||
@@ -466,10 +469,10 @@ issue_1898: {
|
||||
expect: {
|
||||
class Foo {
|
||||
bar() {
|
||||
for (const n of [ 6, 5 ])
|
||||
for (const f of [ 6, 5 ])
|
||||
for (let r of [ 4, 3 ])
|
||||
for (var o of [ 2, 1 ])
|
||||
console.log(n, r, o);
|
||||
console.log(f, r, o);
|
||||
}
|
||||
}
|
||||
new Foo().bar();
|
||||
@@ -494,9 +497,9 @@ issue_1753: {
|
||||
expect: {
|
||||
class SomeClass {
|
||||
constructor(r) {
|
||||
let a = [];
|
||||
for (let s = 0; s < 6; s++)
|
||||
a.push({
|
||||
let s = [];
|
||||
for (let a = 0; a < 6; a++)
|
||||
s.push({
|
||||
mainDrawNumbers: [],
|
||||
extraDrawNumbers: []
|
||||
});
|
||||
@@ -523,9 +526,9 @@ issue_1753_disable: {
|
||||
expect: {
|
||||
class SomeClass {
|
||||
constructor(r) {
|
||||
let a = [];
|
||||
let s = [];
|
||||
for (let r = 0; r < 6; r++)
|
||||
a.push({
|
||||
s.push({
|
||||
mainDrawNumbers: [],
|
||||
extraDrawNumbers: []
|
||||
});
|
||||
@@ -664,3 +667,31 @@ class_expression_statement_unused_toplevel: {
|
||||
}
|
||||
expect_exact: ""
|
||||
}
|
||||
|
||||
export_default_function_decl: {
|
||||
options = {
|
||||
toplevel: true,
|
||||
passes: 3,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
// do not drop "unused" exports
|
||||
export default function Foo() {};
|
||||
export function Far() {};
|
||||
}
|
||||
expect_exact: "export default function Foo(){};export function Far(){};"
|
||||
}
|
||||
|
||||
export_default_class_decl: {
|
||||
options = {
|
||||
toplevel: true,
|
||||
passes: 3,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
// do not drop "unused" exports
|
||||
export default class Car {};
|
||||
export class Cab {};
|
||||
}
|
||||
expect_exact: "export default class Car{};export class Cab{};"
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ issue_1321_no_debug: {
|
||||
}
|
||||
expect: {
|
||||
var x = {};
|
||||
x.b = 1;
|
||||
x["a"] = 2 * x.b;
|
||||
console.log(x.b, x["a"]);
|
||||
x.o = 1;
|
||||
x["a"] = 2 * x.o;
|
||||
console.log(x.o, x["a"]);
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
@@ -30,9 +30,9 @@ issue_1321_debug: {
|
||||
}
|
||||
expect: {
|
||||
var x = {};
|
||||
x.a = 1;
|
||||
x["_$foo$_"] = 2 * x.a;
|
||||
console.log(x.a, x["_$foo$_"]);
|
||||
x.o = 1;
|
||||
x["_$foo$_"] = 2 * x.o;
|
||||
console.log(x.o, x["_$foo$_"]);
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
@@ -49,9 +49,9 @@ issue_1321_with_quoted: {
|
||||
}
|
||||
expect: {
|
||||
var x = {};
|
||||
x.a = 1;
|
||||
x["b"] = 2 * x.a;
|
||||
console.log(x.a, x["b"]);
|
||||
x.o = 1;
|
||||
x["x"] = 2 * x.o;
|
||||
console.log(x.o, x["x"]);
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ same_variable_in_multiple_for_loop: {
|
||||
console.log(o, l);
|
||||
for (let o = 0; o < 2; o++) {
|
||||
console.log(o, l);
|
||||
let c = 2;
|
||||
console.log(c);
|
||||
let e = 2;
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,12 +114,12 @@ same_variable_in_multiple_forIn: {
|
||||
}
|
||||
expect: {
|
||||
var test = [ "a", "b", "c" ];
|
||||
for (let o in test) {
|
||||
console.log(o);
|
||||
let e;
|
||||
e = [ "e", "f", "g" ];
|
||||
for (let o in test)
|
||||
console.log(o);
|
||||
for (let e in test) {
|
||||
console.log(e);
|
||||
let t;
|
||||
t = [ "e", "f", "g" ];
|
||||
for (let e in test)
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
expect_stdout: true
|
||||
@@ -160,8 +160,8 @@ different_variable_in_multiple_for_loop: {
|
||||
console.log(o, l);
|
||||
for (let o = 0; o < 2; o++) {
|
||||
console.log(o, l);
|
||||
let c = 2;
|
||||
console.log(c);
|
||||
let e = 2;
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,12 +241,12 @@ different_variable_in_multiple_forIn: {
|
||||
}
|
||||
expect: {
|
||||
var test = [ "a", "b", "c" ];
|
||||
for (let o in test) {
|
||||
console.log(o);
|
||||
let e;
|
||||
e = [ "e", "f", "g" ];
|
||||
for (let o in test)
|
||||
console.log(o);
|
||||
for (let e in test) {
|
||||
console.log(e);
|
||||
let t;
|
||||
t = [ "e", "f", "g" ];
|
||||
for (let e in test)
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
expect_stdout: true
|
||||
@@ -281,10 +281,10 @@ more_variable_in_multiple_for: {
|
||||
}
|
||||
expect: {
|
||||
for (let o = 9, l = 0; l < 20; l += o) {
|
||||
let c = o++ + l;
|
||||
console.log(o, c, l);
|
||||
for (let l = c, e = c * c, f = 0; f < 10; f++)
|
||||
console.log(o, c, e, l, f);
|
||||
let e = o++ + l;
|
||||
console.log(o, e, l);
|
||||
for (let l = e, t = e * e, c = 0; c < 10; c++)
|
||||
console.log(o, e, t, l, c);
|
||||
}
|
||||
}
|
||||
expect_stdout: true
|
||||
|
||||
@@ -82,7 +82,7 @@ numeric_literal: {
|
||||
' 42: 2,',
|
||||
' "42": 3,',
|
||||
' 37: 4,',
|
||||
' a: 5,',
|
||||
' o: 5,',
|
||||
' 1e42: 6,',
|
||||
' b: 7,',
|
||||
' "1e+42": 8',
|
||||
@@ -92,7 +92,7 @@ numeric_literal: {
|
||||
'',
|
||||
'console.log(obj[42], obj["42"]);',
|
||||
'',
|
||||
'console.log(obj[37], obj["a"], obj[37], obj["37"]);',
|
||||
'console.log(obj[37], obj["o"], obj[37], obj["37"]);',
|
||||
'',
|
||||
'console.log(obj[1e42], obj["b"], obj["1e+42"]);',
|
||||
]
|
||||
|
||||
@@ -45,7 +45,7 @@ export_default_func_1: {
|
||||
input: {
|
||||
export default function f(){};
|
||||
}
|
||||
expect_exact: "export default function(){};"
|
||||
expect_exact: "export default function f(){};"
|
||||
}
|
||||
|
||||
export_default_func_2: {
|
||||
@@ -58,7 +58,7 @@ export_default_func_2: {
|
||||
input: {
|
||||
export default function f(){}(1);
|
||||
}
|
||||
expect_exact: "export default function(){};1;"
|
||||
expect_exact: "export default function f(){};1;"
|
||||
}
|
||||
|
||||
export_default_func_3: {
|
||||
@@ -71,7 +71,7 @@ export_default_func_3: {
|
||||
input: {
|
||||
export default function f(){}(1);
|
||||
}
|
||||
expect_exact: "export default function(){};"
|
||||
expect_exact: "export default function f(){};"
|
||||
}
|
||||
|
||||
export_class_1: {
|
||||
@@ -121,7 +121,7 @@ export_default_class_1: {
|
||||
input: {
|
||||
export default class C {};
|
||||
}
|
||||
expect_exact: "export default class{};"
|
||||
expect_exact: "export default class C{};"
|
||||
}
|
||||
|
||||
export_default_class_2: {
|
||||
@@ -134,7 +134,7 @@ export_default_class_2: {
|
||||
input: {
|
||||
export default class C {}(1);
|
||||
}
|
||||
expect_exact: "export default class{};1;"
|
||||
expect_exact: "export default class C{};1;"
|
||||
}
|
||||
|
||||
export_default_class_3: {
|
||||
@@ -147,7 +147,7 @@ export_default_class_3: {
|
||||
input: {
|
||||
export default class C {}(1);
|
||||
}
|
||||
expect_exact: "export default class{};"
|
||||
expect_exact: "export default class C{};"
|
||||
}
|
||||
|
||||
export_mangle_1: {
|
||||
@@ -159,7 +159,7 @@ export_mangle_1: {
|
||||
return one - two;
|
||||
};
|
||||
}
|
||||
expect_exact: "export function foo(n,o){return n-o};"
|
||||
expect_exact: "export function foo(o,n){return o-n};"
|
||||
}
|
||||
|
||||
export_mangle_2: {
|
||||
@@ -171,7 +171,7 @@ export_mangle_2: {
|
||||
return one - two;
|
||||
};
|
||||
}
|
||||
expect_exact: "export default function n(n,r){return n-r};"
|
||||
expect_exact: "export default function foo(o,t){return o-t};"
|
||||
}
|
||||
|
||||
export_mangle_3: {
|
||||
@@ -189,7 +189,7 @@ export_mangle_3: {
|
||||
}
|
||||
};
|
||||
}
|
||||
expect_exact: "export class C{go(n,r){return n-r+n}};"
|
||||
expect_exact: "export class C{go(r,e){return r-e+r}};"
|
||||
}
|
||||
|
||||
export_mangle_4: {
|
||||
@@ -207,7 +207,7 @@ export_mangle_4: {
|
||||
}
|
||||
};
|
||||
}
|
||||
expect_exact: "export default class n{go(n,r){return n-r+n}};"
|
||||
expect_exact: "export default class C{go(e,r){return e-r+e}};"
|
||||
}
|
||||
|
||||
export_mangle_5: {
|
||||
@@ -221,7 +221,7 @@ export_mangle_5: {
|
||||
}
|
||||
};
|
||||
}
|
||||
expect_exact: "export default{prop:function(n,r){return n-r}};"
|
||||
expect_exact: "export default{prop:function(r,t){return r-t}};"
|
||||
}
|
||||
|
||||
export_mangle_6: {
|
||||
@@ -232,7 +232,7 @@ export_mangle_6: {
|
||||
var baz = 2;
|
||||
export let foo = 1, bar = baz;
|
||||
}
|
||||
expect_exact: "var a=2;export let foo=1,bar=a;"
|
||||
expect_exact: "var o=2;export let foo=1,bar=o;"
|
||||
}
|
||||
|
||||
export_toplevel_1: {
|
||||
@@ -247,7 +247,7 @@ export_toplevel_1: {
|
||||
}
|
||||
expect: {
|
||||
export function g(){};
|
||||
export default function(){};
|
||||
export default function h(){};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ export_toplevel_2: {
|
||||
}
|
||||
expect: {
|
||||
export class B {};
|
||||
export default class {};
|
||||
export default class C {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ compress_new_function: {
|
||||
new Function("aa, bb", 'return aa;');
|
||||
}
|
||||
expect: {
|
||||
Function("a", "b", "return a");
|
||||
Function("n,r", "return n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,30 @@ compress_new_function_with_destruct: {
|
||||
new Function("[[aa]], [{bb}]", 'return aa;');
|
||||
}
|
||||
expect: {
|
||||
Function("a", "[b]", "return a");
|
||||
Function("a", "{bb:b}", "return a");
|
||||
Function("[[a]]", "[{bb:b}]", 'return a');
|
||||
Function("n,[r]", "return n");
|
||||
Function("n,{bb:b}", "return n");
|
||||
Function("[[n]],[{bb:b}]", "return n");
|
||||
}
|
||||
}
|
||||
|
||||
compress_new_function_with_destruct_arrows: {
|
||||
options = {
|
||||
arrows: true,
|
||||
unsafe: true,
|
||||
unsafe_Func: true,
|
||||
ecma: 6
|
||||
}
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
new Function("aa, [bb]", 'return aa;');
|
||||
new Function("aa, {bb}", 'return aa;');
|
||||
new Function("[[aa]], [{bb}]", 'return aa;');
|
||||
}
|
||||
expect: {
|
||||
Function("n,[a]", "return n");
|
||||
Function("b,{bb:n}", "return b");
|
||||
Function("[[b]],[{bb:n}]", "return b");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,7 +419,7 @@ wrap_iife_in_return_call: {
|
||||
expect_exact: '(void console.log("test"))();'
|
||||
}
|
||||
|
||||
pure_annotation: {
|
||||
pure_annotation_1: {
|
||||
options = {
|
||||
inline: true,
|
||||
side_effects: true,
|
||||
@@ -432,6 +432,20 @@ pure_annotation: {
|
||||
expect_exact: ""
|
||||
}
|
||||
|
||||
pure_annotation_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
/*@__PURE__*/(function(n) {
|
||||
console.log("hello", n);
|
||||
}(42));
|
||||
}
|
||||
expect_exact: ""
|
||||
}
|
||||
|
||||
drop_fargs: {
|
||||
options = {
|
||||
cascade: true,
|
||||
@@ -449,9 +463,7 @@ drop_fargs: {
|
||||
}
|
||||
expect: {
|
||||
var a = 1;
|
||||
!function() {
|
||||
a++;
|
||||
}(++a && a.var);
|
||||
++a && a.var, a++;
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: "3"
|
||||
@@ -474,9 +486,7 @@ keep_fargs: {
|
||||
}
|
||||
expect: {
|
||||
var a = 1;
|
||||
!function(a_1) {
|
||||
a++;
|
||||
}(++a && a.var);
|
||||
++a && a.var, a++;
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: "3"
|
||||
|
||||
@@ -1,37 +1,41 @@
|
||||
dont_reuse_prop: {
|
||||
mangle_props = {
|
||||
regex: /asd/
|
||||
};
|
||||
|
||||
}
|
||||
input: {
|
||||
"aaaaaaaaaabbbbb";
|
||||
var obj = {};
|
||||
obj.a = 123;
|
||||
obj.asd = 256;
|
||||
console.log(obj.a);
|
||||
}
|
||||
expect: {
|
||||
"aaaaaaaaaabbbbb";
|
||||
var obj = {};
|
||||
obj.a = 123;
|
||||
obj.b = 256;
|
||||
console.log(obj.a);
|
||||
}
|
||||
expect_stdout: "123"
|
||||
}
|
||||
|
||||
unmangleable_props_should_always_be_reserved: {
|
||||
mangle_props = {
|
||||
regex: /asd/
|
||||
};
|
||||
|
||||
}
|
||||
input: {
|
||||
"aaaaaaaaaabbbbb";
|
||||
var obj = {};
|
||||
obj.asd = 256;
|
||||
obj.a = 123;
|
||||
console.log(obj.a);
|
||||
}
|
||||
expect: {
|
||||
"aaaaaaaaaabbbbb";
|
||||
var obj = {};
|
||||
obj.b = 256;
|
||||
obj.a = 123;
|
||||
console.log(obj.a);
|
||||
}
|
||||
}
|
||||
expect_stdout: "123"
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ getter_setter_mangler: {
|
||||
};
|
||||
}
|
||||
}
|
||||
expect_exact: "function f(n,t){return{get:n,set:t,get g(){},set s(n){},c,a:1,m(){}}}"
|
||||
expect_exact: "function f(t,e){return{get:t,set:e,get g(){},set s(t){},c,a:1,m(){}}}"
|
||||
}
|
||||
|
||||
use_shorthand_opportunity: {
|
||||
@@ -297,7 +297,7 @@ concise_methods_and_mangle_props: {
|
||||
expect: {
|
||||
function x() {
|
||||
obj = {
|
||||
a() { return 1; }
|
||||
o() { return 1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
arrow_functions: {
|
||||
options = {
|
||||
arrows: true,
|
||||
}
|
||||
input: {
|
||||
(a) => b; // 1 args
|
||||
(a, b) => c; // n args
|
||||
@@ -13,6 +16,9 @@ arrow_functions: {
|
||||
}
|
||||
|
||||
arrow_return: {
|
||||
options = {
|
||||
arrows: true,
|
||||
}
|
||||
input: {
|
||||
() => {};
|
||||
() => { return; };
|
||||
|
||||
@@ -135,11 +135,11 @@ mangle_properties: {
|
||||
a['run']({color: "blue", foo: "baz"});
|
||||
}
|
||||
expect: {
|
||||
a["a"] = "bar";
|
||||
a.b = "red";
|
||||
x = {c: 10};
|
||||
a.d(x.c, a.a);
|
||||
a['d']({b: "blue", a: "baz"});
|
||||
a["o"] = "bar";
|
||||
a.a = "red";
|
||||
x = {r: 10};
|
||||
a.b(x.r, a.o);
|
||||
a['b']({a: "blue", o: "baz"});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,16 +178,16 @@ mangle_unquoted_properties: {
|
||||
function f1() {
|
||||
a["foo"] = "bar";
|
||||
a.color = "red";
|
||||
a.b = 2;
|
||||
x = {"bar": 10, c: 7};
|
||||
a.c = 9;
|
||||
a.o = 2;
|
||||
x = {"bar": 10, f: 7};
|
||||
a.f = 9;
|
||||
}
|
||||
function f2() {
|
||||
a.foo = "bar";
|
||||
a['color'] = "red";
|
||||
x = {bar: 10, c: 7};
|
||||
a.c = 9;
|
||||
a.b = 3;
|
||||
x = {bar: 10, f: 7};
|
||||
a.f = 9;
|
||||
a.o = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,3 +241,147 @@ issue_2110_2: {
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
set_immutable_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var a = 1;
|
||||
a.foo += "";
|
||||
if (a.foo) console.log("FAIL");
|
||||
else console.log("PASS");
|
||||
}
|
||||
expect: {
|
||||
1..foo += "";
|
||||
if (1..foo) console.log("FAIL");
|
||||
else console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
set_immutable_2: {
|
||||
options = {
|
||||
cascade: true,
|
||||
conditionals: true,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
var a = 1;
|
||||
a.foo += "";
|
||||
if (a.foo) console.log("FAIL");
|
||||
else console.log("PASS");
|
||||
}
|
||||
expect: {
|
||||
var a = 1;
|
||||
a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
set_immutable_3: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
var a = 1;
|
||||
a.foo += "";
|
||||
if (a.foo) console.log("FAIL");
|
||||
else console.log("PASS");
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
1..foo += "";
|
||||
if (1..foo) console.log("FAIL");
|
||||
else console.log("PASS");
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
set_immutable_4: {
|
||||
options = {
|
||||
cascade: true,
|
||||
conditionals: true,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
var a = 1;
|
||||
a.foo += "";
|
||||
if (a.foo) console.log("FAIL");
|
||||
else console.log("PASS");
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
var a = 1;
|
||||
a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS");
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
set_mutable_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
!function a() {
|
||||
a.foo += "";
|
||||
if (a.foo) console.log("PASS");
|
||||
else console.log("FAIL");
|
||||
}();
|
||||
}
|
||||
expect: {
|
||||
!function a() {
|
||||
if (a.foo += "") console.log("PASS");
|
||||
else console.log("FAIL");
|
||||
}();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
set_mutable_2: {
|
||||
options = {
|
||||
cascade: true,
|
||||
conditionals: true,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
!function a() {
|
||||
a.foo += "";
|
||||
if (a.foo) console.log("PASS");
|
||||
else console.log("FAIL");
|
||||
}();
|
||||
}
|
||||
expect: {
|
||||
!function a() {
|
||||
(a.foo += "") ? console.log("PASS") : console.log("FAIL");
|
||||
}();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -2625,3 +2625,28 @@ issue_2090_2: {
|
||||
expect_stdout: "1"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
for_in_prop: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = {
|
||||
foo: function() {
|
||||
for (this.b in [1, 2]);
|
||||
}
|
||||
};
|
||||
a.foo();
|
||||
console.log(a.b);
|
||||
}
|
||||
expect: {
|
||||
var a = {
|
||||
foo: function() {
|
||||
for (this.b in [1, 2]);
|
||||
}
|
||||
};
|
||||
a.foo();
|
||||
console.log(a.b);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
@@ -255,3 +255,73 @@ issue_1586_2: {
|
||||
}
|
||||
expect_exact: "function f(){try{x()}catch(c){console.log(c.message)}}"
|
||||
}
|
||||
|
||||
issue_2120_1: {
|
||||
mangle = {
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
"aaaaaaaa";
|
||||
var a = 1, b = "FAIL";
|
||||
try {
|
||||
throw 1;
|
||||
} catch (c) {
|
||||
try {
|
||||
throw 0;
|
||||
} catch (a) {
|
||||
if (c) b = "PASS";
|
||||
}
|
||||
}
|
||||
console.log(b);
|
||||
}
|
||||
expect: {
|
||||
"aaaaaaaa";
|
||||
var a = 1, b = "FAIL";
|
||||
try {
|
||||
throw 1;
|
||||
} catch (t) {
|
||||
try {
|
||||
throw 0;
|
||||
} catch (a) {
|
||||
if (t) b = "PASS";
|
||||
}
|
||||
}
|
||||
console.log(b);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2120_2: {
|
||||
mangle = {
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
"aaaaaaaa";
|
||||
var a = 1, b = "FAIL";
|
||||
try {
|
||||
throw 1;
|
||||
} catch (c) {
|
||||
try {
|
||||
throw 0;
|
||||
} catch (a) {
|
||||
if (c) b = "PASS";
|
||||
}
|
||||
}
|
||||
console.log(b);
|
||||
}
|
||||
expect: {
|
||||
"aaaaaaaa";
|
||||
var a = 1, b = "FAIL";
|
||||
try {
|
||||
throw 1;
|
||||
} catch (c) {
|
||||
try {
|
||||
throw 0;
|
||||
} catch (a) {
|
||||
if (c) b = "PASS";
|
||||
}
|
||||
}
|
||||
console.log(b);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
3
test/input/invalid/export_1.js
Normal file
3
test/input/invalid/export_1.js
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
export var V = 1;
|
||||
}
|
||||
1
test/input/invalid/export_2.js
Normal file
1
test/input/invalid/export_2.js
Normal file
@@ -0,0 +1 @@
|
||||
export class{};
|
||||
1
test/input/invalid/export_3.js
Normal file
1
test/input/invalid/export_3.js
Normal file
@@ -0,0 +1 @@
|
||||
export function(){};
|
||||
4
test/input/invalid/for-in_1.js
Normal file
4
test/input/invalid/for-in_1.js
Normal file
@@ -0,0 +1,4 @@
|
||||
var a, b = [1, 2];
|
||||
for (1, 2, a in b) {
|
||||
console.log(a, b[a]);
|
||||
}
|
||||
4
test/input/invalid/for-in_2.js
Normal file
4
test/input/invalid/for-in_2.js
Normal file
@@ -0,0 +1,4 @@
|
||||
var c = [1, 2];
|
||||
for (var a, b in c) {
|
||||
console.log(a, c[a]);
|
||||
}
|
||||
3
test/input/invalid/import.js
Normal file
3
test/input/invalid/import.js
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
import A from "B";
|
||||
}
|
||||
1
test/input/invalid/sequence.js
Normal file
1
test/input/invalid/sequence.js
Normal file
@@ -0,0 +1 @@
|
||||
(a, ...b);
|
||||
@@ -62,23 +62,20 @@ describe("Arrow functions", function() {
|
||||
}
|
||||
});
|
||||
it("Should not accept arrow functions in the middle or end of an expression", function() {
|
||||
var tests = [
|
||||
[
|
||||
"0 + x => 0",
|
||||
"0 + async x => 0",
|
||||
"typeof x => 0",
|
||||
"0 + x => 0"
|
||||
];
|
||||
var test = function(code) {
|
||||
return function() {
|
||||
"typeof async x => 0",
|
||||
"typeof (x) => null",
|
||||
"typeof async (x) => null",
|
||||
].forEach(function(code) {
|
||||
assert.throws(function() {
|
||||
uglify.parse(code);
|
||||
}
|
||||
}
|
||||
var error = function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error &&
|
||||
e.message === "Unexpected token: arrow (=>)";
|
||||
}
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(test(tests[i]), error);
|
||||
}
|
||||
}, function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error && /^Unexpected /.test(e.message);
|
||||
}, code);
|
||||
});
|
||||
});
|
||||
|
||||
it("Should parse a function containing default assignment correctly", function() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var assert = require("assert");
|
||||
var exec = require("child_process").exec;
|
||||
var readFileSync = require("fs").readFileSync;
|
||||
var semver = require("semver");
|
||||
|
||||
function read(path) {
|
||||
return readFileSync(path, "utf8");
|
||||
@@ -9,9 +10,11 @@ function read(path) {
|
||||
describe("bin/uglifyjs", function () {
|
||||
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
|
||||
it("should produce a functional build when using --self", function (done) {
|
||||
this.timeout(30000);
|
||||
this.timeout(60000);
|
||||
|
||||
var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS';
|
||||
var command = uglifyjscmd + ' --self -mc ecma=';
|
||||
command += semver.satisfies(process.version, ">=4") ? "6" : "5";
|
||||
command += ',passes=3,keep_fargs=false,unsafe --wrap WrappedUglifyJS';
|
||||
|
||||
exec(command, function (err, stdout) {
|
||||
if (err) throw err;
|
||||
@@ -533,6 +536,111 @@ describe("bin/uglifyjs", function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (block-level export)", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/export_1.js -m';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/export_1.js:2,4",
|
||||
" export var V = 1;",
|
||||
" ^",
|
||||
"ERROR: Export statement may only appear at top level"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (block-level import)", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/import.js -m';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/import.js:2,4",
|
||||
' import A from "B";',
|
||||
" ^",
|
||||
"ERROR: Import statement may only appear at top level"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (anonymous class)", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/export_2.js';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/export_2.js:1,12",
|
||||
"export class{};",
|
||||
" ^",
|
||||
"ERROR: Unexpected token: punc ({)"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (anonymous function)", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/export_3.js';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/export_3.js:1,15",
|
||||
"export function(){};",
|
||||
" ^",
|
||||
"ERROR: Unexpected token: punc (()"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (spread in sequence)", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/sequence.js';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/sequence.js:1,4",
|
||||
"(a, ...b);",
|
||||
" ^",
|
||||
"ERROR: Unexpected token: expand (...)"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (for-in init)", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/for-in_1.js';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/for-in_1.js:2,5",
|
||||
"for (1, 2, a in b) {",
|
||||
" ^",
|
||||
"ERROR: Invalid left-hand side in for..in loop"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (for-in var)", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/for-in_2.js';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/for-in_2.js:2,5",
|
||||
"for (var a, b in c) {",
|
||||
" ^",
|
||||
"ERROR: Only one variable declaration allowed in for..in loop"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should handle literal string as source map input", function(done) {
|
||||
var command = [
|
||||
uglifyjscmd,
|
||||
|
||||
@@ -2,8 +2,7 @@ var assert = require("assert");
|
||||
var uglify = require("../node");
|
||||
|
||||
describe("Export", function() {
|
||||
it ("Should parse export directives", function() {
|
||||
|
||||
it("Should parse export directives", function() {
|
||||
var inputs = [
|
||||
['export * from "a.js"', ['*'], "a.js"],
|
||||
['export {A} from "a.js"', ['A'], "a.js"],
|
||||
@@ -12,17 +11,17 @@ describe("Export", function() {
|
||||
['export {A, B} from "a.js"', ['A', 'B'], "a.js"],
|
||||
];
|
||||
|
||||
var test = function(code) {
|
||||
function test(code) {
|
||||
return uglify.parse(code);
|
||||
};
|
||||
}
|
||||
|
||||
var extractNames = function(symbols) {
|
||||
function extractNames(symbols) {
|
||||
var ret = [];
|
||||
for (var i = 0; i < symbols.length; i++) {
|
||||
ret.push(symbols[i].name.name)
|
||||
ret.push(symbols[i].foreign_name.name);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
var ast = test(inputs[i][0]);
|
||||
@@ -34,7 +33,7 @@ describe("Export", function() {
|
||||
assert(st instanceof uglify.AST_Export);
|
||||
var actualNames = extractNames(st.exported_names);
|
||||
assert.deepEqual(actualNames, names);
|
||||
assert.equal(st.module_name.value, filename)
|
||||
assert.equal(st.module_name.value, filename);
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -191,15 +191,51 @@ describe("Function", function() {
|
||||
];
|
||||
var test = function(code) {
|
||||
return function() {
|
||||
uglify.parse(code);
|
||||
uglify.parse(code, { ecma: 5 });
|
||||
}
|
||||
}
|
||||
var error = function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error &&
|
||||
e.message === "Invalid function parameter";
|
||||
return e instanceof uglify.JS_Parse_Error;
|
||||
}
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(test(tests[i]), error);
|
||||
assert.throws(test(tests[i]), error, tests[i]);
|
||||
}
|
||||
});
|
||||
it("Should accept trailing commas only for ES8", function() {
|
||||
[
|
||||
"new Foo(a, );",
|
||||
"async(...[1, 2], );",
|
||||
"console.log(...[1, 2], );",
|
||||
"!function(a, b, ){ console.log(a + b); }(3, 4, );",
|
||||
].forEach(function(code) {
|
||||
uglify.parse(code, { ecma: 8 });
|
||||
assert.throws(function() {
|
||||
uglify.parse(code, { ecma: 6 });
|
||||
}, function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error;
|
||||
}, code);
|
||||
});
|
||||
});
|
||||
it("Should not accept invalid trailing commas", function() {
|
||||
var tests = [
|
||||
"f(, );",
|
||||
"(, ) => {};",
|
||||
"(...p, ) => {};",
|
||||
"function f(, ) {}",
|
||||
"function f(...p, ) {}",
|
||||
"function foo(a, b, , ) {}",
|
||||
'console.log("hello", , );',
|
||||
];
|
||||
var test = function(code) {
|
||||
return function() {
|
||||
uglify.parse(code, { ecma: 8 });
|
||||
}
|
||||
}
|
||||
var error = function(e) {
|
||||
return e instanceof uglify.JS_Parse_Error;
|
||||
}
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(test(tests[i]), error, tests[i]);
|
||||
}
|
||||
});
|
||||
it("Should not accept an initializer when parameter is a rest parameter", function() {
|
||||
|
||||
@@ -26,12 +26,12 @@ describe("bin/uglifyjs with input file globs", function() {
|
||||
});
|
||||
});
|
||||
it("bin/uglifyjs with multiple input file globs.", function(done) {
|
||||
var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel';
|
||||
var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel,passes=2';
|
||||
|
||||
exec(command, function(err, stdout) {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",2*11);\n');
|
||||
assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",22);\n');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,29 +2,37 @@ var Uglify = require('../../');
|
||||
var assert = require("assert");
|
||||
|
||||
describe("let", function() {
|
||||
it("Should not produce `let` as a variable name in mangle", function(done) {
|
||||
it("Should not produce reserved keywords as variable name in mangle", function(done) {
|
||||
this.timeout(10000);
|
||||
|
||||
// Produce a lot of variables in a function and run it through mangle.
|
||||
var s = '"use strict"; function foo() {';
|
||||
for (var i = 0; i < 21000; ++i) {
|
||||
var s = '"dddddeeeeelllllooooottttt"; function foo() {';
|
||||
for (var i = 0; i < 18000; i++) {
|
||||
s += "var v" + i + "=0;";
|
||||
}
|
||||
s += '}';
|
||||
var result = Uglify.minify(s, {compress: false});
|
||||
|
||||
// Verify that select keywords and reserved keywords not produced
|
||||
assert.strictEqual(result.code.indexOf("var let="), -1);
|
||||
assert.strictEqual(result.code.indexOf("var do="), -1);
|
||||
assert.strictEqual(result.code.indexOf("var var="), -1);
|
||||
[
|
||||
"do",
|
||||
"let",
|
||||
"var",
|
||||
].forEach(function(name) {
|
||||
assert.strictEqual(result.code.indexOf("var " + name + "="), -1);
|
||||
});
|
||||
|
||||
// Verify that the variable names that appeared immediately before
|
||||
// and after the erroneously generated `let` variable name still exist
|
||||
// and after the erroneously generated variable name still exist
|
||||
// to show the test generated enough symbols.
|
||||
assert(result.code.indexOf("var ket=") >= 0);
|
||||
assert(result.code.indexOf("var met=") >= 0);
|
||||
[
|
||||
"to", "eo",
|
||||
"eet", "fet",
|
||||
"rar", "oar",
|
||||
].forEach(function(name) {
|
||||
assert.ok(result.code.indexOf("var " + name + "=") >= 0);
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var Uglify = require('../../');
|
||||
var assert = require("assert");
|
||||
var readFileSync = require("fs").readFileSync;
|
||||
var run_code = require("../sandbox").run_code;
|
||||
|
||||
function read(path) {
|
||||
return readFileSync(path, "utf8");
|
||||
@@ -20,6 +21,58 @@ describe("minify", function() {
|
||||
assert.strictEqual(result.code, "alert(2);");
|
||||
});
|
||||
|
||||
it("Should work with mangle.cache", function() {
|
||||
var cache = {};
|
||||
var original = "";
|
||||
var compressed = "";
|
||||
[
|
||||
"bar.es5",
|
||||
"baz.es5",
|
||||
"foo.es5",
|
||||
"qux.js",
|
||||
].forEach(function(file) {
|
||||
var code = read("test/input/issue-1242/" + file);
|
||||
var result = Uglify.minify(code, {
|
||||
mangle: {
|
||||
cache: cache,
|
||||
toplevel: true
|
||||
}
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
original += code;
|
||||
compressed += result.code;
|
||||
});
|
||||
assert.strictEqual(JSON.stringify(cache).slice(0, 20), '{"cname":5,"props":{');
|
||||
assert.strictEqual(compressed, 'function n(n){return 3*n}function r(n){return n/2}function c(o){l("Foo:",2*o)}var l=console.log.bind(console);var f=n(3),i=r(12);l("qux",f,i),c(11);');
|
||||
assert.strictEqual(run_code(compressed), run_code(original));
|
||||
});
|
||||
|
||||
it("Should work with nameCache", function() {
|
||||
var cache = {};
|
||||
var original = "";
|
||||
var compressed = "";
|
||||
[
|
||||
"bar.es5",
|
||||
"baz.es5",
|
||||
"foo.es5",
|
||||
"qux.js",
|
||||
].forEach(function(file) {
|
||||
var code = read("test/input/issue-1242/" + file);
|
||||
var result = Uglify.minify(code, {
|
||||
mangle: {
|
||||
toplevel: true
|
||||
},
|
||||
nameCache: cache
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
original += code;
|
||||
compressed += result.code;
|
||||
});
|
||||
assert.strictEqual(JSON.stringify(cache).slice(0, 28), '{"vars":{"cname":5,"props":{');
|
||||
assert.strictEqual(compressed, 'function n(n){return 3*n}function r(n){return n/2}function c(o){l("Foo:",2*o)}var l=console.log.bind(console);var f=n(3),i=r(12);l("qux",f,i),c(11);');
|
||||
assert.strictEqual(run_code(compressed), run_code(original));
|
||||
});
|
||||
|
||||
describe("keep_quoted_props", function() {
|
||||
it("Should preserve quotes in object literals", function() {
|
||||
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
|
||||
@@ -212,7 +265,7 @@ describe("minify", function() {
|
||||
});
|
||||
var err = result.error;
|
||||
assert.ok(err instanceof Error);
|
||||
assert.strictEqual(err.stack.split(/\n/)[0], "Error: Can't handle expression: debugger");
|
||||
assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token: keyword (debugger)");
|
||||
});
|
||||
it("should skip inherited properties", function() {
|
||||
var foo = Object.create({ skip: this });
|
||||
|
||||
@@ -30,4 +30,13 @@ describe("Template string", function() {
|
||||
assert.throws(exec(tests[i]), fail, tests[i]);
|
||||
}
|
||||
});
|
||||
it("Should process all line terminators as LF", function() {
|
||||
[
|
||||
"`a\rb`",
|
||||
"`a\nb`",
|
||||
"`a\r\nb`",
|
||||
].forEach(function(code) {
|
||||
assert.strictEqual(uglify.parse(code).print_to_string(), "`a\\nb`;");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -86,7 +86,6 @@ function run_compress_tests() {
|
||||
log_start_file(file);
|
||||
function test_case(test) {
|
||||
log_test(test.name);
|
||||
U.base54.reset();
|
||||
var output_options = test.beautify || {};
|
||||
var expect;
|
||||
if (test.expect) {
|
||||
@@ -101,9 +100,6 @@ function run_compress_tests() {
|
||||
quote_style: 3,
|
||||
keep_quoted_props: true
|
||||
});
|
||||
if (test.mangle_props) {
|
||||
input = U.mangle_properties(input, test.mangle_props);
|
||||
}
|
||||
var options = U.defaults(test.options, {
|
||||
warnings: false
|
||||
});
|
||||
@@ -118,10 +114,16 @@ function run_compress_tests() {
|
||||
var cmp = new U.Compressor(options, true);
|
||||
var output = cmp.compress(input);
|
||||
output.figure_out_scope(test.mangle);
|
||||
if (test.mangle) {
|
||||
if (test.mangle || test.mangle_props) {
|
||||
U.base54.reset();
|
||||
output.compute_char_frequency(test.mangle);
|
||||
}
|
||||
if (test.mangle) {
|
||||
output.mangle_names(test.mangle);
|
||||
}
|
||||
if (test.mangle_props) {
|
||||
output = U.mangle_properties(output, test.mangle_props);
|
||||
}
|
||||
output = make_code(output, output_options);
|
||||
if (expect != output) {
|
||||
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
|
||||
|
||||
Reference in New Issue
Block a user