Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c28e1a0237 | ||
|
|
1a95007ec1 | ||
|
|
ed80b4a534 | ||
|
|
4f09df238e | ||
|
|
d9ad3c7cbf | ||
|
|
6ea3f7fe34 | ||
|
|
4c4dc2137c | ||
|
|
4aa4b3e694 | ||
|
|
2604aadb37 | ||
|
|
964d5b9aa4 | ||
|
|
b7adbcab1f | ||
|
|
3435af494f | ||
|
|
41c627379c | ||
|
|
e54df2226f | ||
|
|
b1febde3e9 | ||
|
|
193049af19 | ||
|
|
4a0bab0fa3 | ||
|
|
9243b0cb9d | ||
|
|
fc9ba323c4 | ||
|
|
d0689c81bb | ||
|
|
02a84385a0 | ||
|
|
a4889a0f2e | ||
|
|
f29f07aabd | ||
|
|
188e28efd7 | ||
|
|
2df48924cc | ||
|
|
9fc6796d2a | ||
|
|
9fc8a52142 | ||
|
|
3a21861580 | ||
|
|
1dbffd48ea | ||
|
|
22a038e6a2 | ||
|
|
f652372c9a | ||
|
|
ad1fc3b71a | ||
|
|
2b40a5ac62 | ||
|
|
ca3388cf5a | ||
|
|
caa8896a8a | ||
|
|
d13aa3954d | ||
|
|
f64539fb76 | ||
|
|
d56ebd7d7b | ||
|
|
3edfe7d0ee | ||
|
|
7f77edadb3 | ||
|
|
a9511dfbe5 | ||
|
|
064e7aa1bb | ||
|
|
46814f88d9 | ||
|
|
4a19802d0c | ||
|
|
1e9f98aa51 | ||
|
|
11e24d53a1 | ||
|
|
0f509f8336 | ||
|
|
a6ed2c84ac | ||
|
|
a1958aad56 | ||
|
|
672699613e | ||
|
|
645d5bdbc5 | ||
|
|
9af2bbffde | ||
|
|
fcd544cc10 | ||
|
|
1e3bc0caa0 | ||
|
|
8227e8795b | ||
|
|
790b3bcdc6 | ||
|
|
d6e6458f68 | ||
|
|
a54b6703c0 | ||
|
|
8e6266136d | ||
|
|
5c22a1bdf5 | ||
|
|
9794ebf88c | ||
|
|
68394eed93 | ||
|
|
753b4b6cc8 | ||
|
|
a9c1b9f138 | ||
|
|
5af144522a | ||
|
|
483e0cadfb | ||
|
|
4b818056cf | ||
|
|
b956e5f1d9 | ||
|
|
37d7cb8565 | ||
|
|
2b8e206fec | ||
|
|
a869b854fa | ||
|
|
81f5efe39a | ||
|
|
69dde0462b | ||
|
|
7628bcac01 | ||
|
|
75f0bbe6e8 | ||
|
|
478bf4dbdd | ||
|
|
e0f67baf2d | ||
|
|
b14d3df3d2 | ||
|
|
24e58ee70c | ||
|
|
9b1a40dfc3 | ||
|
|
e4b078cff7 | ||
|
|
3bd7ca9961 | ||
|
|
f83aca65b7 | ||
|
|
aebafad41e | ||
|
|
26746ce316 | ||
|
|
dac6efb43d | ||
|
|
8880f4824c | ||
|
|
cb0c576bdd | ||
|
|
3a591c43fe | ||
|
|
db66eca958 | ||
|
|
f2767452e6 | ||
|
|
916faf0a48 | ||
|
|
f36e4e9a78 | ||
|
|
fdf8b5eb71 | ||
|
|
de7ec7f1b7 | ||
|
|
3c8a0bdff4 | ||
|
|
9e8ba27dcd | ||
|
|
719a8fd102 | ||
|
|
3a22e917de | ||
|
|
a9af2c9e62 |
7
.travis.yml
Normal file
7
.travis.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- "0.4"
|
||||||
|
- "0.6"
|
||||||
|
- "0.8"
|
||||||
|
- "0.10"
|
||||||
|
- "0.11"
|
||||||
29
LICENSE
Normal file
29
LICENSE
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
UglifyJS is released under the BSD license:
|
||||||
|
|
||||||
|
Copyright 2012-2013 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials
|
||||||
|
provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||||
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||||
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGE.
|
||||||
329
README.md
329
README.md
@@ -1,5 +1,6 @@
|
|||||||
UglifyJS 2
|
UglifyJS 2
|
||||||
==========
|
==========
|
||||||
|
[](https://travis-ci.org/mishoo/UglifyJS2)
|
||||||
|
|
||||||
UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit.
|
UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit.
|
||||||
|
|
||||||
@@ -45,52 +46,64 @@ files.
|
|||||||
|
|
||||||
The available options are:
|
The available options are:
|
||||||
|
|
||||||
--source-map Specify an output file where to generate source map.
|
```
|
||||||
[string]
|
--source-map Specify an output file where to generate source map.
|
||||||
--source-map-root The path to the original source to be included in the
|
[string]
|
||||||
source map. [string]
|
--source-map-root The path to the original source to be included in the
|
||||||
--source-map-url The path to the source map to be added in //@
|
source map. [string]
|
||||||
sourceMappingURL. Defaults to the value passed with
|
--source-map-url The path to the source map to be added in //#
|
||||||
--source-map. [string]
|
sourceMappingURL. Defaults to the value passed with
|
||||||
--in-source-map Input source map, useful if you're compressing JS that was
|
--source-map. [string]
|
||||||
generated from some other original code.
|
--in-source-map Input source map, useful if you're compressing JS that was
|
||||||
-p, --prefix Skip prefix for original filenames that appear in source
|
generated from some other original code.
|
||||||
maps. For example -p 3 will drop 3 directories from file
|
--screw-ie8 Pass this flag if you don't care about full compliance
|
||||||
names and ensure they are relative paths.
|
with Internet Explorer 6-8 quirks (by default UglifyJS
|
||||||
-o, --output Output file (default STDOUT).
|
will try to be IE-proof). [boolean]
|
||||||
-b, --beautify Beautify output/specify output options. [string]
|
--expr Parse a single expression, rather than a program (for
|
||||||
-m, --mangle Mangle names/pass mangler options. [string]
|
parsing JSON) [boolean]
|
||||||
-r, --reserved Reserved names to exclude from mangling.
|
-p, --prefix Skip prefix for original filenames that appear in source
|
||||||
-c, --compress Enable compressor/pass compressor options. Pass options
|
maps. For example -p 3 will drop 3 directories from file
|
||||||
like -c hoist_vars=false,if_return=false. Use -c with no
|
names and ensure they are relative paths. You can also
|
||||||
argument to use the default compression options. [string]
|
specify -p relative, which will make UglifyJS figure out
|
||||||
-d, --define Global definitions [string]
|
itself the relative paths between original sources, the
|
||||||
--comments Preserve copyright comments in the output. By default this
|
source map and the output file. [string]
|
||||||
works like Google Closure, keeping JSDoc-style comments
|
-o, --output Output file (default STDOUT).
|
||||||
that contain "@license" or "@preserve". You can optionally
|
-b, --beautify Beautify output/specify output options. [string]
|
||||||
pass one of the following arguments to this flag:
|
-m, --mangle Mangle names/pass mangler options. [string]
|
||||||
- "all" to keep all comments
|
-r, --reserved Reserved names to exclude from mangling.
|
||||||
- a valid JS regexp (needs to start with a slash) to keep
|
-c, --compress Enable compressor/pass compressor options. Pass options
|
||||||
only comments that match.
|
like -c hoist_vars=false,if_return=false. Use -c with no
|
||||||
Note that currently not *all* comments can be kept when
|
argument to use the default compression options. [string]
|
||||||
compression is on, because of dead code removal or
|
-d, --define Global definitions [string]
|
||||||
cascading statements into sequences. [string]
|
-e, --enclose Embed everything in a big function, with a configurable
|
||||||
--stats Display operations run time on STDERR. [boolean]
|
parameter/argument list. [string]
|
||||||
--acorn Use Acorn for parsing. [boolean]
|
--comments Preserve copyright comments in the output. By default this
|
||||||
--spidermonkey Assume input fles are SpiderMonkey AST format (as JSON).
|
works like Google Closure, keeping JSDoc-style comments
|
||||||
[boolean]
|
that contain "@license" or "@preserve". You can optionally
|
||||||
--self Build itself (UglifyJS2) as a library (implies
|
pass one of the following arguments to this flag:
|
||||||
--wrap=UglifyJS --export-all) [boolean]
|
- "all" to keep all comments
|
||||||
--wrap Embed everything in a big function, making the “exports”
|
- a valid JS regexp (needs to start with a slash) to keep
|
||||||
and “global” variables available. You need to pass an
|
only comments that match.
|
||||||
argument to this option to specify the name that your
|
Note that currently not *all* comments can be kept when
|
||||||
module will take when included in, say, a browser.
|
compression is on, because of dead code removal or
|
||||||
[string]
|
cascading statements into sequences. [string]
|
||||||
--export-all Only used when --wrap, this tells UglifyJS to add code to
|
--stats Display operations run time on STDERR. [boolean]
|
||||||
automatically export all globals. [boolean]
|
--acorn Use Acorn for parsing. [boolean]
|
||||||
--lint Display some scope warnings [boolean]
|
--spidermonkey Assume input files are SpiderMonkey AST format (as JSON).
|
||||||
-v, --verbose Verbose [boolean]
|
[boolean]
|
||||||
-V, --version Print version number and exit. [boolean]
|
--self Build itself (UglifyJS2) as a library (implies
|
||||||
|
--wrap=UglifyJS --export-all) [boolean]
|
||||||
|
--wrap Embed everything in a big function, making the “exports”
|
||||||
|
and “global” variables available. You need to pass an
|
||||||
|
argument to this option to specify the name that your
|
||||||
|
module will take when included in, say, a browser.
|
||||||
|
[string]
|
||||||
|
--export-all Only used when --wrap, this tells UglifyJS to add code to
|
||||||
|
automatically export all globals. [boolean]
|
||||||
|
--lint Display some scope warnings [boolean]
|
||||||
|
-v, --verbose Verbose [boolean]
|
||||||
|
-V, --version Print version number and exit. [boolean]
|
||||||
|
```
|
||||||
|
|
||||||
Specify `--output` (`-o`) to declare the output file. Otherwise the output
|
Specify `--output` (`-o`) to declare the output file. Otherwise the output
|
||||||
goes to STDOUT.
|
goes to STDOUT.
|
||||||
@@ -141,12 +154,19 @@ input files from the command line.
|
|||||||
|
|
||||||
## Mangler options
|
## Mangler options
|
||||||
|
|
||||||
To enable the mangler you need to pass `--mangle` (`-m`). Optionally you
|
To enable the mangler you need to pass `--mangle` (`-m`). The following
|
||||||
can pass `-m sort=true` (we'll possibly have other flags in the future) in order
|
(comma-separated) options are supported:
|
||||||
to assign shorter names to most frequently used variables. This saves a few
|
|
||||||
hundred bytes on jQuery before gzip, but the output is _bigger_ after gzip
|
- `sort` — to assign shorter names to most frequently used variables. This
|
||||||
(and seems to happen for other libraries I tried it on) therefore it's not
|
saves a few hundred bytes on jQuery before gzip, but the output is
|
||||||
enabled by default.
|
_bigger_ after gzip (and seems to happen for other libraries I tried it
|
||||||
|
on) therefore it's not enabled by default.
|
||||||
|
|
||||||
|
- `toplevel` — mangle names declared in the toplevel scope (disabled by
|
||||||
|
default).
|
||||||
|
|
||||||
|
- `eval` — mangle names visible in scopes where `eval` or `when` are used
|
||||||
|
(disabled by default).
|
||||||
|
|
||||||
When mangling is enabled but you want to prevent certain names from being
|
When mangling is enabled but you want to prevent certain names from being
|
||||||
mangled, you can declare those names with `--reserved` (`-r`) — pass a
|
mangled, you can declare those names with `--reserved` (`-r`) — pass a
|
||||||
@@ -163,15 +183,12 @@ you can pass a comma-separated list of options. Options are in the form
|
|||||||
`foo=bar`, or just `foo` (the latter implies a boolean option that you want
|
`foo=bar`, or just `foo` (the latter implies a boolean option that you want
|
||||||
to set `true`; it's effectively a shortcut for `foo=true`).
|
to set `true`; it's effectively a shortcut for `foo=true`).
|
||||||
|
|
||||||
The defaults should be tuned for maximum compression on most code. Here are
|
|
||||||
the available options (all are `true` by default, except `hoist_vars`):
|
|
||||||
|
|
||||||
- `sequences` -- join consecutive simple statements using the comma operator
|
- `sequences` -- join consecutive simple statements using the comma operator
|
||||||
- `properties` -- rewrite property access using the dot notation, for
|
- `properties` -- rewrite property access using the dot notation, for
|
||||||
example `foo["bar"] → foo.bar`
|
example `foo["bar"] → foo.bar`
|
||||||
- `dead_code` -- remove unreachable code
|
- `dead_code` -- remove unreachable code
|
||||||
- `drop_debugger` -- remove `debugger;` statements
|
- `drop_debugger` -- remove `debugger;` statements
|
||||||
- `unsafe` -- apply "unsafe" transformations (discussion below)
|
- `unsafe` (default: false) -- apply "unsafe" transformations (discussion below)
|
||||||
- `conditionals` -- apply optimizations for `if`-s and conditional
|
- `conditionals` -- apply optimizations for `if`-s and conditional
|
||||||
expressions
|
expressions
|
||||||
- `comparisons` -- apply certain optimizations to binary nodes, for example:
|
- `comparisons` -- apply certain optimizations to binary nodes, for example:
|
||||||
@@ -184,14 +201,33 @@ the available options (all are `true` by default, except `hoist_vars`):
|
|||||||
statically determine the condition
|
statically determine the condition
|
||||||
- `unused` -- drop unreferenced functions and variables
|
- `unused` -- drop unreferenced functions and variables
|
||||||
- `hoist_funs` -- hoist function declarations
|
- `hoist_funs` -- hoist function declarations
|
||||||
- `hoist_vars` -- hoist `var` declarations (this is `false` by default
|
- `hoist_vars` (default: false) -- hoist `var` declarations (this is `false`
|
||||||
because it seems to increase the size of the output in general)
|
by default because it seems to increase the size of the output in general)
|
||||||
- `if_return` -- optimizations for if/return and if/continue
|
- `if_return` -- optimizations for if/return and if/continue
|
||||||
- `join_vars` -- join consecutive `var` statements
|
- `join_vars` -- join consecutive `var` statements
|
||||||
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
|
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
|
||||||
and `x = something(), x` into `x = something()`
|
and `x = something(), x` into `x = something()`
|
||||||
- `warnings` -- display warnings when dropping unreachable code or unused
|
- `warnings` -- display warnings when dropping unreachable code or unused
|
||||||
declarations etc.
|
declarations etc.
|
||||||
|
- `negate_iife` -- negate "Immediately-Called Function Expressions"
|
||||||
|
where the return value is discarded, to avoid the parens that the
|
||||||
|
code generator would insert.
|
||||||
|
|
||||||
|
### The `unsafe` option
|
||||||
|
|
||||||
|
It enables some transformations that *might* break code logic in certain
|
||||||
|
contrived cases, but should be fine for most code. You might want to try it
|
||||||
|
on your own code, it should reduce the minified size. Here's what happens
|
||||||
|
when this flag is on:
|
||||||
|
|
||||||
|
- `new Array(1, 2, 3)` or `Array(1, 2, 3)` → `[1, 2, 3 ]`
|
||||||
|
- `new Object()` → `{}`
|
||||||
|
- `String(exp)` or `exp.toString()` → `"" + exp`
|
||||||
|
- `new Object/RegExp/Function/Error/Array (...)` → we discard the `new`
|
||||||
|
- `typeof foo == "undefined"` → `foo === void 0`
|
||||||
|
- `void 0` → `"undefined"` (if there is a variable named "undefined" in
|
||||||
|
scope; we do it because the variable name will be mangled, typically
|
||||||
|
reduced to a single character).
|
||||||
|
|
||||||
### Conditional compilation
|
### Conditional compilation
|
||||||
|
|
||||||
@@ -199,10 +235,11 @@ You can use the `--define` (`-d`) switch in order to declare global
|
|||||||
variables that UglifyJS will assume to be constants (unless defined in
|
variables that UglifyJS will assume to be constants (unless defined in
|
||||||
scope). For example if you pass `--define DEBUG=false` then, coupled with
|
scope). For example if you pass `--define DEBUG=false` then, coupled with
|
||||||
dead code removal UglifyJS will discard the following from the output:
|
dead code removal UglifyJS will discard the following from the output:
|
||||||
|
```javascript
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
console.log("debug stuff");
|
console.log("debug stuff");
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
UglifyJS will warn about the condition being always false and about dropping
|
UglifyJS will warn about the condition being always false and about dropping
|
||||||
unreachable code; for now there is no option to turn off only this specific
|
unreachable code; for now there is no option to turn off only this specific
|
||||||
@@ -211,10 +248,11 @@ warning, you can pass `warnings=false` to turn off *all* warnings.
|
|||||||
Another way of doing that is to declare your globals as constants in a
|
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
|
separate file and include it into the build. For example you can have a
|
||||||
`build/defines.js` file with the following:
|
`build/defines.js` file with the following:
|
||||||
|
```javascript
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
const PRODUCTION = true;
|
const PRODUCTION = true;
|
||||||
// etc.
|
// etc.
|
||||||
|
```
|
||||||
|
|
||||||
and build your code like this:
|
and build your code like this:
|
||||||
|
|
||||||
@@ -251,9 +289,6 @@ can pass additional arguments that control the code output:
|
|||||||
It doesn't work very well currently, but it does make the code generated
|
It doesn't work very well currently, but it does make the code generated
|
||||||
by UglifyJS more readable.
|
by UglifyJS more readable.
|
||||||
- `max-line-len` (default 32000) -- maximum line length (for uglified code)
|
- `max-line-len` (default 32000) -- maximum line length (for uglified code)
|
||||||
- `ie-proof` (default `true`) -- generate “IE-proof” code (for now this
|
|
||||||
means add brackets around the do/while in code like this: `if (foo) do
|
|
||||||
something(); while (bar); else ...`.
|
|
||||||
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
|
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
|
||||||
`do`, `while` or `with` statements, even if their body is a single
|
`do`, `while` or `with` statements, even if their body is a single
|
||||||
statement.
|
statement.
|
||||||
@@ -273,14 +308,15 @@ keep only comments that match this regexp. For example `--comments
|
|||||||
|
|
||||||
Note, however, that there might be situations where comments are lost. For
|
Note, however, that there might be situations where comments are lost. For
|
||||||
example:
|
example:
|
||||||
|
```javascript
|
||||||
function f() {
|
function f() {
|
||||||
/** @preserve Foo Bar */
|
/** @preserve Foo Bar */
|
||||||
function g() {
|
function g() {
|
||||||
// this function is never called
|
// this function is never called
|
||||||
}
|
}
|
||||||
return something();
|
return something();
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Even though it has "@preserve", the comment will be lost because the inner
|
Even though it has "@preserve", the comment will be lost because the inner
|
||||||
function `g` (which is the AST node to which the comment is attached to) is
|
function `g` (which is the AST node to which the comment is attached to) is
|
||||||
@@ -322,8 +358,9 @@ API Reference
|
|||||||
|
|
||||||
Assuming installation via NPM, you can load UglifyJS in your application
|
Assuming installation via NPM, you can load UglifyJS in your application
|
||||||
like this:
|
like this:
|
||||||
|
```javascript
|
||||||
var UglifyJS = require("uglify-js");
|
var UglifyJS = require("uglify-js");
|
||||||
|
```
|
||||||
|
|
||||||
It exports a lot of names, but I'll discuss here the basics that are needed
|
It exports a lot of names, but I'll discuss here the basics that are needed
|
||||||
for parsing, mangling and compressing a piece of code. The sequence is (1)
|
for parsing, mangling and compressing a piece of code. The sequence is (1)
|
||||||
@@ -334,45 +371,49 @@ parse, (2) compress, (3) mangle, (4) generate output code.
|
|||||||
There's a single toplevel function which combines all the steps. If you
|
There's a single toplevel function which combines all the steps. If you
|
||||||
don't need additional customization, you might want to go with `minify`.
|
don't need additional customization, you might want to go with `minify`.
|
||||||
Example:
|
Example:
|
||||||
|
```javascript
|
||||||
var result = UglifyJS.minify("/path/to/file.js");
|
var result = UglifyJS.minify("/path/to/file.js");
|
||||||
console.log(result.code); // minified output
|
console.log(result.code); // minified output
|
||||||
// if you need to pass code instead of file name
|
// if you need to pass code instead of file name
|
||||||
var result = UglifyJS.minify("var b = function () {};", {fromString: true});
|
var result = UglifyJS.minify("var b = function () {};", {fromString: true});
|
||||||
|
```
|
||||||
|
|
||||||
You can also compress multiple files:
|
You can also compress multiple files:
|
||||||
|
```javascript
|
||||||
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ]);
|
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ]);
|
||||||
console.log(result.code);
|
console.log(result.code);
|
||||||
|
```
|
||||||
|
|
||||||
To generate a source map:
|
To generate a source map:
|
||||||
|
```javascript
|
||||||
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
||||||
outSourceMap: "out.js.map"
|
outSourceMap: "out.js.map"
|
||||||
});
|
});
|
||||||
console.log(result.code); // minified output
|
console.log(result.code); // minified output
|
||||||
console.log(result.map);
|
console.log(result.map);
|
||||||
|
```
|
||||||
|
|
||||||
Note that the source map is not saved in a file, it's just returned in
|
Note that the source map is not saved in a file, it's just returned in
|
||||||
`result.map`. The value passed for `outSourceMap` is only used to set the
|
`result.map`. The value passed for `outSourceMap` is only used to set the
|
||||||
`file` attribute in the source map (see [the spec][sm-spec]).
|
`file` attribute in the source map (see [the spec][sm-spec]).
|
||||||
|
|
||||||
You can also specify sourceRoot property to be included in source map:
|
You can also specify sourceRoot property to be included in source map:
|
||||||
|
```javascript
|
||||||
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
||||||
outSourceMap: "out.js.map",
|
outSourceMap: "out.js.map",
|
||||||
sourceRoot: "http://example.com/src"
|
sourceRoot: "http://example.com/src"
|
||||||
});
|
});
|
||||||
|
```
|
||||||
|
|
||||||
If you're compressing compiled JavaScript and have a source map for it, you
|
If you're compressing compiled JavaScript and have a source map for it, you
|
||||||
can use the `inSourceMap` argument:
|
can use the `inSourceMap` argument:
|
||||||
|
```javascript
|
||||||
var result = UglifyJS.minify("compiled.js", {
|
var result = UglifyJS.minify("compiled.js", {
|
||||||
inSourceMap: "compiled.js.map",
|
inSourceMap: "compiled.js.map",
|
||||||
outSourceMap: "minified.js.map"
|
outSourceMap: "minified.js.map"
|
||||||
});
|
});
|
||||||
// same as before, it returns `code` and `map`
|
// same as before, it returns `code` and `map`
|
||||||
|
```
|
||||||
|
|
||||||
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
|
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
|
||||||
no sense otherwise).
|
no sense otherwise).
|
||||||
@@ -402,8 +443,9 @@ Following there's more detailed API info, in case the `minify` function is
|
|||||||
too simple for your needs.
|
too simple for your needs.
|
||||||
|
|
||||||
#### The parser
|
#### The parser
|
||||||
|
```javascript
|
||||||
var toplevel_ast = UglifyJS.parse(code, options);
|
var toplevel_ast = UglifyJS.parse(code, options);
|
||||||
|
```
|
||||||
|
|
||||||
`options` is optional and if present it must be an object. The following
|
`options` is optional and if present it must be an object. The following
|
||||||
properties are available:
|
properties are available:
|
||||||
@@ -417,15 +459,16 @@ properties are available:
|
|||||||
The last two options are useful when you'd like to minify multiple files and
|
The last two options are useful when you'd like to minify multiple files and
|
||||||
get a single file as the output and a proper source map. Our CLI tool does
|
get a single file as the output and a proper source map. Our CLI tool does
|
||||||
something like this:
|
something like this:
|
||||||
|
```javascript
|
||||||
var toplevel = null;
|
var toplevel = null;
|
||||||
files.forEach(function(file){
|
files.forEach(function(file){
|
||||||
var code = fs.readFileSync(file);
|
var code = fs.readFileSync(file);
|
||||||
toplevel = UglifyJS.parse(code, {
|
toplevel = UglifyJS.parse(code, {
|
||||||
filename: file,
|
filename: file,
|
||||||
toplevel: toplevel
|
toplevel: toplevel
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
```
|
||||||
|
|
||||||
After this, we have in `toplevel` a big AST containing all our files, with
|
After this, we have in `toplevel` a big AST containing all our files, with
|
||||||
each token having proper information about where it came from.
|
each token having proper information about where it came from.
|
||||||
@@ -439,15 +482,17 @@ referenced, if it is a global or not, if a function is using `eval` or the
|
|||||||
`with` statement etc. I will discuss this some place else, for now what's
|
`with` statement etc. I will discuss this some place else, for now what's
|
||||||
important to know is that you need to call the following before doing
|
important to know is that you need to call the following before doing
|
||||||
anything with the tree:
|
anything with the tree:
|
||||||
|
```javascript
|
||||||
toplevel.figure_out_scope()
|
toplevel.figure_out_scope()
|
||||||
|
```
|
||||||
|
|
||||||
#### Compression
|
#### Compression
|
||||||
|
|
||||||
Like this:
|
Like this:
|
||||||
|
```javascript
|
||||||
var compressor = UglifyJS.Compressor(options);
|
var compressor = UglifyJS.Compressor(options);
|
||||||
var compressed_ast = toplevel.transform(compressor);
|
var compressed_ast = toplevel.transform(compressor);
|
||||||
|
```
|
||||||
|
|
||||||
The `options` can be missing. Available options are discussed above in
|
The `options` can be missing. Available options are discussed above in
|
||||||
“Compressor options”. Defaults should lead to best compression in most
|
“Compressor options”. Defaults should lead to best compression in most
|
||||||
@@ -463,23 +508,26 @@ the compressor might drop unused variables / unreachable code and this might
|
|||||||
change the number of identifiers or their position). Optionally, you can
|
change the number of identifiers or their position). Optionally, you can
|
||||||
call a trick that helps after Gzip (counting character frequency in
|
call a trick that helps after Gzip (counting character frequency in
|
||||||
non-mangleable words). Example:
|
non-mangleable words). Example:
|
||||||
|
```javascript
|
||||||
compressed_ast.figure_out_scope();
|
compressed_ast.figure_out_scope();
|
||||||
compressed_ast.compute_char_frequency();
|
compressed_ast.compute_char_frequency();
|
||||||
compressed_ast.mangle_names();
|
compressed_ast.mangle_names();
|
||||||
|
```
|
||||||
|
|
||||||
#### Generating output
|
#### Generating output
|
||||||
|
|
||||||
AST nodes have a `print` method that takes an output stream. Essentially,
|
AST nodes have a `print` method that takes an output stream. Essentially,
|
||||||
to generate code you do this:
|
to generate code you do this:
|
||||||
|
```javascript
|
||||||
var stream = UglifyJS.OutputStream(options);
|
var stream = UglifyJS.OutputStream(options);
|
||||||
compressed_ast.print(stream);
|
compressed_ast.print(stream);
|
||||||
var code = stream.toString(); // this is your minified code
|
var code = stream.toString(); // this is your minified code
|
||||||
|
```
|
||||||
|
|
||||||
or, for a shortcut you can do:
|
or, for a shortcut you can do:
|
||||||
|
```javascript
|
||||||
var code = compressed_ast.print_to_string(options);
|
var code = compressed_ast.print_to_string(options);
|
||||||
|
```
|
||||||
|
|
||||||
As usual, `options` is optional. The output stream accepts a lot of otions,
|
As usual, `options` is optional. The output stream accepts a lot of otions,
|
||||||
most of them documented above in section “Beautifier options”. The two
|
most of them documented above in section “Beautifier options”. The two
|
||||||
@@ -517,16 +565,17 @@ to be a `SourceMap` object (which is a thin wrapper on top of the
|
|||||||
[source-map][source-map] library).
|
[source-map][source-map] library).
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```javascript
|
||||||
|
var source_map = UglifyJS.SourceMap(source_map_options);
|
||||||
|
var stream = UglifyJS.OutputStream({
|
||||||
|
...
|
||||||
|
source_map: source_map
|
||||||
|
});
|
||||||
|
compressed_ast.print(stream);
|
||||||
|
|
||||||
var source_map = UglifyJS.SourceMap(source_map_options);
|
var code = stream.toString();
|
||||||
var stream = UglifyJS.OutputStream({
|
var map = source_map.toString(); // json output for your source map
|
||||||
...
|
```
|
||||||
source_map: source_map
|
|
||||||
});
|
|
||||||
compressed_ast.print(stream);
|
|
||||||
|
|
||||||
var code = stream.toString();
|
|
||||||
var map = source_map.toString(); // json output for your source map
|
|
||||||
|
|
||||||
The `source_map_options` (optional) can contain the following properties:
|
The `source_map_options` (optional) can contain the following properties:
|
||||||
|
|
||||||
|
|||||||
253
bin/uglifyjs
253
bin/uglifyjs
@@ -7,6 +7,8 @@ var UglifyJS = require("../tools/node");
|
|||||||
var sys = require("util");
|
var sys = require("util");
|
||||||
var optimist = require("optimist");
|
var optimist = require("optimist");
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
|
var path = require("path");
|
||||||
|
var async = require("async");
|
||||||
var acorn;
|
var acorn;
|
||||||
var ARGS = optimist
|
var ARGS = optimist
|
||||||
.usage("$0 input1.js [input2.js ...] [options]\n\
|
.usage("$0 input1.js [input2.js ...] [options]\n\
|
||||||
@@ -19,10 +21,14 @@ mangling you need to use `-c` and `-m`.\
|
|||||||
")
|
")
|
||||||
.describe("source-map", "Specify an output file where to generate source map.")
|
.describe("source-map", "Specify an output file where to generate source map.")
|
||||||
.describe("source-map-root", "The path to the original source to be included in the source map.")
|
.describe("source-map-root", "The path to the original source to be included in the source map.")
|
||||||
.describe("source-map-url", "The path to the source map to be added in //@ sourceMappingURL. Defaults to the value passed with --source-map.")
|
.describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL. Defaults to the value passed with --source-map.")
|
||||||
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
|
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
|
||||||
|
.describe("screw-ie8", "Pass this flag if you don't care about full compliance with Internet Explorer 6-8 quirks (by default UglifyJS will try to be IE-proof).")
|
||||||
|
.describe("expr", "Parse a single expression, rather than a program (for parsing JSON)")
|
||||||
.describe("p", "Skip prefix for original filenames that appear in source maps. \
|
.describe("p", "Skip prefix for original filenames that appear in source maps. \
|
||||||
For example -p 3 will drop 3 directories from file names and ensure they are relative paths.")
|
For example -p 3 will drop 3 directories from file names and ensure they are relative paths. \
|
||||||
|
You can also specify -p relative, which will make UglifyJS figure out itself the relative paths between original sources, \
|
||||||
|
the source map and the output file.")
|
||||||
.describe("o", "Output file (default STDOUT).")
|
.describe("o", "Output file (default STDOUT).")
|
||||||
.describe("b", "Beautify output/specify output options.")
|
.describe("b", "Beautify output/specify output options.")
|
||||||
.describe("m", "Mangle names/pass mangler options.")
|
.describe("m", "Mangle names/pass mangler options.")
|
||||||
@@ -31,6 +37,7 @@ For example -p 3 will drop 3 directories from file names and ensure they are rel
|
|||||||
Pass options like -c hoist_vars=false,if_return=false. \
|
Pass options like -c hoist_vars=false,if_return=false. \
|
||||||
Use -c with no argument to use the default compression options.")
|
Use -c with no argument to use the default compression options.")
|
||||||
.describe("d", "Global definitions")
|
.describe("d", "Global definitions")
|
||||||
|
.describe("e", "Embed everything in a big function, with a configurable parameter/argument list.")
|
||||||
|
|
||||||
.describe("comments", "Preserve copyright comments in the output. \
|
.describe("comments", "Preserve copyright comments in the output. \
|
||||||
By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \
|
By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \
|
||||||
@@ -43,7 +50,7 @@ because of dead code removal or cascading statements into sequences.")
|
|||||||
|
|
||||||
.describe("stats", "Display operations run time on STDERR.")
|
.describe("stats", "Display operations run time on STDERR.")
|
||||||
.describe("acorn", "Use Acorn for parsing.")
|
.describe("acorn", "Use Acorn for parsing.")
|
||||||
.describe("spidermonkey", "Assume input fles are SpiderMonkey AST format (as JSON).")
|
.describe("spidermonkey", "Assume input files are SpiderMonkey AST format (as JSON).")
|
||||||
.describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)")
|
.describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)")
|
||||||
.describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \
|
.describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \
|
||||||
You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.")
|
You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.")
|
||||||
@@ -61,6 +68,7 @@ You need to pass an argument to this option to specify the name that your module
|
|||||||
.alias("d", "define")
|
.alias("d", "define")
|
||||||
.alias("r", "reserved")
|
.alias("r", "reserved")
|
||||||
.alias("V", "version")
|
.alias("V", "version")
|
||||||
|
.alias("e", "enclose")
|
||||||
|
|
||||||
.string("source-map")
|
.string("source-map")
|
||||||
.string("source-map-root")
|
.string("source-map-root")
|
||||||
@@ -69,8 +77,13 @@ You need to pass an argument to this option to specify the name that your module
|
|||||||
.string("m")
|
.string("m")
|
||||||
.string("c")
|
.string("c")
|
||||||
.string("d")
|
.string("d")
|
||||||
|
.string("e")
|
||||||
.string("comments")
|
.string("comments")
|
||||||
.string("wrap")
|
.string("wrap")
|
||||||
|
.string("p")
|
||||||
|
|
||||||
|
.boolean("expr")
|
||||||
|
.boolean("screw-ie8")
|
||||||
.boolean("export-all")
|
.boolean("export-all")
|
||||||
.boolean("self")
|
.boolean("self")
|
||||||
.boolean("v")
|
.boolean("v")
|
||||||
@@ -112,18 +125,24 @@ var COMPRESS = getOptions("c", true);
|
|||||||
var MANGLE = getOptions("m", true);
|
var MANGLE = getOptions("m", true);
|
||||||
var BEAUTIFY = getOptions("b", true);
|
var BEAUTIFY = getOptions("b", true);
|
||||||
|
|
||||||
if (COMPRESS && ARGS.d) {
|
if (ARGS.d) {
|
||||||
COMPRESS.global_defs = getOptions("d");
|
if (COMPRESS) COMPRESS.global_defs = getOptions("d");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MANGLE && ARGS.r) {
|
if (ARGS.r) {
|
||||||
MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
|
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
var OUTPUT_OPTIONS = {
|
var OUTPUT_OPTIONS = {
|
||||||
beautify: BEAUTIFY ? true : false
|
beautify: BEAUTIFY ? true : false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (ARGS.screw_ie8) {
|
||||||
|
if (COMPRESS) COMPRESS.screw_ie8 = true;
|
||||||
|
if (MANGLE) MANGLE.screw_ie8 = true;
|
||||||
|
OUTPUT_OPTIONS.screw_ie8 = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (BEAUTIFY)
|
if (BEAUTIFY)
|
||||||
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
|
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
|
||||||
|
|
||||||
@@ -185,9 +204,10 @@ if (files.filter(function(el){ return el == "-" }).length > 1) {
|
|||||||
var STATS = {};
|
var STATS = {};
|
||||||
var OUTPUT_FILE = ARGS.o;
|
var OUTPUT_FILE = ARGS.o;
|
||||||
var TOPLEVEL = null;
|
var TOPLEVEL = null;
|
||||||
|
var P_RELATIVE = ARGS.p && ARGS.p == "relative";
|
||||||
|
|
||||||
var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({
|
var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({
|
||||||
file: OUTPUT_FILE,
|
file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
|
||||||
root: ARGS.source_map_root,
|
root: ARGS.source_map_root,
|
||||||
orig: ORIG_MAP,
|
orig: ORIG_MAP,
|
||||||
}) : null;
|
}) : null;
|
||||||
@@ -206,100 +226,130 @@ try {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
files.forEach(function(file) {
|
async.eachLimit(files, 1, function (file, cb) {
|
||||||
var code = read_whole_file(file);
|
read_whole_file(file, function (err, code) {
|
||||||
if (ARGS.p != null) {
|
if (err) {
|
||||||
file = file.replace(/^\/+/, "").split(/\/+/).slice(ARGS.p).join("/");
|
sys.error("ERROR: can't read file: " + file);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
if (ARGS.p != null) {
|
||||||
|
if (P_RELATIVE) {
|
||||||
|
file = path.relative(path.dirname(ARGS.source_map), file);
|
||||||
|
} else {
|
||||||
|
var p = parseInt(ARGS.p, 10);
|
||||||
|
if (!isNaN(p)) {
|
||||||
|
file = file.replace(/^\/+/, "").split(/\/+/).slice(ARGS.p).join("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time_it("parse", function(){
|
||||||
|
if (ARGS.spidermonkey) {
|
||||||
|
var program = JSON.parse(code);
|
||||||
|
if (!TOPLEVEL) TOPLEVEL = program;
|
||||||
|
else TOPLEVEL.body = TOPLEVEL.body.concat(program.body);
|
||||||
|
}
|
||||||
|
else if (ARGS.acorn) {
|
||||||
|
TOPLEVEL = acorn.parse(code, {
|
||||||
|
locations : true,
|
||||||
|
trackComments : true,
|
||||||
|
sourceFile : file,
|
||||||
|
program : TOPLEVEL
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TOPLEVEL = UglifyJS.parse(code, {
|
||||||
|
filename : file,
|
||||||
|
toplevel : TOPLEVEL,
|
||||||
|
expression : ARGS.expr,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
}, function () {
|
||||||
|
if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){
|
||||||
|
TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ARGS.wrap) {
|
||||||
|
TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all);
|
||||||
}
|
}
|
||||||
time_it("parse", function(){
|
|
||||||
if (ARGS.spidermonkey) {
|
if (ARGS.enclose) {
|
||||||
var program = JSON.parse(code);
|
var arg_parameter_list = ARGS.enclose;
|
||||||
if (!TOPLEVEL) TOPLEVEL = program;
|
if (arg_parameter_list === true) {
|
||||||
else TOPLEVEL.body = TOPLEVEL.body.concat(program.body);
|
arg_parameter_list = [];
|
||||||
}
|
}
|
||||||
else if (ARGS.acorn) {
|
else if (!(arg_parameter_list instanceof Array)) {
|
||||||
TOPLEVEL = acorn.parse(code, {
|
arg_parameter_list = [arg_parameter_list];
|
||||||
locations : true,
|
|
||||||
trackComments : true,
|
|
||||||
sourceFile : file,
|
|
||||||
program : TOPLEVEL
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else {
|
TOPLEVEL = TOPLEVEL.wrap_enclose(arg_parameter_list);
|
||||||
TOPLEVEL = UglifyJS.parse(code, {
|
}
|
||||||
filename: file,
|
|
||||||
toplevel: TOPLEVEL
|
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint;
|
||||||
});
|
|
||||||
};
|
if (SCOPE_IS_NEEDED) {
|
||||||
|
time_it("scope", function(){
|
||||||
|
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8 });
|
||||||
|
if (ARGS.lint) {
|
||||||
|
TOPLEVEL.scope_warnings();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (COMPRESS) {
|
||||||
|
time_it("squeeze", function(){
|
||||||
|
TOPLEVEL = TOPLEVEL.transform(compressor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SCOPE_IS_NEEDED) {
|
||||||
|
time_it("scope", function(){
|
||||||
|
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8 });
|
||||||
|
if (MANGLE) {
|
||||||
|
TOPLEVEL.compute_char_frequency(MANGLE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MANGLE) time_it("mangle", function(){
|
||||||
|
TOPLEVEL.mangle_names(MANGLE);
|
||||||
});
|
});
|
||||||
});
|
time_it("generate", function(){
|
||||||
|
TOPLEVEL.print(output);
|
||||||
if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){
|
|
||||||
TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (ARGS.wrap) {
|
|
||||||
TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all);
|
|
||||||
}
|
|
||||||
|
|
||||||
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint;
|
|
||||||
|
|
||||||
if (SCOPE_IS_NEEDED) {
|
|
||||||
time_it("scope", function(){
|
|
||||||
TOPLEVEL.figure_out_scope();
|
|
||||||
if (ARGS.lint) {
|
|
||||||
TOPLEVEL.scope_warnings();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
if (COMPRESS) {
|
output = output.get();
|
||||||
time_it("squeeze", function(){
|
|
||||||
TOPLEVEL = TOPLEVEL.transform(compressor);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SCOPE_IS_NEEDED) {
|
if (SOURCE_MAP) {
|
||||||
time_it("scope", function(){
|
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
|
||||||
TOPLEVEL.figure_out_scope();
|
var source_map_url = ARGS.source_map_url || (
|
||||||
if (MANGLE) {
|
P_RELATIVE
|
||||||
TOPLEVEL.compute_char_frequency(MANGLE);
|
? path.relative(path.dirname(OUTPUT_FILE), ARGS.source_map)
|
||||||
}
|
: ARGS.source_map
|
||||||
});
|
);
|
||||||
}
|
output += "\n//# sourceMappingURL=" + source_map_url;
|
||||||
|
}
|
||||||
|
|
||||||
if (MANGLE) time_it("mangle", function(){
|
if (OUTPUT_FILE) {
|
||||||
TOPLEVEL.mangle_names(MANGLE);
|
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
|
||||||
});
|
} else {
|
||||||
time_it("generate", function(){
|
sys.print(output);
|
||||||
TOPLEVEL.print(output);
|
sys.error("\n");
|
||||||
});
|
}
|
||||||
|
|
||||||
output = output.get();
|
if (ARGS.stats) {
|
||||||
|
sys.error(UglifyJS.string_template("Timing information (compressed {count} files):", {
|
||||||
if (SOURCE_MAP) {
|
count: files.length
|
||||||
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
|
|
||||||
output += "\n//@ sourceMappingURL=" + (ARGS.source_map_url || ARGS.source_map);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OUTPUT_FILE) {
|
|
||||||
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
|
|
||||||
} else {
|
|
||||||
sys.print(output);
|
|
||||||
sys.error("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ARGS.stats) {
|
|
||||||
sys.error(UglifyJS.string_template("Timing information (compressed {count} files):", {
|
|
||||||
count: files.length
|
|
||||||
}));
|
|
||||||
for (var i in STATS) if (STATS.hasOwnProperty(i)) {
|
|
||||||
sys.error(UglifyJS.string_template("- {name}: {time}s", {
|
|
||||||
name: i,
|
|
||||||
time: (STATS[i] / 1000).toFixed(3)
|
|
||||||
}));
|
}));
|
||||||
|
for (var i in STATS) if (STATS.hasOwnProperty(i)) {
|
||||||
|
sys.error(UglifyJS.string_template("- {name}: {time}s", {
|
||||||
|
name: i,
|
||||||
|
time: (STATS[i] / 1000).toFixed(3)
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
/* -----[ functions ]----- */
|
/* -----[ functions ]----- */
|
||||||
|
|
||||||
@@ -344,17 +394,18 @@ function getOptions(x, constants) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
function read_whole_file(filename) {
|
function read_whole_file(filename, cb) {
|
||||||
if (filename == "-") {
|
if (filename == "-") {
|
||||||
// XXX: this sucks. How does one read the whole STDIN
|
var chunks = [];
|
||||||
// synchronously?
|
process.stdin.setEncoding('utf-8');
|
||||||
filename = "/dev/stdin";
|
process.stdin.on('data', function (chunk) {
|
||||||
}
|
chunks.push(chunk);
|
||||||
try {
|
}).on('end', function () {
|
||||||
return fs.readFileSync(filename, "utf8");
|
cb(null, chunks.join(""));
|
||||||
} catch(ex) {
|
});
|
||||||
sys.error("ERROR: can't read file: " + filename);
|
process.openStdin();
|
||||||
process.exit(1);
|
} else {
|
||||||
|
fs.readFile(filename, "utf-8", cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
24
lib/ast.js
24
lib/ast.js
@@ -285,6 +285,27 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
|||||||
$propdoc: {
|
$propdoc: {
|
||||||
globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
|
globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
|
||||||
},
|
},
|
||||||
|
wrap_enclose: function(arg_parameter_pairs) {
|
||||||
|
var self = this;
|
||||||
|
var args = [];
|
||||||
|
var parameters = [];
|
||||||
|
|
||||||
|
arg_parameter_pairs.forEach(function(pair) {
|
||||||
|
var split = pair.split(":");
|
||||||
|
|
||||||
|
args.push(split[0]);
|
||||||
|
parameters.push(split[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
|
||||||
|
wrapped_tl = parse(wrapped_tl);
|
||||||
|
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
|
||||||
|
if (node instanceof AST_Directive && node.value == "$ORIG") {
|
||||||
|
return MAP.splice(self.body);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return wrapped_tl;
|
||||||
|
},
|
||||||
wrap_commonjs: function(name, export_all) {
|
wrap_commonjs: function(name, export_all) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var to_export = [];
|
var to_export = [];
|
||||||
@@ -924,6 +945,9 @@ TreeWalker.prototype = {
|
|||||||
if (x instanceof type) return x;
|
if (x instanceof type) return x;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
has_directive: function(type) {
|
||||||
|
return this.find_parent(AST_Scope).has_directive(type);
|
||||||
|
},
|
||||||
in_boolean_context: function() {
|
in_boolean_context: function() {
|
||||||
var stack = this.stack;
|
var stack = this.stack;
|
||||||
var i = stack.length, self = stack[--i];
|
var i = stack.length, self = stack[--i];
|
||||||
|
|||||||
160
lib/compress.js
160
lib/compress.js
@@ -52,7 +52,7 @@ function Compressor(options, false_by_default) {
|
|||||||
properties : !false_by_default,
|
properties : !false_by_default,
|
||||||
dead_code : !false_by_default,
|
dead_code : !false_by_default,
|
||||||
drop_debugger : !false_by_default,
|
drop_debugger : !false_by_default,
|
||||||
unsafe : !false_by_default,
|
unsafe : false,
|
||||||
unsafe_comps : false,
|
unsafe_comps : false,
|
||||||
conditionals : !false_by_default,
|
conditionals : !false_by_default,
|
||||||
comparisons : !false_by_default,
|
comparisons : !false_by_default,
|
||||||
@@ -66,6 +66,8 @@ function Compressor(options, false_by_default) {
|
|||||||
join_vars : !false_by_default,
|
join_vars : !false_by_default,
|
||||||
cascade : !false_by_default,
|
cascade : !false_by_default,
|
||||||
side_effects : !false_by_default,
|
side_effects : !false_by_default,
|
||||||
|
negate_iife : !false_by_default,
|
||||||
|
screw_ie8 : false,
|
||||||
|
|
||||||
warnings : true,
|
warnings : true,
|
||||||
global_defs : {}
|
global_defs : {}
|
||||||
@@ -156,7 +158,7 @@ merge(Compressor.prototype, {
|
|||||||
value: val
|
value: val
|
||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
case "boolean":
|
case "boolean":
|
||||||
return make_node(val ? AST_True : AST_False, orig);
|
return make_node(val ? AST_True : AST_False, orig).optimize(compressor);
|
||||||
case "undefined":
|
case "undefined":
|
||||||
return make_node(AST_Undefined, orig).optimize(compressor);
|
return make_node(AST_Undefined, orig).optimize(compressor);
|
||||||
default:
|
default:
|
||||||
@@ -213,6 +215,11 @@ merge(Compressor.prototype, {
|
|||||||
statements = join_consecutive_vars(statements, compressor);
|
statements = join_consecutive_vars(statements, compressor);
|
||||||
}
|
}
|
||||||
} while (CHANGED);
|
} while (CHANGED);
|
||||||
|
|
||||||
|
if (compressor.option("negate_iife")) {
|
||||||
|
negate_iifes(statements, compressor);
|
||||||
|
}
|
||||||
|
|
||||||
return statements;
|
return statements;
|
||||||
|
|
||||||
function eliminate_spurious_blocks(statements) {
|
function eliminate_spurious_blocks(statements) {
|
||||||
@@ -496,6 +503,40 @@ merge(Compressor.prototype, {
|
|||||||
}, []);
|
}, []);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function negate_iifes(statements, compressor) {
|
||||||
|
statements.forEach(function(stat){
|
||||||
|
if (stat instanceof AST_SimpleStatement) {
|
||||||
|
stat.body = (function transform(thing) {
|
||||||
|
return thing.transform(new TreeTransformer(function(node){
|
||||||
|
if (node instanceof AST_Call && node.expression instanceof AST_Function) {
|
||||||
|
return make_node(AST_UnaryPrefix, node, {
|
||||||
|
operator: "!",
|
||||||
|
expression: node
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_Call) {
|
||||||
|
node.expression = transform(node.expression);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_Seq) {
|
||||||
|
node.car = transform(node.car);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_Conditional) {
|
||||||
|
var expr = transform(node.condition);
|
||||||
|
if (expr !== node.condition) {
|
||||||
|
// it has been negated, reverse
|
||||||
|
node.condition = expr;
|
||||||
|
var tmp = node.consequent;
|
||||||
|
node.consequent = node.alternative;
|
||||||
|
node.alternative = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}));
|
||||||
|
})(stat.body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function extract_declarations_from_unreachable_code(compressor, stat, target) {
|
function extract_declarations_from_unreachable_code(compressor, stat, target) {
|
||||||
@@ -610,7 +651,7 @@ merge(Compressor.prototype, {
|
|||||||
// inherits from AST_Statement; however, an AST_Function
|
// inherits from AST_Statement; however, an AST_Function
|
||||||
// isn't really a statement. This could byte in other
|
// isn't really a statement. This could byte in other
|
||||||
// places too. :-( Wish JS had multiple inheritance.
|
// places too. :-( Wish JS had multiple inheritance.
|
||||||
return [ this ];
|
throw def;
|
||||||
});
|
});
|
||||||
function ev(node) {
|
function ev(node) {
|
||||||
return node._eval();
|
return node._eval();
|
||||||
@@ -625,10 +666,24 @@ merge(Compressor.prototype, {
|
|||||||
var e = this.expression;
|
var e = this.expression;
|
||||||
switch (this.operator) {
|
switch (this.operator) {
|
||||||
case "!": return !ev(e);
|
case "!": return !ev(e);
|
||||||
case "typeof": return typeof ev(e);
|
case "typeof":
|
||||||
|
// Function would be evaluated to an array and so typeof would
|
||||||
|
// incorrectly return 'object'. Hence making is a special case.
|
||||||
|
if (e instanceof AST_Function) return typeof function(){};
|
||||||
|
|
||||||
|
e = ev(e);
|
||||||
|
|
||||||
|
// typeof <RegExp> returns "object" or "function" on different platforms
|
||||||
|
// so cannot evaluate reliably
|
||||||
|
if (e instanceof RegExp) throw def;
|
||||||
|
|
||||||
|
return typeof e;
|
||||||
case "void": return void ev(e);
|
case "void": return void ev(e);
|
||||||
case "~": return ~ev(e);
|
case "~": return ~ev(e);
|
||||||
case "-": return -ev(e);
|
case "-":
|
||||||
|
e = ev(e);
|
||||||
|
if (e === 0) throw def;
|
||||||
|
return -e;
|
||||||
case "+": return +ev(e);
|
case "+": return +ev(e);
|
||||||
}
|
}
|
||||||
throw def;
|
throw def;
|
||||||
@@ -669,12 +724,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
def(AST_SymbolRef, function(){
|
def(AST_SymbolRef, function(){
|
||||||
var d = this.definition();
|
var d = this.definition();
|
||||||
if (d && d.constant) {
|
if (d && d.constant && d.init) return ev(d.init);
|
||||||
var orig = d.orig[0];
|
|
||||||
if (orig) orig = orig.init[0];
|
|
||||||
orig = orig && orig.value;
|
|
||||||
if (orig) return ev(orig);
|
|
||||||
}
|
|
||||||
throw def;
|
throw def;
|
||||||
});
|
});
|
||||||
})(function(node, func){
|
})(function(node, func){
|
||||||
@@ -938,7 +988,7 @@ merge(Compressor.prototype, {
|
|||||||
// pass 3: we should drop declarations not in_use
|
// pass 3: we should drop declarations not in_use
|
||||||
var tt = new TreeTransformer(
|
var tt = new TreeTransformer(
|
||||||
function before(node, descend, in_list) {
|
function before(node, descend, in_list) {
|
||||||
if (node instanceof AST_Lambda) {
|
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
|
||||||
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
||||||
var sym = a[i];
|
var sym = a[i];
|
||||||
if (sym.unreferenced()) {
|
if (sym.unreferenced()) {
|
||||||
@@ -1199,8 +1249,6 @@ merge(Compressor.prototype, {
|
|||||||
extract_declarations_from_unreachable_code(compressor, self.body, a);
|
extract_declarations_from_unreachable_code(compressor, self.body, a);
|
||||||
return make_node(AST_BlockStatement, self, { body: a });
|
return make_node(AST_BlockStatement, self, { body: a });
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return self.body;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@@ -1418,11 +1466,18 @@ merge(Compressor.prototype, {
|
|||||||
body: self.expression
|
body: self.expression
|
||||||
}).transform(compressor);
|
}).transform(compressor);
|
||||||
}
|
}
|
||||||
var last_branch = self.body[self.body.length - 1];
|
for(;;) {
|
||||||
if (last_branch) {
|
var last_branch = self.body[self.body.length - 1];
|
||||||
var stat = last_branch.body[last_branch.body.length - 1]; // last statement
|
if (last_branch) {
|
||||||
if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
|
var stat = last_branch.body[last_branch.body.length - 1]; // last statement
|
||||||
last_branch.body.pop();
|
if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
|
||||||
|
last_branch.body.pop();
|
||||||
|
if (last_branch instanceof AST_Default && last_branch.body.length == 0) {
|
||||||
|
self.body.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
var exp = self.expression.evaluate(compressor);
|
var exp = self.expression.evaluate(compressor);
|
||||||
out: if (exp.length == 2) try {
|
out: if (exp.length == 2) try {
|
||||||
@@ -1574,6 +1629,45 @@ merge(Compressor.prototype, {
|
|||||||
operator: "+",
|
operator: "+",
|
||||||
right: make_node(AST_String, self, { value: "" })
|
right: make_node(AST_String, self, { value: "" })
|
||||||
});
|
});
|
||||||
|
case "Function":
|
||||||
|
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);
|
||||||
|
ast.figure_out_scope();
|
||||||
|
var comp = new Compressor(compressor.options);
|
||||||
|
ast = ast.transform(comp);
|
||||||
|
ast.figure_out_scope();
|
||||||
|
ast.mangle_names();
|
||||||
|
var fun = ast.body[0].body.expression;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
|
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
|
||||||
@@ -1711,18 +1805,22 @@ merge(Compressor.prototype, {
|
|||||||
var commutativeOperators = makePredicate("== === != !== * & | ^");
|
var commutativeOperators = makePredicate("== === != !== * & | ^");
|
||||||
|
|
||||||
OPT(AST_Binary, function(self, compressor){
|
OPT(AST_Binary, function(self, compressor){
|
||||||
function reverse(op) {
|
var reverse = compressor.has_directive("use asm") ? noop
|
||||||
if (!(self.left.has_side_effects() && self.right.has_side_effects())) {
|
: function(op, force) {
|
||||||
if (op) self.operator = op;
|
if (force || !(self.left.has_side_effects() || self.right.has_side_effects())) {
|
||||||
var tmp = self.left;
|
if (op) self.operator = op;
|
||||||
self.left = self.right;
|
var tmp = self.left;
|
||||||
self.right = tmp;
|
self.left = self.right;
|
||||||
}
|
self.right = tmp;
|
||||||
};
|
}
|
||||||
|
};
|
||||||
if (commutativeOperators(self.operator)) {
|
if (commutativeOperators(self.operator)) {
|
||||||
if (self.right instanceof AST_Constant
|
if (self.right instanceof AST_Constant
|
||||||
&& !(self.left instanceof AST_Constant)) {
|
&& !(self.left instanceof AST_Constant)) {
|
||||||
reverse();
|
// if right is a constant, whatever side effects the
|
||||||
|
// left side might have could not influence the
|
||||||
|
// result. hence, force switch.
|
||||||
|
reverse(null, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self = self.lift_sequences(compressor);
|
self = self.lift_sequences(compressor);
|
||||||
@@ -1743,8 +1841,8 @@ merge(Compressor.prototype, {
|
|||||||
&& compressor.option("unsafe")) {
|
&& compressor.option("unsafe")) {
|
||||||
if (!(self.right.expression instanceof AST_SymbolRef)
|
if (!(self.right.expression instanceof AST_SymbolRef)
|
||||||
|| !self.right.expression.undeclared()) {
|
|| !self.right.expression.undeclared()) {
|
||||||
self.left = self.right.expression;
|
self.right = self.right.expression;
|
||||||
self.right = make_node(AST_Undefined, self.left).optimize(compressor);
|
self.left = make_node(AST_Undefined, self.left).optimize(compressor);
|
||||||
if (self.operator.length == 2) self.operator += "=";
|
if (self.operator.length == 2) self.operator += "=";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1947,7 +2045,7 @@ merge(Compressor.prototype, {
|
|||||||
var prop = self.property;
|
var prop = self.property;
|
||||||
if (prop instanceof AST_String && compressor.option("properties")) {
|
if (prop instanceof AST_String && compressor.option("properties")) {
|
||||||
prop = prop.getValue();
|
prop = prop.getValue();
|
||||||
if (is_identifier(prop)) {
|
if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) {
|
||||||
return make_node(AST_Dot, self, {
|
return make_node(AST_Dot, self, {
|
||||||
expression : self.expression,
|
expression : self.expression,
|
||||||
property : prop
|
property : prop
|
||||||
|
|||||||
@@ -148,12 +148,14 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
function From_Moz_Unary(M) {
|
function From_Moz_Unary(M) {
|
||||||
return new (M.prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
|
var prefix = "prefix" in M ? M.prefix
|
||||||
|
: M.type == "UnaryExpression" ? true : false;
|
||||||
|
return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
|
||||||
start : my_start_token(M),
|
start : my_start_token(M),
|
||||||
end : my_end_token(M),
|
end : my_end_token(M),
|
||||||
operator : M.operator,
|
operator : M.operator,
|
||||||
expression : from_moz(M.argument)
|
expression : from_moz(M.argument)
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var ME_TO_MOZ = {};
|
var ME_TO_MOZ = {};
|
||||||
|
|||||||
@@ -54,13 +54,13 @@ function OutputStream(options) {
|
|||||||
inline_script : false,
|
inline_script : false,
|
||||||
width : 80,
|
width : 80,
|
||||||
max_line_len : 32000,
|
max_line_len : 32000,
|
||||||
ie_proof : true,
|
|
||||||
beautify : false,
|
beautify : false,
|
||||||
source_map : null,
|
source_map : null,
|
||||||
bracketize : false,
|
bracketize : false,
|
||||||
semicolons : true,
|
semicolons : true,
|
||||||
comments : false,
|
comments : false,
|
||||||
preserve_line : false
|
preserve_line : false,
|
||||||
|
screw_ie8 : false,
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
var indentation = 0;
|
var indentation = 0;
|
||||||
@@ -69,11 +69,16 @@ function OutputStream(options) {
|
|||||||
var current_pos = 0;
|
var current_pos = 0;
|
||||||
var OUTPUT = "";
|
var OUTPUT = "";
|
||||||
|
|
||||||
function to_ascii(str) {
|
function to_ascii(str, identifier) {
|
||||||
return str.replace(/[\u0080-\uffff]/g, function(ch) {
|
return str.replace(/[\u0080-\uffff]/g, function(ch) {
|
||||||
var code = ch.charCodeAt(0).toString(16);
|
var code = ch.charCodeAt(0).toString(16);
|
||||||
while (code.length < 4) code = "0" + code;
|
if (code.length <= 2 && !identifier) {
|
||||||
return "\\u" + code;
|
while (code.length < 2) code = "0" + code;
|
||||||
|
return "\\x" + code;
|
||||||
|
} else {
|
||||||
|
while (code.length < 4) code = "0" + code;
|
||||||
|
return "\\u" + code;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,7 +95,7 @@ function OutputStream(options) {
|
|||||||
case "\u2029": return "\\u2029";
|
case "\u2029": return "\\u2029";
|
||||||
case '"': ++dq; return '"';
|
case '"': ++dq; return '"';
|
||||||
case "'": ++sq; return "'";
|
case "'": ++sq; return "'";
|
||||||
case "\0": return "\\0";
|
case "\0": return "\\x00";
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
});
|
});
|
||||||
@@ -109,7 +114,7 @@ function OutputStream(options) {
|
|||||||
function make_name(name) {
|
function make_name(name) {
|
||||||
name = name.toString();
|
name = name.toString();
|
||||||
if (options.ascii_only)
|
if (options.ascii_only)
|
||||||
name = to_ascii(name);
|
name = to_ascii(name, true);
|
||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -345,18 +350,17 @@ function OutputStream(options) {
|
|||||||
|
|
||||||
AST_Node.DEFMETHOD("print", function(stream, force_parens){
|
AST_Node.DEFMETHOD("print", function(stream, force_parens){
|
||||||
var self = this, generator = self._codegen;
|
var self = this, generator = self._codegen;
|
||||||
stream.push_node(self);
|
function doit() {
|
||||||
if (force_parens || self.needs_parens(stream)) {
|
|
||||||
stream.with_parens(function(){
|
|
||||||
self.add_comments(stream);
|
|
||||||
self.add_source_map(stream);
|
|
||||||
generator(self, stream);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
self.add_comments(stream);
|
self.add_comments(stream);
|
||||||
self.add_source_map(stream);
|
self.add_source_map(stream);
|
||||||
generator(self, stream);
|
generator(self, stream);
|
||||||
}
|
}
|
||||||
|
stream.push_node(self);
|
||||||
|
if (force_parens || self.needs_parens(stream)) {
|
||||||
|
stream.with_parens(doit);
|
||||||
|
} else {
|
||||||
|
doit();
|
||||||
|
}
|
||||||
stream.pop_node();
|
stream.pop_node();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -375,6 +379,16 @@ function OutputStream(options) {
|
|||||||
if (start && !start._comments_dumped) {
|
if (start && !start._comments_dumped) {
|
||||||
start._comments_dumped = true;
|
start._comments_dumped = true;
|
||||||
var comments = start.comments_before;
|
var comments = start.comments_before;
|
||||||
|
|
||||||
|
// XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112
|
||||||
|
// if this node is `return` or `throw`, we cannot allow comments before
|
||||||
|
// the returned or thrown value.
|
||||||
|
if (self instanceof AST_Exit &&
|
||||||
|
self.value && self.value.start.comments_before.length > 0) {
|
||||||
|
comments = (comments || []).concat(self.value.start.comments_before);
|
||||||
|
self.value.start.comments_before = [];
|
||||||
|
}
|
||||||
|
|
||||||
if (c.test) {
|
if (c.test) {
|
||||||
comments = comments.filter(function(comment){
|
comments = comments.filter(function(comment){
|
||||||
return c.test(comment.value);
|
return c.test(comment.value);
|
||||||
@@ -499,11 +513,23 @@ function OutputStream(options) {
|
|||||||
PARENS(AST_New, function(output){
|
PARENS(AST_New, function(output){
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
if (no_constructor_parens(this, output)
|
if (no_constructor_parens(this, output)
|
||||||
&& (p instanceof AST_Dot // (new Date).getTime()
|
&& (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
|
||||||
|| p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
|
|| p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
PARENS(AST_Number, function(output){
|
||||||
|
var p = output.parent();
|
||||||
|
if (this.getValue() < 0 && p instanceof AST_PropAccess && p.expression === this)
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
PARENS(AST_NaN, function(output){
|
||||||
|
var p = output.parent();
|
||||||
|
if (p instanceof AST_PropAccess && p.expression === this)
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
function assign_and_conditional_paren_rules(output) {
|
function assign_and_conditional_paren_rules(output) {
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
// !(a = false) → true
|
// !(a = false) → true
|
||||||
@@ -730,7 +756,7 @@ function OutputStream(options) {
|
|||||||
if (!self.body)
|
if (!self.body)
|
||||||
return output.force_semicolon();
|
return output.force_semicolon();
|
||||||
if (self.body instanceof AST_Do
|
if (self.body instanceof AST_Do
|
||||||
&& output.option("ie_proof")) {
|
&& !output.option("screw_ie8")) {
|
||||||
// https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
|
// https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
|
||||||
// croaks with "syntax error" on code like this: if (foo)
|
// croaks with "syntax error" on code like this: if (foo)
|
||||||
// do ... while(cond); else ... we need block brackets
|
// do ... while(cond); else ... we need block brackets
|
||||||
@@ -932,7 +958,7 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_Dot, function(self, output){
|
DEFPRINT(AST_Dot, function(self, output){
|
||||||
var expr = self.expression;
|
var expr = self.expression;
|
||||||
expr.print(output);
|
expr.print(output);
|
||||||
if (expr instanceof AST_Number) {
|
if (expr instanceof AST_Number && expr.getValue() >= 0) {
|
||||||
if (!/[xa-f.]/i.test(output.last())) {
|
if (!/[xa-f.]/i.test(output.last())) {
|
||||||
output.print(".");
|
output.print(".");
|
||||||
}
|
}
|
||||||
@@ -985,6 +1011,11 @@ function OutputStream(options) {
|
|||||||
a.forEach(function(exp, i){
|
a.forEach(function(exp, i){
|
||||||
if (i) output.comma();
|
if (i) output.comma();
|
||||||
exp.print(output);
|
exp.print(output);
|
||||||
|
// If the final element is a hole, we need to make sure it
|
||||||
|
// doesn't look like a trailing comma, by inserting an actual
|
||||||
|
// trailing comma.
|
||||||
|
if (i === len - 1 && exp instanceof AST_Hole)
|
||||||
|
output.comma();
|
||||||
});
|
});
|
||||||
if (len > 0) output.space();
|
if (len > 0) output.space();
|
||||||
});
|
});
|
||||||
@@ -1006,16 +1037,16 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
||||||
var key = self.key;
|
var key = self.key;
|
||||||
if (output.option("quote_keys")) {
|
if (output.option("quote_keys")) {
|
||||||
output.print_string(key);
|
output.print_string(key + "");
|
||||||
} else if ((typeof key == "number"
|
} else if ((typeof key == "number"
|
||||||
|| !output.option("beautify")
|
|| !output.option("beautify")
|
||||||
&& +key + "" == key)
|
&& +key + "" == key)
|
||||||
&& parseFloat(key) >= 0) {
|
&& parseFloat(key) >= 0) {
|
||||||
output.print(make_num(key));
|
output.print(make_num(key));
|
||||||
} else if (!is_identifier(key)) {
|
} else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
|
||||||
output.print_string(key);
|
|
||||||
} else {
|
|
||||||
output.print_name(key);
|
output.print_name(key);
|
||||||
|
} else {
|
||||||
|
output.print_string(key);
|
||||||
}
|
}
|
||||||
output.colon();
|
output.colon();
|
||||||
self.value.print(output);
|
self.value.print(output);
|
||||||
@@ -1059,6 +1090,9 @@ function OutputStream(options) {
|
|||||||
if (output.option("ascii_only"))
|
if (output.option("ascii_only"))
|
||||||
str = output.to_ascii(str);
|
str = output.to_ascii(str);
|
||||||
output.print(str);
|
output.print(str);
|
||||||
|
var p = output.parent();
|
||||||
|
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
|
||||||
|
output.print(" ");
|
||||||
});
|
});
|
||||||
|
|
||||||
function force_statement(stat, output) {
|
function force_statement(stat, output) {
|
||||||
@@ -1089,7 +1123,7 @@ function OutputStream(options) {
|
|||||||
if (p instanceof AST_Statement && p.body === node)
|
if (p instanceof AST_Statement && p.body === node)
|
||||||
return true;
|
return true;
|
||||||
if ((p instanceof AST_Seq && p.car === node ) ||
|
if ((p instanceof AST_Seq && p.car === node ) ||
|
||||||
(p instanceof AST_Call && p.expression === node ) ||
|
(p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
|
||||||
(p instanceof AST_Dot && p.expression === node ) ||
|
(p instanceof AST_Dot && p.expression === node ) ||
|
||||||
(p instanceof AST_Sub && p.expression === node ) ||
|
(p instanceof AST_Sub && p.expression === node ) ||
|
||||||
(p instanceof AST_Conditional && p.condition === node ) ||
|
(p instanceof AST_Conditional && p.condition === node ) ||
|
||||||
|
|||||||
43
lib/parse.js
43
lib/parse.js
@@ -149,7 +149,7 @@ function is_unicode_connector_punctuation(ch) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function is_identifier(name) {
|
function is_identifier(name) {
|
||||||
return /^[a-z_$][a-z0-9_$]*$/i.test(name) && !RESERVED_WORDS(name);
|
return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name);
|
||||||
};
|
};
|
||||||
|
|
||||||
function is_identifier_start(code) {
|
function is_identifier_start(code) {
|
||||||
@@ -167,6 +167,17 @@ function is_identifier_char(ch) {
|
|||||||
;
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function is_identifier_string(str){
|
||||||
|
var i = str.length;
|
||||||
|
if (i == 0) return false;
|
||||||
|
if (is_digit(str.charCodeAt(0))) return false;
|
||||||
|
while (--i >= 0) {
|
||||||
|
if (!is_identifier_char(str.charAt(i)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
function parse_js_number(num) {
|
function parse_js_number(num) {
|
||||||
if (RE_HEX_NUMBER.test(num)) {
|
if (RE_HEX_NUMBER.test(num)) {
|
||||||
return parseInt(num.substr(2), 16);
|
return parseInt(num.substr(2), 16);
|
||||||
@@ -190,12 +201,6 @@ JS_Parse_Error.prototype.toString = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function js_error(message, filename, line, col, pos) {
|
function js_error(message, filename, line, col, pos) {
|
||||||
AST_Node.warn("ERROR: {message} [{file}:{line},{col}]", {
|
|
||||||
message: message,
|
|
||||||
file: filename,
|
|
||||||
line: line,
|
|
||||||
col: col
|
|
||||||
});
|
|
||||||
throw new JS_Parse_Error(message, line, col, pos);
|
throw new JS_Parse_Error(message, line, col, pos);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -250,7 +255,7 @@ function tokenizer($TEXT, filename) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function token(type, value, is_comment) {
|
function token(type, value, is_comment) {
|
||||||
S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX[value]) ||
|
S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) ||
|
||||||
(type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
|
(type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
|
||||||
(type == "punc" && PUNC_BEFORE_EXPRESSION(value)));
|
(type == "punc" && PUNC_BEFORE_EXPRESSION(value)));
|
||||||
var ret = {
|
var ret = {
|
||||||
@@ -583,9 +588,10 @@ var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "nam
|
|||||||
function parse($TEXT, options) {
|
function parse($TEXT, options) {
|
||||||
|
|
||||||
options = defaults(options, {
|
options = defaults(options, {
|
||||||
strict : false,
|
strict : false,
|
||||||
filename : null,
|
filename : null,
|
||||||
toplevel : null
|
toplevel : null,
|
||||||
|
expression : false
|
||||||
});
|
});
|
||||||
|
|
||||||
var S = {
|
var S = {
|
||||||
@@ -1336,15 +1342,8 @@ function parse($TEXT, options) {
|
|||||||
|
|
||||||
function is_assignable(expr) {
|
function is_assignable(expr) {
|
||||||
if (!options.strict) return true;
|
if (!options.strict) return true;
|
||||||
switch (expr[0]+"") {
|
if (expr instanceof AST_This) return false;
|
||||||
case "dot":
|
return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol);
|
||||||
case "sub":
|
|
||||||
case "new":
|
|
||||||
case "call":
|
|
||||||
return true;
|
|
||||||
case "name":
|
|
||||||
return expr[1] != "this";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var maybe_assign = function(no_in) {
|
var maybe_assign = function(no_in) {
|
||||||
@@ -1388,6 +1387,10 @@ function parse($TEXT, options) {
|
|||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (options.expression) {
|
||||||
|
return expression(true);
|
||||||
|
}
|
||||||
|
|
||||||
return (function(){
|
return (function(){
|
||||||
var start = S.token;
|
var start = S.token;
|
||||||
var body = [];
|
var body = [];
|
||||||
|
|||||||
33
lib/scope.js
33
lib/scope.js
@@ -57,13 +57,17 @@ function SymbolDef(scope, index, orig) {
|
|||||||
|
|
||||||
SymbolDef.prototype = {
|
SymbolDef.prototype = {
|
||||||
unmangleable: function(options) {
|
unmangleable: function(options) {
|
||||||
return this.global
|
return (this.global && !(options && options.toplevel))
|
||||||
|| this.undeclared
|
|| this.undeclared
|
||||||
|| (!(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with));
|
|| (!(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with));
|
||||||
},
|
},
|
||||||
mangle: function(options) {
|
mangle: function(options) {
|
||||||
if (!this.mangled_name && !this.unmangleable(options))
|
if (!this.mangled_name && !this.unmangleable(options)) {
|
||||||
this.mangled_name = this.scope.next_mangled(options);
|
var s = this.scope;
|
||||||
|
if (this.orig[0] instanceof AST_SymbolLambda && !options.screw_ie8)
|
||||||
|
s = s.parent_scope;
|
||||||
|
this.mangled_name = s.next_mangled(options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -121,13 +125,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
node.init_scope_vars();
|
node.init_scope_vars();
|
||||||
}
|
}
|
||||||
if (node instanceof AST_SymbolLambda) {
|
if (node instanceof AST_SymbolLambda) {
|
||||||
//scope.def_function(node);
|
scope.def_function(node);
|
||||||
//
|
|
||||||
// https://github.com/mishoo/UglifyJS2/issues/24 — MSIE
|
|
||||||
// leaks function expression names into the containing
|
|
||||||
// scope. Don't like this fix but seems we can't do any
|
|
||||||
// better. IE: please die. Please!
|
|
||||||
(node.scope = scope.parent_scope).def_function(node);
|
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolDefun) {
|
else if (node instanceof AST_SymbolDefun) {
|
||||||
// Careful here, the scope where this should be defined is
|
// Careful here, the scope where this should be defined is
|
||||||
@@ -141,7 +139,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
|| node instanceof AST_SymbolConst) {
|
|| node instanceof AST_SymbolConst) {
|
||||||
var def = scope.def_variable(node);
|
var def = scope.def_variable(node);
|
||||||
def.constant = node instanceof AST_SymbolConst;
|
def.constant = node instanceof AST_SymbolConst;
|
||||||
def = tw.parent();
|
def.init = tw.parent().value;
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolCatch) {
|
else if (node instanceof AST_SymbolCatch) {
|
||||||
// XXX: this is wrong according to ECMA-262 (12.4). the
|
// XXX: this is wrong according to ECMA-262 (12.4). the
|
||||||
@@ -189,6 +187,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
} else {
|
} else {
|
||||||
g = new SymbolDef(self, globals.size(), node);
|
g = new SymbolDef(self, globals.size(), node);
|
||||||
g.undeclared = true;
|
g.undeclared = true;
|
||||||
|
g.global = true;
|
||||||
globals.set(name, g);
|
globals.set(name, g);
|
||||||
}
|
}
|
||||||
node.thedef = g;
|
node.thedef = g;
|
||||||
@@ -279,14 +278,14 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol){
|
|||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("next_mangled", function(options){
|
AST_Scope.DEFMETHOD("next_mangled", function(options){
|
||||||
var ext = this.enclosed, n = ext.length;
|
var ext = this.enclosed;
|
||||||
out: while (true) {
|
out: while (true) {
|
||||||
var m = base54(++this.cname);
|
var m = base54(++this.cname);
|
||||||
if (!is_identifier(m)) continue; // skip over "do"
|
if (!is_identifier(m)) continue; // skip over "do"
|
||||||
// we must ensure that the mangled name does not shadow a name
|
// we must ensure that the mangled name does not shadow a name
|
||||||
// from some parent scope that is referenced in this or in
|
// from some parent scope that is referenced in this or in
|
||||||
// inner scopes.
|
// inner scopes.
|
||||||
for (var i = n; --i >= 0;) {
|
for (var i = ext.length; --i >= 0;) {
|
||||||
var sym = ext[i];
|
var sym = ext[i];
|
||||||
var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);
|
var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);
|
||||||
if (m == name) continue out;
|
if (m == name) continue out;
|
||||||
@@ -341,9 +340,11 @@ AST_Symbol.DEFMETHOD("global", function(){
|
|||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
|
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
|
||||||
return defaults(options, {
|
return defaults(options, {
|
||||||
except : [],
|
except : [],
|
||||||
eval : false,
|
eval : false,
|
||||||
sort : false
|
sort : false,
|
||||||
|
toplevel : false,
|
||||||
|
screw_ie8 : false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Tree transformer helpers.
|
// Tree transformer helpers.
|
||||||
// XXX: eventually I should refactor the compressor to use this infrastructure.
|
|
||||||
|
|
||||||
function TreeTransformer(before, after) {
|
function TreeTransformer(before, after) {
|
||||||
TreeWalker.call(this);
|
TreeWalker.call(this);
|
||||||
@@ -160,6 +159,7 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
});
|
});
|
||||||
|
|
||||||
_(AST_VarDef, function(self, tw){
|
_(AST_VarDef, function(self, tw){
|
||||||
|
self.name = self.name.transform(tw);
|
||||||
if (self.value) self.value = self.value.transform(tw);
|
if (self.value) self.value = self.value.transform(tw);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -245,6 +245,13 @@ function makePredicate(words) {
|
|||||||
return new Function("str", f);
|
return new Function("str", f);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function all(array, predicate) {
|
||||||
|
for (var i = array.length; --i >= 0;)
|
||||||
|
if (!predicate(array[i]))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
function Dictionary() {
|
function Dictionary() {
|
||||||
this._values = Object.create(null);
|
this._values = Object.create(null);
|
||||||
this._size = 0;
|
this._size = 0;
|
||||||
|
|||||||
13
package.json
13
package.json
@@ -3,20 +3,25 @@
|
|||||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||||
"homepage": "http://lisperator.net/uglifyjs",
|
"homepage": "http://lisperator.net/uglifyjs",
|
||||||
"main": "tools/node.js",
|
"main": "tools/node.js",
|
||||||
"version": "2.2.4",
|
"version": "2.4.0",
|
||||||
"engines": { "node" : ">=0.4.0" },
|
"engines": { "node" : ">=0.4.0" },
|
||||||
"maintainers": [{
|
"maintainers": [{
|
||||||
"name": "Mihai Bazon",
|
"name": "Mihai Bazon",
|
||||||
"email": "mihai.bazon@gmail.com",
|
"email": "mihai.bazon@gmail.com",
|
||||||
"web": "http://lisperator.net/"
|
"web": "http://lisperator.net/"
|
||||||
}],
|
}],
|
||||||
"repositories": [{
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/mishoo/UglifyJS2.git"
|
"url": "https://github.com/mishoo/UglifyJS2.git"
|
||||||
}],
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"async" : "~0.2.6",
|
||||||
"source-map" : "~0.1.7",
|
"source-map" : "~0.1.7",
|
||||||
"optimist" : "~0.3.5"
|
"optimist" : "~0.3.5",
|
||||||
|
"uglify-to-browserify": "~1.0.0"
|
||||||
|
},
|
||||||
|
"browserify": {
|
||||||
|
"transform": [ "uglify-to-browserify" ]
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"uglifyjs" : "bin/uglifyjs"
|
"uglifyjs" : "bin/uglifyjs"
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
holes_and_undefined: {
|
holes_and_undefined: {
|
||||||
input: {
|
input: {
|
||||||
|
w = [1,,];
|
||||||
x = [1, 2, undefined];
|
x = [1, 2, undefined];
|
||||||
y = [1, , 2, ];
|
y = [1, , 2, ];
|
||||||
z = [1, undefined, 3];
|
z = [1, undefined, 3];
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
|
w=[1,,];
|
||||||
x=[1,2,void 0];
|
x=[1,2,void 0];
|
||||||
y=[1,,2];
|
y=[1,,2];
|
||||||
z=[1,void 0,3];
|
z=[1,void 0,3];
|
||||||
|
|||||||
@@ -95,3 +95,27 @@ unused_circular_references_3: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unused_keep_setter_arg: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
var x = {
|
||||||
|
_foo: null,
|
||||||
|
set foo(val) {
|
||||||
|
},
|
||||||
|
get foo() {
|
||||||
|
return this._foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var x = {
|
||||||
|
_foo: null,
|
||||||
|
set foo(val) {
|
||||||
|
},
|
||||||
|
get foo() {
|
||||||
|
return this._foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
typeof_eq_undefined: {
|
typeof_eq_undefined: {
|
||||||
options = {
|
options = {
|
||||||
comparisons: true,
|
comparisons: true
|
||||||
unsafe: false
|
|
||||||
};
|
};
|
||||||
input: { a = typeof b.c != "undefined" }
|
input: { a = typeof b.c != "undefined" }
|
||||||
expect: { a = "undefined" != typeof b.c }
|
expect: { a = "undefined" != typeof b.c }
|
||||||
@@ -13,5 +12,14 @@ typeof_eq_undefined_unsafe: {
|
|||||||
unsafe: true
|
unsafe: true
|
||||||
};
|
};
|
||||||
input: { a = typeof b.c != "undefined" }
|
input: { a = typeof b.c != "undefined" }
|
||||||
expect: { a = b.c !== void 0 }
|
expect: { a = void 0 !== b.c }
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof_eq_undefined_unsafe2: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
unsafe: true
|
||||||
|
};
|
||||||
|
input: { a = "undefined" != typeof b.c }
|
||||||
|
expect: { a = void 0 !== b.c }
|
||||||
}
|
}
|
||||||
|
|||||||
48
test/compress/issue-143.js
Normal file
48
test/compress/issue-143.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* There was an incorrect sort behaviour documented in issue #143:
|
||||||
|
* (x = f(…)) <= x → x >= (x = f(…))
|
||||||
|
*
|
||||||
|
* For example, let the equation be:
|
||||||
|
* (a = parseInt('100')) <= a
|
||||||
|
*
|
||||||
|
* If a was an integer and has the value of 99,
|
||||||
|
* (a = parseInt('100')) <= a → 100 <= 100 → true
|
||||||
|
*
|
||||||
|
* When transformed incorrectly:
|
||||||
|
* a >= (a = parseInt('100')) → 99 >= 100 → false
|
||||||
|
*/
|
||||||
|
|
||||||
|
tranformation_sort_order_equal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) == a }
|
||||||
|
expect: { (a = parseInt('100')) == a }
|
||||||
|
}
|
||||||
|
|
||||||
|
tranformation_sort_order_unequal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) != a }
|
||||||
|
expect: { (a = parseInt('100')) != a }
|
||||||
|
}
|
||||||
|
|
||||||
|
tranformation_sort_order_lesser_or_equal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) <= a }
|
||||||
|
expect: { (a = parseInt('100')) <= a }
|
||||||
|
}
|
||||||
|
tranformation_sort_order_greater_or_equal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) >= a }
|
||||||
|
expect: { (a = parseInt('100')) >= a }
|
||||||
|
}
|
||||||
76
test/compress/negate-iife.js
Normal file
76
test/compress/negate-iife.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
negate_iife_1: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function(){ stuff() })();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function(){ stuff() }();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
negate_iife_2: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function(){ return {} })().x = 10; // should not transform this one
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function(){ return {} })().x = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
negate_iife_3: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true,
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function(){ return true })() ? console.log(true) : console.log(false);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function(){ return true }() ? console.log(false) : console.log(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
negate_iife_3: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true,
|
||||||
|
sequences: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function(){ return true })() ? console.log(true) : console.log(false);
|
||||||
|
(function(){
|
||||||
|
console.log("something");
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function(){ return true }() ? console.log(false) : console.log(true), function(){
|
||||||
|
console.log("something");
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
negate_iife_4: {
|
||||||
|
options = {
|
||||||
|
negate_iife: true,
|
||||||
|
sequences: true,
|
||||||
|
conditionals: true,
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
if ((function(){ return true })()) {
|
||||||
|
console.log(true);
|
||||||
|
} else {
|
||||||
|
console.log(false);
|
||||||
|
}
|
||||||
|
(function(){
|
||||||
|
console.log("something");
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function(){ return true }() ? console.log(false) : console.log(true), function(){
|
||||||
|
console.log("something");
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,9 +17,38 @@ dot_properties: {
|
|||||||
input: {
|
input: {
|
||||||
a["foo"] = "bar";
|
a["foo"] = "bar";
|
||||||
a["if"] = "if";
|
a["if"] = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a["\u0EB3"] = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
|
a["1_1"] = "foo";
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
a.foo = "bar";
|
a.foo = "bar";
|
||||||
a["if"] = "if";
|
a["if"] = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a.\u0EB3 = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
|
a["1_1"] = "foo";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dot_properties_es5: {
|
||||||
|
options = {
|
||||||
|
properties: true,
|
||||||
|
screw_ie8: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
a["foo"] = "bar";
|
||||||
|
a["if"] = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a["\u0EB3"] = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
a.foo = "bar";
|
||||||
|
a.if = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a.\u0EB3 = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,3 +208,53 @@ constant_switch_9: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drop_default_1: {
|
||||||
|
options = { dead_code: true };
|
||||||
|
input: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_default_2: {
|
||||||
|
options = { dead_code: true };
|
||||||
|
input: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz(); break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_default: {
|
||||||
|
options = { dead_code: true };
|
||||||
|
input: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
default:
|
||||||
|
something();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
switch (foo) {
|
||||||
|
case 'bar': baz();
|
||||||
|
default:
|
||||||
|
something();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
25
test/compress/typeof.js
Normal file
25
test/compress/typeof.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
typeof_evaluation: {
|
||||||
|
options = {
|
||||||
|
evaluate: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
a = typeof 1;
|
||||||
|
b = typeof 'test';
|
||||||
|
c = typeof [];
|
||||||
|
d = typeof {};
|
||||||
|
e = typeof /./;
|
||||||
|
f = typeof false;
|
||||||
|
g = typeof function(){};
|
||||||
|
h = typeof undefined;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
a='number';
|
||||||
|
b='string';
|
||||||
|
c=typeof[];
|
||||||
|
d=typeof{};
|
||||||
|
e=typeof/./;
|
||||||
|
f='boolean';
|
||||||
|
g='function';
|
||||||
|
h='undefined';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,6 +63,8 @@ exports.minify = function(files, options) {
|
|||||||
if (typeof files == "string")
|
if (typeof files == "string")
|
||||||
files = [ files ];
|
files = [ files ];
|
||||||
|
|
||||||
|
UglifyJS.base54.reset();
|
||||||
|
|
||||||
// 1. parse
|
// 1. parse
|
||||||
var toplevel = null;
|
var toplevel = null;
|
||||||
files.forEach(function(file){
|
files.forEach(function(file){
|
||||||
@@ -92,17 +94,18 @@ exports.minify = function(files, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. output
|
// 4. output
|
||||||
var map = null;
|
var inMap = options.inSourceMap;
|
||||||
var inMap = null;
|
var output = {};
|
||||||
if (options.inSourceMap) {
|
if (typeof options.inSourceMap == "string") {
|
||||||
inMap = fs.readFileSync(options.inSourceMap, "utf8");
|
inMap = fs.readFileSync(options.inSourceMap, "utf8");
|
||||||
}
|
}
|
||||||
if (options.outSourceMap) map = UglifyJS.SourceMap({
|
if (options.outSourceMap) {
|
||||||
file: options.outSourceMap,
|
output.source_map = UglifyJS.SourceMap({
|
||||||
orig: inMap,
|
file: options.outSourceMap,
|
||||||
root: options.sourceRoot
|
orig: inMap,
|
||||||
});
|
root: options.sourceRoot
|
||||||
var output = { source_map: map };
|
});
|
||||||
|
}
|
||||||
if (options.output) {
|
if (options.output) {
|
||||||
UglifyJS.merge(output, options.output);
|
UglifyJS.merge(output, options.output);
|
||||||
}
|
}
|
||||||
@@ -110,7 +113,7 @@ exports.minify = function(files, options) {
|
|||||||
toplevel.print(stream);
|
toplevel.print(stream);
|
||||||
return {
|
return {
|
||||||
code : stream + "",
|
code : stream + "",
|
||||||
map : map + ""
|
map : output.source_map + ""
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user