Compare commits

...

39 Commits

Author SHA1 Message Date
Alex Lam S.L
4e0a22e5c8 v3.0.13 2017-05-29 10:52:13 +08:00
Alex Lam S.L
1aa38051fb better fix for #512 & #2010 (#2019)
- remove duplicated functionalities
- fix similar issue with `else`
2017-05-29 10:51:41 +08:00
Alex Lam S.L
e62b879b48 display default values in --help options (#2018) 2017-05-28 22:57:20 +08:00
Alex Lam S.L
c6c9f4f5a8 implement --help options (#2017) 2017-05-28 18:21:44 +08:00
Alex Lam S.L
fec14379f6 improve CLI usability (#2016)
Report supported options upon invalid option syntax.

fixes #1883
2017-05-28 04:09:40 +08:00
Alex Lam S.L
79131cd647 extend node_version range on applicable tests (#2015) 2017-05-27 22:18:28 +08:00
Alex Lam S.L
c3f14a1481 v3.0.12 2017-05-27 18:08:09 +08:00
Alex Lam S.L
7b13159cda fix hoist_funs on block-scoped function under "use strict" (#2013)
Technically not part of ES5, but commonly used code exists in the wild.
2017-05-27 17:44:59 +08:00
Alex Lam S.L
95094b9c22 fix if_return on AST_Defun (#2010)
Previous fiix for #1052 perturbs declaration order of functions which leads to incorrect behaviour under "use strict".
2017-05-27 13:41:49 +08:00
kzc
1ff8e9dd38 clarify what --mangle-props does (#2012) 2017-05-27 13:17:30 +08:00
kzc
78309a293d better document mangle properties options (#2009) 2017-05-27 02:28:43 +08:00
kzc
695e182d59 fix and expand --mangle-props documentation (#2008)
fixes #2007
2017-05-27 01:25:51 +08:00
Alex Lam S.L
dc33facfcb fix dead_code on block-scoped function under "use strict" (#2006)
Technically not part of ES5, but commonly used code exists in the wild.
2017-05-26 16:08:51 +08:00
Alex Lam S.L
c70fb60384 clean up lib/scope.js (#2003)
fixes #2004
2017-05-26 03:58:35 +08:00
Alex Lam S.L
793d61499b report timing breakdown (#2000)
fix corner cases with `sourceMap`

fixes #1998
2017-05-25 07:15:55 +08:00
Alex Lam S.L
a277fe168d ensure new line after describe_ast() (#1999) 2017-05-25 02:32:36 +08:00
Alex Lam S.L
7d3b941e6e reinstate describe_ast() on CLI (#1996)
fixes #1995
2017-05-24 02:30:09 +08:00
Alex Lam S.L
e95052a423 v3.0.11 2017-05-23 22:26:59 +08:00
Alex Lam S.L
e667f0acb8 fix source map offset (#1993)
Account for whitespace insertions.

fixes #505
fixes #890
2017-05-23 20:25:48 +08:00
kzc
69ac794bc8 add another minify() options example (#1988) 2017-05-22 12:19:07 +08:00
Alex Lam S.L
efdb65913b improve usability of global_defs in minify() (#1987)
Use `@key` to `parse()` string value as `AST_Node`.

fixes #1986
2017-05-22 01:38:43 +08:00
kzc
a1dedeb3ce more refinement of minify() documentation (#1983) 2017-05-21 04:55:03 +08:00
Alex Lam S.L
d3c4a8e9e7 v3.0.10 2017-05-21 01:30:17 +08:00
kzc
7e164aba8f add "es5" to package.json keywords (#1980) 2017-05-20 22:09:50 +08:00
kzc
22aedef849 document minify() option toplevel (#1979) 2017-05-20 22:09:21 +08:00
Alex Lam S.L
58fae7dc07 enhance if_return to handle return void... (#1977)
fixes #512
2017-05-20 15:58:46 +08:00
kzc
5bf8d7e949 document 3.x minify() does not throw errors (#1975) 2017-05-20 10:49:35 +08:00
kzc
1df9d06f4a document minify warnings and add an error example (#1973) 2017-05-19 17:20:21 +08:00
Alex Lam S.L
3408fc9d32 v3.0.9 2017-05-19 09:35:26 +08:00
Alex Lam S.L
eae26756f1 introduce unsafe_regexp (#1970)
fixes #1964
2017-05-19 09:06:29 +08:00
Alex Lam S.L
43add9416b v3.0.8 2017-05-18 14:49:40 +08:00
Alex Lam S.L
efcf167e5e make expect_stdout node version specific (#1963)
... via semver string on `node_version` label.
2017-05-18 11:28:35 +08:00
Kara
6ed90913ca fix docs for side_effects flag to reflect current behavior (#1966) 2017-05-18 10:51:49 +08:00
Alex Lam S.L
569c21e952 improve RegExp handling (#1959)
- remove `options.output.unescape_regexps`
- preserve original pattern whenever possible

fixes #54
fixes #1929
2017-05-17 20:10:50 +08:00
Alex Lam S.L
87c3a2c0ce remove space_colon (#1960)
Always emit space after colon when `options.output.beautify` is enabled.
2017-05-17 14:07:34 +08:00
Rob Garrison
baef8bf050 update output options in readme (#1958) 2017-05-17 11:54:46 +08:00
alexlamsl
0813c5316f remove Travis CI badge 2017-05-17 10:32:59 +08:00
Alex Lam S.L
c88139492d v3.0.7 2017-05-16 19:59:40 +08:00
Alex Lam S.L
cb45886512 export TreeTransformer (#1950)
- link to existing documentation on `TreeWalker` & `TreeTransformer`
- fix Travis build failures

fixes #1949
2017-05-16 19:59:05 +08:00
32 changed files with 1048 additions and 316 deletions

View File

@@ -1,5 +1,4 @@
language: node_js
before_install: "npm install -g npm"
node_js:
- "0.10"
- "0.12"

300
README.md
View File

@@ -1,6 +1,5 @@
UglifyJS 3
==========
[![Build Status](https://travis-ci.org/mishoo/UglifyJS2.svg)](https://travis-ci.org/mishoo/UglifyJS2)
UglifyJS is a JavaScript parser, minifier, compressor and beautifier toolkit.
@@ -46,6 +45,7 @@ a double dash to prevent input files being used as option arguments:
```
-h, --help Print usage information.
`--help options` for details on available options.
-V, --version Print version number.
-p, --parse <options> Specify parser options:
`acorn` Use Acorn for parsing.
@@ -127,8 +127,8 @@ a double dash to prevent input files being used as option arguments:
the source map.
`url` If specified, path to the source map to append in
`//# sourceMappingURL`.
--stats Display operations run time on STDERR.
--toplevel Compress and/or mangle variables in toplevel scope.
--timings Display operations run time on STDERR.
--toplevel Compress and/or mangle variables in top level scope.
--verbose Print diagnostic messages.
--warn Print warning messages.
--wrap <name> Embed everything in a big function, making the
@@ -203,7 +203,7 @@ Example:
To enable the mangler you need to pass `--mangle` (`-m`). The following
(comma-separated) options are supported:
- `toplevel` — mangle names declared in the toplevel scope (disabled by
- `toplevel` — mangle names declared in the top level scope (disabled by
default).
- `eval` — mangle names visible in scopes where `eval` or `with` are used
@@ -219,24 +219,54 @@ to prevent the `require`, `exports` and `$` names from being changed.
### CLI mangling property names (`--mangle-props`)
**Note:** this will probably break your code. Mangling property names is a
separate step, different from variable name mangling. Pass
`--mangle-props`. It will mangle all properties that are seen in some
object literal, or that are assigned to. For example:
**Note:** THIS WILL PROBABLY BREAK YOUR CODE. Mangling property names
is a separate step, different from variable name mangling. Pass
`--mangle-props` to enable it. It will mangle all properties in the
input code with the exception of built in DOM properties and properties
in core javascript classes. For example:
```javascript
// example.js
var x = {
foo: 1
baz_: 0,
foo_: 1,
calc: function() {
return this.foo_ + this.baz_;
}
};
x.bar = 2;
x["baz"] = 3;
x[condition ? "moo" : "boo"] = 4;
console.log(x.something());
x.bar_ = 2;
x["baz_"] = 3;
console.log(x.calc());
```
Mangle all properties (except for javascript `builtins`):
```bash
$ uglifyjs example.js -c -m --mangle-props
```
```javascript
var x={o:0,_:1,l:function(){return this._+this.o}};x.t=2,x.o=3,console.log(x.l());
```
Mangle all properties except for `reserved` properties:
```bash
$ uglifyjs example.js -c -m --mangle-props reserved=[foo_,bar_]
```
```javascript
var x={o:0,foo_:1,_:function(){return this.foo_+this.o}};x.bar_=2,x.o=3,console.log(x._());
```
Mangle all properties matching a `regex`:
```bash
$ uglifyjs example.js -c -m --mangle-props regex=/_$/
```
```javascript
var x={o:0,_:1,calc:function(){return this._+this.o}};x.l=2,x.o=3,console.log(x.calc());
```
In the above code, `foo`, `bar`, `baz`, `moo` and `boo` will be replaced
with single characters, while `something()` will be left as is.
Combining mangle properties options:
```bash
$ uglifyjs example.js -c -m --mangle-props regex=/_$/,reserved=[bar_]
```
```javascript
var x={o:0,_:1,calc:function(){return this._+this.o}};x.bar_=2,x.o=3,console.log(x.calc());
```
In order for this to be of any use, we avoid mangling standard JS names by
default (`--mangle-props builtins` to override).
@@ -245,7 +275,7 @@ A default exclusion file is provided in `tools/domprops.json` which should
cover most standard JS and DOM properties defined in various browsers. Pass
`--mangle-props domprops` to disable this feature.
You can also use a regular expression to define which property names should be
A regular expression can be used to define which property names should be
mangled. For example, `--mangle-props regex=/^_/` will only mangle property
names that start with an underscore.
@@ -273,9 +303,20 @@ Using quoted property name (`o["foo"]`) reserves the property name (`foo`)
so that it is not mangled throughout the entire script even when used in an
unquoted style (`o.foo`). Example:
```javascript
// stuff.js
var o = {
"foo": 1,
bar: 3
};
o.foo += o.bar;
console.log(o.foo);
```
```bash
$ echo 'var o={"foo":1, bar:3}; o.foo += o.bar; console.log(o.foo);' | uglifyjs --mangle-props keep_quoted -mc
var o={foo:1,a:3};o.foo+=o.a,console.log(o.foo);
$ uglifyjs stuff.js --mangle-props keep_quoted -c -m
```
```javascript
var o={foo:1,o:3};o.foo+=o.o,console.log(o.foo);
```
### Debugging property name mangling
@@ -286,6 +327,13 @@ would mangle to `o._$foo$_` with this option. This allows property mangling
of a large codebase while still being able to debug the code and identify
where mangling is breaking things.
```bash
$ uglifyjs stuff.js --mangle-props debug -c -m
```
```javascript
var o={_$foo$_:1,_$bar$_:3};o._$foo$_+=o._$bar$_,console.log(o._$foo$_);
```
You can also pass a custom suffix using `--mangle-props debug=XYZ`. This would then
mangle `o.foo` to `o._$foo$XYZ_`. You can change this each time you compile a
script to identify how a property got mangled. One technique is to pass a
@@ -302,27 +350,94 @@ like this:
var UglifyJS = require("uglify-js");
```
There is a single high level minification function, `minify(files, options)`, which will
performs all the steps in a configurable manner.
Example:
There is a single high level function, **`minify(code, options)`**,
which will perform all minification [phases](#minify-options) in a configurable
manner. By default `minify()` will enable the options [`compress`](#compress-options)
and [`mangle`](#mangle-options). Example:
```javascript
var result = UglifyJS.minify("var b = function() {};");
console.log(result.code); // minified output
console.log(result.error); // runtime error
var code = "function add(first, second) { return first + second; }";
var result = UglifyJS.minify(code);
console.log(result.error); // runtime error, or `undefined` if no error
console.log(result.code); // minified output: function add(n,d){return n+d}
```
You can also compress multiple files:
You can `minify` more than one JavaScript file at a time by using an object
for the first argument where the keys are file names and the values are source
code:
```javascript
var result = UglifyJS.minify({
"file1.js": "var a = function() {};",
"file2.js": "var b = function() {};"
});
var code = {
"file1.js": "function add(first, second) { return first + second; }",
"file2.js": "console.log(add(1 + 2, 3 + 4));"
};
var result = UglifyJS.minify(code);
console.log(result.code);
// function add(d,n){return d+n}console.log(add(3,7));
```
The `toplevel` option:
```javascript
var code = {
"file1.js": "function add(first, second) { return first + second; }",
"file2.js": "console.log(add(1 + 2, 3 + 4));"
};
var options = { toplevel: true };
var result = UglifyJS.minify(code, options);
console.log(result.code);
// console.log(function(n,o){return n+o}(3,7));
```
An example of a combination of `minify()` options:
```javascript
var code = {
"file1.js": "function add(first, second) { return first + second; }",
"file2.js": "console.log(add(1 + 2, 3 + 4));"
};
var options = {
toplevel: true,
compress: {
global_defs: {
"@console.log": "alert"
},
passes: 2
},
output: {
beautify: false,
preamble: "/* uglified */"
}
};
var result = UglifyJS.minify(code, options);
console.log(result.code);
// /* uglified */
// alert(10);"
```
To produce warnings:
```javascript
var code = "function f(){ var u; return 2 + 3; }";
var options = { warnings: true };
var result = UglifyJS.minify(code, options);
console.log(result.error); // runtime error, `undefined` in this case
console.log(result.warnings); // [ 'Dropping unused variable u [0:1,18]' ]
console.log(result.code); // function f(){return 5}
```
An error example:
```javascript
var result = UglifyJS.minify({"foo.js" : "if (0) else console.log(1);"});
console.log(JSON.stringify(result.error));
// {"message":"Unexpected token: keyword (else)","filename":"foo.js","line":1,"col":7,"pos":7}
```
Note: unlike `uglify-js@2.x`, the `3.x` API does not throw errors. To
achieve a similar effect one could do the following:
```javascript
var result = UglifyJS.minify(code, options);
if (result.error) throw result.error;
```
## Minify options
- `warnings` (default `false`) — pass `true` to display compressor warnings.
- `warnings` (default `false`) — pass `true` to return compressor warnings
in `result.warnings`. Use the value `"verbose"` for more detailed warnings.
- `parse` (default `{}`) — pass an object if you wish to specify some
additional [parse options](#parse-options).
@@ -348,7 +463,7 @@ console.log(result.code);
- `ie8` (default `false`) - set to `true` to support IE8.
## Minify option structure
## Minify options structure
```javascript
{
@@ -463,6 +578,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `unsafe_proto` (default: false) -- optimize expressions like
`Array.prototype.slice.call(a)` into `[].slice.call(a)`
- `unsafe_regexp` (default: false) -- enable substitutions of variables with
`RegExp` values the same way as if they are constants.
- `conditionals` -- apply optimizations for `if`-s and conditional
expressions
@@ -482,7 +600,7 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
assignments do not count as references unless set to `"keep_assign"`)
- `toplevel` -- drop unreferenced functions (`"funcs"`) and/or variables (`"vars"`)
in the toplevel scope (`false` by default, `true` to drop both unreferenced
in the top level scope (`false` by default, `true` to drop both unreferenced
functions and variables)
- `top_retain` -- prevent specific toplevel functions and variables from `unused`
@@ -500,8 +618,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
and `x = something(), x` into `x = something()`
- `collapse_vars` -- Collapse single-use `var` and `const` definitions
when possible.
- `collapse_vars` -- Collapse single-use non-constant variables - side
effects permitting.
- `reduce_vars` -- Improve optimization on variables assigned with and
used as constant values.
@@ -553,16 +671,16 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome.
- `side_effects` -- default `false`. Pass `true` to potentially drop functions
marked as "pure". A function call is marked as "pure" if a comment annotation
`/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For example:
`/*@__PURE__*/foo()`;
- `side_effects` -- default `true`. Pass `false` to disable potentially dropping
functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();`
## Mangle options
- `reserved` - pass an array of identifiers that should be excluded from mangling
- `toplevel` — mangle names declared in the toplevel scope (disabled by
- `toplevel` — mangle names declared in the top level scope (disabled by
default).
- `eval` — mangle names visible in scopes where eval or with are used
@@ -596,10 +714,15 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
### Mangle properties options
- `regex` — Pass a RegExp to only mangle certain names
- `keep_quoted` — Only mangle unquoted property names
- `debug` — Mangle names with the original name still present. Defaults to `false`.
Pass an empty string to enable, or a non-empty string to set the suffix.
- `reserved` (default: `[]`) -- Do not mangle property names listed in the
`reserved` array.
- `regex` (default: `null`) -— Pass a RegExp literal to only mangle property
names matching the regular expression.
- `keep_quoted` (default: `false`) -— Only mangle unquoted property names.
- `debug` (default: `false`) -— Mangle names with the original name still present.
Pass an empty string `""` to enable, or a non-empty string to set the debug suffix.
- `builtins` (default: `false`) -- Use `true` to allow the mangling of builtin
DOM properties. Not recommended to override this setting.
## Output options
@@ -607,36 +730,33 @@ The code generator tries to output shortest code possible by default. In
case you want beautified output, pass `--beautify` (`-b`). Optionally you
can pass additional arguments that control the code output:
- `ascii_only` (default `false`) -- escape Unicode characters in strings and
regexps (affects directives with non-ascii characters becoming invalid)
- `beautify` (default `true`) -- whether to actually beautify the output.
Passing `-b` will set this to true, but you might need to pass `-b` even
when you want to generate minified code, in order to specify additional
arguments, so you can use `-b beautify=false` to override it.
- `indent_level` (default 4)
- `indent_start` (default 0) -- prefix all lines by that many spaces
- `quote_keys` (default `false`) -- pass `true` to quote all keys in literal
objects
- `space_colon` (default `true`) -- insert a space after the colon signs
- `ascii_only` (default `false`) -- escape Unicode characters in strings and
regexps (affects directives with non-ascii characters becoming invalid)
- `inline_script` (default `false`) -- escape the slash in occurrences of
`</script` in strings
- `width` (default 80) -- only takes effect when beautification is on, this
specifies an (orientative) line width that the beautifier will try to
obey. It refers to the width of the line text (excluding indentation).
It doesn't work very well currently, but it does make the code generated
by UglifyJS more readable.
- `max_line_len` (default 32000) -- maximum line length (for uglified code)
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
`do`, `while` or `with` statements, even if their body is a single
statement.
- `semicolons` (default `true`) -- separate statements with semicolons. If
you pass `false` then whenever possible we will use a newline instead of a
semicolon, leading to more readable output of uglified code (size before
gzip could be smaller; size after gzip insignificantly larger).
- `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.
- `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
`</script` in strings
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
quotes from property names in object literals.
- `max_line_len` (default `false`) -- maximum line length (for uglified code)
- `preamble` (default `null`) -- when passed it must be a string and
it will be prepended to the output literally. The source map will
adjust for this text. Can be used to insert a comment containing
licensing information, for example.
- `preserve_line` (default `false`) -- pass `true` to preserve lines, but it
only works if `beautify` is set to `false`.
- `quote_keys` (default `false`) -- pass `true` to quote all keys in literal
objects
- `quote_style` (default `0`) -- preferred quote style for strings (affects
quoted property names and directives as well):
- `0` -- prefers double quotes, switches to single quotes when there are
@@ -644,9 +764,19 @@ can pass additional arguments that control the code output:
- `1` -- always use single quotes
- `2` -- always use double quotes
- `3` -- always use the original quotes
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
quotes from property names in object literals.
- `semicolons` (default `true`) -- separate statements with semicolons. If
you pass `false` then whenever possible we will use a newline instead of a
semicolon, leading to more readable output of uglified code (size before
gzip could be smaller; size after gzip insignificantly larger).
- `shebang` (default `true`) -- preserve shebang `#!` in preamble (bash scripts)
- `width` (default 80) -- only takes effect when beautification is on, this
specifies an (orientative) line width that the beautifier will try to
obey. It refers to the width of the line text (excluding indentation).
It doesn't work very well currently, but it does make the code generated
by UglifyJS more readable.
- `wrap_iife` (default `false`) -- pass `true` to wrap immediately invoked
function expressions. See
[#640](https://github.com/mishoo/UglifyJS2/issues/640) for more details.
# Miscellaneous
@@ -716,8 +846,8 @@ Another way of doing that is to declare your globals as constants in a
separate file and include it into the build. For example you can have a
`build/defines.js` file with the following:
```javascript
const DEBUG = false;
const PRODUCTION = true;
var DEBUG = false;
var PRODUCTION = true;
// etc.
```
@@ -737,7 +867,7 @@ You can also use conditional compilation via the programmatic API. With the diff
property name is `global_defs` and is a compressor property:
```javascript
var result = uglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
var result = UglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
compress: {
dead_code: true,
global_defs: {
@@ -747,6 +877,32 @@ var result = uglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
});
```
To replace an identifier with an arbitrary non-constant expression it is
necessary to prefix the `global_defs` key with `"@"` to instruct UglifyJS
to parse the value as an expression:
```javascript
UglifyJS.minify("alert('hello');", {
compress: {
global_defs: {
"@alert": "console.log"
}
}
}).code;
// returns: 'console.log("hello");'
```
Otherwise it would be replaced as string literal:
```javascript
UglifyJS.minify("alert('hello');", {
compress: {
global_defs: {
"alert": "console.log"
}
}
}).code;
// returns: '"console.log"("hello");'
```
### Using native Uglify AST with `minify()`
```javascript
// example: parse only, produce native Uglify AST
@@ -780,6 +936,12 @@ var result = UglifyJS.minify(ast, {
// result.code contains the minified code in string form.
```
### Working with Uglify AST
Transversal and transformation of the native AST can be performed through
[`TreeWalker`](http://lisperator.net/uglifyjs/walk) and
[`TreeTransformer`](http://lisperator.net/uglifyjs/transform) respectively.
### ESTree / SpiderMonkey AST
UglifyJS has its own abstract syntax tree format; for

View File

@@ -21,9 +21,27 @@ var options = {
compress: false,
mangle: false
};
program.version(info.name + ' ' + info.version);
program.version(info.name + " " + info.version);
program.parseArgv = program.parse;
program.parse = undefined;
if (process.argv.indexOf("ast") >= 0) program.helpInformation = UglifyJS.describe_ast;
else if (process.argv.indexOf("options") >= 0) program.helpInformation = function() {
var text = [];
var options = UglifyJS.default_options();
for (var option in options) {
text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:");
var defs = options[option];
var padding = "";
Object.keys(defs).map(function(name) {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
return [ name, JSON.stringify(defs[name]) ];
}).forEach(function(tokens) {
text.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
});
text.push("");
}
return text.join("\n");
};
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true));
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true));
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true));
@@ -38,7 +56,7 @@ program.option("--keep-fnames", "Do not mangle/drop function names. Useful for c
program.option("--name-cache <file>", "File to hold mangled name mappings.");
program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)");
program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map());
program.option("--stats", "Display operations run time on STDERR.")
program.option("--timings", "Display operations run time on STDERR.")
program.option("--toplevel", "Compress and/or mangle variables in toplevel scope.");
program.option("--verbose", "Print diagnostic messages.");
program.option("--warn", "Print warning messages.");
@@ -114,10 +132,10 @@ if (program.output == "ast") {
};
}
if (program.parse) {
if (program.parse.acorn || program.parse.spidermonkey) {
if (program.sourceMap) fatal("ERROR: inline source map only works with built-in parser");
} else {
if (!program.parse.acorn && !program.parse.spidermonkey) {
options.parse = program.parse;
} else if (program.sourceMap && program.sourceMap.content == "inline") {
fatal("ERROR: inline source map only works with built-in parser");
}
}
var convert_path = function(name) {
@@ -171,7 +189,7 @@ function run() {
UglifyJS.AST_Node.warn_function = function(msg) {
console.error("WARN:", msg);
};
if (program.stats) program.stats = Date.now();
if (program.timings) options.timings = true;
try {
if (program.parse) {
if (program.parse.acorn) {
@@ -258,7 +276,9 @@ function run() {
return value instanceof UglifyJS.Dictionary ? value.toObject() : value;
}));
}
if (program.stats) console.error("Elapsed:", Date.now() - program.stats);
if (result.timings) for (var phase in result.timings) {
console.error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
}
}
function fatal(message) {
@@ -348,7 +368,7 @@ function parse_js(flag, constants) {
}
}));
} catch(ex) {
fatal("Error parsing arguments for '" + flag + "': " + value);
options[value] = null;
}
return options;
}

View File

@@ -83,9 +83,21 @@ function Compressor(options, false_by_default) {
unsafe_comps : false,
unsafe_math : false,
unsafe_proto : false,
unsafe_regexp : false,
unused : !false_by_default,
warnings : false,
}, true);
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]
}));
}
}
var pure_funcs = this.options["pure_funcs"];
if (typeof pure_funcs == "function") {
this.pure_funcs = pure_funcs;
@@ -912,13 +924,52 @@ merge(Compressor.prototype, {
// step. nevertheless, it's good to check.
continue loop;
case stat instanceof AST_If:
var ab = aborts(stat.body);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
var funs = extract_functions_from_statement_array(ret);
var body = as_statement_array_with_return(stat.body, ab);
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(ret)
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
ret = [ stat.transform(compressor) ].concat(funs);
continue loop;
}
var ab = aborts(stat.alternative);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
var funs = extract_functions_from_statement_array(ret);
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(ret)
});
var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body
});
ret = [ stat.transform(compressor) ].concat(funs);
continue loop;
}
if (stat.body instanceof AST_Return) {
var value = stat.body.value;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if (((in_lambda && ret.length == 0)
|| (ret[0] instanceof AST_Return && !ret[0].value))
&& !stat.body.value && !stat.alternative) {
if ((in_lambda && ret.length == 0 || ret[0] instanceof AST_Return && !ret[0].value)
&& !value && !stat.alternative) {
CHANGED = true;
var cond = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
@@ -928,7 +979,7 @@ merge(Compressor.prototype, {
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (ret[0] instanceof AST_Return && stat.body.value && ret[0].value && !stat.alternative) {
if (ret[0] instanceof AST_Return && value && ret[0].value && !stat.alternative) {
CHANGED = true;
stat = stat.clone();
stat.alternative = ret[0];
@@ -938,7 +989,7 @@ merge(Compressor.prototype, {
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (multiple_if_returns && (ret.length == 0 || ret[0] instanceof AST_Return)
&& stat.body.value && !stat.alternative && in_lambda) {
&& value && !stat.alternative && in_lambda) {
CHANGED = true;
stat = stat.clone();
stat.alternative = ret[0] || make_node(AST_Return, stat, {
@@ -947,22 +998,6 @@ merge(Compressor.prototype, {
ret[0] = stat.transform(compressor);
continue loop;
}
//---
// if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
if (!stat.body.value && in_lambda) {
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var body = as_statement_array(stat.alternative).concat(ret);
var funs = extract_functions_from_statement_array(body);
stat.body = make_node(AST_BlockStatement, stat, {
body: body
});
stat.alternative = null;
ret = funs.concat([ stat.transform(compressor) ]);
continue loop;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
@@ -982,48 +1017,6 @@ merge(Compressor.prototype, {
}
}
var ab = aborts(stat.body);
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|| (ab instanceof AST_Continue && self === loop_body(lct))
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
var body = as_statement_array(stat.body).slice(0, -1);
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(ret)
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
ret = [ stat.transform(compressor) ];
continue loop;
}
var ab = aborts(stat.alternative);
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|| (ab instanceof AST_Continue && self === loop_body(lct))
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(ret)
});
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: as_statement_array(stat.alternative).slice(0, -1)
});
ret = [ stat.transform(compressor) ];
continue loop;
}
ret.unshift(stat);
break;
default:
@@ -1043,6 +1036,28 @@ merge(Compressor.prototype, {
}
return false;
}
function is_return_void(value) {
return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
}
function can_merge_flow(ab) {
if (!ab) return false;
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
|| ab instanceof AST_Continue && self === loop_body(lct)
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
}
function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1);
if (ab.value) {
body.push(make_node(AST_SimpleStatement, ab.value, {
body: ab.value.expression
}));
}
return body;
}
};
function eliminate_dead_code(statements, compressor) {
@@ -1209,7 +1224,7 @@ merge(Compressor.prototype, {
target.push(node);
return true;
}
if (node instanceof AST_Defun) {
if (node instanceof AST_Defun && (node === stat || !compressor.has_directive("use strict"))) {
target.push(node);
return true;
}
@@ -2238,11 +2253,12 @@ merge(Compressor.prototype, {
dirs.push(node);
return make_node(AST_EmptyStatement, node);
}
if (node instanceof AST_Defun && hoist_funs) {
if (hoist_funs && node instanceof AST_Defun
&& (tt.parent() === self || !compressor.has_directive("use strict"))) {
hoisted.push(node);
return make_node(AST_EmptyStatement, node);
}
if (node instanceof AST_Var && hoist_vars) {
if (hoist_vars && node instanceof AST_Var) {
node.definitions.forEach(function(def){
vars.set(def.name.name, def);
++vars_found;
@@ -3765,7 +3781,7 @@ merge(Compressor.prototype, {
if (fixed) {
if (d.should_replace === undefined) {
var init = fixed.evaluate(compressor);
if (init !== fixed) {
if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) {
init = make_node_from_constant(init, fixed);
var value_length = init.optimize(compressor).print_to_string().length;
var fn;

View File

@@ -30,9 +30,6 @@ function set_shorthand(name, options, keys) {
function minify(files, options) {
var warn_function = AST_Node.warn_function;
try {
if (typeof files == "string") {
files = [ files ];
}
options = defaults(options, {
compress: {},
ie8: false,
@@ -41,10 +38,14 @@ function minify(files, options) {
output: {},
parse: {},
sourceMap: false,
timings: false,
toplevel: false,
warnings: false,
wrap: false,
}, true);
var timings = options.timings && {
start: Date.now()
};
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]);
@@ -75,10 +76,14 @@ function minify(files, options) {
warnings.push(warning);
};
}
if (timings) timings.parse = Date.now();
var toplevel;
if (files instanceof AST_Toplevel) {
toplevel = files;
} else {
if (typeof files == "string") {
files = [ files ];
}
options.parse = options.parse || {};
options.parse.toplevel = null;
for (var name in files) {
@@ -95,19 +100,23 @@ function minify(files, options) {
if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap);
}
if (options.compress) {
toplevel.figure_out_scope(options.mangle);
toplevel = new Compressor(options.compress).compress(toplevel);
}
if (timings) timings.scope1 = Date.now();
if (options.compress) toplevel.figure_out_scope(options.mangle);
if (timings) timings.compress = Date.now();
if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
if (timings) timings.scope2 = Date.now();
if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now();
if (options.mangle) {
toplevel.figure_out_scope(options.mangle);
base54.reset();
toplevel.compute_char_frequency(options.mangle);
toplevel.mangle_names(options.mangle);
if (options.mangle.properties) {
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
}
if (timings) timings.properties = Date.now();
if (options.mangle && options.mangle.properties) {
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
if (timings) timings.output = Date.now();
var result = {};
if (options.output.ast) {
result.ast = toplevel;
@@ -123,7 +132,9 @@ function minify(files, options) {
root: options.sourceMap.root
});
if (options.sourceMap.includeSources) {
for (var name in files) {
if (files instanceof AST_Toplevel) {
throw new Error("original source content unavailable");
} else for (var name in files) {
options.output.source_map.get().setSourceContent(name, files[name]);
}
}
@@ -142,6 +153,18 @@ function minify(files, options) {
}
}
}
if (timings) {
timings.end = Date.now();
result.timings = {
parse: 1e-3 * (timings.scope1 - timings.parse),
scope: 1e-3 * (timings.compress - timings.scope1 + timings.mangle - timings.scope2),
compress: 1e-3 * (timings.scope2 - timings.compress),
mangle: 1e-3 * (timings.properties - timings.mangle),
properties: 1e-3 * (timings.output - timings.properties),
output: 1e-3 * (timings.end - timings.output),
total: 1e-3 * (timings.end - timings.start)
}
}
if (warnings.length) {
result.warnings = warnings;
}

View File

@@ -70,8 +70,6 @@ function OutputStream(options) {
semicolons : true,
shebang : true,
source_map : null,
space_colon : true,
unescape_regexps : false,
width : 80,
wrap_iife : false,
}, true);
@@ -191,12 +189,43 @@ function OutputStream(options) {
var might_need_semicolon = false;
var might_add_newline = 0;
var last = "";
var mapping_token, mapping_name, mappings = options.source_map && [];
var do_add_mapping = mappings ? function() {
mappings.forEach(function(mapping) {
try {
options.source_map.add(
mapping.token.file,
mapping.line, mapping.col,
mapping.token.line, mapping.token.col,
!mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name
);
} catch(ex) {
AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
file: mapping.token.file,
line: mapping.token.line,
col: mapping.token.col,
cline: mapping.line,
ccol: mapping.col,
name: mapping.name || ""
})
}
});
mappings = [];
} : noop;
var ensure_line_len = options.max_line_len ? function() {
if (current_col > options.max_line_len) {
if (might_add_newline) {
var left = OUTPUT.slice(0, might_add_newline);
var right = OUTPUT.slice(might_add_newline);
if (mappings) {
var delta = right.length - current_col;
mappings.forEach(function(mapping) {
mapping.line++;
mapping.col += delta;
});
}
OUTPUT = left + "\n" + right;
current_line++;
current_pos++;
@@ -206,7 +235,10 @@ function OutputStream(options) {
AST_Node.warn("Output exceeds {max_line_len} characters", options);
}
}
might_add_newline = 0;
if (might_add_newline) {
might_add_newline = 0;
do_add_mapping();
}
} : noop;
var requireSemicolonChars = makePredicate("( [ + * / - , .");
@@ -266,6 +298,18 @@ function OutputStream(options) {
}
might_need_space = false;
}
if (mapping_token) {
mappings.push({
token: mapping_token,
name: mapping_name,
line: current_line,
col: current_col
});
mapping_token = false;
if (!might_add_newline) do_add_mapping();
}
OUTPUT += str;
current_pos += str.length;
var a = str.split(/\r?\n/), n = a.length - 1;
@@ -357,27 +401,12 @@ function OutputStream(options) {
function colon() {
print(":");
if (options.space_colon) space();
space();
};
var add_mapping = options.source_map ? function(token, name) {
try {
if (token) options.source_map.add(
token.file || "?",
current_line, current_col,
token.line, token.col,
(!name && token.type == "name") ? token.value : name
);
} catch(ex) {
AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
file: token.file,
line: token.line,
col: token.col,
cline: current_line,
ccol: current_col,
name: name || ""
})
}
var add_mapping = mappings ? function(token, name) {
mapping_token = token;
mapping_name = name;
} : noop;
function get() {
@@ -1261,45 +1290,14 @@ function OutputStream(options) {
}
});
function regexp_safe_literal(code) {
return [
0x5c , // \
0x2f , // /
0x2e , // .
0x2b , // +
0x2a , // *
0x3f , // ?
0x28 , // (
0x29 , // )
0x5b , // [
0x5d , // ]
0x7b , // {
0x7d , // }
0x24 , // $
0x5e , // ^
0x3a , // :
0x7c , // |
0x21 , // !
0x0a , // \n
0x0d , // \r
0x00 , // \0
0xfeff , // Unicode BOM
0x2028 , // unicode "line separator"
0x2029 , // unicode "paragraph separator"
].indexOf(code) < 0;
};
DEFPRINT(AST_RegExp, function(self, output){
var str = self.getValue().toString();
var regexp = self.getValue();
var str = regexp.toString();
if (regexp.raw_source) {
str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/"));
}
if (output.option("ascii_only")) {
str = output.to_ascii(str);
} else if (output.option("unescape_regexps")) {
str = str.split("\\\\").map(function(str){
return str.replace(/\\u[0-9a-fA-F]{4}|\\x[0-9a-fA-F]{2}/g, function(s){
var code = parseInt(s.substr(2), 16);
return regexp_safe_literal(code) ? String.fromCharCode(code) : s;
});
}).join("\\\\");
}
output.print(str);
var p = output.parent();

View File

@@ -477,31 +477,33 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return name;
};
var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
var read_regexp = with_eof_error("Unterminated regular expression", function(source) {
var prev_backslash = false, ch, in_class = false;
while ((ch = next(true))) if (NEWLINE_CHARS(ch)) {
parse_error("Unexpected line terminator");
} else if (prev_backslash) {
regexp += "\\" + ch;
source += "\\" + ch;
prev_backslash = false;
} else if (ch == "[") {
in_class = true;
regexp += ch;
source += ch;
} else if (ch == "]" && in_class) {
in_class = false;
regexp += ch;
source += ch;
} else if (ch == "/" && !in_class) {
break;
} else if (ch == "\\") {
prev_backslash = true;
} else {
regexp += ch;
source += ch;
}
var mods = read_name();
try {
return token("regexp", new RegExp(regexp, mods));
var regexp = new RegExp(source, mods);
regexp.raw_source = source;
return token("regexp", regexp);
} catch(e) {
parse_error(e.message);
parse_error(e.message);
}
});
@@ -698,7 +700,7 @@ function parse($TEXT, options) {
shebang : true,
strict : false,
toplevel : null,
});
}, true);
var S = {
input : (typeof $TEXT == "string"

View File

@@ -183,16 +183,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
self.walk(tw);
// pass 2: find back references and eval
var func = null;
var globals = self.globals = new Dictionary();
self.globals = new Dictionary();
var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Lambda) {
var prev_func = func;
func = node;
descend();
func = prev_func;
return true;
}
if (node instanceof AST_LoopControl && node.label) {
node.label.thedef.references.push(node);
return true;
@@ -361,9 +353,7 @@ AST_Symbol.DEFMETHOD("unmangleable", function(options){
});
// labels are always mangleable
AST_Label.DEFMETHOD("unmangleable", function(){
return false;
});
AST_Label.DEFMETHOD("unmangleable", return_false);
AST_Symbol.DEFMETHOD("unreferenced", function(){
return this.definition().references.length == 0
@@ -374,13 +364,9 @@ AST_Symbol.DEFMETHOD("undeclared", function(){
return this.definition().undeclared;
});
AST_LabelRef.DEFMETHOD("undeclared", function(){
return false;
});
AST_LabelRef.DEFMETHOD("undeclared", return_false);
AST_Label.DEFMETHOD("undeclared", function(){
return false;
});
AST_Label.DEFMETHOD("undeclared", return_false);
AST_Symbol.DEFMETHOD("definition", function(){
return this.thedef;

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.0.6",
"version": "3.0.13",
"engines": {
"node": ">=0.8.0"
},
@@ -34,10 +34,11 @@
},
"devDependencies": {
"acorn": "~5.0.3",
"mocha": "~2.3.4"
"mocha": "~2.3.4",
"semver": "~5.3.0"
},
"scripts": {
"test": "node test/run-tests.js"
},
"keywords": ["uglify", "uglify-js", "minify", "minifier"]
"keywords": ["uglify", "uglify-js", "minify", "minifier", "es5"]
}

View File

@@ -9,7 +9,7 @@ var args = process.argv.slice(2);
if (!args.length) {
args.push("-mc");
}
args.push("--stats");
args.push("--timings");
var urls = [
"https://code.jquery.com/jquery-3.2.1.js",
"https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js",
@@ -29,12 +29,7 @@ function done() {
var info = results[url];
console.log();
console.log(url);
var elapsed = 0;
console.log(info.log.replace(/Elapsed: ([0-9]+)\s*/g, function(match, time) {
elapsed += 1e-3 * parseInt(time);
return "";
}));
console.log("Run-time:", elapsed.toFixed(3), "s");
console.log(info.log);
console.log("Original:", info.input, "bytes");
console.log("Uglified:", info.output, "bytes");
console.log("SHA1 sum:", info.sha1);

View File

@@ -31,7 +31,7 @@ dead_code_2_should_warn: {
function f() {
g();
x = 10;
throw "foo";
throw new Error("foo");
// completely discarding the `if` would introduce some
// bugs. UglifyJS v1 doesn't deal with this issue; in v2
// we copy any declarations to the upper scope.
@@ -46,16 +46,60 @@ dead_code_2_should_warn: {
})();
}
}
f();
}
expect: {
function f() {
g();
x = 10;
throw "foo";
throw new Error("foo");
var x;
function g(){};
}
f();
}
expect_stdout: true
node_version: "<=4"
}
dead_code_2_should_warn_strict: {
options = {
dead_code: true
};
input: {
"use strict";
function f() {
g();
x = 10;
throw new Error("foo");
// completely discarding the `if` would introduce some
// bugs. UglifyJS v1 doesn't deal with this issue; in v2
// we copy any declarations to the upper scope.
if (x) {
y();
var x;
function g(){};
// but nested declarations should not be kept.
(function(){
var q;
function y(){};
})();
}
}
f();
}
expect: {
"use strict";
function f() {
g();
x = 10;
throw new Error("foo");
var x;
}
f();
}
expect_stdout: true
node_version: ">=4"
}
dead_code_constant_boolean_should_warn_more: {
@@ -78,6 +122,7 @@ dead_code_constant_boolean_should_warn_more: {
foo();
var moo;
}
bar();
}
expect: {
var foo;
@@ -86,8 +131,46 @@ dead_code_constant_boolean_should_warn_more: {
// as for the for, it should keep:
var x = 10, y;
var moo;
bar();
}
expect_stdout: true
node_version: "<=4"
}
dead_code_constant_boolean_should_warn_more_strict: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
side_effects : true,
};
input: {
"use strict";
while (!((foo && bar) || (x + "0"))) {
console.log("unreachable");
var foo;
function bar() {}
}
for (var x = 10, y; x && (y || x) && (!typeof x); ++x) {
asdf();
foo();
var moo;
}
bar();
}
expect: {
"use strict";
var foo;
// nothing for the while
// as for the for, it should keep:
var x = 10, y;
var moo;
bar();
}
expect_stdout: true
node_version: ">=4"
}
try_catch_finally: {

View File

@@ -990,3 +990,50 @@ Infinity_NaN_undefined_LHS: {
"}",
]
}
issue_1964_1: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_regexp: false,
unused: true,
}
input: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect_stdout: "b"
}
issue_1964_2: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_regexp: true,
unused: true,
}
input: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
return "a b c".split(/\s/)[1];
}
console.log(f());
}
expect_stdout: "b"
}

View File

@@ -167,3 +167,81 @@ function_returning_constant_literal: {
}
expect_stdout: "Hello there"
}
hoist_funs: {
options = {
hoist_funs: true,
}
input: {
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
function g() {}
console.log(6, typeof f, typeof g);
}
expect: {
function f() {}
function g() {}
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
console.log(5, typeof f, typeof g);
}
console.log(6, typeof f, typeof g);
}
expect_stdout: [
"1 'function' 'function'",
"2 'function' 'function'",
"4 'function' 'function'",
"5 'function' 'function'",
"6 'function' 'function'",
]
node_version: "<=4"
}
hoist_funs_strict: {
options = {
hoist_funs: true,
}
input: {
"use strict";
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
function g() {}
console.log(6, typeof f, typeof g);
}
expect: {
"use strict";
function g() {}
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
console.log(6, typeof f, typeof g);
}
expect_stdout: [
"1 'undefined' 'function'",
"2 'undefined' 'function'",
"4 'function' 'function'",
"5 'function' 'function'",
"6 'undefined' 'function'",
]
node_version: ">=4"
}

View File

@@ -160,3 +160,17 @@ issue_1801: {
console.log(!0);
}
}
issue_1986: {
options = {
global_defs: {
"@alert": "console.log",
},
}
input: {
alert(42);
}
expect: {
console.log(42);
}
}

View File

@@ -302,3 +302,27 @@ issue_1437_conditionals: {
}
}
}
issue_512: {
options = {
conditionals: true,
if_return: true,
sequences: true,
side_effects: true,
}
input: {
function a() {
if (b()) {
c();
return;
}
throw e;
}
}
expect: {
function a() {
if (!b()) throw e;
c();
}
}
}

View File

@@ -116,3 +116,137 @@ non_hoisted_function_after_return_2b: {
"WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]",
]
}
non_hoisted_function_after_return_strict: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar();
not_called1();
} else {
return baz();
not_called2();
}
function bar() { return 7; }
return not_reached;
function UnusedFunction() {}
function baz() { return 8; }
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return x ? bar() : baz();
function bar() { return 7 }
function baz() { return 8 }
}
console.log(foo(0), foo(1));
}
expect_stdout: "8 7"
expect_warnings: [
'WARN: Dropping unreachable code [test/compress/issue-1034.js:131,16]',
"WARN: Dropping unreachable code [test/compress/issue-1034.js:134,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:137,12]",
"WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:138,21]"
]
}
non_hoisted_function_after_return_2a_strict: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false, passes: 2, warnings: "verbose"
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar(1);
var a = not_called(1);
} else {
return bar(2);
var b = not_called(2);
}
var c = bar(3);
function bar(x) { return 7 - x; }
function nope() {}
return b || c;
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return bar(x ? 1 : 2);
function bar(x) {
return 7 - x;
}
}
console.log(foo(0), foo(1));
}
expect_stdout: "5 6"
expect_warnings: [
"WARN: Dropping unreachable code [test/compress/issue-1034.js:173,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:173,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:176,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:176,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:173,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:180,21]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:178,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:178,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:181,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:176,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:178,16]",
]
}
non_hoisted_function_after_return_2b_strict: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar(1);
} else {
return bar(2);
var b;
}
var c = bar(3);
function bar(x) {
return 7 - x;
}
return b || c;
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return bar(x ? 1 : 2);
function bar(x) { return 7 - x; }
}
console.log(foo(0), foo(1));
}
expect_stdout: "5 6"
expect_warnings: [
// duplicate warnings no longer emitted
"WARN: Dropping unreachable code [test/compress/issue-1034.js:225,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:225,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:227,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:227,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:231,12]",
]
}

View File

@@ -1,90 +1,91 @@
multiple_functions: {
options = { if_return: true, hoist_funs: false };
options = {
hoist_funs: false,
if_return: true,
}
input: {
( function() {
if ( !window ) {
return;
}
function f() {}
function g() {}
} )();
}
expect: {
( function() {
function f() {}
function g() {}
// NOTE: other compression steps will reduce this
// down to just `window`.
if ( window );
function f() {}
function g() {}
} )();
}
}
single_function: {
options = { if_return: true, hoist_funs: false };
options = {
hoist_funs: false,
if_return: true,
}
input: {
( function() {
if ( !window ) {
return;
}
function f() {}
} )();
}
expect: {
( function() {
function f() {}
if ( window );
function f() {}
} )();
}
}
deeply_nested: {
options = { if_return: true, hoist_funs: false };
options = {
hoist_funs: false,
if_return: true,
}
input: {
( function() {
if ( !window ) {
return;
}
function f() {}
function g() {}
if ( !document ) {
return;
}
function h() {}
} )();
}
expect: {
( function() {
function f() {}
function g() {}
function h() {}
// NOTE: other compression steps will reduce this
// down to just `window`.
if ( window )
if (document);
function f() {}
function g() {}
function h() {}
} )();
}
}
not_hoisted_when_already_nested: {
options = { if_return: true, hoist_funs: false };
options = {
hoist_funs: false,
if_return: true,
}
input: {
( function() {
if ( !window ) {
return;
}
if ( foo ) function f() {}
} )();
}
expect: {
@@ -94,3 +95,70 @@ not_hoisted_when_already_nested: {
} )();
}
}
defun_if_return: {
options = {
hoist_funs: false,
if_return: true,
}
input: {
function e() {
function f() {}
if (!window) return;
else function g() {}
function h() {}
}
}
expect: {
function e() {
function f() {}
if (window) function g() {}
function h() {}
}
}
}
defun_hoist_funs: {
options = {
hoist_funs: true,
if_return: true,
}
input: {
function e() {
function f() {}
if (!window) return;
else function g() {}
function h() {}
}
}
expect: {
function e() {
function f() {}
function g() {}
function h() {}
if (window);
}
}
}
defun_else_if_return: {
options = {
hoist_funs: false,
if_return: true,
}
input: {
function e() {
function f() {}
if (window) function g() {}
else return;
function h() {}
}
}
expect: {
function e() {
function f() {}
if (window) function g() {}
function h() {}
}
}
}

View File

@@ -0,0 +1,12 @@
eval_let: {
input: {
eval("let a;");
console.log();
}
expect: {
eval("let a;");
console.log();
}
expect_stdout: ""
node_version: ">=6"
}

View File

@@ -8,3 +8,12 @@ octal_escape_sequence: {
var border_check = "\x20\x30\x38\x30\x00\x30\xc0\x30";
}
}
issue_1929: {
input: {
function f(s) {
return s.split(/[\\/]/);
}
}
expect_exact: "function f(s){return s.split(/[\\\\/]/)}"
}

View File

@@ -0,0 +1,5 @@
function test(callback) {
'aaaaaaaaaaaaaaaa';
callback(err, data);
callback(err, data);
}

View File

@@ -0,0 +1,5 @@
function test(a){
"aaaaaaaaaaaaaaaa"
;a(err,data),a(err,data)
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsidGVzdCIsImNhbGxiYWNrIiwiZXJyIiwiZGF0YSJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsS0FBS0M7QUFDVjtDQUNBQSxFQUFTQyxJQUFLQyxNQUNkRixFQUFTQyxJQUFLQyJ9

View File

@@ -1,2 +1,2 @@
new function(){console.log(3)};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxHQUFyQyxZQUFnQkEsUUFBUUMsSUFBSSIsInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl19
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxJQUFyQyxXQUFnQkEsUUFBUUMsSUFBSSIsInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl19

View File

@@ -14,7 +14,7 @@ if (typeof phantom == "undefined") {
if (!args.length) {
args.push("-mc");
}
args.push("--stats");
args.push("--timings");
var child_process = require("child_process");
try {
require("phantomjs-prebuilt");

View File

@@ -19,7 +19,9 @@ describe("bin/uglifyjs", function () {
eval(stdout);
assert.strictEqual(typeof WrappedUglifyJS, 'object');
assert.strictEqual(WrappedUglifyJS.minify("foo([true,,2+3]);").code, "foo([!0,,5]);");
var result = WrappedUglifyJS.minify("foo([true,,2+3]);");
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, "foo([!0,,5]);");
done();
});
@@ -61,7 +63,7 @@ describe("bin/uglifyjs", function () {
if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=\n");
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=\n");
done();
});
});
@@ -173,7 +175,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(stdout, [
"var bar=function(){function foo(bar){return bar}return foo}();",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=",
"",
].join("\n"));
assert.strictEqual(stderr, "WARN: inline source map not found\n");
@@ -512,7 +514,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(stdout, [
'"use strict";var foo=function foo(x){return"foo "+x};console.log(foo("bar"));',
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmpzIl0sIm5hbWVzIjpbImZvbyIsIngiLCJjb25zb2xlIiwibG9nIl0sIm1hcHBpbmdzIjoiWUFBQSxJQUFJQSxLQUFNLFFBQU5BLEtBQU1DLEdBQUEsTUFBSyxPQUFTQSxFQUN4QkMsU0FBUUMsSUFBSUgsSUFBSSJ9",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmpzIl0sIm5hbWVzIjpbImZvbyIsIngiLCJjb25zb2xlIiwibG9nIl0sIm1hcHBpbmdzIjoiYUFBQSxJQUFJQSxJQUFNLFNBQU5BLElBQU1DLEdBQUEsTUFBSyxPQUFTQSxHQUN4QkMsUUFBUUMsSUFBSUgsSUFBSSJ9",
""
].join("\n"));
done();
@@ -535,4 +537,13 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("Should print supported options on invalid option syntax", function(done) {
var command = uglifyjscmd + " test/input/comments/filter.js -b ascii-only";
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.ok(/^Supported options:\n\{[^}]+}\nERROR: `ascii-only` is not a supported option/.test(stderr), stderr);
done();
});
});
});

View File

@@ -138,13 +138,25 @@ describe("minify", function() {
});
var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsR0FBSUEsR0FBSSxTQUFTQyxHQUFPLE1BQU9BIn0=");
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsSUFBSUEsRUFBSSxTQUFTQyxHQUFPLE9BQU9BIn0=");
});
it("should not append source map to output js when sourceMapInline is not enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };');
var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};");
});
it("should work with max_line_len", function() {
var result = Uglify.minify(read("./test/input/issue-505/input.js"), {
output: {
max_line_len: 20
},
sourceMap: {
url: "inline"
}
});
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, read("./test/input/issue-505/output.js"));
});
});
describe("#__PURE__", function() {
@@ -181,4 +193,19 @@ describe("minify", function() {
assert.strictEqual(err.col, 12);
});
});
describe("global_defs", function() {
it("should throw for non-trivial expressions", function() {
var result = Uglify.minify("alert(42);", {
compress: {
global_defs: {
"@alert": "debugger"
}
}
});
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: Can't handle expression: debugger");
});
});
});

