Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4231f7323e | ||
|
|
da2de350c3 | ||
|
|
41beae4dd7 | ||
|
|
82db9188ac | ||
|
|
3dc9e140e4 | ||
|
|
fed0096556 | ||
|
|
2bdc8802dd | ||
|
|
5ef7cb372a | ||
|
|
4ad7b1dae4 | ||
|
|
9186859cb7 | ||
|
|
47c0713747 | ||
|
|
293c566d6c | ||
|
|
9c306406f1 | ||
|
|
9db0695b10 | ||
|
|
f2af093402 | ||
|
|
b9ad53d1ab | ||
|
|
b0eab71470 | ||
|
|
3493a182b2 | ||
|
|
27c5284d3d | ||
|
|
540220b91b | ||
|
|
84634da4b5 | ||
|
|
1743621889 | ||
|
|
f330ab743a | ||
|
|
4377e932ca | ||
|
|
bac14ba881 | ||
|
|
ec095ed647 | ||
|
|
17e73121fa | ||
|
|
f71e8fd948 | ||
|
|
3e62faa64f | ||
|
|
e9645e017f | ||
|
|
55b5f2a8aa | ||
|
|
4e0a22e5c8 | ||
|
|
1aa38051fb | ||
|
|
e62b879b48 | ||
|
|
c6c9f4f5a8 | ||
|
|
fec14379f6 | ||
|
|
79131cd647 | ||
|
|
c3f14a1481 | ||
|
|
7b13159cda | ||
|
|
95094b9c22 | ||
|
|
1ff8e9dd38 | ||
|
|
78309a293d | ||
|
|
695e182d59 | ||
|
|
dc33facfcb | ||
|
|
c70fb60384 | ||
|
|
793d61499b | ||
|
|
a277fe168d | ||
|
|
7d3b941e6e | ||
|
|
e95052a423 | ||
|
|
e667f0acb8 | ||
|
|
69ac794bc8 | ||
|
|
efdb65913b | ||
|
|
a1dedeb3ce | ||
|
|
d3c4a8e9e7 | ||
|
|
7e164aba8f | ||
|
|
22aedef849 | ||
|
|
58fae7dc07 | ||
|
|
5bf8d7e949 | ||
|
|
1df9d06f4a | ||
|
|
3408fc9d32 | ||
|
|
eae26756f1 | ||
|
|
43add9416b | ||
|
|
efcf167e5e | ||
|
|
6ed90913ca | ||
|
|
569c21e952 | ||
|
|
87c3a2c0ce | ||
|
|
baef8bf050 | ||
|
|
0813c5316f |
@@ -4,8 +4,11 @@ node_js:
|
||||
- "0.12"
|
||||
- "4"
|
||||
- "6"
|
||||
- "8"
|
||||
env:
|
||||
- UGLIFYJS_TEST_ALL=1
|
||||
matrix:
|
||||
fast_finish: true
|
||||
sudo: false
|
||||
cache:
|
||||
directories: tmp
|
||||
|
||||
324
README.md
324
README.md
@@ -1,6 +1,5 @@
|
||||
UglifyJS 3
|
||||
==========
|
||||
[](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
|
||||
{
|
||||
@@ -457,12 +572,18 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
comparison are switching. Compression only works if both `comparisons` and
|
||||
`unsafe_comps` are both set to true.
|
||||
|
||||
- `unsafe_Func` (default: false) -- compress and mangle `Function(args, code)`
|
||||
when both `args` and `code` are string literals.
|
||||
|
||||
- `unsafe_math` (default: false) -- optimize numerical expressions like
|
||||
`2 * x * 3` into `6 * x`, which may give imprecise floating point results.
|
||||
|
||||
- `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 +603,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`
|
||||
@@ -495,13 +616,15 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
|
||||
- `if_return` -- optimizations for if/return and if/continue
|
||||
|
||||
- `inline` -- embed simple functions
|
||||
|
||||
- `join_vars` -- join consecutive `var` statements
|
||||
|
||||
- `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,24 +676,25 @@ 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
|
||||
- `reserved` (default `[]`). Pass an array of identifiers that should be
|
||||
excluded from mangling. Example: `["foo", "bar"]`.
|
||||
|
||||
- `toplevel` — mangle names declared in the toplevel scope (disabled by
|
||||
default).
|
||||
- `toplevel` (default `false`). Pass `true` to mangle names declared in the
|
||||
top level scope.
|
||||
|
||||
- `eval` — mangle names visible in scopes where eval or with are used
|
||||
(disabled by default).
|
||||
- `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).
|
||||
|
||||
- `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).
|
||||
- `eval` (default `false`). Pass `true` to mangle names visible in scopes
|
||||
where `eval` or `with` are used.
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -596,10 +720,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,46 +736,53 @@ 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.
|
||||
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
|
||||
`do`, `while` or `with` statements, even if their body is a single
|
||||
statement.
|
||||
- `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
|
||||
- `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
|
||||
- `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
|
||||
more double quotes in the string itself. `0` is best for gzip size.
|
||||
- `1` -- always use single quotes
|
||||
- `2` -- always use double quotes
|
||||
- `3` -- always use the original quotes
|
||||
- `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.
|
||||
- `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).
|
||||
- `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.
|
||||
- `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
|
||||
more double quotes in the string itself.
|
||||
- `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.
|
||||
|
||||
- `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 +852,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 +873,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 +883,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
|
||||
|
||||
84
bin/uglifyjs
84
bin/uglifyjs
@@ -21,9 +21,20 @@ 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:");
|
||||
text.push(format_object(options[option]));
|
||||
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 +49,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 +125,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) {
|
||||
@@ -139,7 +150,7 @@ if (program.verbose) {
|
||||
}
|
||||
if (program.self) {
|
||||
if (program.args.length) {
|
||||
console.error("WARN: Ignoring input files since --self was passed");
|
||||
print_error("WARN: Ignoring input files since --self was passed");
|
||||
}
|
||||
if (!options.wrap) options.wrap = "UglifyJS";
|
||||
simple_glob(UglifyJS.FILES).forEach(function(name) {
|
||||
@@ -169,9 +180,9 @@ function convert_ast(fn) {
|
||||
|
||||
function run() {
|
||||
UglifyJS.AST_Node.warn_function = function(msg) {
|
||||
console.error("WARN:", msg);
|
||||
print_error("WARN: " + msg);
|
||||
};
|
||||
if (program.stats) program.stats = Date.now();
|
||||
if (program.timings) options.timings = true;
|
||||
try {
|
||||
if (program.parse) {
|
||||
if (program.parse.acorn) {
|
||||
@@ -198,7 +209,7 @@ function run() {
|
||||
if (result.error) {
|
||||
var ex = result.error;
|
||||
if (ex.name == "SyntaxError") {
|
||||
console.error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
|
||||
print_error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
|
||||
var col = ex.col;
|
||||
var lines = files[ex.filename].split(/\r?\n/);
|
||||
var line = lines[ex.line - 1];
|
||||
@@ -207,21 +218,22 @@ function run() {
|
||||
col = line.length;
|
||||
}
|
||||
if (line) {
|
||||
if (col > 40) {
|
||||
line = line.slice(col - 40);
|
||||
col = 40;
|
||||
var limit = 70;
|
||||
if (col > limit) {
|
||||
line = line.slice(col - limit);
|
||||
col = limit;
|
||||
}
|
||||
console.error(line.slice(0, 80));
|
||||
console.error(line.slice(0, col).replace(/\S/g, " ") + "^");
|
||||
print_error(line.slice(0, 80));
|
||||
print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
|
||||
}
|
||||
}
|
||||
if (ex.defs) {
|
||||
console.error("Supported options:");
|
||||
console.error(ex.defs);
|
||||
print_error("Supported options:");
|
||||
print_error(format_object(ex.defs));
|
||||
}
|
||||
fatal(ex);
|
||||
} else if (program.output == "ast") {
|
||||
console.log(JSON.stringify(result.ast, function(key, value) {
|
||||
print(JSON.stringify(result.ast, function(key, value) {
|
||||
if (skip_key(key)) return;
|
||||
if (value instanceof UglifyJS.AST_Token) return;
|
||||
if (value instanceof UglifyJS.Dictionary) return;
|
||||
@@ -237,7 +249,7 @@ function run() {
|
||||
return value;
|
||||
}, 2));
|
||||
} else if (program.output == "spidermonkey") {
|
||||
console.log(JSON.stringify(UglifyJS.minify(result.code, {
|
||||
print(JSON.stringify(UglifyJS.minify(result.code, {
|
||||
compress: false,
|
||||
mangle: false,
|
||||
output: {
|
||||
@@ -251,19 +263,21 @@ function run() {
|
||||
fs.writeFileSync(program.output + ".map", result.map);
|
||||
}
|
||||
} else {
|
||||
console.log(result.code);
|
||||
print(result.code);
|
||||
}
|
||||
if (program.nameCache) {
|
||||
fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) {
|
||||
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) {
|
||||
print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
|
||||
}
|
||||
}
|
||||
|
||||
function fatal(message) {
|
||||
if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:")
|
||||
console.error(message);
|
||||
print_error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -348,7 +362,7 @@ function parse_js(flag, constants) {
|
||||
}
|
||||
}));
|
||||
} catch(ex) {
|
||||
fatal("Error parsing arguments for '" + flag + "': " + value);
|
||||
options[value] = null;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
@@ -357,10 +371,10 @@ function parse_js(flag, constants) {
|
||||
function parse_source_map() {
|
||||
var parse = parse_js("sourceMap", true);
|
||||
return function(value, options) {
|
||||
var hasContent = options && options.sourceMap && "content" in options.sourceMap;
|
||||
var hasContent = options && "content" in options;
|
||||
var settings = parse(value, options);
|
||||
if (!hasContent && settings.content && settings.content != "inline") {
|
||||
console.error("INFO: Using input source map:", settings.content);
|
||||
print_error("INFO: Using input source map: " + settings.content);
|
||||
settings.content = read_file(settings.content, settings.content);
|
||||
}
|
||||
return settings;
|
||||
@@ -382,3 +396,25 @@ function to_cache(key) {
|
||||
function skip_key(key) {
|
||||
return skip_keys.indexOf(key) >= 0;
|
||||
}
|
||||
|
||||
function format_object(obj) {
|
||||
var lines = [];
|
||||
var padding = "";
|
||||
Object.keys(obj).map(function(name) {
|
||||
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
|
||||
return [ name, JSON.stringify(obj[name]) ];
|
||||
}).forEach(function(tokens) {
|
||||
lines.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
|
||||
});
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function print_error(msg) {
|
||||
process.stderr.write(msg);
|
||||
process.stderr.write("\n");
|
||||
}
|
||||
|
||||
function print(txt) {
|
||||
process.stdout.write(txt);
|
||||
process.stdout.write("\n");
|
||||
}
|
||||
|
||||
429
lib/compress.js
429
lib/compress.js
@@ -63,6 +63,7 @@ function Compressor(options, false_by_default) {
|
||||
hoist_vars : false,
|
||||
ie8 : false,
|
||||
if_return : !false_by_default,
|
||||
inline : !false_by_default,
|
||||
join_vars : !false_by_default,
|
||||
keep_fargs : true,
|
||||
keep_fnames : false,
|
||||
@@ -81,11 +82,24 @@ function Compressor(options, false_by_default) {
|
||||
toplevel : !!(options && options["top_retain"]),
|
||||
unsafe : false,
|
||||
unsafe_comps : false,
|
||||
unsafe_Func : 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 +926,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 +981,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 +991,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 +1000,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 +1019,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 +1038,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 +1226,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;
|
||||
}
|
||||
@@ -1402,7 +1419,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
if (value && typeof value == "object") {
|
||||
var props = [];
|
||||
for (var key in value) {
|
||||
for (var key in value) if (HOP(value, key)) {
|
||||
props.push(make_node(AST_ObjectKeyVal, orig, {
|
||||
key: key,
|
||||
value: to_node(value[key], orig)
|
||||
@@ -1649,6 +1666,63 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
throw def;
|
||||
});
|
||||
var object_fns = [
|
||||
'constructor',
|
||||
'toString',
|
||||
'valueOf',
|
||||
];
|
||||
var native_fns = {
|
||||
Array: makePredicate([
|
||||
'indexOf',
|
||||
'join',
|
||||
'lastIndexOf',
|
||||
'slice',
|
||||
].concat(object_fns)),
|
||||
Boolean: makePredicate(object_fns),
|
||||
Number: makePredicate([
|
||||
'toExponential',
|
||||
'toFixed',
|
||||
'toPrecision',
|
||||
].concat(object_fns)),
|
||||
RegExp: makePredicate([
|
||||
'test',
|
||||
].concat(object_fns)),
|
||||
String: makePredicate([
|
||||
'charAt',
|
||||
'charCodeAt',
|
||||
'concat',
|
||||
'indexOf',
|
||||
'italics',
|
||||
'lastIndexOf',
|
||||
'match',
|
||||
'replace',
|
||||
'search',
|
||||
'slice',
|
||||
'split',
|
||||
'substr',
|
||||
'substring',
|
||||
'trim',
|
||||
].concat(object_fns)),
|
||||
};
|
||||
def(AST_Call, function(compressor){
|
||||
var exp = this.expression;
|
||||
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
|
||||
var key = exp.property;
|
||||
if (key instanceof AST_Node) {
|
||||
key = ev(key, compressor);
|
||||
}
|
||||
var val = ev(exp.expression, compressor);
|
||||
if ((val && native_fns[val.constructor.name] || return_false)(key)) {
|
||||
return val[key].apply(val, this.args.map(function(arg) {
|
||||
return ev(arg, compressor);
|
||||
}));
|
||||
}
|
||||
}
|
||||
throw def;
|
||||
});
|
||||
def(AST_New, function(compressor){
|
||||
throw def;
|
||||
});
|
||||
})(function(node, func){
|
||||
node.DEFMETHOD("_eval", func);
|
||||
});
|
||||
@@ -2238,11 +2312,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;
|
||||
@@ -2869,24 +2944,9 @@ merge(Compressor.prototype, {
|
||||
|
||||
OPT(AST_Call, function(self, compressor){
|
||||
var exp = self.expression;
|
||||
if (compressor.option("reduce_vars")
|
||||
&& exp instanceof AST_SymbolRef) {
|
||||
var def = exp.definition();
|
||||
if (compressor.option("reduce_vars") && exp instanceof AST_SymbolRef) {
|
||||
var fixed = exp.fixed_value();
|
||||
if (fixed instanceof AST_Defun) {
|
||||
def.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
|
||||
}
|
||||
if (fixed instanceof AST_Function) {
|
||||
exp = fixed;
|
||||
if (compressor.option("unused")
|
||||
&& def.references.length == 1
|
||||
&& !(def.scope.uses_arguments
|
||||
&& def.orig[0] instanceof AST_SymbolFunarg)
|
||||
&& !def.scope.uses_eval
|
||||
&& compressor.find_parent(AST_Scope) === def.scope) {
|
||||
self.expression = exp;
|
||||
}
|
||||
}
|
||||
if (fixed instanceof AST_Function) exp = fixed;
|
||||
}
|
||||
if (compressor.option("unused")
|
||||
&& exp instanceof AST_Function
|
||||
@@ -2957,63 +3017,6 @@ merge(Compressor.prototype, {
|
||||
operator: "!"
|
||||
}).optimize(compressor);
|
||||
break;
|
||||
case "Function":
|
||||
// new Function() => function(){}
|
||||
if (self.args.length == 0) return make_node(AST_Function, self, {
|
||||
argnames: [],
|
||||
body: []
|
||||
});
|
||||
if (all(self.args, function(x){ return x instanceof AST_String })) {
|
||||
// quite a corner-case, but we can handle it:
|
||||
// https://github.com/mishoo/UglifyJS2/issues/203
|
||||
// if the code argument is a constant, then we can minify it.
|
||||
try {
|
||||
var code = "(function(" + self.args.slice(0, -1).map(function(arg){
|
||||
return arg.value;
|
||||
}).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
|
||||
var ast = parse(code);
|
||||
var mangle = { ie8: compressor.option("ie8") };
|
||||
ast.figure_out_scope(mangle);
|
||||
var comp = new Compressor(compressor.options);
|
||||
ast = ast.transform(comp);
|
||||
ast.figure_out_scope(mangle);
|
||||
ast.mangle_names();
|
||||
var fun;
|
||||
try {
|
||||
ast.walk(new TreeWalker(function(node){
|
||||
if (node instanceof AST_Lambda) {
|
||||
fun = node;
|
||||
throw ast;
|
||||
}
|
||||
}));
|
||||
} catch(ex) {
|
||||
if (ex !== ast) throw ex;
|
||||
};
|
||||
if (!fun) return self;
|
||||
var args = fun.argnames.map(function(arg, i){
|
||||
return make_node(AST_String, self.args[i], {
|
||||
value: arg.print_to_string()
|
||||
});
|
||||
});
|
||||
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;
|
||||
return self;
|
||||
} catch(ex) {
|
||||
if (ex instanceof JS_Parse_Error) {
|
||||
compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
|
||||
compressor.warn(ex.toString());
|
||||
} else {
|
||||
console.log(ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
|
||||
@@ -3096,14 +3099,128 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (compressor.option("unsafe_Func")
|
||||
&& exp instanceof AST_SymbolRef
|
||||
&& exp.undeclared()
|
||||
&& exp.name == "Function") {
|
||||
// new Function() => function(){}
|
||||
if (self.args.length == 0) return make_node(AST_Function, self, {
|
||||
argnames: [],
|
||||
body: []
|
||||
});
|
||||
if (all(self.args, function(x) {
|
||||
return x instanceof AST_String;
|
||||
})) {
|
||||
// quite a corner-case, but we can handle it:
|
||||
// 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) {
|
||||
return arg.value;
|
||||
}).join(",") + "){" + self.args[self.args.length - 1].value + "})";
|
||||
var ast = parse(code);
|
||||
var mangle = { ie8: compressor.option("ie8") };
|
||||
ast.figure_out_scope(mangle);
|
||||
var comp = new Compressor(compressor.options);
|
||||
ast = ast.transform(comp);
|
||||
ast.figure_out_scope(mangle);
|
||||
ast.mangle_names();
|
||||
var fun;
|
||||
ast.walk(new TreeWalker(function(node) {
|
||||
if (fun) return true;
|
||||
if (node instanceof AST_Lambda) {
|
||||
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()
|
||||
});
|
||||
});
|
||||
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;
|
||||
return self;
|
||||
} catch (ex) {
|
||||
if (ex instanceof JS_Parse_Error) {
|
||||
compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
|
||||
compressor.warn(ex.toString());
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exp instanceof AST_Function) {
|
||||
if (exp.body[0] instanceof AST_Return) {
|
||||
var value = exp.body[0].value;
|
||||
var stat = exp.body[0];
|
||||
if (compressor.option("inline") && stat instanceof AST_Return) {
|
||||
var value = stat && 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);
|
||||
}
|
||||
}
|
||||
if (compressor.option("inline")
|
||||
&& !exp.name
|
||||
&& exp.body.length == 1
|
||||
&& !exp.uses_arguments
|
||||
&& !exp.uses_eval
|
||||
&& !self.has_pure_annotation(compressor)) {
|
||||
var value;
|
||||
if (stat instanceof AST_Return) {
|
||||
value = stat.value.clone(true);
|
||||
} else if (stat instanceof AST_SimpleStatement) {
|
||||
value = make_node(AST_UnaryPrefix, stat, {
|
||||
operator: "void",
|
||||
expression: stat.body.clone(true)
|
||||
});
|
||||
}
|
||||
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) {
|
||||
return make_node(AST_VarDef, sym, {
|
||||
name: sym,
|
||||
value: self.args[i] || 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))
|
||||
}));
|
||||
}
|
||||
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);
|
||||
value.walk(new TreeWalker(function(node) {
|
||||
if (value === self) return true;
|
||||
if (node instanceof AST_SymbolRef && exp.variables.has(node.name)) {
|
||||
value = self;
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
if (value !== self) value = best_of(compressor, value, self);
|
||||
} else {
|
||||
value = self;
|
||||
}
|
||||
if (value !== self) return value;
|
||||
}
|
||||
}
|
||||
if (compressor.option("side_effects") && all(exp.body, is_empty)) {
|
||||
var args = self.args.concat(make_node(AST_Undefined, self));
|
||||
return make_sequence(self, args).transform(compressor);
|
||||
@@ -3127,6 +3244,11 @@ merge(Compressor.prototype, {
|
||||
&& is_iife_call(self)) {
|
||||
return self.negate(compressor, true);
|
||||
}
|
||||
var ev = self.evaluate(compressor);
|
||||
if (ev !== self) {
|
||||
ev = make_node_from_constant(ev, self).optimize(compressor);
|
||||
return best_of(compressor, ev, self);
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
@@ -3201,6 +3323,7 @@ merge(Compressor.prototype, {
|
||||
continue;
|
||||
}
|
||||
var parent = null, field;
|
||||
expressions[j] = cdr = cdr.clone();
|
||||
while (true) {
|
||||
if (cdr.equivalent_to(left)) {
|
||||
var car = expressions[i];
|
||||
@@ -3237,7 +3360,7 @@ merge(Compressor.prototype, {
|
||||
break;
|
||||
}
|
||||
parent = cdr;
|
||||
cdr = cdr[field];
|
||||
cdr = cdr[field] = cdr[field].clone();
|
||||
}
|
||||
}
|
||||
end = i;
|
||||
@@ -3757,15 +3880,25 @@ merge(Compressor.prototype, {
|
||||
return make_node(AST_Infinity, self).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.option("evaluate")
|
||||
&& compressor.option("reduce_vars")
|
||||
if (compressor.option("reduce_vars")
|
||||
&& is_lhs(self, compressor.parent()) !== self) {
|
||||
var d = self.definition();
|
||||
var fixed = self.fixed_value();
|
||||
if (fixed) {
|
||||
if (fixed instanceof AST_Defun) {
|
||||
d.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
|
||||
}
|
||||
if (compressor.option("unused")
|
||||
&& fixed instanceof AST_Function
|
||||
&& 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;
|
||||
}
|
||||
if (compressor.option("evaluate") && 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;
|
||||
|
||||
@@ -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,13 +76,17 @@ 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) {
|
||||
for (var name in files) if (HOP(files, name)) {
|
||||
options.parse.filename = name;
|
||||
options.parse.toplevel = parse(files[name], options.parse);
|
||||
if (options.sourceMap && options.sourceMap.content == "inline") {
|
||||
@@ -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) if (HOP(files, name)) {
|
||||
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;
|
||||
}
|
||||
|
||||
147
lib/output.js
147
lib/output.js
@@ -70,8 +70,7 @@ function OutputStream(options) {
|
||||
semicolons : true,
|
||||
shebang : true,
|
||||
source_map : null,
|
||||
space_colon : true,
|
||||
unescape_regexps : false,
|
||||
webkit : false,
|
||||
width : 80,
|
||||
wrap_iife : false,
|
||||
}, true);
|
||||
@@ -191,12 +190,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 +236,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 +299,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 +402,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() {
|
||||
@@ -568,6 +598,13 @@ function OutputStream(options) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (output.option('webkit')) {
|
||||
var p = output.parent();
|
||||
if (p instanceof AST_PropAccess && p.expression === this) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (output.option('wrap_iife')) {
|
||||
var p = output.parent();
|
||||
return p instanceof AST_Call && p.expression === this;
|
||||
@@ -1205,9 +1242,8 @@ function OutputStream(options) {
|
||||
});
|
||||
else output.print("{}");
|
||||
});
|
||||
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
||||
var key = self.key;
|
||||
var quote = self.quote;
|
||||
|
||||
function print_property_name(key, quote, output) {
|
||||
if (output.option("quote_keys")) {
|
||||
output.print_string(key + "");
|
||||
} else if ((typeof key == "number"
|
||||
@@ -1224,20 +1260,24 @@ function OutputStream(options) {
|
||||
} else {
|
||||
output.print_string(key, quote);
|
||||
}
|
||||
}
|
||||
|
||||
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
||||
print_property_name(self.key, self.quote, output);
|
||||
output.colon();
|
||||
self.value.print(output);
|
||||
});
|
||||
DEFPRINT(AST_ObjectSetter, function(self, output){
|
||||
output.print("set");
|
||||
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) {
|
||||
output.print(type);
|
||||
output.space();
|
||||
self.key.print(output);
|
||||
self.value._do_print(output, true);
|
||||
print_property_name(this.key.name, this.quote, output);
|
||||
this.value._do_print(output, true);
|
||||
});
|
||||
DEFPRINT(AST_ObjectSetter, function(self, output){
|
||||
self._print_getter_setter("set", output);
|
||||
});
|
||||
DEFPRINT(AST_ObjectGetter, function(self, output){
|
||||
output.print("get");
|
||||
output.space();
|
||||
self.key.print(output);
|
||||
self.value._do_print(output, true);
|
||||
self._print_getter_setter("get", output);
|
||||
});
|
||||
DEFPRINT(AST_Symbol, function(self, output){
|
||||
var def = self.definition();
|
||||
@@ -1261,45 +1301,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();
|
||||
|
||||
34
lib/parse.js
34
lib/parse.js
@@ -115,8 +115,6 @@ var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,;:"));
|
||||
|
||||
var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
|
||||
|
||||
var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
|
||||
|
||||
/* -----[ Tokenizer ]----- */
|
||||
|
||||
// regexps adapted from http://xregexp.com/plugins/#unicode
|
||||
@@ -477,31 +475,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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -682,9 +682,7 @@ var PRECEDENCE = (function(a, ret){
|
||||
{}
|
||||
);
|
||||
|
||||
var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
|
||||
|
||||
var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
|
||||
var ATOMIC_START_TOKEN = makePredicate([ "atom", "num", "string", "regexp", "name" ]);
|
||||
|
||||
/* -----[ Parser ]----- */
|
||||
|
||||
@@ -698,7 +696,7 @@ function parse($TEXT, options) {
|
||||
shebang : true,
|
||||
strict : false,
|
||||
toplevel : null,
|
||||
});
|
||||
}, true);
|
||||
|
||||
var S = {
|
||||
input : (typeof $TEXT == "string"
|
||||
@@ -1220,7 +1218,6 @@ function parse($TEXT, options) {
|
||||
var tok = S.token, ret;
|
||||
switch (tok.type) {
|
||||
case "name":
|
||||
case "keyword":
|
||||
ret = _make_symbol(AST_SymbolRef);
|
||||
break;
|
||||
case "num":
|
||||
@@ -1250,13 +1247,6 @@ function parse($TEXT, options) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "operator":
|
||||
if (!is_identifier_string(tok.value)) {
|
||||
croak("Invalid getter/setter name: " + tok.value,
|
||||
tok.line, tok.col, tok.pos);
|
||||
}
|
||||
ret = _make_symbol(AST_SymbolRef);
|
||||
break;
|
||||
}
|
||||
next();
|
||||
return ret;
|
||||
@@ -1290,7 +1280,7 @@ function parse($TEXT, options) {
|
||||
func.end = prev();
|
||||
return subscripts(func, allow_calls);
|
||||
}
|
||||
if (ATOMIC_START_TOKEN[S.token.type]) {
|
||||
if (ATOMIC_START_TOKEN(S.token.type)) {
|
||||
return subscripts(as_atom_node(), allow_calls);
|
||||
}
|
||||
unexpected();
|
||||
|
||||
@@ -78,7 +78,8 @@ function mangle_properties(ast, options) {
|
||||
reserved: null,
|
||||
});
|
||||
|
||||
var reserved = options.reserved || [];
|
||||
var reserved = options.reserved;
|
||||
if (!Array.isArray(reserved)) reserved = [];
|
||||
if (!options.builtins) find_builtins(reserved);
|
||||
|
||||
var cache = options.cache;
|
||||
|
||||
26
lib/scope.js
26
lib/scope.js
@@ -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;
|
||||
@@ -391,13 +377,15 @@ AST_Symbol.DEFMETHOD("global", function(){
|
||||
});
|
||||
|
||||
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
|
||||
return defaults(options, {
|
||||
options = defaults(options, {
|
||||
eval : false,
|
||||
ie8 : false,
|
||||
keep_fnames : false,
|
||||
reserved : [],
|
||||
toplevel : false,
|
||||
});
|
||||
if (!Array.isArray(options.reserved)) options.reserved = [];
|
||||
return options;
|
||||
});
|
||||
|
||||
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||
|
||||
@@ -43,13 +43,6 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
function array_to_hash(a) {
|
||||
var ret = Object.create(null);
|
||||
for (var i = 0; i < a.length; ++i)
|
||||
ret[a[i]] = true;
|
||||
return ret;
|
||||
};
|
||||
|
||||
function slice(a, start) {
|
||||
return Array.prototype.slice.call(a, start || 0);
|
||||
};
|
||||
|
||||
@@ -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.7",
|
||||
"version": "3.0.16",
|
||||
"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"]
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
"use strict";
|
||||
|
||||
var createHash = require("crypto").createHash;
|
||||
var fetch = require("./fetch");
|
||||
var fork = require("child_process").fork;
|
||||
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 +30,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);
|
||||
@@ -57,7 +53,8 @@ urls.forEach(function(url) {
|
||||
output: 0,
|
||||
log: ""
|
||||
};
|
||||
require(url.slice(0, url.indexOf(":"))).get(url, function(res) {
|
||||
fetch(url, function(err, res) {
|
||||
if (err) throw err;
|
||||
var uglifyjs = fork("bin/uglifyjs", args, { silent: true });
|
||||
res.on("data", function(data) {
|
||||
results[url].input += data.length;
|
||||
|
||||
@@ -1146,7 +1146,7 @@ collapse_vars_constants: {
|
||||
function f3(x) {
|
||||
var b = x.prop;
|
||||
sideeffect1();
|
||||
return b + -9;
|
||||
return b + (function() { return -9; })();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -751,12 +751,12 @@ issue_1583: {
|
||||
expect: {
|
||||
function m(t) {
|
||||
(function(e) {
|
||||
t = (function() {
|
||||
return (function(a) {
|
||||
return a;
|
||||
})(function(a) {});
|
||||
})();
|
||||
})();
|
||||
t = e();
|
||||
})(function() {
|
||||
return (function(a) {
|
||||
return a;
|
||||
})(function(a) {});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,6 +644,7 @@ unsafe_prototype_function: {
|
||||
call_args: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
}
|
||||
@@ -665,6 +666,7 @@ call_args: {
|
||||
call_args_drop_param: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
keep_fargs: false,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
@@ -780,13 +782,15 @@ unsafe_charAt_noop: {
|
||||
input: {
|
||||
console.log(
|
||||
s.charAt(0),
|
||||
"string".charAt(x)
|
||||
"string".charAt(x),
|
||||
(typeof x).charAt()
|
||||
);
|
||||
}
|
||||
expect: {
|
||||
console.log(
|
||||
s.charAt(0),
|
||||
"string".charAt(x)
|
||||
"string".charAt(x),
|
||||
(typeof x)[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -990,3 +994,78 @@ 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"
|
||||
}
|
||||
|
||||
array_slice_index: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log([1,2,3].slice(1)[1]);
|
||||
}
|
||||
expect: {
|
||||
console.log(3);
|
||||
}
|
||||
expect_stdout: "3"
|
||||
}
|
||||
|
||||
string_charCodeAt: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log("foo".charCodeAt("bar".length));
|
||||
}
|
||||
expect: {
|
||||
console.log(NaN);
|
||||
}
|
||||
expect_stdout: "NaN"
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ iifes_returning_constants_keep_fargs_true: {
|
||||
join_vars : true,
|
||||
reduce_vars : true,
|
||||
cascade : true,
|
||||
inline : true,
|
||||
}
|
||||
input: {
|
||||
(function(){ return -1.23; }());
|
||||
@@ -56,6 +57,7 @@ iifes_returning_constants_keep_fargs_false: {
|
||||
join_vars : true,
|
||||
reduce_vars : true,
|
||||
cascade : true,
|
||||
inline : true,
|
||||
}
|
||||
input: {
|
||||
(function(){ return -1.23; }());
|
||||
@@ -82,6 +84,7 @@ issue_485_crashing_1530: {
|
||||
conditionals: true,
|
||||
dead_code: true,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
}
|
||||
input: {
|
||||
(function(a) {
|
||||
@@ -154,6 +157,7 @@ function_returning_constant_literal: {
|
||||
evaluate: true,
|
||||
cascade: true,
|
||||
unused: true,
|
||||
inline: true,
|
||||
}
|
||||
input: {
|
||||
function greeter() {
|
||||
@@ -167,3 +171,168 @@ 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"
|
||||
}
|
||||
|
||||
issue_203: {
|
||||
options = {
|
||||
keep_fargs: false,
|
||||
side_effects: true,
|
||||
unsafe_Func: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var m = {};
|
||||
var fn = Function("require", "module", "exports", "module.exports = 42;");
|
||||
fn(null, m, m.exports);
|
||||
console.log(m.exports);
|
||||
}
|
||||
expect: {
|
||||
var m = {};
|
||||
var fn = Function("a", "b", "b.exports=42");
|
||||
fn(null, m, m.exports);
|
||||
console.log(m.exports);
|
||||
}
|
||||
expect_stdout: "42"
|
||||
}
|
||||
|
||||
no_webkit: {
|
||||
beautify = {
|
||||
webkit: false,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
1 + 1;
|
||||
}.a = 1);
|
||||
}
|
||||
expect_exact: "console.log(function(){1+1}.a=1);"
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
webkit: {
|
||||
beautify = {
|
||||
webkit: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
1 + 1;
|
||||
}.a = 1);
|
||||
}
|
||||
expect_exact: "console.log((function(){1+1}).a=1);"
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
issue_2084: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
conditionals: true,
|
||||
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;
|
||||
!function(c) {
|
||||
c = 1 + c,
|
||||
c = 1 + (c = 0),
|
||||
0 !== 23..toString() && (c = 1 + c);
|
||||
}(-1),
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "0"
|
||||
}
|
||||
|
||||
@@ -160,3 +160,17 @@ issue_1801: {
|
||||
console.log(!0);
|
||||
}
|
||||
}
|
||||
|
||||
issue_1986: {
|
||||
options = {
|
||||
global_defs: {
|
||||
"@alert": "console.log",
|
||||
},
|
||||
}
|
||||
input: {
|
||||
alert(42);
|
||||
}
|
||||
expect: {
|
||||
console.log(42);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
unary_prefix: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
|
||||
483
test/compress/issue-281.js
Normal file
483
test/compress/issue-281.js
Normal file
@@ -0,0 +1,483 @@
|
||||
collapse_vars_constants: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f1(x) {
|
||||
var a = 4, b = x.prop, c = 5, d = sideeffect1(), e = sideeffect2();
|
||||
return b + (function() { return d - a * e - c; })();
|
||||
}
|
||||
function f2(x) {
|
||||
var a = 4, b = x.prop, c = 5, not_used = sideeffect1(), e = sideeffect2();
|
||||
return b + (function() { return -a * e - c; })();
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f1(x) {
|
||||
var b = x.prop, d = sideeffect1(), e = sideeffect2();
|
||||
return b + (d - 4 * e - 5);
|
||||
}
|
||||
function f2(x) {
|
||||
var b = x.prop;
|
||||
sideeffect1();
|
||||
return b + (-4 * sideeffect2() - 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modified: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f5(b) {
|
||||
var a = function() {
|
||||
return b;
|
||||
}();
|
||||
return b++ + a;
|
||||
}
|
||||
console.log(f5(1));
|
||||
}
|
||||
expect: {
|
||||
function f5(b) {
|
||||
var a = b;
|
||||
return b++ + a;
|
||||
}
|
||||
console.log(f5(1));
|
||||
}
|
||||
expect_stdout: "2"
|
||||
}
|
||||
|
||||
ref_scope: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
var a = 1, b = 2, c = 3;
|
||||
var a = c++, b = b /= a;
|
||||
return function() {
|
||||
return a;
|
||||
}() + b;
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
var a = 1, b = 2, c = 3;
|
||||
b = b /= a = c++;
|
||||
return a + b;
|
||||
}());
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
safe_undefined: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
if_return: true,
|
||||
inline: true,
|
||||
unsafe: false,
|
||||
unused: true,
|
||||
}
|
||||
mangle = {}
|
||||
input: {
|
||||
var a, c;
|
||||
console.log(function(undefined) {
|
||||
return function() {
|
||||
if (a)
|
||||
return b;
|
||||
if (c)
|
||||
return d;
|
||||
};
|
||||
}(1)());
|
||||
}
|
||||
expect: {
|
||||
var a, c;
|
||||
console.log(a ? b : c ? d : void 0);
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
negate_iife_3: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
expression: true,
|
||||
inline: true,
|
||||
negate_iife: true,
|
||||
}
|
||||
input: {
|
||||
(function(){ return t })() ? console.log(true) : console.log(false);
|
||||
}
|
||||
expect: {
|
||||
t ? console.log(true) : console.log(false);
|
||||
}
|
||||
}
|
||||
|
||||
negate_iife_3_off: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
expression: true,
|
||||
inline: true,
|
||||
negate_iife: false,
|
||||
}
|
||||
input: {
|
||||
(function(){ return t })() ? console.log(true) : console.log(false);
|
||||
}
|
||||
expect: {
|
||||
t ? console.log(true) : console.log(false);
|
||||
}
|
||||
}
|
||||
|
||||
negate_iife_4: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
expression: true,
|
||||
inline: true,
|
||||
negate_iife: true,
|
||||
sequences: true,
|
||||
}
|
||||
input: {
|
||||
(function(){ return t })() ? console.log(true) : console.log(false);
|
||||
(function(){
|
||||
console.log("something");
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
t ? console.log(true) : console.log(false), void console.log("something");
|
||||
}
|
||||
}
|
||||
|
||||
negate_iife_5: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
expression: true,
|
||||
inline: true,
|
||||
negate_iife: true,
|
||||
sequences: true,
|
||||
}
|
||||
input: {
|
||||
if ((function(){ return t })()) {
|
||||
foo(true);
|
||||
} else {
|
||||
bar(false);
|
||||
}
|
||||
(function(){
|
||||
console.log("something");
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
t ? foo(true) : bar(false), void console.log("something");
|
||||
}
|
||||
}
|
||||
|
||||
negate_iife_5_off: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
expression: true,
|
||||
inline: true,
|
||||
negate_iife: false,
|
||||
sequences: true,
|
||||
};
|
||||
input: {
|
||||
if ((function(){ return t })()) {
|
||||
foo(true);
|
||||
} else {
|
||||
bar(false);
|
||||
}
|
||||
(function(){
|
||||
console.log("something");
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
t ? foo(true) : bar(false), void console.log("something");
|
||||
}
|
||||
}
|
||||
|
||||
issue_1254_negate_iife_true: {
|
||||
options = {
|
||||
expression: true,
|
||||
inline: true,
|
||||
negate_iife: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
return function() {
|
||||
console.log('test')
|
||||
};
|
||||
})()();
|
||||
}
|
||||
expect_exact: 'void console.log("test");'
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_1254_negate_iife_nested: {
|
||||
options = {
|
||||
expression: true,
|
||||
inline: true,
|
||||
negate_iife: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
return function() {
|
||||
console.log('test')
|
||||
};
|
||||
})()()()()();
|
||||
}
|
||||
expect_exact: '(void console.log("test"))()()();'
|
||||
}
|
||||
|
||||
negate_iife_issue_1073: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
negate_iife: true,
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
unused: true,
|
||||
};
|
||||
input: {
|
||||
new (function(a) {
|
||||
return function Foo() {
|
||||
this.x = a;
|
||||
console.log(this);
|
||||
};
|
||||
}(7))();
|
||||
}
|
||||
expect: {
|
||||
new function() {
|
||||
this.x = 7,
|
||||
console.log(this);
|
||||
}();
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_1288_side_effects: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
negate_iife: true,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
};
|
||||
input: {
|
||||
if (w) ;
|
||||
else {
|
||||
(function f() {})();
|
||||
}
|
||||
if (!x) {
|
||||
(function() {
|
||||
x = {};
|
||||
})();
|
||||
}
|
||||
if (y)
|
||||
(function() {})();
|
||||
else
|
||||
(function(z) {
|
||||
return z;
|
||||
})(0);
|
||||
}
|
||||
expect: {
|
||||
w;
|
||||
x || (x = {});
|
||||
y;
|
||||
}
|
||||
}
|
||||
|
||||
inner_var_for_in_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
var a = 1, b = 2;
|
||||
for (b in (function() {
|
||||
return x(a, b, c);
|
||||
})()) {
|
||||
var c = 3, d = 4;
|
||||
x(a, b, c, d);
|
||||
}
|
||||
x(a, b, c, d);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
var a = 1, b = 2;
|
||||
for (b in x(1, b, c)) {
|
||||
var c = 3, d = 4;
|
||||
x(1, b, c, d);
|
||||
}
|
||||
x(1, b, c, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
issue_1595_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
passes: 2,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function f(a) {
|
||||
return g(a + 1);
|
||||
})(2);
|
||||
}
|
||||
expect: {
|
||||
g(3);
|
||||
}
|
||||
}
|
||||
|
||||
issue_1758: {
|
||||
options = {
|
||||
inline: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function(c) {
|
||||
var undefined = 42;
|
||||
return function() {
|
||||
c--;
|
||||
c--, c.toString();
|
||||
return;
|
||||
}();
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function(c) {
|
||||
var undefined = 42;
|
||||
return c--, c--, void c.toString();
|
||||
}());
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
}
|
||||
wrap_iife: {
|
||||
options = {
|
||||
inline: true,
|
||||
negate_iife: false,
|
||||
}
|
||||
beautify = {
|
||||
wrap_iife: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
return function() {
|
||||
console.log('test')
|
||||
};
|
||||
})()();
|
||||
}
|
||||
expect_exact: 'void console.log("test");'
|
||||
}
|
||||
|
||||
wrap_iife_in_expression: {
|
||||
options = {
|
||||
inline: true,
|
||||
negate_iife: false,
|
||||
}
|
||||
beautify = {
|
||||
wrap_iife: true,
|
||||
}
|
||||
input: {
|
||||
foo = (function () {
|
||||
return bar();
|
||||
})();
|
||||
}
|
||||
expect_exact: 'foo=bar();'
|
||||
}
|
||||
|
||||
wrap_iife_in_return_call: {
|
||||
options = {
|
||||
inline: true,
|
||||
negate_iife: false,
|
||||
}
|
||||
beautify = {
|
||||
wrap_iife: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
return (function() {
|
||||
console.log('test')
|
||||
})();
|
||||
})()();
|
||||
}
|
||||
expect_exact: '(void console.log("test"))();'
|
||||
}
|
||||
|
||||
pure_annotation: {
|
||||
options = {
|
||||
inline: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
/*@__PURE__*/(function() {
|
||||
console.log("hello");
|
||||
}());
|
||||
}
|
||||
expect_exact: ""
|
||||
}
|
||||
|
||||
drop_fargs: {
|
||||
options = {
|
||||
cascade: true,
|
||||
inline: true,
|
||||
keep_fargs: false,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var a = 1;
|
||||
!function(a_1) {
|
||||
a++;
|
||||
}(a++ + (a && a.var));
|
||||
console.log(a);
|
||||
}
|
||||
expect: {
|
||||
var a = 1;
|
||||
!function() {
|
||||
a++;
|
||||
}(++a && a.var);
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: "3"
|
||||
}
|
||||
|
||||
keep_fargs: {
|
||||
options = {
|
||||
cascade: true,
|
||||
inline: true,
|
||||
keep_fargs: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var a = 1;
|
||||
!function(a_1) {
|
||||
a++;
|
||||
}(a++ + (a && a.var));
|
||||
console.log(a);
|
||||
}
|
||||
expect: {
|
||||
var a = 1;
|
||||
!function(a_1) {
|
||||
a++;
|
||||
}(++a && a.var);
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: "3"
|
||||
}
|
||||
@@ -22,7 +22,8 @@ negate_iife_1_off: {
|
||||
|
||||
negate_iife_2: {
|
||||
options = {
|
||||
negate_iife: true
|
||||
inline: true,
|
||||
negate_iife: true,
|
||||
};
|
||||
input: {
|
||||
(function(){ return {} })().x = 10;
|
||||
@@ -32,6 +33,7 @@ negate_iife_2: {
|
||||
|
||||
negate_iife_2_side_effects: {
|
||||
options = {
|
||||
inline: true,
|
||||
negate_iife: true,
|
||||
side_effects: true,
|
||||
}
|
||||
@@ -58,6 +60,7 @@ negate_iife_3_evaluate: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
negate_iife: true,
|
||||
}
|
||||
input: {
|
||||
@@ -100,6 +103,7 @@ negate_iife_3_off_evaluate: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
negate_iife: false,
|
||||
}
|
||||
input: {
|
||||
|
||||
38
test/compress/node_version.js
Normal file
38
test/compress/node_version.js
Normal file
@@ -0,0 +1,38 @@
|
||||
eval_let_6: {
|
||||
input: {
|
||||
eval("let a;");
|
||||
console.log();
|
||||
}
|
||||
expect: {
|
||||
eval("let a;");
|
||||
console.log();
|
||||
}
|
||||
expect_stdout: ""
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
eval_let_4: {
|
||||
input: {
|
||||
eval("let a;");
|
||||
console.log();
|
||||
}
|
||||
expect: {
|
||||
eval("let a;");
|
||||
console.log();
|
||||
}
|
||||
expect_stdout: SyntaxError("Block-scoped declarations (let, const, function, class) not yet supported outside strict mode")
|
||||
node_version: "4"
|
||||
}
|
||||
|
||||
eval_let_0: {
|
||||
input: {
|
||||
eval("let a;");
|
||||
console.log();
|
||||
}
|
||||
expect: {
|
||||
eval("let a;");
|
||||
console.log();
|
||||
}
|
||||
expect_stdout: SyntaxError("Unexpected identifier")
|
||||
node_version: "<=0.12"
|
||||
}
|
||||
@@ -555,3 +555,105 @@ native_prototype: {
|
||||
"".indexOf.call(e, "bar");
|
||||
}
|
||||
}
|
||||
|
||||
accessor_boolean: {
|
||||
input: {
|
||||
var a = 1;
|
||||
var b = {
|
||||
get true() {
|
||||
return a;
|
||||
},
|
||||
set false(c) {
|
||||
a = c;
|
||||
}
|
||||
};
|
||||
console.log(b.true, b.false = 2, b.true);
|
||||
}
|
||||
expect_exact: 'var a=1;var b={get true(){return a},set false(c){a=c}};console.log(b.true,b.false=2,b.true);'
|
||||
expect_stdout: "1 2 2"
|
||||
}
|
||||
|
||||
accessor_get_set: {
|
||||
input: {
|
||||
var a = 1;
|
||||
var b = {
|
||||
get set() {
|
||||
return a;
|
||||
},
|
||||
set get(c) {
|
||||
a = c;
|
||||
}
|
||||
};
|
||||
console.log(b.set, b.get = 2, b.set);
|
||||
}
|
||||
expect_exact: 'var a=1;var b={get set(){return a},set get(c){a=c}};console.log(b.set,b.get=2,b.set);'
|
||||
expect_stdout: "1 2 2"
|
||||
}
|
||||
|
||||
accessor_null_undefined: {
|
||||
input: {
|
||||
var a = 1;
|
||||
var b = {
|
||||
get null() {
|
||||
return a;
|
||||
},
|
||||
set undefined(c) {
|
||||
a = c;
|
||||
}
|
||||
};
|
||||
console.log(b.null, b.undefined = 2, b.null);
|
||||
}
|
||||
expect_exact: 'var a=1;var b={get null(){return a},set undefined(c){a=c}};console.log(b.null,b.undefined=2,b.null);'
|
||||
expect_stdout: "1 2 2"
|
||||
}
|
||||
|
||||
accessor_number: {
|
||||
input: {
|
||||
var a = 1;
|
||||
var b = {
|
||||
get 42() {
|
||||
return a;
|
||||
},
|
||||
set 42(c) {
|
||||
a = c;
|
||||
}
|
||||
};
|
||||
console.log(b[42], b[42] = 2, b[42]);
|
||||
}
|
||||
expect_exact: 'var a=1;var b={get 42(){return a},set 42(c){a=c}};console.log(b[42],b[42]=2,b[42]);'
|
||||
expect_stdout: "1 2 2"
|
||||
}
|
||||
|
||||
accessor_string: {
|
||||
input: {
|
||||
var a = 1;
|
||||
var b = {
|
||||
get "a-b"() {
|
||||
return a;
|
||||
},
|
||||
set "a-b"(c) {
|
||||
a = c;
|
||||
}
|
||||
};
|
||||
console.log(b["a-b"], b["a-b"] = 2, b["a-b"]);
|
||||
}
|
||||
expect_exact: 'var a=1;var b={get"a-b"(){return a},set"a-b"(c){a=c}};console.log(b["a-b"],b["a-b"]=2,b["a-b"]);'
|
||||
expect_stdout: "1 2 2"
|
||||
}
|
||||
|
||||
accessor_this: {
|
||||
input: {
|
||||
var a = 1;
|
||||
var b = {
|
||||
get this() {
|
||||
return a;
|
||||
},
|
||||
set this(c) {
|
||||
a = c;
|
||||
}
|
||||
};
|
||||
console.log(b.this, b.this = 2, b.this);
|
||||
}
|
||||
expect_exact: 'var a=1;var b={get this(){return a},set this(c){a=c}};console.log(b.this,b.this=2,b.this);'
|
||||
expect_stdout: "1 2 2"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ reduce_vars: {
|
||||
options = {
|
||||
conditionals : true,
|
||||
evaluate : true,
|
||||
inline : true,
|
||||
global_defs : {
|
||||
C : 0
|
||||
},
|
||||
@@ -1032,6 +1033,7 @@ defun_inline_2: {
|
||||
defun_inline_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
passes: 2,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
@@ -1054,6 +1056,7 @@ defun_inline_3: {
|
||||
|
||||
defun_call: {
|
||||
options = {
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
@@ -1080,6 +1083,7 @@ defun_call: {
|
||||
|
||||
defun_redefine: {
|
||||
options = {
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
@@ -1112,6 +1116,7 @@ defun_redefine: {
|
||||
|
||||
func_inline: {
|
||||
options = {
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
@@ -1138,6 +1143,7 @@ func_inline: {
|
||||
|
||||
func_modified: {
|
||||
options = {
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
@@ -1311,19 +1317,47 @@ iife_func_side_effects: {
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function x() {
|
||||
console.log("x");
|
||||
}
|
||||
function y() {
|
||||
console.log("y");
|
||||
}
|
||||
function z() {
|
||||
console.log("z");
|
||||
}
|
||||
(function(a, b, c) {
|
||||
return b();
|
||||
function y() {
|
||||
console.log("FAIL");
|
||||
}
|
||||
return y + b();
|
||||
})(x(), function() {
|
||||
return y();
|
||||
}, z());
|
||||
}
|
||||
expect: {
|
||||
function x() {
|
||||
console.log("x");
|
||||
}
|
||||
function y() {
|
||||
console.log("y");
|
||||
}
|
||||
function z() {
|
||||
console.log("z");
|
||||
}
|
||||
(function(a, b, c) {
|
||||
return function() {
|
||||
return y();
|
||||
}();
|
||||
})(x(), 0, z());
|
||||
console.log("FAIL");
|
||||
} + b();
|
||||
})(x(), function() {
|
||||
return y();
|
||||
}, z());
|
||||
}
|
||||
expect_stdout: [
|
||||
"x",
|
||||
"z",
|
||||
"y",
|
||||
]
|
||||
}
|
||||
|
||||
issue_1595_1: {
|
||||
@@ -1687,6 +1721,7 @@ redefine_arguments_1: {
|
||||
redefine_arguments_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
keep_fargs: false,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
@@ -1723,6 +1758,7 @@ redefine_arguments_2: {
|
||||
redefine_arguments_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
keep_fargs: false,
|
||||
passes: 3,
|
||||
reduce_vars: true,
|
||||
@@ -1799,6 +1835,7 @@ redefine_farg_1: {
|
||||
redefine_farg_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
keep_fargs: false,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
@@ -1835,6 +1872,7 @@ redefine_farg_2: {
|
||||
redefine_farg_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
keep_fargs: false,
|
||||
passes: 3,
|
||||
reduce_vars: true,
|
||||
|
||||
14
test/compress/sandbox.js
Normal file
14
test/compress/sandbox.js
Normal file
@@ -0,0 +1,14 @@
|
||||
console_log: {
|
||||
input: {
|
||||
console.log("%% %s");
|
||||
console.log("%% %s", "%s");
|
||||
}
|
||||
expect: {
|
||||
console.log("%% %s");
|
||||
console.log("%% %s", "%s");
|
||||
}
|
||||
expect_stdout: [
|
||||
"%% %s",
|
||||
"% %s",
|
||||
]
|
||||
}
|
||||
@@ -710,3 +710,23 @@ issue_27: {
|
||||
})(jQuery);
|
||||
}
|
||||
}
|
||||
|
||||
issue_2062: {
|
||||
options = {
|
||||
booleans: true,
|
||||
cascade: true,
|
||||
conditionals: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
var a = 1;
|
||||
if ([ a || a++ + a--, a++ + a--, a && a.var ]);
|
||||
console.log(a);
|
||||
}
|
||||
expect: {
|
||||
var a = 1;
|
||||
a || (a++, a--), a++, --a && a.var;
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
@@ -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(/[\\\\/]/)}"
|
||||
}
|
||||
|
||||
31
test/fetch.js
Normal file
31
test/fetch.js
Normal file
@@ -0,0 +1,31 @@
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
|
||||
try {
|
||||
fs.mkdirSync("./tmp");
|
||||
} catch (e) {
|
||||
if (e.code != "EEXIST") throw e;
|
||||
}
|
||||
|
||||
function local(url) {
|
||||
return path.join("./tmp", encodeURIComponent(url));
|
||||
}
|
||||
|
||||
function read(url) {
|
||||
return fs.createReadStream(local(url));
|
||||
}
|
||||
|
||||
module.exports = function(url, callback) {
|
||||
var result = read(url);
|
||||
result.on("error", function(e) {
|
||||
if (e.code != "ENOENT") return callback(e);
|
||||
require(url.slice(0, url.indexOf(":"))).get(url, function(res) {
|
||||
if (res.statusCode !== 200) return callback(res);
|
||||
res.pipe(fs.createWriteStream(local(url)).on("close", function() {
|
||||
callback(null, read(url));
|
||||
}));
|
||||
});
|
||||
}).on("open", function() {
|
||||
callback(null, result);
|
||||
});
|
||||
};
|
||||
1
test/input/issue-2082/sample.js
Normal file
1
test/input/issue-2082/sample.js
Normal file
@@ -0,0 +1 @@
|
||||
console.log(x);
|
||||
1
test/input/issue-2082/sample.js.map
Normal file
1
test/input/issue-2082/sample.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version": 3,"sources": ["index.js"],"mappings": ";"}
|
||||
5
test/input/issue-505/input.js
Normal file
5
test/input/issue-505/input.js
Normal file
@@ -0,0 +1,5 @@
|
||||
function test(callback) {
|
||||
'aaaaaaaaaaaaaaaa';
|
||||
callback(err, data);
|
||||
callback(err, data);
|
||||
}
|
||||
5
test/input/issue-505/output.js
Normal file
5
test/input/issue-505/output.js
Normal file
@@ -0,0 +1,5 @@
|
||||
function test(a){
|
||||
"aaaaaaaaaaaaaaaa"
|
||||
;a(err,data),a(err,data)
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsidGVzdCIsImNhbGxiYWNrIiwiZXJyIiwiZGF0YSJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsS0FBS0M7QUFDVjtDQUNBQSxFQUFTQyxJQUFLQyxNQUNkRixFQUFTQyxJQUFLQyJ9
|
||||
@@ -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
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
var site = "http://browserbench.org/JetStream/";
|
||||
var site = "http://browserbench.org/JetStream";
|
||||
if (typeof phantom == "undefined") {
|
||||
// workaround for tty output truncation upon process.exit()
|
||||
[process.stdout, process.stderr].forEach(function(stream){
|
||||
@@ -11,45 +11,69 @@ if (typeof phantom == "undefined") {
|
||||
stream._handle.setBlocking(true);
|
||||
});
|
||||
var args = process.argv.slice(2);
|
||||
var debug = args.indexOf("--debug");
|
||||
if (debug >= 0) {
|
||||
args.splice(debug, 1);
|
||||
debug = true;
|
||||
} else {
|
||||
debug = false;
|
||||
}
|
||||
if (!args.length) {
|
||||
args.push("-mc");
|
||||
args.push("-mcb", "beautify=false,webkit");
|
||||
}
|
||||
args.push("--stats");
|
||||
args.push("--timings");
|
||||
var child_process = require("child_process");
|
||||
try {
|
||||
require("phantomjs-prebuilt");
|
||||
} catch(e) {
|
||||
child_process.execSync("npm install phantomjs-prebuilt@2.1.14");
|
||||
}
|
||||
var fetch = require("./fetch");
|
||||
var http = require("http");
|
||||
var server = http.createServer(function(request, response) {
|
||||
request.resume();
|
||||
var url = decodeURIComponent(request.url.slice(1));
|
||||
var stderr = "";
|
||||
var uglifyjs = child_process.fork("bin/uglifyjs", args, {
|
||||
silent: true
|
||||
}).on("exit", function(code) {
|
||||
console.log("uglifyjs", url.indexOf(site) == 0 ? url.slice(site.length) : url, args.join(" "));
|
||||
console.log(stderr);
|
||||
if (code) throw new Error("uglifyjs failed with code " + code);
|
||||
});
|
||||
uglifyjs.stderr.on("data", function(data) {
|
||||
stderr += data;
|
||||
}).setEncoding("utf8");
|
||||
uglifyjs.stdout.pipe(response);
|
||||
http.get(url, function(res) {
|
||||
res.pipe(uglifyjs.stdin);
|
||||
});
|
||||
}).listen().on("listening", function() {
|
||||
var phantomjs = require("phantomjs-prebuilt");
|
||||
var program = phantomjs.exec(process.argv[1], server.address().port);
|
||||
program.stdout.pipe(process.stdout);
|
||||
program.stderr.pipe(process.stderr);
|
||||
program.on("exit", function(code) {
|
||||
server.close();
|
||||
if (code) throw new Error("JetStream failed!");
|
||||
console.log("JetStream completed successfully.");
|
||||
var url = site + request.url;
|
||||
fetch(url, function(err, res) {
|
||||
if (err) throw err;
|
||||
response.writeHead(200, {
|
||||
"Content-Type": {
|
||||
css: "text/css",
|
||||
js: "application/javascript",
|
||||
png: "image/png"
|
||||
}[url.slice(url.lastIndexOf(".") + 1)] || "text/html; charset=utf-8"
|
||||
});
|
||||
if (/\.js$/.test(url)) {
|
||||
var stderr = "";
|
||||
var uglifyjs = child_process.fork("bin/uglifyjs", args, {
|
||||
silent: true
|
||||
}).on("exit", function(code) {
|
||||
console.log("uglifyjs", url.slice(site.length + 1), args.join(" "));
|
||||
console.log(stderr);
|
||||
if (code) throw new Error("uglifyjs failed with code " + code);
|
||||
});
|
||||
uglifyjs.stderr.on("data", function(data) {
|
||||
stderr += data;
|
||||
}).setEncoding("utf8");
|
||||
uglifyjs.stdout.pipe(response);
|
||||
res.pipe(uglifyjs.stdin);
|
||||
} else {
|
||||
res.pipe(response);
|
||||
}
|
||||
});
|
||||
}).listen();
|
||||
server.on("listening", function() {
|
||||
var port = server.address().port;
|
||||
if (debug) {
|
||||
console.log("http://localhost:" + port + "/");
|
||||
} else {
|
||||
child_process.exec("npm install phantomjs-prebuilt@2.1.14 --no-save", function(error) {
|
||||
if (error) throw error;
|
||||
var program = require("phantomjs-prebuilt").exec(process.argv[1], port);
|
||||
program.stdout.pipe(process.stdout);
|
||||
program.stderr.pipe(process.stderr);
|
||||
program.on("exit", function(code) {
|
||||
server.close();
|
||||
if (code) throw new Error("JetStream failed!");
|
||||
console.log("JetStream completed successfully.");
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
server.timeout = 0;
|
||||
} else {
|
||||
@@ -63,10 +87,6 @@ if (typeof phantom == "undefined") {
|
||||
phantom.exit(1);
|
||||
};
|
||||
var url = "http://localhost:" + require("system").args[1] + "/";
|
||||
page.onResourceRequested = function(requestData, networkRequest) {
|
||||
if (/\.js$/.test(requestData.url))
|
||||
networkRequest.changeUrl(url + encodeURIComponent(requestData.url));
|
||||
}
|
||||
page.onConsoleMessage = function(msg) {
|
||||
if (/Error:/i.test(msg)) {
|
||||
console.error(msg);
|
||||
@@ -77,8 +97,8 @@ if (typeof phantom == "undefined") {
|
||||
phantom.exit();
|
||||
}
|
||||
};
|
||||
page.open(site, function(status) {
|
||||
if (status != "success") phantomjs.exit(1);
|
||||
page.open(url, function(status) {
|
||||
if (status != "success") phantom.exit(1);
|
||||
page.evaluate(function() {
|
||||
JetStream.switchToQuick();
|
||||
JetStream.start();
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -75,6 +77,23 @@ describe("bin/uglifyjs", function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("should not consider source map file content as source map file name (issue #2082)", function (done) {
|
||||
var command = [
|
||||
uglifyjscmd,
|
||||
"test/input/issue-2082/sample.js",
|
||||
"--source-map", "content=test/input/issue-2082/sample.js.map",
|
||||
"--source-map", "url=inline",
|
||||
].join(" ");
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
|
||||
var stderrLines = stderr.split('\n');
|
||||
assert.strictEqual(stderrLines[0], 'INFO: Using input source map: test/input/issue-2082/sample.js.map');
|
||||
assert.notStrictEqual(stderrLines[1], 'INFO: Using input source map: {"version": 3,"sources": ["index.js"],"mappings": ";"}');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should work with --keep-fnames (mangle only)", function (done) {
|
||||
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m';
|
||||
|
||||
@@ -173,7 +192,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 +531,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 +554,33 @@ 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[\s\S]*?\nERROR: `ascii-only` is not a supported option/.test(stderr), stderr);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should work with --mangle reserved=[]", function (done) {
|
||||
var command = uglifyjscmd + ' test/input/issue-505/input.js -m reserved=[callback]';
|
||||
|
||||
exec(command, function (err, stdout) {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(stdout, 'function test(callback){"aaaaaaaaaaaaaaaa";callback(err,data);callback(err,data)}\n');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should work with --mangle reserved=false", function (done) {
|
||||
var command = uglifyjscmd + ' test/input/issue-505/input.js -m reserved=false';
|
||||
|
||||
exec(command, function (err, stdout) {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(stdout, 'function test(a){"aaaaaaaaaaaaaaaa";a(err,data);a(err,data)}\n');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ describe("bin/uglifyjs with input file globs", function() {
|
||||
exec(command, function(err, stdout) {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(stdout, 'var print=console.log.bind(console),a=function(n){return 3*n}(3),b=function(n){return n/2}(12);print("qux",a,b),function(n){print("Foo:",2*n)}(11);\n');
|
||||
assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",2*11);\n');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,6 +13,13 @@ describe("minify", function() {
|
||||
assert.strictEqual(result.code, 'function foo(n){return n?3:7}');
|
||||
});
|
||||
|
||||
it("Should skip inherited keys from `files`", function() {
|
||||
var files = Object.create({ skip: this });
|
||||
files[0] = "alert(1 + 1)";
|
||||
var result = Uglify.minify(files);
|
||||
assert.strictEqual(result.code, "alert(2);");
|
||||
});
|
||||
|
||||
describe("keep_quoted_props", function() {
|
||||
it("Should preserve quotes in object literals", function() {
|
||||
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
|
||||
@@ -106,7 +113,7 @@ describe("minify", function() {
|
||||
content: "inline"
|
||||
}
|
||||
});
|
||||
assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();");
|
||||
assert.strictEqual(result.code, "var bar=function(){return function(bar){return bar}}();");
|
||||
assert.strictEqual(warnings.length, 1);
|
||||
assert.strictEqual(warnings[0], "inline source map not found");
|
||||
} finally {
|
||||
@@ -138,13 +145,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 +200,31 @@ 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");
|
||||
});
|
||||
it("should skip inherited properties", function() {
|
||||
var foo = Object.create({ skip: this });
|
||||
foo.bar = 42;
|
||||
var result = Uglify.minify("alert(FOO);", {
|
||||
compress: {
|
||||
global_defs: {
|
||||
FOO: foo
|
||||
}
|
||||
}
|
||||
});
|
||||
assert.strictEqual(result.code, "alert({bar:42});");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
var assert = require("assert");
|
||||
var semver = require("semver");
|
||||
var spawn = require("child_process").spawn;
|
||||
|
||||
if (!process.env.UGLIFYJS_TEST_ALL) return;
|
||||
|
||||
function run(command, args, done) {
|
||||
var id = setInterval(function() {
|
||||
process.stdout.write("\0");
|
||||
}, 5 * 60 * 1000);
|
||||
spawn(command, args, {
|
||||
stdio: "ignore"
|
||||
stdio: [ "ignore", 1, 2 ]
|
||||
}).on("exit", function(code) {
|
||||
clearInterval(id);
|
||||
assert.strictEqual(code, 0);
|
||||
done();
|
||||
});
|
||||
@@ -36,11 +33,9 @@ describe("test/benchmark.js", function() {
|
||||
});
|
||||
});
|
||||
|
||||
if (semver.satisfies(process.version, "0.12")) return;
|
||||
describe("test/jetstream.js", function() {
|
||||
this.timeout(20 * 60 * 1000);
|
||||
it("Should install phantomjs-prebuilt", function(done) {
|
||||
run("npm", ["install", "phantomjs-prebuilt@2.1.14"], done);
|
||||
});
|
||||
[
|
||||
"-mc",
|
||||
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
|
||||
@@ -48,6 +43,7 @@ describe("test/jetstream.js", function() {
|
||||
it("Should pass with options " + options, function(done) {
|
||||
var args = options.split(/ /);
|
||||
args.unshift("test/jetstream.js");
|
||||
args.push("-b", "beautify=false,webkit");
|
||||
run(process.argv[0], args, done);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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,11 +291,27 @@ 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) {
|
||||
test[label.name] = stat.body.value;
|
||||
if (stat.TYPE == "SimpleStatement") {
|
||||
var body = stat.body;
|
||||
if (body instanceof U.AST_Boolean) {
|
||||
test[label.name] = body.value;
|
||||
} else if (body instanceof U.AST_Call) {
|
||||
var ctor = global[body.expression.name];
|
||||
assert.ok(ctor === Error || ctor.prototype instanceof Error, tmpl("Unsupported expect_stdout format [{line},{col}]", {
|
||||
line: label.start.line,
|
||||
col: label.start.col
|
||||
}));
|
||||
test[label.name] = ctor.apply(null, body.args.map(function(node) {
|
||||
assert.ok(node instanceof U.AST_Constant, tmpl("Unsupported expect_stdout format [{line},{col}]", {
|
||||
line: label.start.line,
|
||||
col: label.start.col
|
||||
}));
|
||||
return node.value;
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
test[label.name] = read_string(stat) + "\n";
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
var semver = require("semver");
|
||||
var vm = require("vm");
|
||||
|
||||
function safe_log(arg, level) {
|
||||
@@ -18,23 +19,25 @@ function safe_log(arg, level) {
|
||||
|
||||
var FUNC_TOSTRING = [
|
||||
"Function.prototype.toString = Function.prototype.valueOf = function() {",
|
||||
" var id = 0;",
|
||||
" var id = 100000;",
|
||||
" return function() {",
|
||||
' if (this === Array) return "[Function: Array]";',
|
||||
' if (this === Object) return "[Function: Object]";',
|
||||
" var i = this.name;",
|
||||
' if (typeof i != "number") {',
|
||||
" i = ++id;",
|
||||
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
|
||||
' Object.defineProperty(this, "name", {',
|
||||
" get: function() {",
|
||||
" return i;",
|
||||
" }",
|
||||
" });",
|
||||
] : [], [
|
||||
" }",
|
||||
' return "[Function: " + i + "]";',
|
||||
" }",
|
||||
"}();",
|
||||
].join("\n");
|
||||
]).join("\n");
|
||||
exports.run_code = function(code) {
|
||||
var stdout = "";
|
||||
var original_write = process.stdout.write;
|
||||
@@ -49,7 +52,10 @@ exports.run_code = function(code) {
|
||||
"}();",
|
||||
].join("\n"), {
|
||||
console: {
|
||||
log: function() {
|
||||
log: function(msg) {
|
||||
if (arguments.length == 1 && typeof msg == "string") {
|
||||
return console.log("%s", msg);
|
||||
}
|
||||
return console.log.apply(console, [].map.call(arguments, function(arg) {
|
||||
return safe_log(arg, 3);
|
||||
}));
|
||||
@@ -63,7 +69,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;
|
||||
|
||||
@@ -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
|
||||
|
||||
169
test/ufuzz.js
169
test/ufuzz.js
@@ -12,7 +12,7 @@
|
||||
stream._handle.setBlocking(true);
|
||||
});
|
||||
|
||||
var UglifyJS = require("./node");
|
||||
var UglifyJS = require("..");
|
||||
var randomBytes = require("crypto").randomBytes;
|
||||
var sandbox = require("./sandbox");
|
||||
|
||||
@@ -102,23 +102,23 @@ for (var i = 2; i < process.argv.length; ++i) {
|
||||
case '--help':
|
||||
case '-h':
|
||||
case '-?':
|
||||
console.log('** UglifyJS fuzzer help **');
|
||||
console.log('Valid options (optional):');
|
||||
console.log('<number>: generate this many cases (if used must be first arg)');
|
||||
console.log('-v: print every generated test case');
|
||||
console.log('-V: print every 100th generated test case');
|
||||
console.log('-t <int>: generate this many toplevels per run (more take longer)');
|
||||
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)');
|
||||
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)');
|
||||
console.log('-s2 <statement name>: force the second level statement to be this one (see list below)');
|
||||
console.log('--no-catch-redef: do not redefine catch variables');
|
||||
console.log('--no-directive: do not generate directives');
|
||||
console.log('--use-strict: generate "use strict"');
|
||||
console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
|
||||
console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
|
||||
console.log('--without-stmt <statement names>: a comma delimited black list of statements never to generate');
|
||||
console.log('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID));
|
||||
console.log('** UglifyJS fuzzer exiting **');
|
||||
println('** UglifyJS fuzzer help **');
|
||||
println('Valid options (optional):');
|
||||
println('<number>: generate this many cases (if used must be first arg)');
|
||||
println('-v: print every generated test case');
|
||||
println('-V: print every 100th generated test case');
|
||||
println('-t <int>: generate this many toplevels per run (more take longer)');
|
||||
println('-r <int>: maximum recursion depth for generator (higher takes longer)');
|
||||
println('-s1 <statement name>: force the first level statement to be this one (see list below)');
|
||||
println('-s2 <statement name>: force the second level statement to be this one (see list below)');
|
||||
println('--no-catch-redef: do not redefine catch variables');
|
||||
println('--no-directive: do not generate directives');
|
||||
println('--use-strict: generate "use strict"');
|
||||
println('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
|
||||
println('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
|
||||
println('--without-stmt <statement names>: a comma delimited black list of statements never to generate');
|
||||
println('List of accepted statement names: ' + Object.keys(STMT_ARG_TO_ID));
|
||||
println('** UglifyJS fuzzer exiting **');
|
||||
return 0;
|
||||
default:
|
||||
// first arg may be a number.
|
||||
@@ -941,7 +941,17 @@ if (require.main !== module) {
|
||||
return;
|
||||
}
|
||||
|
||||
function try_beautify(code, result) {
|
||||
function println(msg) {
|
||||
if (typeof msg != "undefined") process.stdout.write(msg);
|
||||
process.stdout.write("\n");
|
||||
}
|
||||
|
||||
function errorln(msg) {
|
||||
if (typeof msg != "undefined") process.stderr.write(msg);
|
||||
process.stderr.write("\n");
|
||||
}
|
||||
|
||||
function try_beautify(code, result, printfn) {
|
||||
var beautified = UglifyJS.minify(code, {
|
||||
compress: false,
|
||||
mangle: false,
|
||||
@@ -951,51 +961,34 @@ function try_beautify(code, result) {
|
||||
},
|
||||
});
|
||||
if (beautified.error) {
|
||||
console.log("// !!! beautify failed !!!");
|
||||
console.log(beautified.error.stack);
|
||||
printfn("// !!! beautify failed !!!");
|
||||
printfn(beautified.error.stack);
|
||||
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) {
|
||||
console.log("// (beautified)");
|
||||
console.log(beautified.code);
|
||||
printfn("// (beautified)");
|
||||
printfn(beautified.code);
|
||||
return;
|
||||
}
|
||||
console.log("//");
|
||||
console.log(code);
|
||||
printfn("//");
|
||||
printfn(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;
|
||||
m[component] = o;
|
||||
var result = UglifyJS.minify(original_code, m);
|
||||
if (result.error) {
|
||||
console.log("Error testing options." + component + "." + name);
|
||||
console.log(result.error);
|
||||
errorln("Error testing options." + component + "." + name);
|
||||
errorln(result.error.stack);
|
||||
} else {
|
||||
var r = sandbox.run_code(result.code);
|
||||
return sandbox.same_stdout(original_result, r);
|
||||
@@ -1003,49 +996,49 @@ function log_suspects(minify_options, component) {
|
||||
}
|
||||
});
|
||||
if (suspects.length > 0) {
|
||||
console.log("Suspicious", component, "options:");
|
||||
errorln("Suspicious " + component + " options:");
|
||||
suspects.forEach(function(name) {
|
||||
console.log(" " + name);
|
||||
errorln(" " + name);
|
||||
});
|
||||
console.log();
|
||||
errorln();
|
||||
}
|
||||
}
|
||||
|
||||
function log(options) {
|
||||
if (!ok) console.log('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
|
||||
console.log("//=============================================================");
|
||||
if (!ok) console.log("// !!!!!! Failed... round", round);
|
||||
console.log("// original code");
|
||||
try_beautify(original_code, original_result);
|
||||
console.log();
|
||||
console.log();
|
||||
console.log("//-------------------------------------------------------------");
|
||||
if (!ok) errorln('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
|
||||
errorln("//=============================================================");
|
||||
if (!ok) errorln("// !!!!!! Failed... round " + round);
|
||||
errorln("// original code");
|
||||
try_beautify(original_code, original_result, errorln);
|
||||
errorln();
|
||||
errorln();
|
||||
errorln("//-------------------------------------------------------------");
|
||||
if (typeof uglify_code == "string") {
|
||||
console.log("// uglified code");
|
||||
try_beautify(uglify_code, uglify_result);
|
||||
console.log();
|
||||
console.log();
|
||||
console.log("original result:");
|
||||
console.log(original_result);
|
||||
console.log("uglified result:");
|
||||
console.log(uglify_result);
|
||||
errorln("// uglified code");
|
||||
try_beautify(uglify_code, uglify_result, errorln);
|
||||
errorln();
|
||||
errorln();
|
||||
errorln("original result:");
|
||||
errorln(typeof original_result == "string" ? original_result : original_result.stack);
|
||||
errorln("uglified result:");
|
||||
errorln(typeof uglify_result == "string" ? uglify_result : uglify_result.stack);
|
||||
} else {
|
||||
console.log("// !!! uglify failed !!!");
|
||||
console.log(uglify_code.stack);
|
||||
errorln("// !!! uglify failed !!!");
|
||||
errorln(uglify_code.stack);
|
||||
if (typeof original_result != "string") {
|
||||
console.log();
|
||||
console.log();
|
||||
console.log("original stacktrace:");
|
||||
console.log(original_result.stack);
|
||||
errorln();
|
||||
errorln();
|
||||
errorln("original stacktrace:");
|
||||
errorln(original_result.stack);
|
||||
}
|
||||
}
|
||||
console.log("minify(options):");
|
||||
errorln("minify(options):");
|
||||
options = JSON.parse(options);
|
||||
console.log(options);
|
||||
console.log();
|
||||
errorln(JSON.stringify(options, null, 2));
|
||||
errorln();
|
||||
if (!ok && typeof uglify_code == "string") {
|
||||
Object.keys(default_options).forEach(log_suspects.bind(null, options));
|
||||
console.log("!!!!!! Failed... round", round);
|
||||
errorln("!!!!!! Failed... round " + round);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1075,19 +1068,19 @@ for (var round = 1; round <= num_iterations; round++) {
|
||||
}
|
||||
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
|
||||
else if (typeof original_result != "string") {
|
||||
console.log("//=============================================================");
|
||||
console.log("// original code");
|
||||
try_beautify(original_code, original_result);
|
||||
console.log();
|
||||
console.log();
|
||||
console.log("original result:");
|
||||
console.log(original_result);
|
||||
console.log();
|
||||
println("//=============================================================");
|
||||
println("// original code");
|
||||
try_beautify(original_code, original_result, println);
|
||||
println();
|
||||
println();
|
||||
println("original result:");
|
||||
println(original_result.stack);
|
||||
println();
|
||||
}
|
||||
if (!ok && isFinite(num_iterations)) {
|
||||
console.log();
|
||||
println();
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
console.log();
|
||||
println();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user