View File

@@ -15,7 +15,9 @@ describe("spidermonkey export/import sanity test", function() {
eval(stdout);
assert.strictEqual(typeof SpiderUglify, "object");
assert.strictEqual(SpiderUglify.minify("foo([true,,2+3]);").code, "foo([!0,,5]);");
var result = SpiderUglify.minify("foo([true,,2+3]);");
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, "foo([!0,,5]);");
done();
});

View File

@@ -5,6 +5,7 @@ var path = require("path");
var fs = require("fs");
var assert = require("assert");
var sandbox = require("./sandbox");
var semver = require("semver");
var tests_dir = path.dirname(module.filename);
var failures = 0;
@@ -164,7 +165,8 @@ function run_compress_tests() {
failed_files[file] = 1;
}
}
if (test.expect_stdout) {
if (test.expect_stdout
&& (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = sandbox.run_code(input_code);
if (test.expect_stdout === true) {
test.expect_stdout = stdout;
@@ -274,7 +276,14 @@ function parse_test(file) {
if (node instanceof U.AST_LabeledStatement) {
var label = node.label;
assert.ok(
["input", "expect", "expect_exact", "expect_warnings", "expect_stdout"].indexOf(label.name) >= 0,
[
"input",
"expect",
"expect_exact",
"expect_warnings",
"expect_stdout",
"node_version",
].indexOf(label.name) >= 0,
tmpl("Unsupported label {name} [{line},{col}]", {
name: label.name,
line: label.start.line,
@@ -282,7 +291,7 @@ function parse_test(file) {
})
);
var stat = node.body;
if (label.name == "expect_exact") {
if (label.name == "expect_exact" || label.name == "node_version") {
test[label.name] = read_string(stat);
} else if (label.name == "expect_stdout") {
if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) {

View File

@@ -1,3 +1,4 @@
var semver = require("semver");
var vm = require("vm");
function safe_log(arg, level) {
@@ -63,7 +64,7 @@ exports.run_code = function(code) {
process.stdout.write = original_write;
}
};
exports.same_stdout = ~process.version.lastIndexOf("v0.12.", 0) ? function(expected, actual) {
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
if (typeof expected != typeof actual) return false;
if (typeof expected != "string") {
if (expected.name != actual.name) return false;

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("./node");
var UglifyJS = require("..");
var ok = require("assert");
module.exports = function () {
@@ -26,11 +26,11 @@ module.exports = function () {
}
function source_map(js) {
var source_map = UglifyJS.SourceMap();
var stream = UglifyJS.OutputStream({ source_map: source_map });
var parsed = UglifyJS.parse(js);
parsed.print(stream);
return JSON.parse(source_map.toString());
return JSON.parse(UglifyJS.minify(js, {
compress: false,
mangle: false,
sourceMap: true
}).map);
}
// Run standalone

View File

@@ -12,7 +12,7 @@
stream._handle.setBlocking(true);
});
var UglifyJS = require("./node");
var UglifyJS = require("..");
var randomBytes = require("crypto").randomBytes;
var sandbox = require("./sandbox");
@@ -962,32 +962,15 @@ function try_beautify(code, result) {
console.log(code);
}
function infer_options(ctor) {
try {
ctor({ 0: 0 });
} catch (e) {
return e.defs;
}
}
var default_options = {
compress: infer_options(UglifyJS.Compressor),
mangle: {
"cache": null,
"eval": false,
"ie8": false,
"keep_fnames": false,
"toplevel": false,
},
output: infer_options(UglifyJS.OutputStream),
};
var default_options = UglifyJS.default_options();
function log_suspects(minify_options, component) {
var options = component in minify_options ? minify_options[component] : true;
if (!options) return;
options = UglifyJS.defaults(options, default_options[component]);
var suspects = Object.keys(default_options[component]).filter(function(name) {
if (options[name]) {
if (typeof options != "object") options = {};
var defs = default_options[component];
var suspects = Object.keys(defs).filter(function(name) {
if ((name in options ? options : defs)[name]) {
var m = JSON.parse(JSON.stringify(minify_options));
var o = JSON.parse(JSON.stringify(options));
o[name] = false;

View File

@@ -1,4 +1,5 @@
exports["Dictionary"] = Dictionary;
exports["TreeWalker"] = TreeWalker;
exports["TreeTransformer"] = TreeTransformer;
exports["minify"] = minify;
exports["_push_uniq"] = push_uniq;

View File

@@ -61,5 +61,22 @@ function describe_ast() {
}
};
doitem(AST_Node);
return out + "";
return out + "\n";
}
function infer_options(options) {
var result = UglifyJS.minify("", options);
return result.error && result.error.defs;
}
UglifyJS.default_options = function() {
var defs = {};
Object.keys(infer_options({ 0: 0 })).forEach(function(component) {
var options = {};
options[component] = { 0: 0 };
if (options = infer_options(options)) {
defs[component] = options;
}
});
return defs;
};