Compare commits
268 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
294861ba96 | ||
|
|
11b0efdf84 | ||
|
|
5486b68850 | ||
|
|
bdd8e34f63 | ||
|
|
6547437725 | ||
|
|
9662228f6a | ||
|
|
31a9b05c96 | ||
|
|
63b01fe8f9 | ||
|
|
7a4ed9d200 | ||
|
|
d5c651a5e5 | ||
|
|
cdba43cfa4 | ||
|
|
a123e232b9 | ||
|
|
601780acc1 | ||
|
|
7c3fee9e31 | ||
|
|
929de2b0de | ||
|
|
12e6ad326c | ||
|
|
00c8d1d241 | ||
|
|
af2472d85e | ||
|
|
3eb9101918 | ||
|
|
0a38a688f9 | ||
|
|
f4c2ea37bf | ||
|
|
915f907186 | ||
|
|
799509e145 | ||
|
|
b5a7197ae5 | ||
|
|
1b703349cf | ||
|
|
4a7179ff91 | ||
|
|
f97da4294a | ||
|
|
918c17bd88 | ||
|
|
8b71c6559b | ||
|
|
26641f3fb2 | ||
|
|
ebe118dc79 | ||
|
|
70e5b6f15b | ||
|
|
57e0fafd5c | ||
|
|
8439c8ba98 | ||
|
|
5c4e470d43 | ||
|
|
6605d15783 | ||
|
|
ac8db977b9 | ||
|
|
88b77ddaa9 | ||
|
|
fe4e9f9d97 | ||
|
|
8c6af09ae0 | ||
|
|
6f3e35bb3f | ||
|
|
174404c0f3 | ||
|
|
60c4030a4d | ||
|
|
ac810dc07a | ||
|
|
0cabedc526 | ||
|
|
5cd26c005b | ||
|
|
bd99b00413 | ||
|
|
9e2f9f7910 | ||
|
|
e87c77ed41 | ||
|
|
774bda13cd | ||
|
|
15b5f70338 | ||
|
|
7f48d5b33c | ||
|
|
b6968b6bd2 | ||
|
|
08b80302eb | ||
|
|
645626ebe8 | ||
|
|
d895c09c70 | ||
|
|
08623aa6a7 | ||
|
|
c898a26117 | ||
|
|
619adb0308 | ||
|
|
7691bebea5 | ||
|
|
3c4346728e | ||
|
|
18d37ac761 | ||
|
|
63d35f8f6d | ||
|
|
7dbe961b2d | ||
|
|
94c4daaf9e | ||
|
|
37ee9de902 | ||
|
|
83db98ad3b | ||
|
|
bd0ae6569f | ||
|
|
841a661071 | ||
|
|
7491d07666 | ||
|
|
335e349314 | ||
|
|
2a88d07b3a | ||
|
|
a887cde9f2 | ||
|
|
b5623b19d4 | ||
|
|
6b2861e086 | ||
|
|
d5138f7467 | ||
|
|
eac67b2816 | ||
|
|
ce10072824 | ||
|
|
dff54a6552 | ||
|
|
1940fb682c | ||
|
|
17eef5a3c2 | ||
|
|
9f1f21b810 | ||
|
|
a8e67d157e | ||
|
|
e870c7db45 | ||
|
|
6500f8c52c | ||
|
|
4d2f7d83af | ||
|
|
99945fcd04 | ||
|
|
0d952ae43d | ||
|
|
593677d2ff | ||
|
|
c69294c449 | ||
|
|
2a06c7758e | ||
|
|
7ee1ec91a2 | ||
|
|
233fb62bd8 | ||
|
|
6637c267a5 | ||
|
|
99233c44cc | ||
|
|
33528002b4 | ||
|
|
20542a37a8 | ||
|
|
5fd12451f9 | ||
|
|
ba939ccd6c | ||
|
|
3a5f354846 | ||
|
|
fcde6109b0 | ||
|
|
e3bd223dac | ||
|
|
6c8db6eae1 | ||
|
|
3ff0b9e0c9 | ||
|
|
464a942a95 | ||
|
|
d7a4a4a462 | ||
|
|
759b3f7d6d | ||
|
|
958b6c2e57 | ||
|
|
ab15d676d7 | ||
|
|
66761d7ecf | ||
|
|
3afad58a93 | ||
|
|
170e8b519e | ||
|
|
f8684f418a | ||
|
|
881bda7f59 | ||
|
|
9854deb626 | ||
|
|
d6814050dd | ||
|
|
252fc65558 | ||
|
|
8108c7ffaf | ||
|
|
ba9936a572 | ||
|
|
905b601178 | ||
|
|
63fb2d5a44 | ||
|
|
85a5fc0aeb | ||
|
|
4fba3e0b80 | ||
|
|
9d398d999c | ||
|
|
f47b2b52a5 | ||
|
|
fedb6191a1 | ||
|
|
5bf617ebde | ||
|
|
0b82e1cd5b | ||
|
|
9aef34a816 | ||
|
|
0ac6918a41 | ||
|
|
65ee5af78c | ||
|
|
c6fa291571 | ||
|
|
bce4307e9e | ||
|
|
96ad94ab41 | ||
|
|
a5b60217ce | ||
|
|
44fd6694eb | ||
|
|
e48db3a8b6 | ||
|
|
e637bdaf4e | ||
|
|
d558abbdb7 | ||
|
|
4aed0830e5 | ||
|
|
d2dda34b2a | ||
|
|
c3a10c135e | ||
|
|
92e4340732 | ||
|
|
7b22f2031f | ||
|
|
3b14582d6b | ||
|
|
274e1b3dc7 | ||
|
|
de58b0289d | ||
|
|
efea52a4f4 | ||
|
|
763bd36b60 | ||
|
|
0552dbd93c | ||
|
|
18c63ff3d8 | ||
|
|
e04ef56243 | ||
|
|
5d60484553 | ||
|
|
3c846e6f7b | ||
|
|
2850dc69fd | ||
|
|
94205c3a37 | ||
|
|
2ada34b229 | ||
|
|
db396da734 | ||
|
|
0262b4244c | ||
|
|
73ca767d06 | ||
|
|
3ec11c781b | ||
|
|
a79ff060d0 | ||
|
|
43991f8d2f | ||
|
|
6b82069e1a | ||
|
|
276b9a31cd | ||
|
|
5801fa39e9 | ||
|
|
f0ab1b02e6 | ||
|
|
36c28e02fd | ||
|
|
e1c3861832 | ||
|
|
ecfd881ac6 | ||
|
|
81b7335267 | ||
|
|
bb010c2253 | ||
|
|
03b6121194 | ||
|
|
3ef092332b | ||
|
|
540c19792f | ||
|
|
80d1c8206b | ||
|
|
d36faffeca | ||
|
|
7c8c9b94bc | ||
|
|
f5eeed7665 | ||
|
|
80cfd063e2 | ||
|
|
aa45f6586e | ||
|
|
0c80d21e01 | ||
|
|
375c88245a | ||
|
|
ea3430102c | ||
|
|
9de7199b88 | ||
|
|
ae07714927 | ||
|
|
0e41a3fad4 | ||
|
|
61e850ceb5 | ||
|
|
992b6b9fcc | ||
|
|
7b71344051 | ||
|
|
605362f89d | ||
|
|
fbbaa42ee5 | ||
|
|
099992ecae | ||
|
|
d78ae20e64 | ||
|
|
5c02d65ddb | ||
|
|
d36067cd35 | ||
|
|
f1b2134dd1 | ||
|
|
74cda80d3b | ||
|
|
9a3a848cc8 | ||
|
|
a1a4c2ada7 | ||
|
|
189dbf02b6 | ||
|
|
42ecd42ac0 | ||
|
|
a10f6a96d7 | ||
|
|
0d232a1422 | ||
|
|
285bffd2c6 | ||
|
|
61c233a08e | ||
|
|
d2d716483a | ||
|
|
f16033aafd | ||
|
|
ae5366a31d | ||
|
|
6b23cbc852 | ||
|
|
7f9bc9e863 | ||
|
|
13219cebcb | ||
|
|
93a6e5780e | ||
|
|
fe55e0d93d | ||
|
|
e1f0747e4c | ||
|
|
e37b67d013 | ||
|
|
ad18689d92 | ||
|
|
0f80b1058d | ||
|
|
0d48af3f36 | ||
|
|
4613644cce | ||
|
|
718e475613 | ||
|
|
aa5dd15352 | ||
|
|
5bff65c132 | ||
|
|
24bc09b79b | ||
|
|
37c17d5541 | ||
|
|
120948fa48 | ||
|
|
66e6f0c3cb | ||
|
|
f7447efa8c | ||
|
|
f4d36a58c2 | ||
|
|
6d1c3e1aec | ||
|
|
73cc0505f5 | ||
|
|
c75f5a1fd8 | ||
|
|
39d8880f2c | ||
|
|
5538ec7bd8 | ||
|
|
f101d6429b | ||
|
|
fe06fc85d3 | ||
|
|
f36a1eaa8b | ||
|
|
a64bdda9ae | ||
|
|
01d19b4b52 | ||
|
|
f0c1a01bc2 | ||
|
|
7be680d3f8 | ||
|
|
57dab1e1db | ||
|
|
21b3c890a1 | ||
|
|
fb0ec720a4 | ||
|
|
7971ed33d1 | ||
|
|
885835a655 | ||
|
|
4c64554808 | ||
|
|
548beeb6b1 | ||
|
|
e3066f9577 | ||
|
|
e391367488 | ||
|
|
18ddf2f7b5 | ||
|
|
f8ee5a0785 | ||
|
|
b467a3c877 | ||
|
|
f2d48e9019 | ||
|
|
5e314bf3e9 | ||
|
|
05ba26c7c8 | ||
|
|
87b72364a4 | ||
|
|
0e3ff1f970 | ||
|
|
ec3e74d7f4 | ||
|
|
62bda71c85 | ||
|
|
83e0939088 | ||
|
|
9798d96e37 | ||
|
|
6006dd933d | ||
|
|
ac2caf1088 | ||
|
|
8511e80f48 | ||
|
|
91bc3f1f92 | ||
|
|
8463b48f90 | ||
|
|
524a8a42a4 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
/node_modules/
|
||||
/npm-debug.log
|
||||
tmp/
|
||||
node_modules/
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
language: node_js
|
||||
before_install: "npm install -g npm"
|
||||
node_js:
|
||||
- "0.4"
|
||||
- "0.8"
|
||||
- "0.12"
|
||||
- "0.10"
|
||||
- "0.11"
|
||||
- "4"
|
||||
matrix:
|
||||
fast_finish: true
|
||||
sudo: false
|
||||
|
||||
303
README.md
303
README.md
@@ -1,6 +1,6 @@
|
||||
UglifyJS 2
|
||||
==========
|
||||
[](https://travis-ci.org/mishoo/UglifyJS2)
|
||||
[](https://travis-ci.org/mishoo/UglifyJS2)
|
||||
|
||||
UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit.
|
||||
|
||||
@@ -44,73 +44,100 @@ variable/function declared in another file will be matched properly.
|
||||
If you want to read from STDIN instead, pass a single dash instead of input
|
||||
files.
|
||||
|
||||
If you wish to pass your options before the input files, separate the two with
|
||||
a double dash to prevent input files being used as option arguments:
|
||||
|
||||
uglifyjs --compress --mangle -- input.js
|
||||
|
||||
The available options are:
|
||||
|
||||
```
|
||||
--source-map Specify an output file where to generate source map.
|
||||
[string]
|
||||
--source-map-root The path to the original source to be included in the
|
||||
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 source map.
|
||||
--source-map-url The path to the source map to be added in //#
|
||||
sourceMappingURL. Defaults to the value passed with
|
||||
--source-map. [string]
|
||||
--source-map-include-sources
|
||||
Pass this flag if you want to include the content of
|
||||
source files in the source map as sourcesContent
|
||||
property. [boolean]
|
||||
--in-source-map Input source map, useful if you're compressing JS that was
|
||||
generated from some other original code.
|
||||
--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). [boolean]
|
||||
--expr Parse a single expression, rather than a program (for
|
||||
parsing JSON) [boolean]
|
||||
-p, --prefix 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. 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. [string]
|
||||
sourceMappingURL. Defaults to the value passed
|
||||
with --source-map.
|
||||
--source-map-include-sources Pass this flag if you want to include the
|
||||
content of source files in the source map as
|
||||
sourcesContent property.
|
||||
--in-source-map Input source map, useful if you're compressing
|
||||
JS that was generated from some other original
|
||||
code.
|
||||
--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).
|
||||
--expr Parse a single expression, rather than a
|
||||
program (for parsing JSON)
|
||||
-p, --prefix 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. 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.
|
||||
-o, --output Output file (default STDOUT).
|
||||
-b, --beautify Beautify output/specify output options. [string]
|
||||
-m, --mangle Mangle names/pass mangler options. [string]
|
||||
-b, --beautify Beautify output/specify output options.
|
||||
-m, --mangle Mangle names/pass mangler options.
|
||||
-r, --reserved Reserved names to exclude from mangling.
|
||||
-c, --compress Enable compressor/pass compressor options. Pass options
|
||||
like -c hoist_vars=false,if_return=false. Use -c with no
|
||||
argument to use the default compression options. [string]
|
||||
-d, --define Global definitions [string]
|
||||
-e, --enclose Embed everything in a big function, with a configurable
|
||||
parameter/argument list. [string]
|
||||
--comments Preserve copyright comments in the output. By default this
|
||||
works like Google Closure, keeping JSDoc-style comments
|
||||
that contain "@license" or "@preserve". You can optionally
|
||||
pass one of the following arguments to this flag:
|
||||
-c, --compress Enable compressor/pass compressor options. Pass
|
||||
options like -c
|
||||
hoist_vars=false,if_return=false. Use -c with
|
||||
no argument to use the default compression
|
||||
options.
|
||||
-d, --define Global definitions
|
||||
-e, --enclose Embed everything in a big function, with a
|
||||
configurable parameter/argument list.
|
||||
--comments Preserve copyright comments in the output. By
|
||||
default this works like Google Closure, keeping
|
||||
JSDoc-style comments that contain "@license" or
|
||||
"@preserve". You can optionally pass one of the
|
||||
following arguments to this flag:
|
||||
- "all" to keep all comments
|
||||
- a valid JS regexp (needs to start with a slash) to keep
|
||||
only comments that match.
|
||||
Note that currently not *all* comments can be kept when
|
||||
compression is on, because of dead code removal or
|
||||
cascading statements into sequences. [string]
|
||||
--preamble Preamble to prepend to the output. You can use this to
|
||||
insert a comment, for example for licensing information.
|
||||
This will not be parsed, but the source map will adjust
|
||||
for its presence.
|
||||
--stats Display operations run time on STDERR. [boolean]
|
||||
--acorn Use Acorn for parsing. [boolean]
|
||||
--spidermonkey Assume input files are SpiderMonkey AST format (as JSON).
|
||||
[boolean]
|
||||
- a valid JS regexp (needs to start with a
|
||||
slash) to keep only comments that match.
|
||||
Note that currently not *all* comments can be
|
||||
kept when compression is on, because of dead
|
||||
code removal or cascading statements into
|
||||
sequences.
|
||||
--preamble Preamble to prepend to the output. You can use
|
||||
this to insert a comment, for example for
|
||||
licensing information. This will not be
|
||||
parsed, but the source map will adjust for its
|
||||
presence.
|
||||
--stats Display operations run time on STDERR.
|
||||
--acorn Use Acorn for parsing.
|
||||
--spidermonkey Assume input files are SpiderMonkey AST format
|
||||
(as JSON).
|
||||
--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]
|
||||
--wrap=UglifyJS --export-all)
|
||||
--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.
|
||||
--export-all Only used when --wrap, this tells UglifyJS to
|
||||
add code to automatically export all globals.
|
||||
--lint Display some scope warnings
|
||||
-v, --verbose Verbose
|
||||
-V, --version Print version number and exit.
|
||||
--noerr Don't throw an error for unknown options in -c,
|
||||
-b or -m.
|
||||
--bare-returns Allow return outside of functions. Useful when
|
||||
minifying CommonJS modules and Userscripts that
|
||||
may be anonymous function wrapped (IIFE) by the
|
||||
.user.js engine `caller`.
|
||||
--keep-fnames Do not mangle/drop function names. Useful for
|
||||
code relying on Function.prototype.name.
|
||||
--reserved-file File containing reserved names
|
||||
--reserve-domprops Make (most?) DOM properties reserved for
|
||||
--mangle-props
|
||||
--mangle-props Mangle property names
|
||||
--mangle-regex Only mangle property names matching the regex
|
||||
--name-cache File to hold mangled names mappings
|
||||
--pure-funcs List of functions that can be safely removed if
|
||||
their return value is not used [array]
|
||||
```
|
||||
|
||||
Specify `--output` (`-o`) to declare the output file. Otherwise the output
|
||||
@@ -184,6 +211,73 @@ comma-separated list of names. For example:
|
||||
|
||||
to prevent the `require`, `exports` and `$` names from being changed.
|
||||
|
||||
### Mangling property names (`--mangle-props`)
|
||||
|
||||
**Note:** this will probably break your code. Mangling property names is a
|
||||
separate step, different from variable name mangling. Pass
|
||||
`--mangle-props`. It will mangle all properties that are seen in some
|
||||
object literal, or that are assigned to. For example:
|
||||
|
||||
```js
|
||||
var x = {
|
||||
foo: 1
|
||||
};
|
||||
|
||||
x.bar = 2;
|
||||
x["baz"] = 3;
|
||||
x[condition ? "moo" : "boo"] = 4;
|
||||
console.log(x.something());
|
||||
```
|
||||
|
||||
In the above code, `foo`, `bar`, `baz`, `moo` and `boo` will be replaced
|
||||
with single characters, while `something()` will be left as is.
|
||||
|
||||
In order for this to be of any use, we should avoid mangling standard JS
|
||||
names. For instance, if your code would contain `x.length = 10`, then
|
||||
`length` becomes a candidate for mangling and it will be mangled throughout
|
||||
the code, regardless if it's being used as part of your own objects or
|
||||
accessing an array's length. To avoid that, you can use `--reserved-file`
|
||||
to pass a filename that should contain the names to be excluded from
|
||||
mangling. This file can be used both for excluding variable names and
|
||||
property names. It could look like this, for example:
|
||||
|
||||
```js
|
||||
{
|
||||
"vars": [ "define", "require", ... ],
|
||||
"props": [ "length", "prototype", ... ]
|
||||
}
|
||||
```
|
||||
|
||||
`--reserved-file` can be an array of file names (either a single
|
||||
comma-separated argument, or you can pass multiple `--reserved-file`
|
||||
arguments) — in this case it will exclude names from all those files.
|
||||
|
||||
A default exclusion file is provided in `tools/domprops.json` which should
|
||||
cover most standard JS and DOM properties defined in various browsers. Pass
|
||||
`--reserve-domprops` to read that in.
|
||||
|
||||
You can also use a regular expression to define which property names should be
|
||||
mangled. For example, `--mangle-regex="/^_/"` will only mangle property names
|
||||
that start with an underscore.
|
||||
|
||||
When you compress multiple files using this option, in order for them to
|
||||
work together in the end we need to ensure somehow that one property gets
|
||||
mangled to the same name in all of them. For this, pass `--name-cache
|
||||
filename.json` and UglifyJS will maintain these mappings in a file which can
|
||||
then be reused. It should be initially empty. Example:
|
||||
|
||||
```
|
||||
rm -f /tmp/cache.json # start fresh
|
||||
uglifyjs file1.js file2.js --mangle-props --name-cache /tmp/cache.json -o part1.js
|
||||
uglifyjs file3.js file4.js --mangle-props --name-cache /tmp/cache.json -o part2.js
|
||||
```
|
||||
|
||||
Now, `part1.js` and `part2.js` will be consistent with each other in terms
|
||||
of mangled property names.
|
||||
|
||||
Using the name cache is not necessary if you compress all your files in a
|
||||
single call to UglifyJS.
|
||||
|
||||
## Compressor options
|
||||
|
||||
You need to pass `--compress` (`-c`) to enable the compressor. Optionally
|
||||
@@ -231,6 +325,9 @@ to set `true`; it's effectively a shortcut for `foo=true`).
|
||||
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
|
||||
and `x = something(), x` into `x = something()`
|
||||
|
||||
- `collapse_vars` -- default `false`. Collapse single-use `var` and `const`
|
||||
definitions when possible.
|
||||
|
||||
- `warnings` -- display warnings when dropping unreachable code or unused
|
||||
declarations etc.
|
||||
|
||||
@@ -256,6 +353,15 @@ to set `true`; it's effectively a shortcut for `foo=true`).
|
||||
- `drop_console` -- default `false`. Pass `true` to discard calls to
|
||||
`console.*` functions.
|
||||
|
||||
- `keep_fargs` -- default `true`. Prevents the
|
||||
compressor from discarding unused function arguments. You need this
|
||||
for code which relies on `Function.length`.
|
||||
|
||||
- `keep_fnames` -- default `false`. Pass `true` to prevent the
|
||||
compressor from mangling/discarding function names. Useful for code relying on
|
||||
`Function.prototype.name`.
|
||||
|
||||
|
||||
### The `unsafe` option
|
||||
|
||||
It enables some transformations that *might* break code logic in certain
|
||||
@@ -263,14 +369,14 @@ 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 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).
|
||||
reduced to a single character)
|
||||
|
||||
### Conditional compilation
|
||||
|
||||
@@ -294,6 +400,8 @@ separate file and include it into the build. For example you can have a
|
||||
```javascript
|
||||
const DEBUG = false;
|
||||
const PRODUCTION = true;
|
||||
// Alternative for environments that don't support `const`
|
||||
/** @const */ var STAGING = false;
|
||||
// etc.
|
||||
```
|
||||
|
||||
@@ -303,8 +411,8 @@ and build your code like this:
|
||||
|
||||
UglifyJS will notice the constants and, since they cannot be altered, it
|
||||
will evaluate references to them to the value itself and drop unreachable
|
||||
code as usual. The possible downside of this approach is that the build
|
||||
will contain the `const` declarations.
|
||||
code as usual. The build will contain the `const` declarations if you use
|
||||
them. If you are targeting < ES6 environments, use `/** @const */ var`.
|
||||
|
||||
<a name="codegen-options"></a>
|
||||
## Beautifier options
|
||||
@@ -343,6 +451,13 @@ can pass additional arguments that control the code output:
|
||||
it will be prepended to the output literally. The source map will
|
||||
adjust for this text. Can be used to insert a comment containing
|
||||
licensing information, for example.
|
||||
- `quote_style` (default `0`) -- preferred quote style for strings (affects
|
||||
quoted property names and directives as well):
|
||||
- `0` -- prefers double quotes, switches to single quotes when there are
|
||||
more double quotes in the string itself.
|
||||
- `1` -- always use single quotes
|
||||
- `2` -- always use double quotes
|
||||
- `3` -- always use the original quotes
|
||||
|
||||
### Keeping copyright notices or other comments
|
||||
|
||||
@@ -400,6 +515,38 @@ Acorn is really fast (e.g. 250ms instead of 380ms on some 650K code), but
|
||||
converting the SpiderMonkey tree that Acorn produces takes another 150ms so
|
||||
in total it's a bit more than just using UglifyJS's own parser.
|
||||
|
||||
### Using UglifyJS to transform SpiderMonkey AST
|
||||
|
||||
Now you can use UglifyJS as any other intermediate tool for transforming
|
||||
JavaScript ASTs in SpiderMonkey format.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
function uglify(ast, options, mangle) {
|
||||
// Conversion from SpiderMonkey AST to internal format
|
||||
var uAST = UglifyJS.AST_Node.from_mozilla_ast(ast);
|
||||
|
||||
// Compression
|
||||
uAST.figure_out_scope();
|
||||
uAST = uAST.transform(UglifyJS.Compressor(options));
|
||||
|
||||
// Mangling (optional)
|
||||
if (mangle) {
|
||||
uAST.figure_out_scope();
|
||||
uAST.compute_char_frequency();
|
||||
uAST.mangle_names();
|
||||
}
|
||||
|
||||
// Back-conversion to SpiderMonkey AST
|
||||
return uAST.to_mozilla_ast();
|
||||
}
|
||||
```
|
||||
|
||||
Check out
|
||||
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
|
||||
for details.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
@@ -462,6 +609,16 @@ var result = UglifyJS.minify("compiled.js", {
|
||||
// same as before, it returns `code` and `map`
|
||||
```
|
||||
|
||||
If your input source map is not in a file, you can pass it in as an object
|
||||
using the `inSourceMap` argument:
|
||||
|
||||
```javascript
|
||||
var result = UglifyJS.minify("compiled.js", {
|
||||
inSourceMap: JSON.parse(my_source_map_string),
|
||||
outSourceMap: "minified.js.map"
|
||||
});
|
||||
```
|
||||
|
||||
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
|
||||
no sense otherwise).
|
||||
|
||||
@@ -474,6 +631,9 @@ Other options:
|
||||
|
||||
- `mangle` — pass `false` to skip mangling names.
|
||||
|
||||
- `mangleProperties` (default `false`) — pass an object to specify custom
|
||||
mangle property options.
|
||||
|
||||
- `output` (default `null`) — pass an object if you wish to specify
|
||||
additional [output options][codegen]. The defaults are optimized
|
||||
for best compression.
|
||||
@@ -481,6 +641,13 @@ Other options:
|
||||
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
|
||||
Pass an object to specify custom [compressor options][compressor].
|
||||
|
||||
- `parse` (default {}) — pass an object if you wish to specify some
|
||||
additional [parser options][parser]. (not all options available... see below)
|
||||
|
||||
##### mangleProperties options
|
||||
|
||||
- `regex` — Pass a RegExp to only mangle certain names (maps to the `--mange-regex` CLI arguments option)
|
||||
|
||||
We could add more options to `UglifyJS.minify` — if you need additional
|
||||
functionality please suggest!
|
||||
|
||||
@@ -499,6 +666,9 @@ properties are available:
|
||||
|
||||
- `strict` — disable automatic semicolon insertion and support for trailing
|
||||
comma in arrays and objects
|
||||
- `bare_returns` — Allow return outside of functions. (maps to the
|
||||
`--bare-returns` CLI arguments option and available to `minify` `parse`
|
||||
other options object)
|
||||
- `filename` — the name of the file where this code is coming from
|
||||
- `toplevel` — a `toplevel` node (as returned by a previous invocation of
|
||||
`parse`)
|
||||
@@ -576,7 +746,7 @@ or, for a shortcut you can do:
|
||||
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 options,
|
||||
most of them documented above in section “Beautifier options”. The two
|
||||
which we care about here are `source_map` and `comments`.
|
||||
|
||||
@@ -633,8 +803,9 @@ The `source_map_options` (optional) can contain the following properties:
|
||||
came from. It can be simply a string in JSON, or a JSON object containing
|
||||
the original source map.
|
||||
|
||||
[acorn]: https://github.com/marijnh/acorn
|
||||
[acorn]: https://github.com/ternjs/acorn
|
||||
[source-map]: https://github.com/mozilla/source-map
|
||||
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
|
||||
[codegen]: http://lisperator.net/uglifyjs/codegen
|
||||
[compressor]: http://lisperator.net/uglifyjs/compress
|
||||
[parser]: http://lisperator.net/uglifyjs/parser
|
||||
|
||||
77
bin/extract-props.js
Executable file
77
bin/extract-props.js
Executable file
@@ -0,0 +1,77 @@
|
||||
#! /usr/bin/env node
|
||||
|
||||
"use strict";
|
||||
|
||||
var U2 = require("../tools/node");
|
||||
var fs = require("fs");
|
||||
var yargs = require("yargs");
|
||||
var ARGS = yargs
|
||||
.describe("o", "Output file")
|
||||
.argv;
|
||||
var files = ARGS._.slice();
|
||||
var output = {
|
||||
vars: {},
|
||||
props: {}
|
||||
};
|
||||
|
||||
if (ARGS.o) try {
|
||||
output = JSON.parse(fs.readFileSync(ARGS.o, "utf8"));
|
||||
} catch(ex) {}
|
||||
|
||||
files.forEach(getProps);
|
||||
|
||||
if (ARGS.o) {
|
||||
fs.writeFileSync(ARGS.o, JSON.stringify(output, null, 2), "utf8");
|
||||
} else {
|
||||
console.log("%s", JSON.stringify(output, null, 2));
|
||||
}
|
||||
|
||||
function getProps(filename) {
|
||||
var code = fs.readFileSync(filename, "utf8");
|
||||
var ast = U2.parse(code);
|
||||
|
||||
ast.walk(new U2.TreeWalker(function(node){
|
||||
if (node instanceof U2.AST_ObjectKeyVal) {
|
||||
add(node.key);
|
||||
}
|
||||
else if (node instanceof U2.AST_ObjectProperty) {
|
||||
add(node.key.name);
|
||||
}
|
||||
else if (node instanceof U2.AST_Dot) {
|
||||
add(node.property);
|
||||
}
|
||||
else if (node instanceof U2.AST_Sub) {
|
||||
addStrings(node.property);
|
||||
}
|
||||
}));
|
||||
|
||||
function addStrings(node) {
|
||||
var out = {};
|
||||
try {
|
||||
(function walk(node){
|
||||
node.walk(new U2.TreeWalker(function(node){
|
||||
if (node instanceof U2.AST_Seq) {
|
||||
walk(node.cdr);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof U2.AST_String) {
|
||||
add(node.value);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof U2.AST_Conditional) {
|
||||
walk(node.consequent);
|
||||
walk(node.alternative);
|
||||
return true;
|
||||
}
|
||||
throw out;
|
||||
}));
|
||||
})(node);
|
||||
} catch(ex) {
|
||||
if (ex !== out) throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
function add(name) {
|
||||
output.props[name] = true;
|
||||
}
|
||||
}
|
||||
195
bin/uglifyjs
195
bin/uglifyjs
@@ -5,12 +5,12 @@
|
||||
|
||||
var UglifyJS = require("../tools/node");
|
||||
var sys = require("util");
|
||||
var optimist = require("optimist");
|
||||
var yargs = require("yargs");
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var async = require("async");
|
||||
var acorn;
|
||||
var ARGS = optimist
|
||||
var ARGS = yargs
|
||||
.usage("$0 input1.js [input2.js ...] [options]\n\
|
||||
Use a single dash to read input from the standard input.\
|
||||
\n\n\
|
||||
@@ -64,6 +64,16 @@ You need to pass an argument to this option to specify the name that your module
|
||||
.describe("v", "Verbose")
|
||||
.describe("V", "Print version number and exit.")
|
||||
.describe("noerr", "Don't throw an error for unknown options in -c, -b or -m.")
|
||||
.describe("bare-returns", "Allow return outside of functions. Useful when minifying CommonJS modules.")
|
||||
.describe("keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.")
|
||||
.describe("quotes", "Quote style (0 - auto, 1 - single, 2 - double, 3 - original)")
|
||||
.describe("reserved-file", "File containing reserved names")
|
||||
.describe("reserve-domprops", "Make (most?) DOM properties reserved for --mangle-props")
|
||||
.describe("mangle-props", "Mangle property names")
|
||||
.describe("mangle-regex", "Only mangle property names matching the regex")
|
||||
.describe("name-cache", "File to hold mangled names mappings")
|
||||
.describe("pure-funcs", "List of functions that can be safely removed if their return value is not used")
|
||||
.describe("dump-spidermonkey-ast", "Dump SpiderMonkey AST to stdout.")
|
||||
|
||||
.alias("p", "prefix")
|
||||
.alias("o", "output")
|
||||
@@ -75,18 +85,28 @@ You need to pass an argument to this option to specify the name that your module
|
||||
.alias("r", "reserved")
|
||||
.alias("V", "version")
|
||||
.alias("e", "enclose")
|
||||
.alias("q", "quotes")
|
||||
|
||||
.string("source-map")
|
||||
.string("source-map-root")
|
||||
.string("source-map-url")
|
||||
.string("b")
|
||||
.string("beautify")
|
||||
.string("m")
|
||||
.string("mangle")
|
||||
.string("c")
|
||||
.string("compress")
|
||||
.string("d")
|
||||
.string("define")
|
||||
.string("e")
|
||||
.string("enclose")
|
||||
.string("comments")
|
||||
.string("wrap")
|
||||
.string("p")
|
||||
.string("prefix")
|
||||
.string("name-cache")
|
||||
.array("reserved-file")
|
||||
.array("pure-funcs")
|
||||
|
||||
.boolean("expr")
|
||||
.boolean("source-map-include-sources")
|
||||
@@ -94,12 +114,19 @@ You need to pass an argument to this option to specify the name that your module
|
||||
.boolean("export-all")
|
||||
.boolean("self")
|
||||
.boolean("v")
|
||||
.boolean("verbose")
|
||||
.boolean("stats")
|
||||
.boolean("acorn")
|
||||
.boolean("spidermonkey")
|
||||
.boolean("dump-spidermonkey-ast")
|
||||
.boolean("lint")
|
||||
.boolean("V")
|
||||
.boolean("version")
|
||||
.boolean("noerr")
|
||||
.boolean("bare-returns")
|
||||
.boolean("keep-fnames")
|
||||
.boolean("mangle-props")
|
||||
.boolean("reserve-domprops")
|
||||
|
||||
.wrap(80)
|
||||
|
||||
@@ -110,24 +137,24 @@ normalize(ARGS);
|
||||
|
||||
if (ARGS.noerr) {
|
||||
UglifyJS.DefaultsError.croak = function(msg, defs) {
|
||||
sys.error("WARN: " + msg);
|
||||
print_error("WARN: " + msg);
|
||||
};
|
||||
}
|
||||
|
||||
if (ARGS.version || ARGS.V) {
|
||||
var json = require("../package.json");
|
||||
sys.puts(json.name + ' ' + json.version);
|
||||
print(json.name + ' ' + json.version);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (ARGS.ast_help) {
|
||||
var desc = UglifyJS.describe_ast();
|
||||
sys.puts(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
|
||||
print(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (ARGS.h || ARGS.help) {
|
||||
sys.puts(optimist.help());
|
||||
print(yargs.help());
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
@@ -138,18 +165,58 @@ if (ARGS.acorn) {
|
||||
var COMPRESS = getOptions("c", true);
|
||||
var MANGLE = getOptions("m", true);
|
||||
var BEAUTIFY = getOptions("b", true);
|
||||
var RESERVED = null;
|
||||
|
||||
if (ARGS.reserved_file) ARGS.reserved_file.forEach(function(filename){
|
||||
RESERVED = UglifyJS.readReservedFile(filename, RESERVED);
|
||||
});
|
||||
|
||||
if (ARGS.reserve_domprops) {
|
||||
RESERVED = UglifyJS.readDefaultReservedFile(RESERVED);
|
||||
}
|
||||
|
||||
if (ARGS.d) {
|
||||
if (COMPRESS) COMPRESS.global_defs = getOptions("d");
|
||||
}
|
||||
|
||||
if (ARGS.pure_funcs) {
|
||||
if (COMPRESS) COMPRESS.pure_funcs = ARGS.pure_funcs;
|
||||
}
|
||||
|
||||
if (ARGS.r) {
|
||||
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
|
||||
}
|
||||
|
||||
if (RESERVED && MANGLE) {
|
||||
if (!MANGLE.except) MANGLE.except = RESERVED.vars;
|
||||
else MANGLE.except = MANGLE.except.concat(RESERVED.vars);
|
||||
}
|
||||
|
||||
function readNameCache(key) {
|
||||
return UglifyJS.readNameCache(ARGS.name_cache, key);
|
||||
}
|
||||
|
||||
function writeNameCache(key, cache) {
|
||||
return UglifyJS.writeNameCache(ARGS.name_cache, key, cache);
|
||||
}
|
||||
|
||||
function extractRegex(str) {
|
||||
if (/^\/.*\/[a-zA-Z]*$/.test(str)) {
|
||||
var regex_pos = str.lastIndexOf("/");
|
||||
return new RegExp(str.substr(1, regex_pos - 1), str.substr(regex_pos + 1));
|
||||
} else {
|
||||
throw new Error("Invalid regular expression: " + str);
|
||||
}
|
||||
}
|
||||
|
||||
if (ARGS.quotes === true) {
|
||||
ARGS.quotes = 3;
|
||||
}
|
||||
|
||||
var OUTPUT_OPTIONS = {
|
||||
beautify: BEAUTIFY ? true : false,
|
||||
preamble: ARGS.preamble || null,
|
||||
beautify : BEAUTIFY ? true : false,
|
||||
preamble : ARGS.preamble || null,
|
||||
quote_style : ARGS.quotes != null ? ARGS.quotes : 0
|
||||
};
|
||||
|
||||
if (ARGS.screw_ie8) {
|
||||
@@ -158,12 +225,21 @@ if (ARGS.screw_ie8) {
|
||||
OUTPUT_OPTIONS.screw_ie8 = true;
|
||||
}
|
||||
|
||||
if (ARGS.keep_fnames) {
|
||||
if (COMPRESS) COMPRESS.keep_fnames = true;
|
||||
if (MANGLE) MANGLE.keep_fnames = true;
|
||||
}
|
||||
|
||||
if (BEAUTIFY)
|
||||
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
|
||||
|
||||
if (ARGS.comments) {
|
||||
if (/^\//.test(ARGS.comments)) {
|
||||
OUTPUT_OPTIONS.comments = new Function("return(" + ARGS.comments + ")")();
|
||||
if (ARGS.comments != null) {
|
||||
if (/^\/.*\/[a-zA-Z]*$/.test(ARGS.comments)) {
|
||||
try {
|
||||
OUTPUT_OPTIONS.comments = extractRegex(ARGS.comments);
|
||||
} catch (e) {
|
||||
print_error("ERROR: Invalid --comments: " + e.message);
|
||||
}
|
||||
} else if (ARGS.comments == "all") {
|
||||
OUTPUT_OPTIONS.comments = true;
|
||||
} else {
|
||||
@@ -182,11 +258,10 @@ var files = ARGS._.slice();
|
||||
|
||||
if (ARGS.self) {
|
||||
if (files.length > 0) {
|
||||
sys.error("WARN: Ignoring input files since --self was passed");
|
||||
print_error("WARN: Ignoring input files since --self was passed");
|
||||
}
|
||||
files = UglifyJS.FILES;
|
||||
if (!ARGS.wrap) ARGS.wrap = "UglifyJS";
|
||||
ARGS.export_all = true;
|
||||
}
|
||||
|
||||
var ORIG_MAP = ARGS.in_source_map;
|
||||
@@ -194,7 +269,7 @@ var ORIG_MAP = ARGS.in_source_map;
|
||||
if (ORIG_MAP) {
|
||||
ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP));
|
||||
if (files.length == 0) {
|
||||
sys.error("INFO: Using file from the input source map: " + ORIG_MAP.file);
|
||||
print_error("INFO: Using file from the input source map: " + ORIG_MAP.file);
|
||||
files = [ ORIG_MAP.file ];
|
||||
}
|
||||
if (ARGS.source_map_root == null) {
|
||||
@@ -207,12 +282,12 @@ if (files.length == 0) {
|
||||
}
|
||||
|
||||
if (files.indexOf("-") >= 0 && ARGS.source_map) {
|
||||
sys.error("ERROR: Source map doesn't work with input from STDIN");
|
||||
print_error("ERROR: Source map doesn't work with input from STDIN");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (files.filter(function(el){ return el == "-" }).length > 1) {
|
||||
sys.error("ERROR: Can read a single file from STDIN (two or more dashes specified)");
|
||||
print_error("ERROR: Can read a single file from STDIN (two or more dashes specified)");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -235,9 +310,9 @@ try {
|
||||
var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
|
||||
} catch(ex) {
|
||||
if (ex instanceof UglifyJS.DefaultsError) {
|
||||
sys.error(ex.msg);
|
||||
sys.error("Supported options:");
|
||||
sys.error(sys.inspect(ex.defs));
|
||||
print_error(ex.msg);
|
||||
print_error("Supported options:");
|
||||
print_error(sys.inspect(ex.defs));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -245,12 +320,12 @@ try {
|
||||
async.eachLimit(files, 1, function (file, cb) {
|
||||
read_whole_file(file, function (err, code) {
|
||||
if (err) {
|
||||
sys.error("ERROR: can't read file: " + file);
|
||||
print_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);
|
||||
file = path.relative(path.dirname(ARGS.source_map), file).replace(/\\/g, '/');
|
||||
} else {
|
||||
var p = parseInt(ARGS.p, 10);
|
||||
if (!isNaN(p)) {
|
||||
@@ -278,12 +353,13 @@ async.eachLimit(files, 1, function (file, cb) {
|
||||
filename : file,
|
||||
toplevel : TOPLEVEL,
|
||||
expression : ARGS.expr,
|
||||
bare_returns : ARGS.bare_returns,
|
||||
});
|
||||
} catch(ex) {
|
||||
if (ex instanceof UglifyJS.JS_Parse_Error) {
|
||||
sys.error("Parse error at " + file + ":" + ex.line + "," + ex.col);
|
||||
sys.error(ex.message);
|
||||
sys.error(ex.stack);
|
||||
print_error("Parse error at " + file + ":" + ex.line + "," + ex.col);
|
||||
print_error(ex.message);
|
||||
print_error(ex.stack);
|
||||
process.exit(1);
|
||||
}
|
||||
throw ex;
|
||||
@@ -297,11 +373,11 @@ async.eachLimit(files, 1, function (file, cb) {
|
||||
TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
|
||||
});
|
||||
|
||||
if (ARGS.wrap) {
|
||||
if (ARGS.wrap != null) {
|
||||
TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all);
|
||||
}
|
||||
|
||||
if (ARGS.enclose) {
|
||||
if (ARGS.enclose != null) {
|
||||
var arg_parameter_list = ARGS.enclose;
|
||||
if (arg_parameter_list === true) {
|
||||
arg_parameter_list = [];
|
||||
@@ -312,11 +388,33 @@ async.eachLimit(files, 1, function (file, cb) {
|
||||
TOPLEVEL = TOPLEVEL.wrap_enclose(arg_parameter_list);
|
||||
}
|
||||
|
||||
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint;
|
||||
if (ARGS.mangle_props || ARGS.name_cache) (function(){
|
||||
var reserved = RESERVED ? RESERVED.props : null;
|
||||
var cache = readNameCache("props");
|
||||
var regex;
|
||||
|
||||
try {
|
||||
regex = ARGS.mangle_regex ? extractRegex(ARGS.mangle_regex) : null;
|
||||
} catch (e) {
|
||||
print_error("ERROR: Invalid --mangle-regex: " + e.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
TOPLEVEL = UglifyJS.mangle_properties(TOPLEVEL, {
|
||||
reserved : reserved,
|
||||
cache : cache,
|
||||
only_cache : !ARGS.mangle_props,
|
||||
regex : regex
|
||||
});
|
||||
writeNameCache("props", cache);
|
||||
})();
|
||||
|
||||
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint
|
||||
var TL_CACHE = readNameCache("vars");
|
||||
|
||||
if (SCOPE_IS_NEEDED) {
|
||||
time_it("scope", function(){
|
||||
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8 });
|
||||
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE });
|
||||
if (ARGS.lint) {
|
||||
TOPLEVEL.scope_warnings();
|
||||
}
|
||||
@@ -331,17 +429,20 @@ async.eachLimit(files, 1, function (file, cb) {
|
||||
|
||||
if (SCOPE_IS_NEEDED) {
|
||||
time_it("scope", function(){
|
||||
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8 });
|
||||
if (MANGLE) {
|
||||
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE });
|
||||
if (MANGLE && !TL_CACHE) {
|
||||
TOPLEVEL.compute_char_frequency(MANGLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (MANGLE) time_it("mangle", function(){
|
||||
MANGLE.cache = TL_CACHE;
|
||||
TOPLEVEL.mangle_names(MANGLE);
|
||||
});
|
||||
|
||||
writeNameCache("vars", TL_CACHE);
|
||||
|
||||
if (ARGS.source_map_include_sources) {
|
||||
for (var file in SOURCES_CONTENT) {
|
||||
if (SOURCES_CONTENT.hasOwnProperty(file)) {
|
||||
@@ -350,6 +451,9 @@ async.eachLimit(files, 1, function (file, cb) {
|
||||
}
|
||||
}
|
||||
|
||||
if (ARGS.dump_spidermonkey_ast) {
|
||||
print(JSON.stringify(TOPLEVEL.to_mozilla_ast(), null, 2));
|
||||
} else {
|
||||
time_it("generate", function(){
|
||||
TOPLEVEL.print(output);
|
||||
});
|
||||
@@ -369,15 +473,16 @@ async.eachLimit(files, 1, function (file, cb) {
|
||||
if (OUTPUT_FILE) {
|
||||
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
|
||||
} else {
|
||||
sys.print(output);
|
||||
print(output);
|
||||
}
|
||||
}
|
||||
|
||||
if (ARGS.stats) {
|
||||
sys.error(UglifyJS.string_template("Timing information (compressed {count} files):", {
|
||||
print_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", {
|
||||
print_error(UglifyJS.string_template("- {name}: {time}s", {
|
||||
name: i,
|
||||
time: (STATS[i] / 1000).toFixed(3)
|
||||
}));
|
||||
@@ -394,17 +499,19 @@ function normalize(o) {
|
||||
}
|
||||
}
|
||||
|
||||
function getOptions(x, constants) {
|
||||
x = ARGS[x];
|
||||
if (!x) return null;
|
||||
function getOptions(flag, constants) {
|
||||
var x = ARGS[flag];
|
||||
if (x == null || x === false) return null;
|
||||
var ret = {};
|
||||
if (x !== true) {
|
||||
if (x !== "") {
|
||||
if (Array.isArray(x)) x = x.map(function (v) { return "(" + v + ")"; }).join(", ");
|
||||
|
||||
var ast;
|
||||
try {
|
||||
ast = UglifyJS.parse(x, { expression: true });
|
||||
} catch(ex) {
|
||||
if (ex instanceof UglifyJS.JS_Parse_Error) {
|
||||
sys.error("Error parsing arguments in: " + x);
|
||||
print_error("Error parsing arguments for flag `" + flag + "': " + x);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -423,8 +530,8 @@ function getOptions(x, constants) {
|
||||
ret[name] = true;
|
||||
return true; // no descend
|
||||
}
|
||||
sys.error(node.TYPE)
|
||||
sys.error("Error parsing arguments in: " + x);
|
||||
print_error(node.TYPE)
|
||||
print_error("Error parsing arguments for flag `" + flag + "': " + x);
|
||||
process.exit(1);
|
||||
}));
|
||||
}
|
||||
@@ -456,3 +563,11 @@ function time_it(name, cont) {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function print_error(msg) {
|
||||
console.error("%s", msg);
|
||||
}
|
||||
|
||||
function print(txt) {
|
||||
console.log("%s", txt);
|
||||
}
|
||||
|
||||
82
lib/ast.js
82
lib/ast.js
@@ -81,10 +81,11 @@ function DEFNODE(type, props, methods, base) {
|
||||
ctor.DEFMETHOD = function(name, method) {
|
||||
this.prototype[name] = method;
|
||||
};
|
||||
exports["AST_" + type] = ctor;
|
||||
return ctor;
|
||||
};
|
||||
|
||||
var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_before file", {
|
||||
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw", {
|
||||
}, null);
|
||||
|
||||
var AST_Node = DEFNODE("Node", "start end", {
|
||||
@@ -120,11 +121,12 @@ var AST_Debugger = DEFNODE("Debugger", null, {
|
||||
$documentation: "Represents a debugger statement",
|
||||
}, AST_Statement);
|
||||
|
||||
var AST_Directive = DEFNODE("Directive", "value scope", {
|
||||
var AST_Directive = DEFNODE("Directive", "value scope quote", {
|
||||
$documentation: "Represents a directive, like \"use strict\";",
|
||||
$propdoc: {
|
||||
value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
|
||||
scope: "[AST_Scope/S] The scope that this directive affects"
|
||||
scope: "[AST_Scope/S] The scope that this directive affects",
|
||||
quote: "[string] the original quote character"
|
||||
},
|
||||
}, AST_Statement);
|
||||
|
||||
@@ -205,21 +207,27 @@ var AST_DWLoop = DEFNODE("DWLoop", "condition", {
|
||||
$documentation: "Base class for do/while statements",
|
||||
$propdoc: {
|
||||
condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement"
|
||||
},
|
||||
}
|
||||
}, AST_IterationStatement);
|
||||
|
||||
var AST_Do = DEFNODE("Do", null, {
|
||||
$documentation: "A `do` statement",
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function(){
|
||||
this.body._walk(visitor);
|
||||
this.condition._walk(visitor);
|
||||
});
|
||||
}
|
||||
}, AST_DWLoop);
|
||||
|
||||
var AST_While = DEFNODE("While", null, {
|
||||
$documentation: "A `while` statement",
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function(){
|
||||
this.condition._walk(visitor);
|
||||
this.body._walk(visitor);
|
||||
});
|
||||
}
|
||||
}, AST_IterationStatement);
|
||||
|
||||
var AST_Do = DEFNODE("Do", null, {
|
||||
$documentation: "A `do` statement",
|
||||
}, AST_DWLoop);
|
||||
|
||||
var AST_While = DEFNODE("While", null, {
|
||||
$documentation: "A `while` statement",
|
||||
}, AST_DWLoop);
|
||||
|
||||
var AST_For = DEFNODE("For", "init condition step", {
|
||||
@@ -322,12 +330,11 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
}
|
||||
}));
|
||||
}
|
||||
var wrapped_tl = "(function(exports, global){ global['" + name + "'] = exports; '$ORIG'; '$EXPORTS'; }({}, (function(){return this}())))";
|
||||
var wrapped_tl = "(function(exports, global){ '$ORIG'; '$EXPORTS'; global['" + name + "'] = exports; }({}, (function(){return this}())))";
|
||||
wrapped_tl = parse(wrapped_tl);
|
||||
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
|
||||
if (node instanceof AST_SimpleStatement) {
|
||||
node = node.body;
|
||||
if (node instanceof AST_String) switch (node.getValue()) {
|
||||
if (node instanceof AST_Directive) {
|
||||
switch (node.value) {
|
||||
case "$ORIG":
|
||||
return MAP.splice(self.body);
|
||||
case "$EXPORTS":
|
||||
@@ -760,8 +767,11 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
||||
}
|
||||
});
|
||||
|
||||
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, {
|
||||
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
|
||||
$documentation: "A key: value object property",
|
||||
$propdoc: {
|
||||
quote: "[string] the original quote character"
|
||||
}
|
||||
}, AST_ObjectProperty);
|
||||
|
||||
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
|
||||
@@ -846,17 +856,19 @@ var AST_Constant = DEFNODE("Constant", null, {
|
||||
}
|
||||
});
|
||||
|
||||
var AST_String = DEFNODE("String", "value", {
|
||||
var AST_String = DEFNODE("String", "value quote", {
|
||||
$documentation: "A string literal",
|
||||
$propdoc: {
|
||||
value: "[string] the contents of this string"
|
||||
value: "[string] the contents of this string",
|
||||
quote: "[string] the original quote character"
|
||||
}
|
||||
}, AST_Constant);
|
||||
|
||||
var AST_Number = DEFNODE("Number", "value", {
|
||||
var AST_Number = DEFNODE("Number", "value literal", {
|
||||
$documentation: "A number literal",
|
||||
$propdoc: {
|
||||
value: "[number] the numeric value"
|
||||
value: "[number] the numeric value",
|
||||
literal: "[string] numeric value as string (optional)"
|
||||
}
|
||||
}, AST_Constant);
|
||||
|
||||
@@ -915,27 +927,36 @@ var AST_True = DEFNODE("True", null, {
|
||||
function TreeWalker(callback) {
|
||||
this.visit = callback;
|
||||
this.stack = [];
|
||||
this.directives = Object.create(null);
|
||||
};
|
||||
TreeWalker.prototype = {
|
||||
_visit: function(node, descend) {
|
||||
this.stack.push(node);
|
||||
this.push(node);
|
||||
var ret = this.visit(node, descend ? function(){
|
||||
descend.call(node);
|
||||
} : noop);
|
||||
if (!ret && descend) {
|
||||
descend.call(node);
|
||||
}
|
||||
this.stack.pop();
|
||||
this.pop(node);
|
||||
return ret;
|
||||
},
|
||||
parent: function(n) {
|
||||
return this.stack[this.stack.length - 2 - (n || 0)];
|
||||
},
|
||||
push: function (node) {
|
||||
if (node instanceof AST_Lambda) {
|
||||
this.directives = Object.create(this.directives);
|
||||
} else if (node instanceof AST_Directive) {
|
||||
this.directives[node.value] = this.directives[node.value] ? "up" : true;
|
||||
}
|
||||
this.stack.push(node);
|
||||
},
|
||||
pop: function() {
|
||||
return this.stack.pop();
|
||||
pop: function(node) {
|
||||
this.stack.pop();
|
||||
if (node instanceof AST_Lambda) {
|
||||
this.directives = Object.getPrototypeOf(this.directives);
|
||||
}
|
||||
},
|
||||
self: function() {
|
||||
return this.stack[this.stack.length - 1];
|
||||
@@ -948,7 +969,16 @@ TreeWalker.prototype = {
|
||||
}
|
||||
},
|
||||
has_directive: function(type) {
|
||||
return this.find_parent(AST_Scope).has_directive(type);
|
||||
var dir = this.directives[type];
|
||||
if (dir) return dir;
|
||||
var node = this.stack[this.stack.length - 1];
|
||||
if (node instanceof AST_Scope) {
|
||||
for (var i = 0; i < node.body.length; ++i) {
|
||||
var st = node.body[i];
|
||||
if (!(st instanceof AST_Directive)) break;
|
||||
if (st.value == type) return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
in_boolean_context: function() {
|
||||
var stack = this.stack;
|
||||
|
||||
494
lib/compress.js
494
lib/compress.js
@@ -61,10 +61,12 @@ function Compressor(options, false_by_default) {
|
||||
loops : !false_by_default,
|
||||
unused : !false_by_default,
|
||||
hoist_funs : !false_by_default,
|
||||
keep_fargs : false,
|
||||
keep_fargs : true,
|
||||
keep_fnames : false,
|
||||
hoist_vars : false,
|
||||
if_return : !false_by_default,
|
||||
join_vars : !false_by_default,
|
||||
collapse_vars : false,
|
||||
cascade : !false_by_default,
|
||||
side_effects : !false_by_default,
|
||||
pure_getters : false,
|
||||
@@ -110,6 +112,7 @@ merge(Compressor.prototype, {
|
||||
node.DEFMETHOD("optimize", function(compressor){
|
||||
var self = this;
|
||||
if (self._optimized) return self;
|
||||
if (compressor.has_directive("use asm")) return self;
|
||||
var opt = optimizer(self, compressor);
|
||||
opt._optimized = true;
|
||||
if (opt === self) return opt;
|
||||
@@ -162,10 +165,10 @@ merge(Compressor.prototype, {
|
||||
return make_node(AST_Undefined, orig).optimize(compressor);
|
||||
default:
|
||||
if (val === null) {
|
||||
return make_node(AST_Null, orig).optimize(compressor);
|
||||
return make_node(AST_Null, orig, { value: null }).optimize(compressor);
|
||||
}
|
||||
if (val instanceof RegExp) {
|
||||
return make_node(AST_RegExp, orig).optimize(compressor);
|
||||
return make_node(AST_RegExp, orig, { value: val }).optimize(compressor);
|
||||
}
|
||||
throw new Error(string_template("Can't handle constant of type: {type}", {
|
||||
type: typeof val
|
||||
@@ -173,6 +176,23 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
};
|
||||
|
||||
// we shouldn't compress (1,func)(something) to
|
||||
// func(something) because that changes the meaning of
|
||||
// the func (becomes lexical instead of global).
|
||||
function maintain_this_binding(parent, orig, val) {
|
||||
if (parent instanceof AST_Call && parent.expression === orig) {
|
||||
if (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name === "eval") {
|
||||
return make_node(AST_Seq, orig, {
|
||||
car: make_node(AST_Number, orig, {
|
||||
value: 0
|
||||
}),
|
||||
cdr: val
|
||||
});
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
function as_statement_array(thing) {
|
||||
if (thing === null) return [];
|
||||
if (thing instanceof AST_BlockStatement) return thing.body;
|
||||
@@ -197,7 +217,7 @@ merge(Compressor.prototype, {
|
||||
};
|
||||
|
||||
function tighten_body(statements, compressor) {
|
||||
var CHANGED;
|
||||
var CHANGED, max_iter = 10;
|
||||
do {
|
||||
CHANGED = false;
|
||||
if (compressor.option("angular")) {
|
||||
@@ -216,7 +236,10 @@ merge(Compressor.prototype, {
|
||||
if (compressor.option("join_vars")) {
|
||||
statements = join_consecutive_vars(statements, compressor);
|
||||
}
|
||||
} while (CHANGED);
|
||||
if (compressor.option("collapse_vars")) {
|
||||
statements = collapse_single_use_vars(statements, compressor);
|
||||
}
|
||||
} while (CHANGED && max_iter-- > 0);
|
||||
|
||||
if (compressor.option("negate_iife")) {
|
||||
negate_iifes(statements, compressor);
|
||||
@@ -224,7 +247,175 @@ merge(Compressor.prototype, {
|
||||
|
||||
return statements;
|
||||
|
||||
function collapse_single_use_vars(statements, compressor) {
|
||||
// Iterate statements backwards looking for a statement with a var/const
|
||||
// declaration immediately preceding it. Grab the rightmost var definition
|
||||
// and if it has exactly one reference then attempt to replace its reference
|
||||
// in the statement with the var value and then erase the var definition.
|
||||
|
||||
var self = compressor.self();
|
||||
var var_defs_removed = false;
|
||||
for (var stat_index = statements.length; --stat_index >= 0;) {
|
||||
var stat = statements[stat_index];
|
||||
if (stat instanceof AST_Definitions) continue;
|
||||
|
||||
// Process child blocks of statement if present.
|
||||
[stat, stat.body, stat.alternative, stat.bcatch, stat.bfinally].forEach(function(node) {
|
||||
node && node.body && collapse_single_use_vars(node.body, compressor);
|
||||
});
|
||||
|
||||
// The variable definition must precede a statement.
|
||||
if (stat_index <= 0) break;
|
||||
var prev_stat_index = stat_index - 1;
|
||||
var prev_stat = statements[prev_stat_index];
|
||||
if (!(prev_stat instanceof AST_Definitions)) continue;
|
||||
var var_defs = prev_stat.definitions;
|
||||
if (var_defs == null) continue;
|
||||
|
||||
var var_names_seen = {};
|
||||
var side_effects_encountered = false;
|
||||
var lvalues_encountered = false;
|
||||
var lvalues = {};
|
||||
|
||||
// Scan variable definitions from right to left.
|
||||
for (var var_defs_index = var_defs.length; --var_defs_index >= 0;) {
|
||||
|
||||
// Obtain var declaration and var name with basic sanity check.
|
||||
var var_decl = var_defs[var_defs_index];
|
||||
if (var_decl.value == null) break;
|
||||
var var_name = var_decl.name.name;
|
||||
if (!var_name || !var_name.length) break;
|
||||
|
||||
// Bail if we've seen a var definition of same name before.
|
||||
if (var_name in var_names_seen) break;
|
||||
var_names_seen[var_name] = true;
|
||||
|
||||
// Only interested in cases with just one reference to the variable.
|
||||
var def = self.find_variable && self.find_variable(var_name);
|
||||
if (!def || !def.references || def.references.length !== 1 || var_name == "arguments") {
|
||||
side_effects_encountered = true;
|
||||
continue;
|
||||
}
|
||||
var ref = def.references[0];
|
||||
|
||||
// Don't replace ref if eval() or with statement in scope.
|
||||
if (ref.scope.uses_eval || ref.scope.uses_with) break;
|
||||
|
||||
// Constant single use vars can be replaced in any scope.
|
||||
if (var_decl.value.is_constant(compressor)) {
|
||||
var ctt = new TreeTransformer(function(node) {
|
||||
if (node === ref)
|
||||
return replace_var(node, ctt.parent(), true);
|
||||
});
|
||||
stat.transform(ctt);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Restrict var replacement to constants if side effects encountered.
|
||||
if (side_effects_encountered |= lvalues_encountered) continue;
|
||||
|
||||
// Non-constant single use vars can only be replaced in same scope.
|
||||
if (ref.scope !== self) {
|
||||
side_effects_encountered |= var_decl.value.has_side_effects(compressor);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Detect lvalues in var value.
|
||||
var tw = new TreeWalker(function(node){
|
||||
if (node instanceof AST_SymbolRef && is_lvalue(node, tw.parent())) {
|
||||
lvalues[node.name] = lvalues_encountered = true;
|
||||
}
|
||||
});
|
||||
var_decl.value.walk(tw);
|
||||
|
||||
// Replace the non-constant single use var in statement if side effect free.
|
||||
var unwind = false;
|
||||
var tt = new TreeTransformer(
|
||||
function preorder(node) {
|
||||
if (unwind) return node;
|
||||
var parent = tt.parent();
|
||||
if (node instanceof AST_Lambda
|
||||
|| node instanceof AST_Try
|
||||
|| node instanceof AST_With
|
||||
|| node instanceof AST_Case
|
||||
|| node instanceof AST_IterationStatement
|
||||
|| (parent instanceof AST_If && node !== parent.condition)
|
||||
|| (parent instanceof AST_Conditional && node !== parent.condition)
|
||||
|| (parent instanceof AST_Binary
|
||||
&& (parent.operator == "&&" || parent.operator == "||")
|
||||
&& node === parent.right)
|
||||
|| (parent instanceof AST_Switch && node !== parent.expression)) {
|
||||
return side_effects_encountered = unwind = true, node;
|
||||
}
|
||||
},
|
||||
function postorder(node) {
|
||||
if (unwind) return node;
|
||||
if (node === ref)
|
||||
return unwind = true, replace_var(node, tt.parent(), false);
|
||||
if (side_effects_encountered |= node.has_side_effects(compressor))
|
||||
return unwind = true, node;
|
||||
if (lvalues_encountered && node instanceof AST_SymbolRef && node.name in lvalues) {
|
||||
side_effects_encountered = true;
|
||||
return unwind = true, node;
|
||||
}
|
||||
}
|
||||
);
|
||||
stat.transform(tt);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove extraneous empty statments in block after removing var definitions.
|
||||
// Leave at least one statement in `statements`.
|
||||
if (var_defs_removed) for (var i = statements.length; --i >= 0;) {
|
||||
if (statements.length > 1 && statements[i] instanceof AST_EmptyStatement)
|
||||
statements.splice(i, 1);
|
||||
}
|
||||
|
||||
return statements;
|
||||
|
||||
function is_lvalue(node, parent) {
|
||||
return node instanceof AST_SymbolRef && (
|
||||
(parent instanceof AST_Assign && node === parent.left)
|
||||
|| (parent instanceof AST_Unary && parent.expression === node
|
||||
&& (parent.operator == "++" || parent.operator == "--")));
|
||||
}
|
||||
function replace_var(node, parent, is_constant) {
|
||||
if (is_lvalue(node, parent)) return node;
|
||||
|
||||
// Remove var definition and return its value to the TreeTransformer to replace.
|
||||
var value = maintain_this_binding(parent, node, var_decl.value);
|
||||
var_decl.value = null;
|
||||
|
||||
var_defs.splice(var_defs_index, 1);
|
||||
if (var_defs.length === 0) {
|
||||
statements[prev_stat_index] = make_node(AST_EmptyStatement, self);
|
||||
var_defs_removed = true;
|
||||
}
|
||||
// Further optimize statement after substitution.
|
||||
stat.walk(new TreeWalker(function(node){
|
||||
delete node._squeezed;
|
||||
delete node._optimized;
|
||||
}));
|
||||
|
||||
compressor.warn("Replacing " + (is_constant ? "constant" : "variable") +
|
||||
" " + var_name + " [{file}:{line},{col}]", node.start);
|
||||
CHANGED = true;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function process_for_angular(statements) {
|
||||
function has_inject(comment) {
|
||||
return /@ngInject/.test(comment.value);
|
||||
}
|
||||
function make_arguments_names_list(func) {
|
||||
return func.argnames.map(function(sym){
|
||||
return make_node(AST_String, sym, { value: sym.name });
|
||||
});
|
||||
}
|
||||
function make_array(orig, elements) {
|
||||
return make_node(AST_Array, orig, { elements: elements });
|
||||
}
|
||||
function make_injector(func, name) {
|
||||
return make_node(AST_SimpleStatement, func, {
|
||||
body: make_node(AST_Assign, func, {
|
||||
@@ -233,27 +424,44 @@ merge(Compressor.prototype, {
|
||||
expression: make_node(AST_SymbolRef, name, name),
|
||||
property: "$inject"
|
||||
}),
|
||||
right: make_node(AST_Array, func, {
|
||||
elements: func.argnames.map(function(sym){
|
||||
return make_node(AST_String, sym, { value: sym.name });
|
||||
})
|
||||
})
|
||||
right: make_array(func, make_arguments_names_list(func))
|
||||
})
|
||||
});
|
||||
}
|
||||
function check_expression(body) {
|
||||
if (body && body.args) {
|
||||
// if this is a function call check all of arguments passed
|
||||
body.args.forEach(function(argument, index, array) {
|
||||
var comments = argument.start.comments_before;
|
||||
// if the argument is function preceded by @ngInject
|
||||
if (argument instanceof AST_Lambda && comments.length && has_inject(comments[0])) {
|
||||
// replace the function with an array of names of its parameters and function at the end
|
||||
array[index] = make_array(argument, make_arguments_names_list(argument).concat(argument));
|
||||
}
|
||||
});
|
||||
// if this is chained call check previous one recursively
|
||||
if (body.expression && body.expression.expression) {
|
||||
check_expression(body.expression.expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
return statements.reduce(function(a, stat){
|
||||
a.push(stat);
|
||||
|
||||
if (stat.body && stat.body.args) {
|
||||
check_expression(stat.body);
|
||||
} else {
|
||||
var token = stat.start;
|
||||
var comments = token.comments_before;
|
||||
if (comments && comments.length > 0) {
|
||||
var last = comments.pop();
|
||||
if (/@ngInject/.test(last.value)) {
|
||||
if (has_inject(last)) {
|
||||
// case 1: defun
|
||||
if (stat instanceof AST_Defun) {
|
||||
a.push(make_injector(stat, stat.name));
|
||||
}
|
||||
else if (stat instanceof AST_Definitions) {
|
||||
stat.definitions.forEach(function(def){
|
||||
stat.definitions.forEach(function(def) {
|
||||
if (def.value && def.value instanceof AST_Lambda) {
|
||||
a.push(make_injector(def.value, def.name));
|
||||
}
|
||||
@@ -264,6 +472,8 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
}, []);
|
||||
}
|
||||
@@ -352,7 +562,12 @@ merge(Compressor.prototype, {
|
||||
continue loop;
|
||||
}
|
||||
//---
|
||||
if (ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
|
||||
// XXX: what was the intention of this case?
|
||||
// if sequences is not enabled, this can lead to an endless loop (issue #866).
|
||||
// however, with sequences on this helps producing slightly better output for
|
||||
// the example code.
|
||||
if (compressor.option("sequences")
|
||||
&& ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
|
||||
&& (!stat.alternative || stat.alternative instanceof AST_SimpleStatement)) {
|
||||
CHANGED = true;
|
||||
ret.push(make_node(AST_Return, ret[0], {
|
||||
@@ -458,7 +673,7 @@ merge(Compressor.prototype, {
|
||||
seq = [];
|
||||
};
|
||||
statements.forEach(function(stat){
|
||||
if (stat instanceof AST_SimpleStatement) seq.push(stat.body);
|
||||
if (stat instanceof AST_SimpleStatement && seq.length < 2000) seq.push(stat.body);
|
||||
else push_seq(), ret.push(stat);
|
||||
});
|
||||
push_seq();
|
||||
@@ -689,6 +904,32 @@ merge(Compressor.prototype, {
|
||||
return [ this ];
|
||||
}
|
||||
});
|
||||
AST_Node.DEFMETHOD("is_constant", function(compressor){
|
||||
// Accomodate when compress option evaluate=false
|
||||
// as well as the common constant expressions !0 and !1
|
||||
return this instanceof AST_Constant
|
||||
|| (this instanceof AST_UnaryPrefix && this.operator == "!"
|
||||
&& this.expression instanceof AST_Constant)
|
||||
|| this.evaluate(compressor).length > 1;
|
||||
});
|
||||
// Obtain the constant value of an expression already known to be constant.
|
||||
// Result only valid iff this.is_constant(compressor) is true.
|
||||
AST_Node.DEFMETHOD("constant_value", function(compressor){
|
||||
// Accomodate when option evaluate=false.
|
||||
if (this instanceof AST_Constant) return this.value;
|
||||
// Accomodate the common constant expressions !0 and !1 when option evaluate=false.
|
||||
if (this instanceof AST_UnaryPrefix
|
||||
&& this.operator == "!"
|
||||
&& this.expression instanceof AST_Constant) {
|
||||
return !this.expression.value;
|
||||
}
|
||||
var result = this.evaluate(compressor)
|
||||
if (result.length > 1) {
|
||||
return result[1];
|
||||
}
|
||||
// should never be reached
|
||||
return undefined;
|
||||
});
|
||||
def(AST_Statement, function(){
|
||||
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
|
||||
});
|
||||
@@ -775,6 +1016,14 @@ merge(Compressor.prototype, {
|
||||
if (d && d.constant && d.init) return ev(d.init, compressor);
|
||||
throw def;
|
||||
});
|
||||
def(AST_Dot, function(compressor){
|
||||
if (compressor.option("unsafe") && this.property == "length") {
|
||||
var str = ev(this.expression, compressor);
|
||||
if (typeof str == "string")
|
||||
return str.length;
|
||||
}
|
||||
throw def;
|
||||
});
|
||||
})(function(node, func){
|
||||
node.DEFMETHOD("_eval", func);
|
||||
});
|
||||
@@ -857,6 +1106,7 @@ merge(Compressor.prototype, {
|
||||
def(AST_Call, function(compressor){
|
||||
var pure = compressor.option("pure_funcs");
|
||||
if (!pure) return true;
|
||||
if (typeof pure == "function") return pure(this);
|
||||
return pure.indexOf(this.expression.print_to_string()) < 0;
|
||||
});
|
||||
|
||||
@@ -889,7 +1139,9 @@ merge(Compressor.prototype, {
|
||||
|| this.operator == "--"
|
||||
|| this.expression.has_side_effects(compressor);
|
||||
});
|
||||
def(AST_SymbolRef, function(compressor){ return false });
|
||||
def(AST_SymbolRef, function(compressor){
|
||||
return this.global() && this.undeclared();
|
||||
});
|
||||
def(AST_Object, function(compressor){
|
||||
for (var i = this.properties.length; --i >= 0;)
|
||||
if (this.properties[i].has_side_effects(compressor))
|
||||
@@ -939,7 +1191,7 @@ merge(Compressor.prototype, {
|
||||
def(AST_BlockStatement, block_aborts);
|
||||
def(AST_SwitchBranch, block_aborts);
|
||||
def(AST_If, function(){
|
||||
return this.alternative && aborts(this.body) && aborts(this.alternative);
|
||||
return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
|
||||
});
|
||||
})(function(node, func){
|
||||
node.DEFMETHOD("aborts", func);
|
||||
@@ -948,7 +1200,7 @@ merge(Compressor.prototype, {
|
||||
/* -----[ optimizers ]----- */
|
||||
|
||||
OPT(AST_Directive, function(self, compressor){
|
||||
if (self.scope.has_directive(self.value) !== self.scope) {
|
||||
if (compressor.has_directive(self.value) === "up") {
|
||||
return make_node(AST_EmptyStatement, self);
|
||||
}
|
||||
return self;
|
||||
@@ -984,6 +1236,7 @@ merge(Compressor.prototype, {
|
||||
|
||||
AST_Scope.DEFMETHOD("drop_unused", function(compressor){
|
||||
var self = this;
|
||||
if (compressor.has_directive("use asm")) return self;
|
||||
if (compressor.option("unused")
|
||||
&& !(self instanceof AST_Toplevel)
|
||||
&& !self.uses_eval
|
||||
@@ -1127,12 +1380,12 @@ merge(Compressor.prototype, {
|
||||
return make_node(AST_EmptyStatement, node);
|
||||
}
|
||||
if (def.length == 0) {
|
||||
return side_effects;
|
||||
return in_list ? MAP.splice(side_effects.body) : side_effects;
|
||||
}
|
||||
node.definitions = def;
|
||||
if (side_effects) {
|
||||
side_effects.body.unshift(node);
|
||||
node = side_effects;
|
||||
return in_list ? MAP.splice(side_effects.body) : side_effects;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -1163,9 +1416,10 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
|
||||
var self = this;
|
||||
if (compressor.has_directive("use asm")) return self;
|
||||
var hoist_funs = compressor.option("hoist_funs");
|
||||
var hoist_vars = compressor.option("hoist_vars");
|
||||
var self = this;
|
||||
if (hoist_funs || hoist_vars) {
|
||||
var dirs = [];
|
||||
var hoisted = [];
|
||||
@@ -1200,7 +1454,10 @@ merge(Compressor.prototype, {
|
||||
var seq = node.to_assignments();
|
||||
var p = tt.parent();
|
||||
if (p instanceof AST_ForIn && p.init === node) {
|
||||
if (seq == null) return node.definitions[0].name;
|
||||
if (seq == null) {
|
||||
var def = node.definitions[0].name;
|
||||
return make_node(AST_SymbolRef, def, def);
|
||||
}
|
||||
return seq;
|
||||
}
|
||||
if (p instanceof AST_For && p.init === node) {
|
||||
@@ -1431,9 +1688,13 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (is_empty(self.alternative)) self.alternative = null;
|
||||
var negated = self.condition.negate(compressor);
|
||||
var negated_is_best = best_of(self.condition, negated) === negated;
|
||||
var self_condition_length = self.condition.print_to_string().length;
|
||||
var negated_length = negated.print_to_string().length;
|
||||
var negated_is_best = negated_length < self_condition_length;
|
||||
if (self.alternative && negated_is_best) {
|
||||
negated_is_best = false; // because we already do the switch here.
|
||||
// no need to swap values of self_condition_length and negated_length
|
||||
// here because they are only used in an equality comparison later on.
|
||||
self.condition = negated;
|
||||
var tmp = self.body;
|
||||
self.body = self.alternative || make_node(AST_EmptyStatement);
|
||||
@@ -1455,6 +1716,13 @@ merge(Compressor.prototype, {
|
||||
}).transform(compressor);
|
||||
}
|
||||
if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
|
||||
if (self_condition_length === negated_length && !negated_is_best
|
||||
&& self.condition instanceof AST_Binary && self.condition.operator == "||") {
|
||||
// although the code length of self.condition and negated are the same,
|
||||
// negated does not require additional surrounding parentheses.
|
||||
// see https://github.com/mishoo/UglifyJS2/issues/979
|
||||
negated_is_best = true;
|
||||
}
|
||||
if (negated_is_best) return make_node(AST_SimpleStatement, self, {
|
||||
body: make_node(AST_Binary, self, {
|
||||
operator : "||",
|
||||
@@ -1656,7 +1924,7 @@ merge(Compressor.prototype, {
|
||||
|
||||
OPT(AST_Function, function(self, compressor){
|
||||
self = AST_Lambda.prototype.optimize.call(self, compressor);
|
||||
if (compressor.option("unused")) {
|
||||
if (compressor.option("unused") && !compressor.option("keep_fnames")) {
|
||||
if (self.name && self.name.unreferenced()) {
|
||||
self.name = null;
|
||||
}
|
||||
@@ -1712,6 +1980,11 @@ merge(Compressor.prototype, {
|
||||
}).transform(compressor);
|
||||
break;
|
||||
case "Function":
|
||||
// new Function() => function(){}
|
||||
if (self.args.length == 0) return make_node(AST_Function, self, {
|
||||
argnames: [],
|
||||
body: []
|
||||
});
|
||||
if (all(self.args, function(x){ return x instanceof AST_String })) {
|
||||
// quite a corner-case, but we can handle it:
|
||||
// https://github.com/mishoo/UglifyJS2/issues/203
|
||||
@@ -1737,6 +2010,7 @@ merge(Compressor.prototype, {
|
||||
} catch(ex) {
|
||||
if (ex !== ast) throw ex;
|
||||
};
|
||||
if (!fun) return self;
|
||||
var args = fun.argnames.map(function(arg, i){
|
||||
return make_node(AST_String, self.args[i], {
|
||||
value: arg.print_to_string()
|
||||
@@ -1826,13 +2100,18 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
if (compressor.option("drop_console")) {
|
||||
if (self.expression instanceof AST_PropAccess &&
|
||||
self.expression.expression instanceof AST_SymbolRef &&
|
||||
self.expression.expression.name == "console" &&
|
||||
self.expression.expression.undeclared()) {
|
||||
if (self.expression instanceof AST_PropAccess) {
|
||||
var name = self.expression.expression;
|
||||
while (name.expression) {
|
||||
name = name.expression;
|
||||
}
|
||||
if (name instanceof AST_SymbolRef
|
||||
&& name.name == "console"
|
||||
&& name.undeclared()) {
|
||||
return make_node(AST_Undefined, self).transform(compressor);
|
||||
}
|
||||
}
|
||||
}
|
||||
return self.evaluate(compressor)[0];
|
||||
});
|
||||
|
||||
@@ -1857,17 +2136,7 @@ merge(Compressor.prototype, {
|
||||
if (!compressor.option("side_effects"))
|
||||
return self;
|
||||
if (!self.car.has_side_effects(compressor)) {
|
||||
// we shouldn't compress (1,eval)(something) to
|
||||
// eval(something) because that changes the meaning of
|
||||
// eval (becomes lexical instead of global).
|
||||
var p;
|
||||
if (!(self.cdr instanceof AST_SymbolRef
|
||||
&& self.cdr.name == "eval"
|
||||
&& self.cdr.undeclared()
|
||||
&& (p = compressor.parent()) instanceof AST_Call
|
||||
&& p.expression === self)) {
|
||||
return self.cdr;
|
||||
}
|
||||
return maintain_this_binding(compressor.parent(), self, self.cdr);
|
||||
}
|
||||
if (compressor.option("cascade")) {
|
||||
if (self.car instanceof AST_Assign
|
||||
@@ -1890,7 +2159,7 @@ merge(Compressor.prototype, {
|
||||
if (self.cdr instanceof AST_UnaryPrefix
|
||||
&& self.cdr.operator == "void"
|
||||
&& !self.cdr.expression.has_side_effects(compressor)) {
|
||||
self.cdr.operator = self.car;
|
||||
self.cdr.expression = self.car;
|
||||
return self.cdr;
|
||||
}
|
||||
if (self.cdr instanceof AST_Undefined) {
|
||||
@@ -1979,15 +2248,14 @@ merge(Compressor.prototype, {
|
||||
var commutativeOperators = makePredicate("== === != !== * & | ^");
|
||||
|
||||
OPT(AST_Binary, function(self, compressor){
|
||||
var reverse = compressor.has_directive("use asm") ? noop
|
||||
: function(op, force) {
|
||||
function reverse(op, force) {
|
||||
if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) {
|
||||
if (op) self.operator = op;
|
||||
var tmp = self.left;
|
||||
self.left = self.right;
|
||||
self.right = tmp;
|
||||
}
|
||||
};
|
||||
}
|
||||
if (commutativeOperators(self.operator)) {
|
||||
if (self.right instanceof AST_Constant
|
||||
&& !(self.left instanceof AST_Constant)) {
|
||||
@@ -2052,12 +2320,44 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (compressor.option("conditionals")) {
|
||||
if (self.operator == "&&") {
|
||||
var ll = self.left.evaluate(compressor);
|
||||
if (ll.length > 1) {
|
||||
if (ll[1]) {
|
||||
compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
|
||||
return maintain_this_binding(compressor.parent(), self, self.right.evaluate(compressor)[0]);
|
||||
} else {
|
||||
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
|
||||
return maintain_this_binding(compressor.parent(), self, ll[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (self.operator == "||") {
|
||||
var ll = self.left.evaluate(compressor);
|
||||
if (ll.length > 1) {
|
||||
if (ll[1]) {
|
||||
compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
|
||||
return maintain_this_binding(compressor.parent(), self, ll[0]);
|
||||
} else {
|
||||
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
|
||||
return maintain_this_binding(compressor.parent(), self, self.right.evaluate(compressor)[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) {
|
||||
case "&&":
|
||||
var ll = self.left.evaluate(compressor);
|
||||
var rr = self.right.evaluate(compressor);
|
||||
if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) {
|
||||
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
|
||||
if (self.left.has_side_effects(compressor)) {
|
||||
return make_node(AST_Seq, self, {
|
||||
car: self.left,
|
||||
cdr: make_node(AST_False)
|
||||
}).optimize(compressor);
|
||||
}
|
||||
return make_node(AST_False, self);
|
||||
}
|
||||
if (ll.length > 1 && ll[1]) {
|
||||
@@ -2072,6 +2372,12 @@ merge(Compressor.prototype, {
|
||||
var rr = self.right.evaluate(compressor);
|
||||
if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) {
|
||||
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
|
||||
if (self.left.has_side_effects(compressor)) {
|
||||
return make_node(AST_Seq, self, {
|
||||
car: self.left,
|
||||
cdr: make_node(AST_True)
|
||||
}).optimize(compressor);
|
||||
}
|
||||
return make_node(AST_True, self);
|
||||
}
|
||||
if (ll.length > 1 && !ll[1]) {
|
||||
@@ -2091,7 +2397,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (compressor.option("comparisons")) {
|
||||
if (compressor.option("comparisons") && self.is_boolean()) {
|
||||
if (!(compressor.parent() instanceof AST_Binary)
|
||||
|| compressor.parent() instanceof AST_Assign) {
|
||||
var negated = make_node(AST_UnaryPrefix, self, {
|
||||
@@ -2166,10 +2472,11 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
}
|
||||
// x * (y * z) ==> x * y * z
|
||||
// x && (y && z) ==> x && y && z
|
||||
// x || (y || z) ==> x || y || z
|
||||
if (self.right instanceof AST_Binary
|
||||
&& self.right.operator == self.operator
|
||||
&& (self.operator == "*" || self.operator == "&&" || self.operator == "||"))
|
||||
&& (self.operator == "&&" || self.operator == "||"))
|
||||
{
|
||||
self.left = make_node(AST_Binary, self.left, {
|
||||
operator : self.operator,
|
||||
@@ -2183,7 +2490,15 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_SymbolRef, function(self, compressor){
|
||||
if (self.undeclared()) {
|
||||
function isLHS(symbol, parent) {
|
||||
return (
|
||||
parent instanceof AST_Binary &&
|
||||
parent.operator === '=' &&
|
||||
parent.left === symbol
|
||||
);
|
||||
}
|
||||
|
||||
if (self.undeclared() && !isLHS(self, compressor.parent())) {
|
||||
var defines = compressor.option("global_defs");
|
||||
if (defines && defines.hasOwnProperty(self.name)) {
|
||||
return make_node_from_constant(compressor, defines[self.name], self);
|
||||
@@ -2192,14 +2507,22 @@ merge(Compressor.prototype, {
|
||||
case "undefined":
|
||||
return make_node(AST_Undefined, self);
|
||||
case "NaN":
|
||||
return make_node(AST_NaN, self);
|
||||
return make_node(AST_NaN, self).transform(compressor);
|
||||
case "Infinity":
|
||||
return make_node(AST_Infinity, self);
|
||||
return make_node(AST_Infinity, self).transform(compressor);
|
||||
}
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_Infinity, function (self, compressor) {
|
||||
return make_node(AST_Binary, self, {
|
||||
operator : '/',
|
||||
left : make_node(AST_Number, self, {value: 1}),
|
||||
right : make_node(AST_Number, self, {value: 0})
|
||||
});
|
||||
});
|
||||
|
||||
OPT(AST_Undefined, function(self, compressor){
|
||||
if (compressor.option("unsafe")) {
|
||||
var scope = compressor.find_parent(AST_Scope);
|
||||
@@ -2243,10 +2566,10 @@ merge(Compressor.prototype, {
|
||||
if (cond.length > 1) {
|
||||
if (cond[1]) {
|
||||
compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
|
||||
return self.consequent;
|
||||
return maintain_this_binding(compressor.parent(), self, self.consequent);
|
||||
} else {
|
||||
compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
|
||||
return self.alternative;
|
||||
return maintain_this_binding(compressor.parent(), self, self.alternative);
|
||||
}
|
||||
}
|
||||
var negated = cond[0].negate(compressor);
|
||||
@@ -2263,6 +2586,7 @@ merge(Compressor.prototype, {
|
||||
&& alternative instanceof AST_Assign
|
||||
&& consequent.operator == alternative.operator
|
||||
&& consequent.left.equivalent_to(alternative.left)
|
||||
&& !consequent.left.has_side_effects(compressor)
|
||||
) {
|
||||
/*
|
||||
* Stuff like this:
|
||||
@@ -2283,6 +2607,7 @@ merge(Compressor.prototype, {
|
||||
if (consequent instanceof AST_Call
|
||||
&& alternative.TYPE === consequent.TYPE
|
||||
&& consequent.args.length == alternative.args.length
|
||||
&& !consequent.expression.has_side_effects(compressor)
|
||||
&& consequent.expression.equivalent_to(alternative.expression)) {
|
||||
if (consequent.args.length == 0) {
|
||||
return make_node(AST_Seq, self, {
|
||||
@@ -2312,7 +2637,52 @@ merge(Compressor.prototype, {
|
||||
alternative: alternative
|
||||
});
|
||||
}
|
||||
// y?1:1 --> 1
|
||||
if (consequent.is_constant(compressor)
|
||||
&& alternative.is_constant(compressor)
|
||||
&& consequent.equivalent_to(alternative)) {
|
||||
var consequent_value = consequent.constant_value();
|
||||
if (self.condition.has_side_effects(compressor)) {
|
||||
return AST_Seq.from_array([self.condition, make_node_from_constant(compressor, consequent_value, self)]);
|
||||
} else {
|
||||
return make_node_from_constant(compressor, consequent_value, self);
|
||||
}
|
||||
}
|
||||
|
||||
// y?true:false --> !!y
|
||||
if (is_true(consequent) && is_false(alternative)) {
|
||||
if (self.condition.is_boolean()) {
|
||||
// boolean_expression ? true : false --> boolean_expression
|
||||
return self.condition;
|
||||
}
|
||||
self.condition = self.condition.negate(compressor);
|
||||
return make_node(AST_UnaryPrefix, self.condition, {
|
||||
operator: "!",
|
||||
expression: self.condition
|
||||
});
|
||||
}
|
||||
// y?false:true --> !y
|
||||
if (is_false(consequent) && is_true(alternative)) {
|
||||
return self.condition.negate(compressor)
|
||||
}
|
||||
return self;
|
||||
|
||||
// AST_True or !0
|
||||
function is_true(node) {
|
||||
return node instanceof AST_True
|
||||
|| (node instanceof AST_UnaryPrefix
|
||||
&& node.operator == "!"
|
||||
&& node.expression instanceof AST_Constant
|
||||
&& !node.expression.value);
|
||||
}
|
||||
// AST_False or !1
|
||||
function is_false(node) {
|
||||
return node instanceof AST_False
|
||||
|| (node instanceof AST_UnaryPrefix
|
||||
&& node.operator == "!"
|
||||
&& node.expression instanceof AST_Constant
|
||||
&& !!node.expression.value);
|
||||
}
|
||||
});
|
||||
|
||||
OPT(AST_Boolean, function(self, compressor){
|
||||
@@ -2349,7 +2719,7 @@ merge(Compressor.prototype, {
|
||||
return make_node(AST_Dot, self, {
|
||||
expression : self.expression,
|
||||
property : prop
|
||||
});
|
||||
}).optimize(compressor);
|
||||
}
|
||||
var v = parseFloat(prop);
|
||||
if (!isNaN(v) && v.toString() == prop) {
|
||||
@@ -2361,8 +2731,21 @@ merge(Compressor.prototype, {
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_Dot, function(self, compressor){
|
||||
var prop = self.property;
|
||||
if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) {
|
||||
return make_node(AST_Sub, self, {
|
||||
expression : self.expression,
|
||||
property : make_node(AST_String, self, {
|
||||
value: prop
|
||||
})
|
||||
}).optimize(compressor);
|
||||
}
|
||||
return self.evaluate(compressor)[0];
|
||||
});
|
||||
|
||||
function literals_in_boolean_context(self, compressor) {
|
||||
if (compressor.option("booleans") && compressor.in_boolean_context()) {
|
||||
if (compressor.option("booleans") && compressor.in_boolean_context() && !self.has_side_effects(compressor)) {
|
||||
return make_node(AST_True, self);
|
||||
}
|
||||
return self;
|
||||
@@ -2371,4 +2754,11 @@ merge(Compressor.prototype, {
|
||||
OPT(AST_Object, literals_in_boolean_context);
|
||||
OPT(AST_RegExp, literals_in_boolean_context);
|
||||
|
||||
OPT(AST_Return, function(self, compressor){
|
||||
if (self.value instanceof AST_Undefined) {
|
||||
self.value = null;
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
@@ -46,37 +46,44 @@
|
||||
(function(){
|
||||
|
||||
var MOZ_TO_ME = {
|
||||
TryStatement : function(M) {
|
||||
ExpressionStatement: function(M) {
|
||||
var expr = M.expression;
|
||||
if (expr.type === "Literal" && typeof expr.value === "string") {
|
||||
return new AST_Directive({
|
||||
start: my_start_token(M),
|
||||
end: my_end_token(M),
|
||||
value: expr.value
|
||||
});
|
||||
}
|
||||
return new AST_SimpleStatement({
|
||||
start: my_start_token(M),
|
||||
end: my_end_token(M),
|
||||
body: from_moz(expr)
|
||||
});
|
||||
},
|
||||
TryStatement: function(M) {
|
||||
var handlers = M.handlers || [M.handler];
|
||||
if (handlers.length > 1 || M.guardedHandlers && M.guardedHandlers.length) {
|
||||
throw new Error("Multiple catch clauses are not supported.");
|
||||
}
|
||||
return new AST_Try({
|
||||
start : my_start_token(M),
|
||||
end : my_end_token(M),
|
||||
body : from_moz(M.block).body,
|
||||
bcatch : from_moz(M.handlers ? M.handlers[0] : M.handler),
|
||||
bcatch : from_moz(handlers[0]),
|
||||
bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null
|
||||
});
|
||||
},
|
||||
CatchClause : function(M) {
|
||||
return new AST_Catch({
|
||||
start : my_start_token(M),
|
||||
end : my_end_token(M),
|
||||
argname : from_moz(M.param),
|
||||
body : from_moz(M.body).body
|
||||
});
|
||||
},
|
||||
ObjectExpression : function(M) {
|
||||
return new AST_Object({
|
||||
start : my_start_token(M),
|
||||
end : my_end_token(M),
|
||||
properties : M.properties.map(function(prop){
|
||||
var key = prop.key;
|
||||
Property: function(M) {
|
||||
var key = M.key;
|
||||
var name = key.type == "Identifier" ? key.name : key.value;
|
||||
var args = {
|
||||
start : my_start_token(key),
|
||||
end : my_end_token(prop.value),
|
||||
end : my_end_token(M.value),
|
||||
key : name,
|
||||
value : from_moz(prop.value)
|
||||
value : from_moz(M.value)
|
||||
};
|
||||
switch (prop.kind) {
|
||||
switch (M.kind) {
|
||||
case "init":
|
||||
return new AST_ObjectKeyVal(args);
|
||||
case "set":
|
||||
@@ -86,13 +93,21 @@
|
||||
args.value.name = from_moz(key);
|
||||
return new AST_ObjectGetter(args);
|
||||
}
|
||||
},
|
||||
ObjectExpression: function(M) {
|
||||
return new AST_Object({
|
||||
start : my_start_token(M),
|
||||
end : my_end_token(M),
|
||||
properties : M.properties.map(function(prop){
|
||||
prop.type = "Property";
|
||||
return from_moz(prop)
|
||||
})
|
||||
});
|
||||
},
|
||||
SequenceExpression : function(M) {
|
||||
SequenceExpression: function(M) {
|
||||
return AST_Seq.from_array(M.expressions.map(from_moz));
|
||||
},
|
||||
MemberExpression : function(M) {
|
||||
MemberExpression: function(M) {
|
||||
return new (M.computed ? AST_Sub : AST_Dot)({
|
||||
start : my_start_token(M),
|
||||
end : my_end_token(M),
|
||||
@@ -100,7 +115,7 @@
|
||||
expression : from_moz(M.object)
|
||||
});
|
||||
},
|
||||
SwitchCase : function(M) {
|
||||
SwitchCase: function(M) {
|
||||
return new (M.test ? AST_Case : AST_Default)({
|
||||
start : my_start_token(M),
|
||||
end : my_end_token(M),
|
||||
@@ -108,7 +123,14 @@
|
||||
body : M.consequent.map(from_moz)
|
||||
});
|
||||
},
|
||||
Literal : function(M) {
|
||||
VariableDeclaration: function(M) {
|
||||
return new (M.kind === "const" ? AST_Const : AST_Var)({
|
||||
start : my_start_token(M),
|
||||
end : my_end_token(M),
|
||||
definitions : M.declarations.map(from_moz)
|
||||
});
|
||||
},
|
||||
Literal: function(M) {
|
||||
var val = M.value, args = {
|
||||
start : my_start_token(M),
|
||||
end : my_end_token(M)
|
||||
@@ -124,16 +146,20 @@
|
||||
case "boolean":
|
||||
return new (val ? AST_True : AST_False)(args);
|
||||
default:
|
||||
args.value = val;
|
||||
var rx = M.regex;
|
||||
if (rx && rx.pattern) {
|
||||
// RegExpLiteral as per ESTree AST spec
|
||||
args.value = new RegExp(rx.pattern, rx.flags).toString();
|
||||
} else {
|
||||
// support legacy RegExp
|
||||
args.value = M.regex && M.raw ? M.raw : val;
|
||||
}
|
||||
return new AST_RegExp(args);
|
||||
}
|
||||
},
|
||||
UnaryExpression: From_Moz_Unary,
|
||||
UpdateExpression: From_Moz_Unary,
|
||||
Identifier: function(M) {
|
||||
var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
|
||||
return new (M.name == "this" ? AST_This
|
||||
: p.type == "LabeledStatement" ? AST_Label
|
||||
return new ( p.type == "LabeledStatement" ? AST_Label
|
||||
: p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar)
|
||||
: p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
|
||||
: p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
|
||||
@@ -147,7 +173,8 @@
|
||||
}
|
||||
};
|
||||
|
||||
function From_Moz_Unary(M) {
|
||||
MOZ_TO_ME.UpdateExpression =
|
||||
MOZ_TO_ME.UnaryExpression = function To_Moz_Unary(M) {
|
||||
var prefix = "prefix" in M ? M.prefix
|
||||
: M.type == "UnaryExpression" ? true : false;
|
||||
return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
|
||||
@@ -158,14 +185,9 @@
|
||||
});
|
||||
};
|
||||
|
||||
var ME_TO_MOZ = {};
|
||||
|
||||
map("Node", AST_Node);
|
||||
map("Program", AST_Toplevel, "body@body");
|
||||
map("Function", AST_Function, "id>name, params@argnames, body%body");
|
||||
map("EmptyStatement", AST_EmptyStatement);
|
||||
map("BlockStatement", AST_BlockStatement, "body@body");
|
||||
map("ExpressionStatement", AST_SimpleStatement, "expression>body");
|
||||
map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
|
||||
map("LabeledStatement", AST_LabeledStatement, "label>label, body>body");
|
||||
map("BreakStatement", AST_Break, "label>label");
|
||||
@@ -180,71 +202,284 @@
|
||||
map("ForInStatement", AST_ForIn, "left>init, right>object, body>body");
|
||||
map("DebuggerStatement", AST_Debugger);
|
||||
map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body");
|
||||
map("VariableDeclaration", AST_Var, "declarations@definitions");
|
||||
map("VariableDeclarator", AST_VarDef, "id>name, init>value");
|
||||
map("CatchClause", AST_Catch, "param>argname, body%body");
|
||||
|
||||
map("ThisExpression", AST_This);
|
||||
map("ArrayExpression", AST_Array, "elements@elements");
|
||||
map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body");
|
||||
map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
|
||||
map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
|
||||
map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
|
||||
map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
|
||||
map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative");
|
||||
map("NewExpression", AST_New, "callee>expression, arguments@args");
|
||||
map("CallExpression", AST_Call, "callee>expression, arguments@args");
|
||||
|
||||
def_to_moz(AST_Directive, function To_Moz_Directive(M) {
|
||||
return {
|
||||
type: "ExpressionStatement",
|
||||
expression: {
|
||||
type: "Literal",
|
||||
value: M.value
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_SimpleStatement, function To_Moz_ExpressionStatement(M) {
|
||||
return {
|
||||
type: "ExpressionStatement",
|
||||
expression: to_moz(M.body)
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_SwitchBranch, function To_Moz_SwitchCase(M) {
|
||||
return {
|
||||
type: "SwitchCase",
|
||||
test: to_moz(M.expression),
|
||||
consequent: M.body.map(to_moz)
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_Try, function To_Moz_TryStatement(M) {
|
||||
return {
|
||||
type: "TryStatement",
|
||||
block: to_moz_block(M),
|
||||
handler: to_moz(M.bcatch),
|
||||
guardedHandlers: [],
|
||||
finalizer: to_moz(M.bfinally)
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_Catch, function To_Moz_CatchClause(M) {
|
||||
return {
|
||||
type: "CatchClause",
|
||||
param: to_moz(M.argname),
|
||||
guard: null,
|
||||
body: to_moz_block(M)
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) {
|
||||
return {
|
||||
type: "VariableDeclaration",
|
||||
kind: M instanceof AST_Const ? "const" : "var",
|
||||
declarations: M.definitions.map(to_moz)
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) {
|
||||
return {
|
||||
type: "SequenceExpression",
|
||||
expressions: M.to_array().map(to_moz)
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_PropAccess, function To_Moz_MemberExpression(M) {
|
||||
var isComputed = M instanceof AST_Sub;
|
||||
return {
|
||||
type: "MemberExpression",
|
||||
object: to_moz(M.expression),
|
||||
computed: isComputed,
|
||||
property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property}
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_Unary, function To_Moz_Unary(M) {
|
||||
return {
|
||||
type: M.operator == "++" || M.operator == "--" ? "UpdateExpression" : "UnaryExpression",
|
||||
operator: M.operator,
|
||||
prefix: M instanceof AST_UnaryPrefix,
|
||||
argument: to_moz(M.expression)
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_Binary, function To_Moz_BinaryExpression(M) {
|
||||
return {
|
||||
type: M.operator == "&&" || M.operator == "||" ? "LogicalExpression" : "BinaryExpression",
|
||||
left: to_moz(M.left),
|
||||
operator: M.operator,
|
||||
right: to_moz(M.right)
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_Object, function To_Moz_ObjectExpression(M) {
|
||||
return {
|
||||
type: "ObjectExpression",
|
||||
properties: M.properties.map(to_moz)
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
|
||||
var key = (
|
||||
is_identifier(M.key)
|
||||
? {type: "Identifier", name: M.key}
|
||||
: {type: "Literal", value: M.key}
|
||||
);
|
||||
var kind;
|
||||
if (M instanceof AST_ObjectKeyVal) {
|
||||
kind = "init";
|
||||
} else
|
||||
if (M instanceof AST_ObjectGetter) {
|
||||
kind = "get";
|
||||
} else
|
||||
if (M instanceof AST_ObjectSetter) {
|
||||
kind = "set";
|
||||
}
|
||||
return {
|
||||
type: "Property",
|
||||
kind: kind,
|
||||
key: key,
|
||||
value: to_moz(M.value)
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_Symbol, function To_Moz_Identifier(M) {
|
||||
var def = M.definition();
|
||||
return {
|
||||
type: "Identifier",
|
||||
name: def ? def.mangled_name || def.name : M.name
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) {
|
||||
var value = M.value;
|
||||
return {
|
||||
type: "Literal",
|
||||
value: value,
|
||||
raw: value.toString(),
|
||||
regex: {
|
||||
pattern: value.source,
|
||||
flags: value.toString().match(/[gimuy]*$/)[0]
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_Constant, function To_Moz_Literal(M) {
|
||||
var value = M.value;
|
||||
if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) {
|
||||
return {
|
||||
type: "UnaryExpression",
|
||||
operator: "-",
|
||||
prefix: true,
|
||||
argument: {
|
||||
type: "Literal",
|
||||
value: -value,
|
||||
raw: M.start.raw
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: "Literal",
|
||||
value: value,
|
||||
raw: M.start.raw
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_Atom, function To_Moz_Atom(M) {
|
||||
return {
|
||||
type: "Identifier",
|
||||
name: String(M.value)
|
||||
};
|
||||
});
|
||||
|
||||
AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
|
||||
AST_Null.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
|
||||
AST_Hole.DEFMETHOD("to_mozilla_ast", function To_Moz_ArrayHole() { return null });
|
||||
|
||||
AST_Block.DEFMETHOD("to_mozilla_ast", AST_BlockStatement.prototype.to_mozilla_ast);
|
||||
AST_Lambda.DEFMETHOD("to_mozilla_ast", AST_Function.prototype.to_mozilla_ast);
|
||||
|
||||
/* -----[ tools ]----- */
|
||||
|
||||
function raw_token(moznode) {
|
||||
if (moznode.type == "Literal") {
|
||||
return moznode.raw != null ? moznode.raw : moznode.value + "";
|
||||
}
|
||||
}
|
||||
|
||||
function my_start_token(moznode) {
|
||||
var loc = moznode.loc, start = loc && loc.start;
|
||||
var range = moznode.range;
|
||||
return new AST_Token({
|
||||
file : moznode.loc && moznode.loc.source,
|
||||
line : moznode.loc && moznode.loc.start.line,
|
||||
col : moznode.loc && moznode.loc.start.column,
|
||||
pos : moznode.start,
|
||||
endpos : moznode.start
|
||||
file : loc && loc.source,
|
||||
line : start && start.line,
|
||||
col : start && start.column,
|
||||
pos : range ? range[0] : moznode.start,
|
||||
endline : start && start.line,
|
||||
endcol : start && start.column,
|
||||
endpos : range ? range[0] : moznode.start,
|
||||
raw : raw_token(moznode),
|
||||
});
|
||||
};
|
||||
|
||||
function my_end_token(moznode) {
|
||||
var loc = moznode.loc, end = loc && loc.end;
|
||||
var range = moznode.range;
|
||||
return new AST_Token({
|
||||
file : moznode.loc && moznode.loc.source,
|
||||
line : moznode.loc && moznode.loc.end.line,
|
||||
col : moznode.loc && moznode.loc.end.column,
|
||||
pos : moznode.end,
|
||||
endpos : moznode.end
|
||||
file : loc && loc.source,
|
||||
line : end && end.line,
|
||||
col : end && end.column,
|
||||
pos : range ? range[1] : moznode.end,
|
||||
endline : end && end.line,
|
||||
endcol : end && end.column,
|
||||
endpos : range ? range[1] : moznode.end,
|
||||
raw : raw_token(moznode),
|
||||
});
|
||||
};
|
||||
|
||||
function map(moztype, mytype, propmap) {
|
||||
var moz_to_me = "function From_Moz_" + moztype + "(M){\n";
|
||||
moz_to_me += "return new mytype({\n" +
|
||||
moz_to_me += "return new U2." + mytype.name + "({\n" +
|
||||
"start: my_start_token(M),\n" +
|
||||
"end: my_end_token(M)";
|
||||
|
||||
var me_to_moz = "function To_Moz_" + moztype + "(M){\n";
|
||||
me_to_moz += "return {\n" +
|
||||
"type: " + JSON.stringify(moztype);
|
||||
|
||||
if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){
|
||||
var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop);
|
||||
if (!m) throw new Error("Can't understand property map: " + prop);
|
||||
var moz = "M." + m[1], how = m[2], my = m[3];
|
||||
var moz = m[1], how = m[2], my = m[3];
|
||||
moz_to_me += ",\n" + my + ": ";
|
||||
if (how == "@") {
|
||||
moz_to_me += moz + ".map(from_moz)";
|
||||
} else if (how == ">") {
|
||||
moz_to_me += "from_moz(" + moz + ")";
|
||||
} else if (how == "=") {
|
||||
moz_to_me += moz;
|
||||
} else if (how == "%") {
|
||||
moz_to_me += "from_moz(" + moz + ").body";
|
||||
} else throw new Error("Can't understand operator in propmap: " + prop);
|
||||
me_to_moz += ",\n" + moz + ": ";
|
||||
switch (how) {
|
||||
case "@":
|
||||
moz_to_me += "M." + moz + ".map(from_moz)";
|
||||
me_to_moz += "M." + my + ".map(to_moz)";
|
||||
break;
|
||||
case ">":
|
||||
moz_to_me += "from_moz(M." + moz + ")";
|
||||
me_to_moz += "to_moz(M." + my + ")";
|
||||
break;
|
||||
case "=":
|
||||
moz_to_me += "M." + moz;
|
||||
me_to_moz += "M." + my;
|
||||
break;
|
||||
case "%":
|
||||
moz_to_me += "from_moz(M." + moz + ").body";
|
||||
me_to_moz += "to_moz_block(M)";
|
||||
break;
|
||||
default:
|
||||
throw new Error("Can't understand operator in propmap: " + prop);
|
||||
}
|
||||
});
|
||||
moz_to_me += "\n})}";
|
||||
|
||||
// moz_to_me = parse(moz_to_me).print_to_string({ beautify: true });
|
||||
// console.log(moz_to_me);
|
||||
moz_to_me += "\n})\n}";
|
||||
me_to_moz += "\n}\n}";
|
||||
|
||||
moz_to_me = new Function("mytype", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
|
||||
mytype, my_start_token, my_end_token, from_moz
|
||||
//moz_to_me = parse(moz_to_me).print_to_string({ beautify: true });
|
||||
//me_to_moz = parse(me_to_moz).print_to_string({ beautify: true });
|
||||
//console.log(moz_to_me);
|
||||
|
||||
moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
|
||||
exports, my_start_token, my_end_token, from_moz
|
||||
);
|
||||
return MOZ_TO_ME[moztype] = moz_to_me;
|
||||
me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")(
|
||||
to_moz, to_moz_block
|
||||
);
|
||||
MOZ_TO_ME[moztype] = moz_to_me;
|
||||
def_to_moz(mytype, me_to_moz);
|
||||
};
|
||||
|
||||
var FROM_MOZ_STACK = null;
|
||||
@@ -264,4 +499,39 @@
|
||||
return ast;
|
||||
};
|
||||
|
||||
function set_moz_loc(mynode, moznode, myparent) {
|
||||
var start = mynode.start;
|
||||
var end = mynode.end;
|
||||
if (start.pos != null && end.endpos != null) {
|
||||
moznode.range = [start.pos, end.endpos];
|
||||
}
|
||||
if (start.line) {
|
||||
moznode.loc = {
|
||||
start: {line: start.line, column: start.col},
|
||||
end: end.endline ? {line: end.endline, column: end.endcol} : null
|
||||
};
|
||||
if (start.file) {
|
||||
moznode.loc.source = start.file;
|
||||
}
|
||||
}
|
||||
return moznode;
|
||||
};
|
||||
|
||||
function def_to_moz(mytype, handler) {
|
||||
mytype.DEFMETHOD("to_mozilla_ast", function() {
|
||||
return set_moz_loc(this, handler(this));
|
||||
});
|
||||
};
|
||||
|
||||
function to_moz(node) {
|
||||
return node != null ? node.to_mozilla_ast() : null;
|
||||
};
|
||||
|
||||
function to_moz_block(node) {
|
||||
return {
|
||||
type: "BlockStatement",
|
||||
body: node.body.map(to_moz)
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
143
lib/output.js
143
lib/output.js
@@ -60,9 +60,11 @@ function OutputStream(options) {
|
||||
bracketize : false,
|
||||
semicolons : true,
|
||||
comments : false,
|
||||
shebang : true,
|
||||
preserve_line : false,
|
||||
screw_ie8 : false,
|
||||
preamble : null,
|
||||
quote_style : 0
|
||||
}, true);
|
||||
|
||||
var indentation = 0;
|
||||
@@ -84,32 +86,51 @@ function OutputStream(options) {
|
||||
});
|
||||
};
|
||||
|
||||
function make_string(str) {
|
||||
function make_string(str, quote) {
|
||||
var dq = 0, sq = 0;
|
||||
str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s){
|
||||
str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s){
|
||||
switch (s) {
|
||||
case "\\": return "\\\\";
|
||||
case "\b": return "\\b";
|
||||
case "\f": return "\\f";
|
||||
case "\n": return "\\n";
|
||||
case "\r": return "\\r";
|
||||
case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B";
|
||||
case "\u2028": return "\\u2028";
|
||||
case "\u2029": return "\\u2029";
|
||||
case '"': ++dq; return '"';
|
||||
case "'": ++sq; return "'";
|
||||
case "\0": return "\\x00";
|
||||
case "\ufeff": return "\\ufeff";
|
||||
}
|
||||
return s;
|
||||
});
|
||||
function quote_single() {
|
||||
return "'" + str.replace(/\x27/g, "\\'") + "'";
|
||||
}
|
||||
function quote_double() {
|
||||
return '"' + str.replace(/\x22/g, '\\"') + '"';
|
||||
}
|
||||
if (options.ascii_only) str = to_ascii(str);
|
||||
if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'";
|
||||
else return '"' + str.replace(/\x22/g, '\\"') + '"';
|
||||
switch (options.quote_style) {
|
||||
case 1:
|
||||
return quote_single();
|
||||
case 2:
|
||||
return quote_double();
|
||||
case 3:
|
||||
return quote == "'" ? quote_single() : quote_double();
|
||||
default:
|
||||
return dq > sq ? quote_single() : quote_double();
|
||||
}
|
||||
};
|
||||
|
||||
function encode_string(str) {
|
||||
var ret = make_string(str);
|
||||
if (options.inline_script)
|
||||
function encode_string(str, quote) {
|
||||
var ret = make_string(str, quote);
|
||||
if (options.inline_script) {
|
||||
ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
|
||||
ret = ret.replace(/\x3c!--/g, "\\x3c!--");
|
||||
ret = ret.replace(/--\x3e/g, "--\\x3e");
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
@@ -145,6 +166,8 @@ function OutputStream(options) {
|
||||
str = String(str);
|
||||
var ch = str.charAt(0);
|
||||
if (might_need_semicolon) {
|
||||
might_need_semicolon = false;
|
||||
|
||||
if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) {
|
||||
if (options.semicolons || requireSemicolonChars(ch)) {
|
||||
OUTPUT += ";";
|
||||
@@ -155,12 +178,17 @@ function OutputStream(options) {
|
||||
current_pos++;
|
||||
current_line++;
|
||||
current_col = 0;
|
||||
|
||||
if (/^\s+$/.test(str)) {
|
||||
// reset the semicolon flag, since we didn't print one
|
||||
// now and might still have to later
|
||||
might_need_semicolon = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.beautify)
|
||||
might_need_space = false;
|
||||
}
|
||||
might_need_semicolon = false;
|
||||
maybe_newline();
|
||||
}
|
||||
|
||||
if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
|
||||
@@ -221,7 +249,7 @@ function OutputStream(options) {
|
||||
|
||||
var newline = options.beautify ? function() {
|
||||
print("\n");
|
||||
} : noop;
|
||||
} : maybe_newline;
|
||||
|
||||
var semicolon = options.beautify ? function() {
|
||||
print(";");
|
||||
@@ -323,7 +351,7 @@ function OutputStream(options) {
|
||||
force_semicolon : force_semicolon,
|
||||
to_ascii : to_ascii,
|
||||
print_name : function(name) { print(make_name(name)) },
|
||||
print_string : function(str) { print(encode_string(str)) },
|
||||
print_string : function(str, quote) { print(encode_string(str, quote)) },
|
||||
next_indent : next_indent,
|
||||
with_indent : with_indent,
|
||||
with_block : with_block,
|
||||
@@ -354,8 +382,13 @@ function OutputStream(options) {
|
||||
nodetype.DEFMETHOD("_codegen", generator);
|
||||
};
|
||||
|
||||
var use_asm = false;
|
||||
|
||||
AST_Node.DEFMETHOD("print", function(stream, force_parens){
|
||||
var self = this, generator = self._codegen;
|
||||
var self = this, generator = self._codegen, prev_use_asm = use_asm;
|
||||
if (self instanceof AST_Directive && self.value == "use asm") {
|
||||
use_asm = true;
|
||||
}
|
||||
function doit() {
|
||||
self.add_comments(stream);
|
||||
self.add_source_map(stream);
|
||||
@@ -368,6 +401,9 @@ function OutputStream(options) {
|
||||
doit();
|
||||
}
|
||||
stream.pop_node();
|
||||
if (self instanceof AST_Lambda) {
|
||||
use_asm = prev_use_asm;
|
||||
}
|
||||
});
|
||||
|
||||
AST_Node.DEFMETHOD("print_to_string", function(options){
|
||||
@@ -380,7 +416,6 @@ function OutputStream(options) {
|
||||
|
||||
AST_Node.DEFMETHOD("add_comments", function(output){
|
||||
var c = output.option("comments"), self = this;
|
||||
if (c) {
|
||||
var start = self.start;
|
||||
if (start && !start._comments_dumped) {
|
||||
start._comments_dumped = true;
|
||||
@@ -403,15 +438,28 @@ function OutputStream(options) {
|
||||
}));
|
||||
}
|
||||
|
||||
if (c.test) {
|
||||
if (!c) {
|
||||
comments = comments.filter(function(comment) {
|
||||
return comment.type == "comment5";
|
||||
});
|
||||
} else if (c.test) {
|
||||
comments = comments.filter(function(comment){
|
||||
return c.test(comment.value);
|
||||
return comment.type == "comment5" || c.test(comment.value);
|
||||
});
|
||||
} else if (typeof c == "function") {
|
||||
comments = comments.filter(function(comment){
|
||||
return c(self, comment);
|
||||
return comment.type == "comment5" || c(self, comment);
|
||||
});
|
||||
}
|
||||
|
||||
// Keep single line comments after nlb, after nlb
|
||||
if (!output.option("beautify") && comments.length > 0 &&
|
||||
/comment[134]/.test(comments[0].type) &&
|
||||
output.col() !== 0 && comments[0].nlb)
|
||||
{
|
||||
output.print("\n");
|
||||
}
|
||||
|
||||
comments.forEach(function(c){
|
||||
if (/comment[134]/.test(c.type)) {
|
||||
output.print("//" + c.value + "\n");
|
||||
@@ -426,15 +474,24 @@ function OutputStream(options) {
|
||||
output.space();
|
||||
}
|
||||
}
|
||||
});
|
||||
else if (output.pos() === 0 && c.type == "comment5" && output.option("shebang")) {
|
||||
output.print("#!" + c.value + "\n");
|
||||
output.indent();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* -----[ PARENTHESES ]----- */
|
||||
|
||||
function PARENS(nodetype, func) {
|
||||
if (Array.isArray(nodetype)) {
|
||||
nodetype.forEach(function(nodetype){
|
||||
PARENS(nodetype, func);
|
||||
});
|
||||
} else {
|
||||
nodetype.DEFMETHOD("needs_parens", func);
|
||||
}
|
||||
};
|
||||
|
||||
PARENS(AST_Node, function(){
|
||||
@@ -453,7 +510,7 @@ function OutputStream(options) {
|
||||
return first_in_statement(output);
|
||||
});
|
||||
|
||||
PARENS(AST_Unary, function(output){
|
||||
PARENS([ AST_Unary, AST_Undefined ], function(output){
|
||||
var p = output.parent();
|
||||
return p instanceof AST_PropAccess && p.expression === this;
|
||||
});
|
||||
@@ -543,13 +600,7 @@ function OutputStream(options) {
|
||||
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) {
|
||||
PARENS([ AST_Assign, AST_Conditional ], function (output){
|
||||
var p = output.parent();
|
||||
// !(a = false) → true
|
||||
if (p instanceof AST_Unary)
|
||||
@@ -566,15 +617,12 @@ function OutputStream(options) {
|
||||
// (a = foo)["prop"] —or— (a = foo).prop
|
||||
if (p instanceof AST_PropAccess && p.expression === this)
|
||||
return true;
|
||||
};
|
||||
|
||||
PARENS(AST_Assign, assign_and_conditional_paren_rules);
|
||||
PARENS(AST_Conditional, assign_and_conditional_paren_rules);
|
||||
});
|
||||
|
||||
/* -----[ PRINTERS ]----- */
|
||||
|
||||
DEFPRINT(AST_Directive, function(self, output){
|
||||
output.print_string(self.value);
|
||||
output.print_string(self.value, self.quote);
|
||||
output.semicolon();
|
||||
});
|
||||
DEFPRINT(AST_Debugger, function(self, output){
|
||||
@@ -656,7 +704,7 @@ function OutputStream(options) {
|
||||
output.print("for");
|
||||
output.space();
|
||||
output.with_parens(function(){
|
||||
if (self.init) {
|
||||
if (self.init && !(self.init instanceof AST_EmptyStatement)) {
|
||||
if (self.init instanceof AST_Definitions) {
|
||||
self.init.print(output);
|
||||
} else {
|
||||
@@ -1009,16 +1057,24 @@ function OutputStream(options) {
|
||||
output.print(self.operator);
|
||||
});
|
||||
DEFPRINT(AST_Binary, function(self, output){
|
||||
var op = self.operator;
|
||||
self.left.print(output);
|
||||
if (op[0] == ">" /* ">>" ">>>" ">" ">=" */
|
||||
&& self.left instanceof AST_UnaryPostfix
|
||||
&& self.left.operator == "--") {
|
||||
// space is mandatory to avoid outputting -->
|
||||
output.print(" ");
|
||||
} else {
|
||||
// the space is optional depending on "beautify"
|
||||
output.space();
|
||||
output.print(self.operator);
|
||||
if (self.operator == "<"
|
||||
}
|
||||
output.print(op);
|
||||
if ((op == "<" || op == "<<")
|
||||
&& self.right instanceof AST_UnaryPrefix
|
||||
&& self.right.operator == "!"
|
||||
&& self.right.expression instanceof AST_UnaryPrefix
|
||||
&& self.right.expression.operator == "--") {
|
||||
// space is mandatory to avoid outputting <!--
|
||||
// http://javascript.spec.whatwg.org/#comment-syntax
|
||||
output.print(" ");
|
||||
} else {
|
||||
// the space is optional depending on "beautify"
|
||||
@@ -1070,6 +1126,7 @@ function OutputStream(options) {
|
||||
});
|
||||
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
||||
var key = self.key;
|
||||
var quote = self.quote;
|
||||
if (output.option("quote_keys")) {
|
||||
output.print_string(key + "");
|
||||
} else if ((typeof key == "number"
|
||||
@@ -1080,7 +1137,7 @@ function OutputStream(options) {
|
||||
} else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
|
||||
output.print_name(key);
|
||||
} else {
|
||||
output.print_string(key);
|
||||
output.print_string(key, quote);
|
||||
}
|
||||
output.colon();
|
||||
self.value.print(output);
|
||||
@@ -1106,10 +1163,10 @@ function OutputStream(options) {
|
||||
});
|
||||
DEFPRINT(AST_Hole, noop);
|
||||
DEFPRINT(AST_Infinity, function(self, output){
|
||||
output.print("1/0");
|
||||
output.print("Infinity");
|
||||
});
|
||||
DEFPRINT(AST_NaN, function(self, output){
|
||||
output.print("0/0");
|
||||
output.print("NaN");
|
||||
});
|
||||
DEFPRINT(AST_This, function(self, output){
|
||||
output.print("this");
|
||||
@@ -1118,10 +1175,14 @@ function OutputStream(options) {
|
||||
output.print(self.getValue());
|
||||
});
|
||||
DEFPRINT(AST_String, function(self, output){
|
||||
output.print_string(self.getValue());
|
||||
output.print_string(self.getValue(), self.quote);
|
||||
});
|
||||
DEFPRINT(AST_Number, function(self, output){
|
||||
if (use_asm && self.start.raw != null) {
|
||||
output.print(self.start.raw);
|
||||
} else {
|
||||
output.print(make_num(self.getValue()));
|
||||
}
|
||||
});
|
||||
|
||||
function regexp_safe_literal(code) {
|
||||
@@ -1297,6 +1358,12 @@ function OutputStream(options) {
|
||||
DEFMAP(AST_Finally, basic_sourcemap_gen);
|
||||
DEFMAP(AST_Definitions, basic_sourcemap_gen);
|
||||
DEFMAP(AST_Constant, basic_sourcemap_gen);
|
||||
DEFMAP(AST_ObjectSetter, function(self, output){
|
||||
output.add_mapping(self.start, self.key.name);
|
||||
});
|
||||
DEFMAP(AST_ObjectGetter, function(self, output){
|
||||
output.add_mapping(self.start, self.key.name);
|
||||
});
|
||||
DEFMAP(AST_ObjectProperty, function(self, output){
|
||||
output.add_mapping(self.start, self.key);
|
||||
});
|
||||
|
||||
115
lib/parse.js
115
lib/parse.js
@@ -59,7 +59,6 @@ var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
|
||||
|
||||
var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
|
||||
var RE_OCT_NUMBER = /^0[0-7]+$/;
|
||||
var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
|
||||
|
||||
var OPERATORS = makePredicate([
|
||||
"in",
|
||||
@@ -108,7 +107,7 @@ var OPERATORS = makePredicate([
|
||||
"||"
|
||||
]);
|
||||
|
||||
var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000"));
|
||||
var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF"));
|
||||
|
||||
var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:"));
|
||||
|
||||
@@ -120,7 +119,8 @@ var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
|
||||
|
||||
// regexps adapted from http://xregexp.com/plugins/#unicode
|
||||
var UNICODE = {
|
||||
letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0523\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0621-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971\\u0972\\u097B-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D28\\u0D2A-\\u0D39\\u0D3D\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC\\u0EDD\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8B\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10D0-\\u10FA\\u10FC\\u1100-\\u1159\\u115F-\\u11A2\\u11A8-\\u11F9\\u1200-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u1676\\u1681-\\u169A\\u16A0-\\u16EA\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19A9\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u2094\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2C6F\\u2C71-\\u2C7D\\u2C80-\\u2CE4\\u2D00-\\u2D25\\u2D30-\\u2D65\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31B7\\u31F0-\\u31FF\\u3400\\u4DB5\\u4E00\\u9FC3\\uA000-\\uA48C\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA65F\\uA662-\\uA66E\\uA67F-\\uA697\\uA717-\\uA71F\\uA722-\\uA788\\uA78B\\uA78C\\uA7FB-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA90A-\\uA925\\uA930-\\uA946\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAC00\\uD7A3\\uF900-\\uFA2D\\uFA30-\\uFA6A\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"),
|
||||
letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0-\\u08B2\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"),
|
||||
digit: new RegExp("[\\u0030-\\u0039\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0BE6-\\u0BEF\\u0C66-\\u0C6F\\u0CE6-\\u0CEF\\u0D66-\\u0D6F\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F29\\u1040-\\u1049\\u1090-\\u1099\\u17E0-\\u17E9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19D9\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\uA620-\\uA629\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19]"),
|
||||
non_spacing_mark: new RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065E\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0900-\\u0902\\u093C\\u0941-\\u0948\\u094D\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09BC\\u09C1-\\u09C4\\u09CD\\u09E2\\u09E3\\u0A01\\u0A02\\u0A3C\\u0A41\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81\\u0A82\\u0ABC\\u0AC1-\\u0AC5\\u0AC7\\u0AC8\\u0ACD\\u0AE2\\u0AE3\\u0B01\\u0B3C\\u0B3F\\u0B41-\\u0B44\\u0B4D\\u0B56\\u0B62\\u0B63\\u0B82\\u0BC0\\u0BCD\\u0C3E-\\u0C40\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0CBC\\u0CBF\\u0CC6\\u0CCC\\u0CCD\\u0CE2\\u0CE3\\u0D41-\\u0D44\\u0D4D\\u0D62\\u0D63\\u0DCA\\u0DD2-\\u0DD4\\u0DD6\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EB9\\u0EBB\\u0EBC\\u0EC8-\\u0ECD\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F71-\\u0F7E\\u0F80-\\u0F84\\u0F86\\u0F87\\u0F90-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102D-\\u1030\\u1032-\\u1037\\u1039\\u103A\\u103D\\u103E\\u1058\\u1059\\u105E-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108D\\u109D\\u135F\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B7-\\u17BD\\u17C6\\u17C9-\\u17D3\\u17DD\\u180B-\\u180D\\u18A9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193B\\u1A17\\u1A18\\u1A56\\u1A58-\\u1A5E\\u1A60\\u1A62\\u1A65-\\u1A6C\\u1A73-\\u1A7C\\u1A7F\\u1B00-\\u1B03\\u1B34\\u1B36-\\u1B3A\\u1B3C\\u1B42\\u1B6B-\\u1B73\\u1B80\\u1B81\\u1BA2-\\u1BA5\\u1BA8\\u1BA9\\u1C2C-\\u1C33\\u1C36\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE0\\u1CE2-\\u1CE8\\u1CED\\u1DC0-\\u1DE6\\u1DFD-\\u1DFF\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2CEF-\\u2CF1\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F\\uA67C\\uA67D\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA825\\uA826\\uA8C4\\uA8E0-\\uA8F1\\uA926-\\uA92D\\uA947-\\uA951\\uA980-\\uA982\\uA9B3\\uA9B6-\\uA9B9\\uA9BC\\uAA29-\\uAA2E\\uAA31\\uAA32\\uAA35\\uAA36\\uAA43\\uAA4C\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uABE5\\uABE8\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE26]"),
|
||||
space_combining_mark: new RegExp("[\\u0903\\u093E-\\u0940\\u0949-\\u094C\\u094E\\u0982\\u0983\\u09BE-\\u09C0\\u09C7\\u09C8\\u09CB\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB\\u0ACC\\u0B02\\u0B03\\u0B3E\\u0B40\\u0B47\\u0B48\\u0B4B\\u0B4C\\u0B57\\u0BBE\\u0BBF\\u0BC1\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7\\u0CC8\\u0CCA\\u0CCB\\u0CD5\\u0CD6\\u0D02\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0F3E\\u0F3F\\u0F7F\\u102B\\u102C\\u1031\\u1038\\u103B\\u103C\\u1056\\u1057\\u1062-\\u1064\\u1067-\\u106D\\u1083\\u1084\\u1087-\\u108C\\u108F\\u109A-\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930\\u1931\\u1933-\\u1938\\u19B0-\\u19C0\\u19C8\\u19C9\\u1A19-\\u1A1B\\u1A55\\u1A57\\u1A61\\u1A63\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B44\\u1B82\\u1BA1\\u1BA6\\u1BA7\\u1BAA\\u1C24-\\u1C2B\\u1C34\\u1C35\\u1CE1\\u1CF2\\uA823\\uA824\\uA827\\uA880\\uA881\\uA8B4-\\uA8C3\\uA952\\uA953\\uA983\\uA9B4\\uA9B5\\uA9BA\\uA9BB\\uA9BD-\\uA9C0\\uAA2F\\uAA30\\uAA33\\uAA34\\uAA4D\\uAA7B\\uABE3\\uABE4\\uABE6\\uABE7\\uABE9\\uABEA\\uABEC]"),
|
||||
connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]")
|
||||
@@ -133,13 +133,17 @@ function is_letter(code) {
|
||||
};
|
||||
|
||||
function is_digit(code) {
|
||||
return code >= 48 && code <= 57; //XXX: find out if "UnicodeDigit" means something else than 0..9
|
||||
return code >= 48 && code <= 57;
|
||||
};
|
||||
|
||||
function is_alphanumeric_char(code) {
|
||||
return is_digit(code) || is_letter(code);
|
||||
};
|
||||
|
||||
function is_unicode_digit(code) {
|
||||
return UNICODE.digit.test(String.fromCharCode(code));
|
||||
}
|
||||
|
||||
function is_unicode_combining_mark(ch) {
|
||||
return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch);
|
||||
};
|
||||
@@ -164,6 +168,7 @@ function is_identifier_char(ch) {
|
||||
|| code == 8205 // \u200d: zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c)
|
||||
|| is_unicode_combining_mark(ch)
|
||||
|| is_unicode_connector_punctuation(ch)
|
||||
|| is_unicode_digit(code)
|
||||
;
|
||||
};
|
||||
|
||||
@@ -176,13 +181,15 @@ function parse_js_number(num) {
|
||||
return parseInt(num.substr(2), 16);
|
||||
} else if (RE_OCT_NUMBER.test(num)) {
|
||||
return parseInt(num.substr(1), 8);
|
||||
} else if (RE_DEC_NUMBER.test(num)) {
|
||||
return parseFloat(num);
|
||||
} else {
|
||||
var val = parseFloat(num);
|
||||
if (val == num) return val;
|
||||
}
|
||||
};
|
||||
|
||||
function JS_Parse_Error(message, line, col, pos) {
|
||||
function JS_Parse_Error(message, filename, line, col, pos) {
|
||||
this.message = message;
|
||||
this.filename = filename;
|
||||
this.line = line;
|
||||
this.col = col;
|
||||
this.pos = pos;
|
||||
@@ -194,7 +201,7 @@ JS_Parse_Error.prototype.toString = function() {
|
||||
};
|
||||
|
||||
function js_error(message, filename, line, col, pos) {
|
||||
throw new JS_Parse_Error(message, line, col, pos);
|
||||
throw new JS_Parse_Error(message, filename, line, col, pos);
|
||||
};
|
||||
|
||||
function is_token(token, type, val) {
|
||||
@@ -203,10 +210,10 @@ function is_token(token, type, val) {
|
||||
|
||||
var EX_EOF = {};
|
||||
|
||||
function tokenizer($TEXT, filename, html5_comments) {
|
||||
function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
|
||||
var S = {
|
||||
text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/\uFEFF/g, ''),
|
||||
text : $TEXT,
|
||||
filename : filename,
|
||||
pos : 0,
|
||||
tokpos : 0,
|
||||
@@ -225,10 +232,15 @@ function tokenizer($TEXT, filename, html5_comments) {
|
||||
var ch = S.text.charAt(S.pos++);
|
||||
if (signal_eof && !ch)
|
||||
throw EX_EOF;
|
||||
if (ch == "\n") {
|
||||
if ("\r\n\u2028\u2029".indexOf(ch) >= 0) {
|
||||
S.newline_before = S.newline_before || !in_string;
|
||||
++S.line;
|
||||
S.col = 0;
|
||||
if (!in_string && ch == "\r" && peek() == "\n") {
|
||||
// treat a \r\n sequence as a single \n
|
||||
++S.pos;
|
||||
ch = "\n";
|
||||
}
|
||||
} else {
|
||||
++S.col;
|
||||
}
|
||||
@@ -267,10 +279,15 @@ function tokenizer($TEXT, filename, html5_comments) {
|
||||
line : S.tokline,
|
||||
col : S.tokcol,
|
||||
pos : S.tokpos,
|
||||
endline : S.line,
|
||||
endcol : S.col,
|
||||
endpos : S.pos,
|
||||
nlb : S.newline_before,
|
||||
file : filename
|
||||
};
|
||||
if (/^(?:num|string|regexp)$/i.test(type)) {
|
||||
ret.raw = $TEXT.substring(ret.pos, ret.endpos);
|
||||
}
|
||||
if (!is_comment) {
|
||||
ret.comments_before = S.comments_before;
|
||||
S.comments_before = [];
|
||||
@@ -284,7 +301,8 @@ function tokenizer($TEXT, filename, html5_comments) {
|
||||
};
|
||||
|
||||
function skip_whitespace() {
|
||||
while (WHITESPACE_CHARS(peek()))
|
||||
var ch;
|
||||
while (WHITESPACE_CHARS(ch = peek()) || ch == "\u2028" || ch == "\u2029")
|
||||
next();
|
||||
};
|
||||
|
||||
@@ -339,8 +357,13 @@ function tokenizer($TEXT, filename, html5_comments) {
|
||||
case 120 : return String.fromCharCode(hex_bytes(2)); // \x
|
||||
case 117 : return String.fromCharCode(hex_bytes(4)); // \u
|
||||
case 10 : return ""; // newline
|
||||
default : return ch;
|
||||
case 13 : // \r
|
||||
if (peek() == "\n") { // DOS newline
|
||||
next(true, in_string);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return ch;
|
||||
};
|
||||
|
||||
function hex_bytes(n) {
|
||||
@@ -354,10 +377,10 @@ function tokenizer($TEXT, filename, html5_comments) {
|
||||
return num;
|
||||
};
|
||||
|
||||
var read_string = with_eof_error("Unterminated string constant", function(){
|
||||
var read_string = with_eof_error("Unterminated string constant", function(quote_char){
|
||||
var quote = next(), ret = "";
|
||||
for (;;) {
|
||||
var ch = next(true);
|
||||
var ch = next(true, true);
|
||||
if (ch == "\\") {
|
||||
// read OctalEscapeSequence (XXX: deprecated if "strict mode")
|
||||
// https://github.com/mishoo/UglifyJS/issues/178
|
||||
@@ -376,10 +399,13 @@ function tokenizer($TEXT, filename, html5_comments) {
|
||||
if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
|
||||
else ch = read_escaped_char(true);
|
||||
}
|
||||
else if ("\r\n\u2028\u2029".indexOf(ch) >= 0) parse_error("Unterminated string constant");
|
||||
else if (ch == quote) break;
|
||||
ret += ch;
|
||||
}
|
||||
return token("string", ret);
|
||||
var tok = token("string", ret);
|
||||
tok.quote = quote_char;
|
||||
return tok;
|
||||
});
|
||||
|
||||
function skip_line_comment(type) {
|
||||
@@ -392,6 +418,7 @@ function tokenizer($TEXT, filename, html5_comments) {
|
||||
ret = S.text.substring(S.pos, i);
|
||||
S.pos = i;
|
||||
}
|
||||
S.col = S.tokcol + (S.pos - S.tokpos);
|
||||
S.comments_before.push(token(type, ret, true));
|
||||
S.regex_allowed = regex_allowed;
|
||||
return next_token();
|
||||
@@ -457,7 +484,11 @@ function tokenizer($TEXT, filename, html5_comments) {
|
||||
regexp += ch;
|
||||
}
|
||||
var mods = read_name();
|
||||
try {
|
||||
return token("regexp", new RegExp(regexp, mods));
|
||||
} catch(e) {
|
||||
parse_error(e.message);
|
||||
}
|
||||
});
|
||||
|
||||
function read_operator(prefix) {
|
||||
@@ -533,7 +564,7 @@ function tokenizer($TEXT, filename, html5_comments) {
|
||||
if (!ch) return token("eof");
|
||||
var code = ch.charCodeAt(0);
|
||||
switch (code) {
|
||||
case 34: case 39: return read_string();
|
||||
case 34: case 39: return read_string(ch);
|
||||
case 46: return handle_dot();
|
||||
case 47: return handle_slash();
|
||||
}
|
||||
@@ -541,6 +572,13 @@ function tokenizer($TEXT, filename, html5_comments) {
|
||||
if (PUNC_CHARS(ch)) return token("punc", next());
|
||||
if (OPERATOR_CHARS(ch)) return read_operator();
|
||||
if (code == 92 || is_identifier_start(code)) return read_word();
|
||||
|
||||
if (shebang) {
|
||||
if (S.pos == 0 && looking_at("#!")) {
|
||||
forward(2);
|
||||
return skip_line_comment("comment5");
|
||||
}
|
||||
}
|
||||
parse_error("Unexpected character '" + ch + "'");
|
||||
};
|
||||
|
||||
@@ -609,12 +647,14 @@ function parse($TEXT, options) {
|
||||
toplevel : null,
|
||||
expression : false,
|
||||
html5_comments : true,
|
||||
bare_returns : false,
|
||||
shebang : true,
|
||||
});
|
||||
|
||||
var S = {
|
||||
input : (typeof $TEXT == "string"
|
||||
? tokenizer($TEXT, options.filename,
|
||||
options.html5_comments)
|
||||
options.html5_comments, options.shebang)
|
||||
: $TEXT),
|
||||
token : null,
|
||||
prev : null,
|
||||
@@ -685,9 +725,9 @@ function parse($TEXT, options) {
|
||||
);
|
||||
};
|
||||
|
||||
function semicolon() {
|
||||
function semicolon(optional) {
|
||||
if (is("punc", ";")) next();
|
||||
else if (!can_insert_semicolon()) unexpected();
|
||||
else if (!optional && !can_insert_semicolon()) unexpected();
|
||||
};
|
||||
|
||||
function parenthesised() {
|
||||
@@ -722,8 +762,14 @@ function parse($TEXT, options) {
|
||||
case "string":
|
||||
var dir = S.in_directives, stat = simple_statement();
|
||||
// XXXv2: decide how to fix directives
|
||||
if (dir && stat.body instanceof AST_String && !is("punc", ","))
|
||||
return new AST_Directive({ value: stat.body.value });
|
||||
if (dir && stat.body instanceof AST_String && !is("punc", ",")) {
|
||||
return new AST_Directive({
|
||||
start : stat.body.start,
|
||||
end : stat.body.end,
|
||||
quote : stat.body.quote,
|
||||
value : stat.body.value,
|
||||
});
|
||||
}
|
||||
return stat;
|
||||
case "num":
|
||||
case "regexp":
|
||||
@@ -769,7 +815,7 @@ function parse($TEXT, options) {
|
||||
case "do":
|
||||
return new AST_Do({
|
||||
body : in_loop(statement),
|
||||
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(), tmp)
|
||||
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(true), tmp)
|
||||
});
|
||||
|
||||
case "while":
|
||||
@@ -788,7 +834,7 @@ function parse($TEXT, options) {
|
||||
return if_();
|
||||
|
||||
case "return":
|
||||
if (S.in_function == 0)
|
||||
if (S.in_function == 0 && !options.bare_returns)
|
||||
croak("'return' outside of function");
|
||||
return new AST_Return({
|
||||
value: ( is("punc", ";")
|
||||
@@ -1080,7 +1126,7 @@ function parse($TEXT, options) {
|
||||
});
|
||||
};
|
||||
|
||||
var new_ = function() {
|
||||
var new_ = function(allow_calls) {
|
||||
var start = S.token;
|
||||
expect_token("operator", "new");
|
||||
var newexp = expr_atom(false), args;
|
||||
@@ -1095,7 +1141,7 @@ function parse($TEXT, options) {
|
||||
expression : newexp,
|
||||
args : args,
|
||||
end : prev()
|
||||
}), true);
|
||||
}), allow_calls);
|
||||
};
|
||||
|
||||
function as_atom_node() {
|
||||
@@ -1109,7 +1155,12 @@ function parse($TEXT, options) {
|
||||
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
|
||||
break;
|
||||
case "string":
|
||||
ret = new AST_String({ start: tok, end: tok, value: tok.value });
|
||||
ret = new AST_String({
|
||||
start : tok,
|
||||
end : tok,
|
||||
value : tok.value,
|
||||
quote : tok.quote
|
||||
});
|
||||
break;
|
||||
case "regexp":
|
||||
ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
|
||||
@@ -1127,6 +1178,13 @@ function parse($TEXT, options) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "operator":
|
||||
if (!is_identifier_string(tok.value)) {
|
||||
throw new JS_Parse_Error("Invalid getter/setter name: " + tok.value,
|
||||
tok.file, tok.line, tok.col, tok.pos);
|
||||
}
|
||||
ret = _make_symbol(AST_SymbolRef);
|
||||
break;
|
||||
}
|
||||
next();
|
||||
return ret;
|
||||
@@ -1134,7 +1192,7 @@ function parse($TEXT, options) {
|
||||
|
||||
var expr_atom = function(allow_calls) {
|
||||
if (is("operator", "new")) {
|
||||
return new_();
|
||||
return new_(allow_calls);
|
||||
}
|
||||
var start = S.token;
|
||||
if (is("punc")) {
|
||||
@@ -1222,6 +1280,7 @@ function parse($TEXT, options) {
|
||||
expect(":");
|
||||
a.push(new AST_ObjectKeyVal({
|
||||
start : start,
|
||||
quote : start.quote,
|
||||
key : name,
|
||||
value : expression(false),
|
||||
end : prev()
|
||||
|
||||
223
lib/propmangle.js
Normal file
223
lib/propmangle.js
Normal file
@@ -0,0 +1,223 @@
|
||||
/***********************************************************************
|
||||
|
||||
A JavaScript tokenizer / parser / beautifier / compressor.
|
||||
https://github.com/mishoo/UglifyJS2
|
||||
|
||||
-------------------------------- (C) ---------------------------------
|
||||
|
||||
Author: Mihai Bazon
|
||||
<mihai.bazon@gmail.com>
|
||||
http://mihai.bazon.net/blog
|
||||
|
||||
Distributed under the BSD license:
|
||||
|
||||
Copyright 2012 (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.
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
"use strict";
|
||||
|
||||
function find_builtins() {
|
||||
var a = [];
|
||||
[ Object, Array, Function, Number,
|
||||
String, Boolean, Error, Math,
|
||||
Date, RegExp
|
||||
].forEach(function(ctor){
|
||||
Object.getOwnPropertyNames(ctor).map(add);
|
||||
if (ctor.prototype) {
|
||||
Object.getOwnPropertyNames(ctor.prototype).map(add);
|
||||
}
|
||||
});
|
||||
function add(name) {
|
||||
push_uniq(a, name);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function mangle_properties(ast, options) {
|
||||
options = defaults(options, {
|
||||
reserved : null,
|
||||
cache : null,
|
||||
only_cache : false,
|
||||
regex : null
|
||||
});
|
||||
|
||||
var reserved = options.reserved;
|
||||
if (reserved == null)
|
||||
reserved = find_builtins();
|
||||
|
||||
var cache = options.cache;
|
||||
if (cache == null) {
|
||||
cache = {
|
||||
cname: -1,
|
||||
props: new Dictionary()
|
||||
};
|
||||
}
|
||||
|
||||
var regex = options.regex;
|
||||
|
||||
var names_to_mangle = [];
|
||||
var unmangleable = [];
|
||||
|
||||
// step 1: find candidates to mangle
|
||||
ast.walk(new TreeWalker(function(node){
|
||||
if (node instanceof AST_ObjectKeyVal) {
|
||||
add(node.key);
|
||||
}
|
||||
else if (node instanceof AST_ObjectProperty) {
|
||||
// setter or getter, since KeyVal is handled above
|
||||
add(node.key.name);
|
||||
}
|
||||
else if (node instanceof AST_Dot) {
|
||||
if (this.parent() instanceof AST_Assign) {
|
||||
add(node.property);
|
||||
}
|
||||
}
|
||||
else if (node instanceof AST_Sub) {
|
||||
if (this.parent() instanceof AST_Assign) {
|
||||
addStrings(node.property);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// step 2: transform the tree, renaming properties
|
||||
return ast.transform(new TreeTransformer(function(node){
|
||||
if (node instanceof AST_ObjectKeyVal) {
|
||||
node.key = mangle(node.key);
|
||||
}
|
||||
else if (node instanceof AST_ObjectProperty) {
|
||||
// setter or getter
|
||||
node.key.name = mangle(node.key.name);
|
||||
}
|
||||
else if (node instanceof AST_Dot) {
|
||||
node.property = mangle(node.property);
|
||||
}
|
||||
else if (node instanceof AST_Sub) {
|
||||
node.property = mangleStrings(node.property);
|
||||
}
|
||||
// else if (node instanceof AST_String) {
|
||||
// if (should_mangle(node.value)) {
|
||||
// AST_Node.warn(
|
||||
// "Found \"{prop}\" property candidate for mangling in an arbitrary string [{file}:{line},{col}]", {
|
||||
// file : node.start.file,
|
||||
// line : node.start.line,
|
||||
// col : node.start.col,
|
||||
// prop : node.value
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
}));
|
||||
|
||||
// only function declarations after this line
|
||||
|
||||
function can_mangle(name) {
|
||||
if (unmangleable.indexOf(name) >= 0) return false;
|
||||
if (reserved.indexOf(name) >= 0) return false;
|
||||
if (options.only_cache) {
|
||||
return cache.props.has(name);
|
||||
}
|
||||
if (/^[0-9.]+$/.test(name)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function should_mangle(name) {
|
||||
if (regex && !regex.test(name)) return false;
|
||||
if (reserved.indexOf(name) >= 0) return false;
|
||||
return cache.props.has(name)
|
||||
|| names_to_mangle.indexOf(name) >= 0;
|
||||
}
|
||||
|
||||
function add(name) {
|
||||
if (can_mangle(name))
|
||||
push_uniq(names_to_mangle, name);
|
||||
|
||||
if (!should_mangle(name)) {
|
||||
push_uniq(unmangleable, name);
|
||||
}
|
||||
}
|
||||
|
||||
function mangle(name) {
|
||||
if (!should_mangle(name)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
var mangled = cache.props.get(name);
|
||||
if (!mangled) {
|
||||
do {
|
||||
mangled = base54(++cache.cname);
|
||||
} while (!can_mangle(mangled));
|
||||
cache.props.set(name, mangled);
|
||||
}
|
||||
return mangled;
|
||||
}
|
||||
|
||||
function addStrings(node) {
|
||||
var out = {};
|
||||
try {
|
||||
(function walk(node){
|
||||
node.walk(new TreeWalker(function(node){
|
||||
if (node instanceof AST_Seq) {
|
||||
walk(node.cdr);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_String) {
|
||||
add(node.value);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Conditional) {
|
||||
walk(node.consequent);
|
||||
walk(node.alternative);
|
||||
return true;
|
||||
}
|
||||
throw out;
|
||||
}));
|
||||
})(node);
|
||||
} catch(ex) {
|
||||
if (ex !== out) throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
function mangleStrings(node) {
|
||||
return node.transform(new TreeTransformer(function(node){
|
||||
if (node instanceof AST_Seq) {
|
||||
node.cdr = mangleStrings(node.cdr);
|
||||
}
|
||||
else if (node instanceof AST_String) {
|
||||
node.value = mangle(node.value);
|
||||
}
|
||||
else if (node instanceof AST_Conditional) {
|
||||
node.consequent = mangleStrings(node.consequent);
|
||||
node.alternative = mangleStrings(node.alternative);
|
||||
}
|
||||
return node;
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
117
lib/scope.js
117
lib/scope.js
@@ -57,29 +57,44 @@ function SymbolDef(scope, index, orig) {
|
||||
|
||||
SymbolDef.prototype = {
|
||||
unmangleable: function(options) {
|
||||
return (this.global && !(options && options.toplevel))
|
||||
if (!options) options = {};
|
||||
|
||||
return (this.global && !options.toplevel)
|
||||
|| this.undeclared
|
||||
|| (!(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with));
|
||||
|| (!options.eval && (this.scope.uses_eval || this.scope.uses_with))
|
||||
|| (options.keep_fnames
|
||||
&& (this.orig[0] instanceof AST_SymbolLambda
|
||||
|| this.orig[0] instanceof AST_SymbolDefun));
|
||||
},
|
||||
mangle: function(options) {
|
||||
if (!this.mangled_name && !this.unmangleable(options)) {
|
||||
var cache = options.cache && options.cache.props;
|
||||
if (this.global && cache && cache.has(this.name)) {
|
||||
this.mangled_name = cache.get(this.name);
|
||||
}
|
||||
else if (!this.mangled_name && !this.unmangleable(options)) {
|
||||
var s = this.scope;
|
||||
if (!options.screw_ie8 && this.orig[0] instanceof AST_SymbolLambda)
|
||||
s = s.parent_scope;
|
||||
this.mangled_name = s.next_mangled(options, this);
|
||||
if (this.global && cache) {
|
||||
cache.set(this.name, this.mangled_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
options = defaults(options, {
|
||||
screw_ie8: false
|
||||
screw_ie8: false,
|
||||
cache: null
|
||||
});
|
||||
|
||||
// pass 1: setup scope chaining and handle definitions
|
||||
var self = this;
|
||||
var scope = self.parent_scope = null;
|
||||
var labels = new Dictionary();
|
||||
var defun = null;
|
||||
var last_var_had_const_pragma = false;
|
||||
var nesting = 0;
|
||||
var tw = new TreeWalker(function(node, descend){
|
||||
if (options.screw_ie8 && node instanceof AST_Catch) {
|
||||
@@ -95,16 +110,24 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
node.init_scope_vars(nesting);
|
||||
var save_scope = node.parent_scope = scope;
|
||||
var save_defun = defun;
|
||||
var save_labels = labels;
|
||||
defun = scope = node;
|
||||
labels = new Dictionary();
|
||||
++nesting; descend(); --nesting;
|
||||
scope = save_scope;
|
||||
defun = save_defun;
|
||||
labels = save_labels;
|
||||
return true; // don't descend again in TreeWalker
|
||||
}
|
||||
if (node instanceof AST_Directive) {
|
||||
node.scope = scope;
|
||||
push_uniq(scope.directives, node.value);
|
||||
return true;
|
||||
if (node instanceof AST_LabeledStatement) {
|
||||
var l = node.label;
|
||||
if (labels.has(l.name)) {
|
||||
throw new Error(string_template("Label {name} defined twice", l));
|
||||
}
|
||||
labels.set(l.name, l);
|
||||
descend();
|
||||
labels.del(l.name);
|
||||
return true; // no descend again
|
||||
}
|
||||
if (node instanceof AST_With) {
|
||||
for (var s = scope; s; s = s.parent_scope)
|
||||
@@ -114,6 +137,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
if (node instanceof AST_Symbol) {
|
||||
node.scope = scope;
|
||||
}
|
||||
if (node instanceof AST_Label) {
|
||||
node.thedef = node;
|
||||
node.references = [];
|
||||
}
|
||||
if (node instanceof AST_SymbolLambda) {
|
||||
defun.def_function(node);
|
||||
}
|
||||
@@ -125,16 +152,28 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
// later.
|
||||
(node.scope = defun.parent_scope).def_function(node);
|
||||
}
|
||||
else if (node instanceof AST_Var) {
|
||||
last_var_had_const_pragma = node.has_const_pragma();
|
||||
}
|
||||
else if (node instanceof AST_SymbolVar
|
||||
|| node instanceof AST_SymbolConst) {
|
||||
var def = defun.def_variable(node);
|
||||
def.constant = node instanceof AST_SymbolConst;
|
||||
def.constant = node instanceof AST_SymbolConst || last_var_had_const_pragma;
|
||||
def.init = tw.parent().value;
|
||||
}
|
||||
else if (node instanceof AST_SymbolCatch) {
|
||||
(options.screw_ie8 ? scope : defun)
|
||||
.def_variable(node);
|
||||
}
|
||||
else if (node instanceof AST_LabelRef) {
|
||||
var sym = labels.get(node.name);
|
||||
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
|
||||
name: node.name,
|
||||
line: node.start.line,
|
||||
col: node.start.col
|
||||
}));
|
||||
node.thedef = sym;
|
||||
}
|
||||
});
|
||||
self.walk(tw);
|
||||
|
||||
@@ -149,8 +188,17 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
func = prev_func;
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_LoopControl && node.label) {
|
||||
node.label.thedef.references.push(node);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
var name = node.name;
|
||||
if (name == "eval" && tw.parent() instanceof AST_Call) {
|
||||
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
|
||||
s.uses_eval = true;
|
||||
}
|
||||
}
|
||||
var sym = node.scope.find_variable(name);
|
||||
if (!sym) {
|
||||
var g;
|
||||
@@ -163,10 +211,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
globals.set(name, g);
|
||||
}
|
||||
node.thedef = g;
|
||||
if (name == "eval" && tw.parent() instanceof AST_Call) {
|
||||
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope)
|
||||
s.uses_eval = true;
|
||||
}
|
||||
if (func && name == "arguments") {
|
||||
func.uses_arguments = true;
|
||||
}
|
||||
@@ -178,10 +222,13 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
}
|
||||
});
|
||||
self.walk(tw);
|
||||
|
||||
if (options.cache) {
|
||||
this.cname = options.cache.cname;
|
||||
}
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
|
||||
this.directives = []; // contains the directives defined in this scope, i.e. "use strict"
|
||||
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
|
||||
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
|
||||
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
|
||||
@@ -192,13 +239,13 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
|
||||
this.nesting = nesting; // the nesting level of this scope (0 means toplevel)
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("strict", function(){
|
||||
return this.has_directive("use strict");
|
||||
});
|
||||
|
||||
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
|
||||
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
|
||||
this.uses_arguments = false;
|
||||
|
||||
var symbol = new AST_VarDef({ name: "arguments", start: this.start, end: this.end });
|
||||
var def = new SymbolDef(this, this.variables.size(), symbol);
|
||||
this.variables.set(symbol.name, def);
|
||||
});
|
||||
|
||||
AST_SymbolRef.DEFMETHOD("reference", function() {
|
||||
@@ -219,11 +266,6 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
|
||||
|| (this.parent_scope && this.parent_scope.find_variable(name));
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("has_directive", function(value){
|
||||
return this.parent_scope && this.parent_scope.has_directive(value)
|
||||
|| (this.directives.indexOf(value) >= 0 ? this : null);
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("def_function", function(symbol){
|
||||
this.functions.set(symbol.name, this.def_variable(symbol));
|
||||
});
|
||||
@@ -320,24 +362,44 @@ AST_Symbol.DEFMETHOD("global", function(){
|
||||
return this.definition().global;
|
||||
});
|
||||
|
||||
AST_Var.DEFMETHOD("has_const_pragma", function() {
|
||||
var comments_before = this.start && this.start.comments_before;
|
||||
var lastComment = comments_before && comments_before[comments_before.length - 1];
|
||||
return lastComment && /@const\b/.test(lastComment.value);
|
||||
});
|
||||
|
||||
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
|
||||
return defaults(options, {
|
||||
except : [],
|
||||
eval : false,
|
||||
sort : false,
|
||||
toplevel : false,
|
||||
screw_ie8 : false
|
||||
screw_ie8 : false,
|
||||
keep_fnames : false
|
||||
});
|
||||
});
|
||||
|
||||
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||
options = this._default_mangler_options(options);
|
||||
|
||||
// Never mangle arguments
|
||||
options.except.push('arguments');
|
||||
|
||||
// We only need to mangle declaration nodes. Special logic wired
|
||||
// into the code generator will display the mangled name if it's
|
||||
// present (and for AST_SymbolRef-s it'll use the mangled name of
|
||||
// the AST_SymbolDeclaration that it points to).
|
||||
var lname = -1;
|
||||
var to_mangle = [];
|
||||
|
||||
if (options.cache) {
|
||||
this.globals.each(function(symbol){
|
||||
if (options.except.indexOf(symbol.name) < 0) {
|
||||
to_mangle.push(symbol);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var tw = new TreeWalker(function(node, descend){
|
||||
if (node instanceof AST_LabeledStatement) {
|
||||
// lname is incremented when we get to the AST_Label
|
||||
@@ -372,6 +434,10 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||
});
|
||||
this.walk(tw);
|
||||
to_mangle.forEach(function(def){ def.mangle(options) });
|
||||
|
||||
if (options.cache) {
|
||||
options.cache.cname = this.cname;
|
||||
}
|
||||
});
|
||||
|
||||
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
|
||||
@@ -471,7 +537,9 @@ var base54 = (function() {
|
||||
base54.freq = function(){ return frequency };
|
||||
function base54(num) {
|
||||
var ret = "", base = 54;
|
||||
num++;
|
||||
do {
|
||||
num--;
|
||||
ret += String.fromCharCode(chars[num % base]);
|
||||
num = Math.floor(num / base);
|
||||
base = 64;
|
||||
@@ -532,6 +600,7 @@ AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
|
||||
}
|
||||
if (options.unreferenced
|
||||
&& (node instanceof AST_SymbolDeclaration || node instanceof AST_Label)
|
||||
&& !(node instanceof AST_SymbolCatch)
|
||||
&& node.unreferenced()) {
|
||||
AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {
|
||||
type: node instanceof AST_Label ? "Label" : "Symbol",
|
||||
|
||||
@@ -70,7 +70,7 @@ function SourceMap(options) {
|
||||
source = info.source;
|
||||
orig_line = info.line;
|
||||
orig_col = info.column;
|
||||
name = info.name;
|
||||
name = info.name || name;
|
||||
}
|
||||
generator.addMapping({
|
||||
generated : { line: gen_line + options.dest_line_diff, column: gen_col },
|
||||
@@ -82,6 +82,6 @@ function SourceMap(options) {
|
||||
return {
|
||||
add : add,
|
||||
get : function() { return generator },
|
||||
toString : function() { return generator.toString() }
|
||||
toString : function() { return JSON.stringify(generator.toJSON()); }
|
||||
};
|
||||
};
|
||||
|
||||
@@ -64,13 +64,13 @@ TreeTransformer.prototype = new TreeWalker;
|
||||
x = this;
|
||||
descend(x, tw);
|
||||
} else {
|
||||
tw.stack[tw.stack.length - 1] = x = this.clone();
|
||||
tw.stack[tw.stack.length - 1] = x = this;
|
||||
descend(x, tw);
|
||||
y = tw.after(x, in_list);
|
||||
if (y !== undefined) x = y;
|
||||
}
|
||||
}
|
||||
tw.pop();
|
||||
tw.pop(this);
|
||||
return x;
|
||||
});
|
||||
};
|
||||
|
||||
12
lib/utils.js
12
lib/utils.js
@@ -106,10 +106,12 @@ function defaults(args, defs, croak) {
|
||||
};
|
||||
|
||||
function merge(obj, ext) {
|
||||
var count = 0;
|
||||
for (var i in ext) if (ext.hasOwnProperty(i)) {
|
||||
obj[i] = ext[i];
|
||||
count++;
|
||||
}
|
||||
return obj;
|
||||
return count;
|
||||
};
|
||||
|
||||
function noop() {};
|
||||
@@ -298,5 +300,11 @@ Dictionary.prototype = {
|
||||
for (var i in this._values)
|
||||
ret.push(f(this._values[i], i.substr(1)));
|
||||
return ret;
|
||||
}
|
||||
},
|
||||
toObject: function() { return this._values }
|
||||
};
|
||||
Dictionary.fromObject = function(obj) {
|
||||
var dict = new Dictionary();
|
||||
dict._size = merge(dict._values, obj);
|
||||
return dict;
|
||||
};
|
||||
|
||||
128
npm-shrinkwrap.json
generated
Normal file
128
npm-shrinkwrap.json
generated
Normal file
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"name": "uglify-js",
|
||||
"version": "2.4.24",
|
||||
"dependencies": {
|
||||
"abbrev": {
|
||||
"version": "1.0.7",
|
||||
"from": "abbrev@>=1.0.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.7.tgz"
|
||||
},
|
||||
"amdefine": {
|
||||
"version": "1.0.0",
|
||||
"from": "amdefine@>=0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz"
|
||||
},
|
||||
"async": {
|
||||
"version": "0.2.10",
|
||||
"from": "async@>=0.2.6 <0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz"
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "1.2.1",
|
||||
"from": "camelcase@>=1.0.2 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz"
|
||||
},
|
||||
"decamelize": {
|
||||
"version": "1.0.0",
|
||||
"from": "decamelize@>=1.0.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.0.0.tgz"
|
||||
},
|
||||
"deep-is": {
|
||||
"version": "0.1.3",
|
||||
"from": "deep-is@>=0.1.2 <0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz"
|
||||
},
|
||||
"esprima": {
|
||||
"version": "1.1.1",
|
||||
"from": "esprima@>=1.1.1 <1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz"
|
||||
},
|
||||
"estraverse": {
|
||||
"version": "1.5.1",
|
||||
"from": "estraverse@>=1.5.1 <1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz"
|
||||
},
|
||||
"esutils": {
|
||||
"version": "1.0.0",
|
||||
"from": "esutils@>=1.0.0 <1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz"
|
||||
},
|
||||
"fast-levenshtein": {
|
||||
"version": "1.0.7",
|
||||
"from": "fast-levenshtein@>=1.0.0 <1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.0.7.tgz"
|
||||
},
|
||||
"levn": {
|
||||
"version": "0.2.5",
|
||||
"from": "levn@>=0.2.5 <0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.2.5.tgz"
|
||||
},
|
||||
"nopt": {
|
||||
"version": "2.1.2",
|
||||
"from": "nopt@>=2.1.2 <2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz"
|
||||
},
|
||||
"optionator": {
|
||||
"version": "0.5.0",
|
||||
"from": "optionator@>=0.5.0 <0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.5.0.tgz"
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.1.2",
|
||||
"from": "prelude-ls@>=1.1.1 <1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz"
|
||||
},
|
||||
"reflect": {
|
||||
"version": "0.1.3",
|
||||
"from": "git://github.com/zaach/reflect.js.git",
|
||||
"resolved": "git://github.com/zaach/reflect.js.git#286bcd79661c96ecc404357d3c0e35fdb54a6967"
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.5.1",
|
||||
"from": "source-map@>=0.5.1 <0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.1.tgz"
|
||||
},
|
||||
"type-check": {
|
||||
"version": "0.3.1",
|
||||
"from": "type-check@>=0.3.1 <0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.1.tgz"
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "2.4.24",
|
||||
"from": "git://github.com/mishoo/UglifyJS2.git",
|
||||
"resolved": "git://github.com/mishoo/UglifyJS2.git#2a06c7758e24a64740473c8031eafbb7fefa213f",
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.1.34",
|
||||
"from": "source-map@0.1.34",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"uglify-to-browserify": {
|
||||
"version": "1.0.2",
|
||||
"from": "uglify-to-browserify@>=1.0.0 <1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz"
|
||||
},
|
||||
"window-size": {
|
||||
"version": "0.1.0",
|
||||
"from": "window-size@0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz"
|
||||
},
|
||||
"wordwrap": {
|
||||
"version": "0.0.2",
|
||||
"from": "wordwrap@0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz"
|
||||
},
|
||||
"yargs": {
|
||||
"version": "3.10.0",
|
||||
"from": "yargs@>=3.10.0 <3.11.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz"
|
||||
},
|
||||
"zeparser": {
|
||||
"version": "0.0.7",
|
||||
"from": "git://github.com/qfox/ZeParser.git",
|
||||
"resolved": "git://github.com/qfox/ZeParser.git#c99240c5ba7054c467733800ff38265958a2dda9"
|
||||
}
|
||||
}
|
||||
}
|
||||
56
package.json
56
package.json
@@ -2,29 +2,53 @@
|
||||
"name": "uglify-js",
|
||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||
"homepage": "http://lisperator.net/uglifyjs",
|
||||
"main": "tools/node.js",
|
||||
"version": "2.4.14",
|
||||
"engines": { "node" : ">=0.4.0" },
|
||||
"maintainers": [{
|
||||
"name": "Mihai Bazon",
|
||||
"email": "mihai.bazon@gmail.com",
|
||||
"web": "http://lisperator.net/"
|
||||
}],
|
||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||
"license": "BSD-2-Clause",
|
||||
"version": "2.6.2",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
"maintainers": [
|
||||
"Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mishoo/UglifyJS2.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/mishoo/UglifyJS2/issues"
|
||||
},
|
||||
"main": "tools/node.js",
|
||||
"bin": {
|
||||
"uglifyjs": "bin/uglifyjs"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
"lib",
|
||||
"tools",
|
||||
"LICENSE"
|
||||
],
|
||||
"dependencies": {
|
||||
"async" : "~0.2.6",
|
||||
"source-map" : "~0.1.33",
|
||||
"optimist" : "~0.3.5",
|
||||
"uglify-to-browserify": "~1.0.0"
|
||||
"async": "~0.2.6",
|
||||
"source-map": "~0.5.1",
|
||||
"uglify-to-browserify": "~1.0.0",
|
||||
"yargs": "~3.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"acorn": "~0.6.0",
|
||||
"escodegen": "~1.3.3",
|
||||
"esfuzz": "~0.3.1",
|
||||
"estraverse": "~1.5.1",
|
||||
"mocha": "~2.3.4"
|
||||
},
|
||||
"browserify": {
|
||||
"transform": [ "uglify-to-browserify" ]
|
||||
"transform": [
|
||||
"uglify-to-browserify"
|
||||
]
|
||||
},
|
||||
"bin": {
|
||||
"uglifyjs" : "bin/uglifyjs"
|
||||
"scripts": {
|
||||
"shrinkwrap": "rm ./npm-shrinkwrap.json; rm -rf ./node_modules; npm i && npm shrinkwrap && npm outdated",
|
||||
"test": "node test/run-tests.js"
|
||||
},
|
||||
"scripts": {"test": "node test/run-tests.js"}
|
||||
"keywords": ["uglify", "uglify-js", "minify", "minifier"]
|
||||
}
|
||||
|
||||
67
test/compress/angular-inject.js
vendored
Normal file
67
test/compress/angular-inject.js
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
ng_inject_defun: {
|
||||
options = {
|
||||
angular: true
|
||||
};
|
||||
input: {
|
||||
/*@ngInject*/
|
||||
function Controller(dependency) {
|
||||
return dependency;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function Controller(dependency) {
|
||||
return dependency;
|
||||
}
|
||||
Controller.$inject=['dependency']
|
||||
}
|
||||
}
|
||||
|
||||
ng_inject_assignment: {
|
||||
options = {
|
||||
angular: true
|
||||
};
|
||||
input: {
|
||||
/*@ngInject*/
|
||||
var Controller = function(dependency) {
|
||||
return dependency;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var Controller = function(dependency) {
|
||||
return dependency;
|
||||
}
|
||||
Controller.$inject=['dependency']
|
||||
}
|
||||
}
|
||||
|
||||
ng_inject_inline: {
|
||||
options = {
|
||||
angular: true
|
||||
};
|
||||
input: {
|
||||
angular.module('a').
|
||||
factory('b',
|
||||
/*@ngInject*/
|
||||
function(dependency) {
|
||||
return dependency;
|
||||
}).
|
||||
directive('c',
|
||||
/*@ngInject*/
|
||||
function(anotherDependency) {
|
||||
return anotherDependency;
|
||||
})
|
||||
}
|
||||
expect: {
|
||||
angular.module('a').
|
||||
factory('b',[
|
||||
'dependency',
|
||||
function(dependency) {
|
||||
return dependency;
|
||||
}]).
|
||||
directive('c',[
|
||||
'anotherDependency',
|
||||
function(anotherDependency) {
|
||||
return anotherDependency;
|
||||
}])
|
||||
}
|
||||
}
|
||||
106
test/compress/asm.js
Normal file
106
test/compress/asm.js
Normal file
@@ -0,0 +1,106 @@
|
||||
asm_mixed: {
|
||||
options = {
|
||||
sequences : true,
|
||||
properties : true,
|
||||
dead_code : true,
|
||||
drop_debugger : true,
|
||||
conditionals : true,
|
||||
comparisons : true,
|
||||
evaluate : true,
|
||||
booleans : true,
|
||||
loops : true,
|
||||
unused : true,
|
||||
hoist_funs : true,
|
||||
keep_fargs : true,
|
||||
keep_fnames : false,
|
||||
hoist_vars : true,
|
||||
if_return : true,
|
||||
join_vars : true,
|
||||
cascade : true,
|
||||
side_effects : true,
|
||||
negate_iife : true
|
||||
};
|
||||
input: {
|
||||
// adapted from http://asmjs.org/spec/latest/
|
||||
function asm_GeometricMean(stdlib, foreign, buffer) {
|
||||
"use asm";
|
||||
var exp = stdlib.Math.exp;
|
||||
var log = stdlib.Math.log;
|
||||
var values = new stdlib.Float64Array(buffer);
|
||||
function logSum(start, end) {
|
||||
start = start|0;
|
||||
end = end|0;
|
||||
var sum = 0.0, p = 0, q = 0;
|
||||
// asm.js forces byte addressing of the heap by requiring shifting by 3
|
||||
for (p = start << 3, q = end << 3; (p|0) < (q|0); p = (p + 8)|0) {
|
||||
sum = sum + +log(values[p>>3]);
|
||||
}
|
||||
return +sum;
|
||||
}
|
||||
function geometricMean(start, end) {
|
||||
start = start|0;
|
||||
end = end|0;
|
||||
return +exp(+logSum(start, end) / +((end - start)|0));
|
||||
}
|
||||
return { geometricMean: geometricMean };
|
||||
}
|
||||
function no_asm_GeometricMean(stdlib, foreign, buffer) {
|
||||
var exp = stdlib.Math.exp;
|
||||
var log = stdlib.Math.log;
|
||||
var values = new stdlib.Float64Array(buffer);
|
||||
function logSum(start, end) {
|
||||
start = start|0;
|
||||
end = end|0;
|
||||
var sum = 0.0, p = 0, q = 0;
|
||||
// asm.js forces byte addressing of the heap by requiring shifting by 3
|
||||
for (p = start << 3, q = end << 3; (p|0) < (q|0); p = (p + 8)|0) {
|
||||
sum = sum + +log(values[p>>3]);
|
||||
}
|
||||
return +sum;
|
||||
}
|
||||
function geometricMean(start, end) {
|
||||
start = start|0;
|
||||
end = end|0;
|
||||
return +exp(+logSum(start, end) / +((end - start)|0));
|
||||
}
|
||||
return { geometricMean: geometricMean };
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function asm_GeometricMean(stdlib, foreign, buffer) {
|
||||
"use asm";
|
||||
var exp = stdlib.Math.exp;
|
||||
var log = stdlib.Math.log;
|
||||
var values = new stdlib.Float64Array(buffer);
|
||||
function logSum(start, end) {
|
||||
start = start | 0;
|
||||
end = end | 0;
|
||||
var sum = 0.0, p = 0, q = 0;
|
||||
for (p = start << 3, q = end << 3; (p | 0) < (q | 0); p = p + 8 | 0) {
|
||||
sum = sum + +log(values[p >> 3]);
|
||||
}
|
||||
return +sum;
|
||||
}
|
||||
function geometricMean(start, end) {
|
||||
start = start | 0;
|
||||
end = end | 0;
|
||||
return +exp(+logSum(start, end) / +(end - start | 0));
|
||||
}
|
||||
return { geometricMean: geometricMean };
|
||||
}
|
||||
function no_asm_GeometricMean(stdlib, foreign, buffer) {
|
||||
function logSum(start, end) {
|
||||
start = 0 | start, end = 0 | end;
|
||||
var sum = 0, p = 0, q = 0;
|
||||
for (p = start << 3, q = end << 3; (0 | q) > (0 | p); p = p + 8 | 0) sum += +log(values[p >> 3]);
|
||||
return +sum;
|
||||
}
|
||||
function geometricMean(start, end) {
|
||||
return start = 0 | start, end = 0 | end, +exp(+logSum(start, end) / +(end - start | 0));
|
||||
}
|
||||
var exp = stdlib.Math.exp, log = stdlib.Math.log, values = new stdlib.Float64Array(buffer);
|
||||
return { geometricMean: geometricMean };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1155
test/compress/collapse_vars.js
Normal file
1155
test/compress/collapse_vars.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -53,6 +53,7 @@ ifs_3_should_warn: {
|
||||
booleans : true
|
||||
};
|
||||
input: {
|
||||
var x, y;
|
||||
if (x && !(x + "1") && y) { // 1
|
||||
var qq;
|
||||
foo();
|
||||
@@ -68,6 +69,7 @@ ifs_3_should_warn: {
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var x, y;
|
||||
var qq; bar(); // 1
|
||||
var jj; foo(); // 2
|
||||
}
|
||||
@@ -84,7 +86,9 @@ ifs_4: {
|
||||
x(foo)[10].bar.baz = something_else();
|
||||
}
|
||||
expect: {
|
||||
x(foo)[10].bar.baz = (foo && bar) ? something() : something_else();
|
||||
foo && bar
|
||||
? x(foo)[10].bar.baz = something()
|
||||
: x(foo)[10].bar.baz = something_else();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +135,7 @@ ifs_6: {
|
||||
comparisons: true
|
||||
};
|
||||
input: {
|
||||
var x;
|
||||
if (!foo && !bar && !baz && !boo) {
|
||||
x = 10;
|
||||
} else {
|
||||
@@ -138,6 +143,7 @@ ifs_6: {
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var x;
|
||||
x = foo || bar || baz || boo ? 20 : 10;
|
||||
}
|
||||
}
|
||||
@@ -147,6 +153,7 @@ cond_1: {
|
||||
conditionals: true
|
||||
};
|
||||
input: {
|
||||
var do_something; // if undeclared it's assumed to have side-effects
|
||||
if (some_condition()) {
|
||||
do_something(x);
|
||||
} else {
|
||||
@@ -154,6 +161,7 @@ cond_1: {
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var do_something;
|
||||
do_something(some_condition() ? x : y);
|
||||
}
|
||||
}
|
||||
@@ -163,6 +171,7 @@ cond_2: {
|
||||
conditionals: true
|
||||
};
|
||||
input: {
|
||||
var x, FooBar;
|
||||
if (some_condition()) {
|
||||
x = new FooBar(1);
|
||||
} else {
|
||||
@@ -170,6 +179,7 @@ cond_2: {
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var x, FooBar;
|
||||
x = new FooBar(some_condition() ? 1 : 2);
|
||||
}
|
||||
}
|
||||
@@ -179,6 +189,7 @@ cond_3: {
|
||||
conditionals: true
|
||||
};
|
||||
input: {
|
||||
var FooBar;
|
||||
if (some_condition()) {
|
||||
new FooBar(1);
|
||||
} else {
|
||||
@@ -186,6 +197,7 @@ cond_3: {
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var FooBar;
|
||||
some_condition() ? new FooBar(1) : FooBar(2);
|
||||
}
|
||||
}
|
||||
@@ -195,6 +207,7 @@ cond_4: {
|
||||
conditionals: true
|
||||
};
|
||||
input: {
|
||||
var do_something;
|
||||
if (some_condition()) {
|
||||
do_something();
|
||||
} else {
|
||||
@@ -202,6 +215,7 @@ cond_4: {
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var do_something;
|
||||
some_condition(), do_something();
|
||||
}
|
||||
}
|
||||
@@ -232,3 +246,569 @@ cond_5: {
|
||||
some_condition() && some_other_condition() && do_something();
|
||||
}
|
||||
}
|
||||
|
||||
cond_7: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate : true
|
||||
};
|
||||
input: {
|
||||
var x, y, z, a, b;
|
||||
// compress these
|
||||
if (y) {
|
||||
x = 1+1;
|
||||
} else {
|
||||
x = 2;
|
||||
}
|
||||
|
||||
if (y) {
|
||||
x = 1+1;
|
||||
} else if (z) {
|
||||
x = 2;
|
||||
} else {
|
||||
x = 3-1;
|
||||
}
|
||||
|
||||
x = y ? 'foo' : 'fo'+'o';
|
||||
|
||||
x = y ? 'foo' : y ? 'foo' : 'fo'+'o';
|
||||
|
||||
// Compress conditions that have side effects
|
||||
if (condition()) {
|
||||
x = 10+10;
|
||||
} else {
|
||||
x = 20;
|
||||
}
|
||||
|
||||
if (z) {
|
||||
x = 'fuji';
|
||||
} else if (condition()) {
|
||||
x = 'fu'+'ji';
|
||||
} else {
|
||||
x = 'fuji';
|
||||
}
|
||||
|
||||
x = condition() ? 'foobar' : 'foo'+'bar';
|
||||
|
||||
// don't compress these
|
||||
x = y ? a : b;
|
||||
|
||||
x = y ? 'foo' : 'fo';
|
||||
}
|
||||
expect: {
|
||||
var x, y, z, a, b;
|
||||
x = 2;
|
||||
x = 2;
|
||||
x = 'foo';
|
||||
x = 'foo';
|
||||
x = (condition(), 20);
|
||||
x = z ? 'fuji' : (condition(), 'fuji');
|
||||
x = (condition(), 'foobar');
|
||||
x = y ? a : b;
|
||||
x = y ? 'foo' : 'fo';
|
||||
}
|
||||
}
|
||||
|
||||
cond_7_1: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate : true
|
||||
};
|
||||
input: {
|
||||
var x;
|
||||
// access to global should be assumed to have side effects
|
||||
if (y) {
|
||||
x = 1+1;
|
||||
} else {
|
||||
x = 2;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var x;
|
||||
x = (y, 2);
|
||||
}
|
||||
}
|
||||
|
||||
cond_8: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate : true,
|
||||
booleans : false
|
||||
};
|
||||
input: {
|
||||
var a;
|
||||
// compress these
|
||||
a = condition ? true : false;
|
||||
a = !condition ? true : false;
|
||||
a = condition() ? true : false;
|
||||
|
||||
a = condition ? !0 : !1;
|
||||
a = !condition ? !null : !2;
|
||||
a = condition() ? !0 : !-3.5;
|
||||
|
||||
if (condition) {
|
||||
a = true;
|
||||
} else {
|
||||
a = false;
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
a = !0;
|
||||
} else {
|
||||
a = !1;
|
||||
}
|
||||
|
||||
a = condition ? false : true;
|
||||
a = !condition ? false : true;
|
||||
a = condition() ? false : true;
|
||||
|
||||
a = condition ? !3 : !0;
|
||||
a = !condition ? !2 : !0;
|
||||
a = condition() ? !1 : !0;
|
||||
|
||||
if (condition) {
|
||||
a = false;
|
||||
} else {
|
||||
a = true;
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
a = !1;
|
||||
} else {
|
||||
a = !0;
|
||||
}
|
||||
|
||||
// don't compress these
|
||||
a = condition ? 1 : false;
|
||||
a = !condition ? true : 0;
|
||||
a = condition ? 1 : 0;
|
||||
}
|
||||
expect: {
|
||||
var a;
|
||||
a = !!condition;
|
||||
a = !condition;
|
||||
a = !!condition();
|
||||
|
||||
a = !!condition;
|
||||
a = !condition;
|
||||
a = !!condition();
|
||||
|
||||
a = !!condition;
|
||||
a = !!condition;
|
||||
|
||||
a = !condition;
|
||||
a = !!condition;
|
||||
a = !condition();
|
||||
|
||||
a = !condition;
|
||||
a = !!condition;
|
||||
a = !condition();
|
||||
|
||||
a = !condition;
|
||||
a = !condition;
|
||||
|
||||
a = condition ? 1 : false;
|
||||
a = condition ? 0 : true;
|
||||
a = condition ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
cond_8b: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate : true,
|
||||
booleans : true
|
||||
};
|
||||
input: {
|
||||
var a;
|
||||
// compress these
|
||||
a = condition ? true : false;
|
||||
a = !condition ? true : false;
|
||||
a = condition() ? true : false;
|
||||
|
||||
a = condition ? !0 : !1;
|
||||
a = !condition ? !null : !2;
|
||||
a = condition() ? !0 : !-3.5;
|
||||
|
||||
if (condition) {
|
||||
a = true;
|
||||
} else {
|
||||
a = false;
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
a = !0;
|
||||
} else {
|
||||
a = !1;
|
||||
}
|
||||
|
||||
a = condition ? false : true;
|
||||
a = !condition ? false : true;
|
||||
a = condition() ? false : true;
|
||||
|
||||
a = condition ? !3 : !0;
|
||||
a = !condition ? !2 : !0;
|
||||
a = condition() ? !1 : !0;
|
||||
|
||||
if (condition) {
|
||||
a = false;
|
||||
} else {
|
||||
a = true;
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
a = !1;
|
||||
} else {
|
||||
a = !0;
|
||||
}
|
||||
|
||||
a = condition ? 1 : false;
|
||||
a = !condition ? true : 0;
|
||||
a = condition ? 1 : 0;
|
||||
}
|
||||
expect: {
|
||||
var a;
|
||||
a = !!condition;
|
||||
a = !condition;
|
||||
a = !!condition();
|
||||
|
||||
a = !!condition;
|
||||
a = !condition;
|
||||
a = !!condition();
|
||||
|
||||
a = !!condition;
|
||||
a = !!condition;
|
||||
|
||||
a = !condition;
|
||||
a = !!condition;
|
||||
a = !condition();
|
||||
|
||||
a = !condition;
|
||||
a = !!condition;
|
||||
a = !condition();
|
||||
|
||||
a = !condition;
|
||||
a = !condition;
|
||||
|
||||
a = condition ? 1 : !1;
|
||||
a = condition ? 0 : !0;
|
||||
a = condition ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
cond_8c: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate : false,
|
||||
booleans : false
|
||||
};
|
||||
input: {
|
||||
var a;
|
||||
// compress these
|
||||
a = condition ? true : false;
|
||||
a = !condition ? true : false;
|
||||
a = condition() ? true : false;
|
||||
|
||||
a = condition ? !0 : !1;
|
||||
a = !condition ? !null : !2;
|
||||
a = condition() ? !0 : !-3.5;
|
||||
|
||||
if (condition) {
|
||||
a = true;
|
||||
} else {
|
||||
a = false;
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
a = !0;
|
||||
} else {
|
||||
a = !1;
|
||||
}
|
||||
|
||||
a = condition ? false : true;
|
||||
a = !condition ? false : true;
|
||||
a = condition() ? false : true;
|
||||
|
||||
a = condition ? !3 : !0;
|
||||
a = !condition ? !2 : !0;
|
||||
a = condition() ? !1 : !0;
|
||||
|
||||
if (condition) {
|
||||
a = false;
|
||||
} else {
|
||||
a = true;
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
a = !1;
|
||||
} else {
|
||||
a = !0;
|
||||
}
|
||||
|
||||
a = condition ? 1 : false;
|
||||
a = !condition ? true : 0;
|
||||
a = condition ? 1 : 0;
|
||||
}
|
||||
expect: {
|
||||
var a;
|
||||
a = !!condition;
|
||||
a = !condition;
|
||||
a = !!condition();
|
||||
|
||||
a = !!condition;
|
||||
a = !condition;
|
||||
a = condition() ? !0 : !-3.5;
|
||||
|
||||
a = !!condition;
|
||||
a = !!condition;
|
||||
|
||||
a = !condition;
|
||||
a = !!condition;
|
||||
a = !condition();
|
||||
|
||||
a = !condition;
|
||||
a = !!condition;
|
||||
a = !condition();
|
||||
|
||||
a = !condition;
|
||||
a = !condition;
|
||||
|
||||
a = condition ? 1 : false;
|
||||
a = condition ? 0 : true;
|
||||
a = condition ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
conditional_and: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate : true
|
||||
};
|
||||
input: {
|
||||
var a;
|
||||
// compress these
|
||||
|
||||
a = true && condition;
|
||||
a = 1 && console.log("a");
|
||||
a = 2 * 3 && 2 * condition;
|
||||
a = 5 == 5 && condition + 3;
|
||||
a = "string" && 4 - condition;
|
||||
a = 5 + "" && condition / 5;
|
||||
a = -4.5 && 6 << condition;
|
||||
a = 6 && 7;
|
||||
|
||||
a = false && condition;
|
||||
a = NaN && console.log("b");
|
||||
a = 0 && console.log("c");
|
||||
a = undefined && 2 * condition;
|
||||
a = null && condition + 3;
|
||||
a = 2 * 3 - 6 && 4 - condition;
|
||||
a = 10 == 7 && condition / 5;
|
||||
a = !"string" && 6 % condition;
|
||||
a = 0 && 7;
|
||||
|
||||
// don't compress these
|
||||
|
||||
a = condition && true;
|
||||
a = console.log("a") && 2;
|
||||
a = 4 - condition && "string";
|
||||
a = 6 << condition && -4.5;
|
||||
|
||||
a = condition && false;
|
||||
a = console.log("b") && NaN;
|
||||
a = console.log("c") && 0;
|
||||
a = 2 * condition && undefined;
|
||||
a = condition + 3 && null;
|
||||
|
||||
}
|
||||
expect: {
|
||||
var a;
|
||||
|
||||
a = condition;
|
||||
a = console.log("a");
|
||||
a = 2 * condition;
|
||||
a = condition + 3;
|
||||
a = 4 - condition;
|
||||
a = condition / 5;
|
||||
a = 6 << condition;
|
||||
a = 7;
|
||||
|
||||
a = false;
|
||||
a = NaN;
|
||||
a = 0;
|
||||
a = void 0;
|
||||
a = null;
|
||||
a = 0;
|
||||
a = false;
|
||||
a = false;
|
||||
a = 0;
|
||||
|
||||
a = condition && true;
|
||||
a = console.log("a") && 2;
|
||||
a = 4 - condition && "string";
|
||||
a = 6 << condition && -4.5;
|
||||
|
||||
a = condition && false;
|
||||
a = console.log("b") && NaN;
|
||||
a = console.log("c") && 0;
|
||||
a = 2 * condition && void 0;
|
||||
a = condition + 3 && null;
|
||||
}
|
||||
}
|
||||
|
||||
conditional_or: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate : true
|
||||
};
|
||||
input: {
|
||||
var a;
|
||||
// compress these
|
||||
|
||||
a = true || condition;
|
||||
a = 1 || console.log("a");
|
||||
a = 2 * 3 || 2 * condition;
|
||||
a = 5 == 5 || condition + 3;
|
||||
a = "string" || 4 - condition;
|
||||
a = 5 + "" || condition / 5;
|
||||
a = -4.5 || 6 << condition;
|
||||
a = 6 || 7;
|
||||
|
||||
a = false || condition;
|
||||
a = 0 || console.log("b");
|
||||
a = NaN || console.log("c");
|
||||
a = undefined || 2 * condition;
|
||||
a = null || condition + 3;
|
||||
a = 2 * 3 - 6 || 4 - condition;
|
||||
a = 10 == 7 || condition / 5;
|
||||
a = !"string" || 6 % condition;
|
||||
a = null || 7;
|
||||
|
||||
a = console.log(undefined && condition || null);
|
||||
a = console.log(undefined || condition && null);
|
||||
|
||||
// don't compress these
|
||||
|
||||
a = condition || true;
|
||||
a = console.log("a") || 2;
|
||||
a = 4 - condition || "string";
|
||||
a = 6 << condition || -4.5;
|
||||
|
||||
a = condition || false;
|
||||
a = console.log("b") || NaN;
|
||||
a = console.log("c") || 0;
|
||||
a = 2 * condition || undefined;
|
||||
a = condition + 3 || null;
|
||||
|
||||
}
|
||||
expect: {
|
||||
var a;
|
||||
|
||||
a = true;
|
||||
a = 1;
|
||||
a = 6;
|
||||
a = true;
|
||||
a = "string";
|
||||
a = "5";
|
||||
a = -4.5;
|
||||
a = 6;
|
||||
|
||||
a = condition;
|
||||
a = console.log("b");
|
||||
a = console.log("c");
|
||||
a = 2 * condition;
|
||||
a = condition + 3;
|
||||
a = 4 - condition;
|
||||
a = condition / 5;
|
||||
a = 6 % condition;
|
||||
a = 7;
|
||||
|
||||
a = console.log(null);
|
||||
a = console.log(condition && null);
|
||||
|
||||
a = condition || true;
|
||||
a = console.log("a") || 2;
|
||||
a = 4 - condition || "string";
|
||||
a = 6 << condition || -4.5;
|
||||
|
||||
a = condition || false;
|
||||
a = console.log("b") || NaN;
|
||||
a = console.log("c") || 0;
|
||||
a = 2 * condition || void 0;
|
||||
a = condition + 3 || null;
|
||||
}
|
||||
}
|
||||
|
||||
trivial_boolean_ternary_expressions : {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate : true,
|
||||
booleans : true
|
||||
};
|
||||
input: {
|
||||
f('foo' in m ? true : false);
|
||||
f('foo' in m ? false : true);
|
||||
|
||||
f(g ? true : false);
|
||||
f(foo() ? true : false);
|
||||
f("bar" ? true : false);
|
||||
f(5 ? true : false);
|
||||
f(5.7 ? true : false);
|
||||
f(x - y ? true : false);
|
||||
|
||||
f(x == y ? true : false);
|
||||
f(x === y ? !0 : !1);
|
||||
f(x < y ? !0 : false);
|
||||
f(x <= y ? true : false);
|
||||
f(x > y ? true : !1);
|
||||
f(x >= y ? !0 : !1);
|
||||
|
||||
f(g ? false : true);
|
||||
f(foo() ? false : true);
|
||||
f("bar" ? false : true);
|
||||
f(5 ? false : true);
|
||||
f(5.7 ? false : true);
|
||||
f(x - y ? false : true);
|
||||
|
||||
f(x == y ? !1 : !0);
|
||||
f(x === y ? false : true);
|
||||
|
||||
f(x < y ? false : true);
|
||||
f(x <= y ? false : !0);
|
||||
f(x > y ? !1 : true);
|
||||
f(x >= y ? !1 : !0);
|
||||
}
|
||||
expect: {
|
||||
f('foo' in m);
|
||||
f(!('foo' in m));
|
||||
|
||||
f(!!g);
|
||||
f(!!foo());
|
||||
f(!0);
|
||||
f(!0);
|
||||
f(!0);
|
||||
f(!!(x - y));
|
||||
|
||||
f(x == y);
|
||||
f(x === y);
|
||||
f(x < y);
|
||||
f(x <= y);
|
||||
f(x > y);
|
||||
f(x >= y);
|
||||
|
||||
f(!g);
|
||||
f(!foo());
|
||||
f(!1);
|
||||
f(!1);
|
||||
f(!1);
|
||||
f(!(x - y));
|
||||
|
||||
f(x != y);
|
||||
f(x !== y);
|
||||
|
||||
f(!(x < y));
|
||||
f(!(x <= y));
|
||||
f(!(x > y));
|
||||
f(!(x >= y));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ dead_code_constant_boolean_should_warn_more: {
|
||||
var foo;
|
||||
function bar() {}
|
||||
}
|
||||
for (var x = 10; x && (y || x) && (!typeof x); ++x) {
|
||||
for (var x = 10, y; x && (y || x) && (!typeof x); ++x) {
|
||||
asdf();
|
||||
foo();
|
||||
var moo;
|
||||
@@ -83,7 +83,124 @@ dead_code_constant_boolean_should_warn_more: {
|
||||
function bar() {}
|
||||
// nothing for the while
|
||||
// as for the for, it should keep:
|
||||
var x = 10;
|
||||
var x = 10, y;
|
||||
var moo;
|
||||
}
|
||||
}
|
||||
|
||||
dead_code_const_declaration: {
|
||||
options = {
|
||||
dead_code : true,
|
||||
loops : true,
|
||||
booleans : true,
|
||||
conditionals : true,
|
||||
evaluate : true
|
||||
};
|
||||
input: {
|
||||
var unused;
|
||||
const CONST_FOO = false;
|
||||
if (CONST_FOO) {
|
||||
console.log("unreachable");
|
||||
var moo;
|
||||
function bar() {}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var unused;
|
||||
const CONST_FOO = !1;
|
||||
var moo;
|
||||
function bar() {}
|
||||
}
|
||||
}
|
||||
|
||||
dead_code_const_annotation: {
|
||||
options = {
|
||||
dead_code : true,
|
||||
loops : true,
|
||||
booleans : true,
|
||||
conditionals : true,
|
||||
evaluate : true
|
||||
};
|
||||
input: {
|
||||
var unused;
|
||||
/** @const */ var CONST_FOO_ANN = false;
|
||||
if (CONST_FOO_ANN) {
|
||||
console.log("unreachable");
|
||||
var moo;
|
||||
function bar() {}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var unused;
|
||||
var CONST_FOO_ANN = !1;
|
||||
var moo;
|
||||
function bar() {}
|
||||
}
|
||||
}
|
||||
|
||||
dead_code_const_annotation_regex: {
|
||||
options = {
|
||||
dead_code : true,
|
||||
loops : true,
|
||||
booleans : true,
|
||||
conditionals : true,
|
||||
evaluate : true
|
||||
};
|
||||
input: {
|
||||
var unused;
|
||||
// @constraint this shouldn't be a constant
|
||||
var CONST_FOO_ANN = false;
|
||||
if (CONST_FOO_ANN) {
|
||||
console.log("reachable");
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var unused;
|
||||
var CONST_FOO_ANN = !1;
|
||||
CONST_FOO_ANN && console.log('reachable');
|
||||
}
|
||||
}
|
||||
|
||||
dead_code_const_annotation_complex_scope: {
|
||||
options = {
|
||||
dead_code : true,
|
||||
loops : true,
|
||||
booleans : true,
|
||||
conditionals : true,
|
||||
evaluate : true
|
||||
};
|
||||
input: {
|
||||
var unused_var;
|
||||
/** @const */ var test = 'test';
|
||||
// @const
|
||||
var CONST_FOO_ANN = false;
|
||||
var unused_var_2;
|
||||
if (CONST_FOO_ANN) {
|
||||
console.log("unreachable");
|
||||
var moo;
|
||||
function bar() {}
|
||||
}
|
||||
if (test === 'test') {
|
||||
var beef = 'good';
|
||||
/** @const */ var meat = 'beef';
|
||||
var pork = 'bad';
|
||||
if (meat === 'pork') {
|
||||
console.log('also unreachable');
|
||||
} else if (pork === 'good') {
|
||||
console.log('reached, not const');
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var unused_var;
|
||||
var test = 'test';
|
||||
var CONST_FOO_ANN = !1;
|
||||
var unused_var_2;
|
||||
var moo;
|
||||
function bar() {}
|
||||
var beef = 'good';
|
||||
var meat = 'beef';
|
||||
var pork = 'bad';
|
||||
'good' === pork && console.log('reached, not const');
|
||||
}
|
||||
}
|
||||
|
||||
24
test/compress/drop-console.js
Normal file
24
test/compress/drop-console.js
Normal file
@@ -0,0 +1,24 @@
|
||||
drop_console_1: {
|
||||
options = {};
|
||||
input: {
|
||||
console.log('foo');
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
expect: {
|
||||
console.log('foo');
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
drop_console_1: {
|
||||
options = { drop_console: true };
|
||||
input: {
|
||||
console.log('foo');
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
expect: {
|
||||
// with regular compression these will be stripped out as well
|
||||
void 0;
|
||||
void 0;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
unused_funarg_1: {
|
||||
options = { unused: true };
|
||||
options = { unused: true, keep_fargs: false };
|
||||
input: {
|
||||
function f(a, b, c, d, e) {
|
||||
return a + b;
|
||||
@@ -13,7 +13,7 @@ unused_funarg_1: {
|
||||
}
|
||||
|
||||
unused_funarg_2: {
|
||||
options = { unused: true };
|
||||
options = { unused: true, keep_fargs: false };
|
||||
input: {
|
||||
function f(a, b, c, d, e) {
|
||||
return a + c;
|
||||
@@ -163,3 +163,17 @@ used_var_in_catch: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keep_fnames: {
|
||||
options = { unused: true, keep_fnames: true, unsafe: true };
|
||||
input: {
|
||||
function foo() {
|
||||
return function bar(baz) {};
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function foo() {
|
||||
return function bar(baz) {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
71
test/compress/html_comments.js
Normal file
71
test/compress/html_comments.js
Normal file
@@ -0,0 +1,71 @@
|
||||
html_comment_in_expression: {
|
||||
input: {
|
||||
function f(a, b, x, y) { return a < !--b && x-- > y; }
|
||||
}
|
||||
expect_exact: "function f(a,b,x,y){return a< !--b&&x-- >y}";
|
||||
}
|
||||
|
||||
html_comment_in_less_than: {
|
||||
input: {
|
||||
function f(a, b) { return a < !--b; }
|
||||
}
|
||||
expect_exact: "function f(a,b){return a< !--b}";
|
||||
}
|
||||
|
||||
html_comment_in_left_shift: {
|
||||
input: {
|
||||
function f(a, b) { return a << !--b; }
|
||||
}
|
||||
expect_exact: "function f(a,b){return a<< !--b}";
|
||||
}
|
||||
|
||||
html_comment_in_right_shift: {
|
||||
input: {
|
||||
function f(a, b) { return a-- >> b; }
|
||||
}
|
||||
expect_exact: "function f(a,b){return a-- >>b}";
|
||||
}
|
||||
|
||||
html_comment_in_zero_fill_right_shift: {
|
||||
input: {
|
||||
function f(a, b) { return a-- >>> b; }
|
||||
}
|
||||
expect_exact: "function f(a,b){return a-- >>>b}";
|
||||
}
|
||||
|
||||
html_comment_in_greater_than: {
|
||||
input: {
|
||||
function f(a, b) { return a-- > b; }
|
||||
}
|
||||
expect_exact: "function f(a,b){return a-- >b}";
|
||||
}
|
||||
|
||||
html_comment_in_greater_than_or_equal: {
|
||||
input: {
|
||||
function f(a, b) { return a-- >= b; }
|
||||
}
|
||||
expect_exact: "function f(a,b){return a-- >=b}";
|
||||
}
|
||||
|
||||
html_comment_in_right_shift_assign: {
|
||||
input: {
|
||||
// Note: illegal javascript
|
||||
function f(a, b) { return a-- >>= b; }
|
||||
}
|
||||
expect_exact: "function f(a,b){return a-- >>=b}";
|
||||
}
|
||||
|
||||
html_comment_in_zero_fill_right_shift_assign: {
|
||||
input: {
|
||||
// Note: illegal javascript
|
||||
function f(a, b) { return a-- >>>= b; }
|
||||
}
|
||||
expect_exact: "function f(a,b){return a-- >>>=b}";
|
||||
}
|
||||
|
||||
html_comment_in_string_literal: {
|
||||
input: {
|
||||
function f() { return "<!--HTML-->comment in<!--string literal-->"; }
|
||||
}
|
||||
expect_exact: 'function f(){return"\\x3c!--HTML--\\x3ecomment in\\x3c!--string literal--\\x3e"}';
|
||||
}
|
||||
@@ -9,3 +9,50 @@ keep_name_of_setter: {
|
||||
input: { a = { set foo () {} } }
|
||||
expect: { a = { set foo () {} } }
|
||||
}
|
||||
|
||||
setter_with_operator_keys: {
|
||||
input: {
|
||||
var tokenCodes = {
|
||||
get instanceof(){
|
||||
return test0;
|
||||
},
|
||||
set instanceof(value){
|
||||
test0 = value;
|
||||
},
|
||||
set typeof(value){
|
||||
test1 = value;
|
||||
},
|
||||
get typeof(){
|
||||
return test1;
|
||||
},
|
||||
set else(value){
|
||||
test2 = value;
|
||||
},
|
||||
get else(){
|
||||
return test2;
|
||||
}
|
||||
};
|
||||
}
|
||||
expect: {
|
||||
var tokenCodes = {
|
||||
get instanceof(){
|
||||
return test0;
|
||||
},
|
||||
set instanceof(value){
|
||||
test0 = value;
|
||||
},
|
||||
set typeof(value){
|
||||
test1 = value;
|
||||
},
|
||||
get typeof(){
|
||||
return test1;
|
||||
},
|
||||
set else(value){
|
||||
test2 = value;
|
||||
},
|
||||
get else(){
|
||||
return test2;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
11
test/compress/issue-208.js
Normal file
11
test/compress/issue-208.js
Normal file
@@ -0,0 +1,11 @@
|
||||
do_not_update_lhs: {
|
||||
options = { global_defs: { DEBUG: false } };
|
||||
input: { DEBUG = false; }
|
||||
expect: { DEBUG = false; }
|
||||
}
|
||||
|
||||
do_update_rhs: {
|
||||
options = { global_defs: { DEBUG: false } };
|
||||
input: { MY_DEBUG = DEBUG; }
|
||||
expect: { MY_DEBUG = false; }
|
||||
}
|
||||
25
test/compress/issue-597.js
Normal file
25
test/compress/issue-597.js
Normal file
@@ -0,0 +1,25 @@
|
||||
NaN_and_Infinity_must_have_parens: {
|
||||
options = {};
|
||||
input: {
|
||||
Infinity.toString();
|
||||
NaN.toString();
|
||||
}
|
||||
expect: {
|
||||
(1/0).toString();
|
||||
NaN.toString(); // transformation to 0/0 dropped
|
||||
}
|
||||
}
|
||||
|
||||
NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined: {
|
||||
options = {};
|
||||
input: {
|
||||
var Infinity, NaN;
|
||||
Infinity.toString();
|
||||
NaN.toString();
|
||||
}
|
||||
expect: {
|
||||
var Infinity, NaN;
|
||||
Infinity.toString();
|
||||
NaN.toString();
|
||||
}
|
||||
}
|
||||
21
test/compress/issue-611.js
Normal file
21
test/compress/issue-611.js
Normal file
@@ -0,0 +1,21 @@
|
||||
issue_611: {
|
||||
options = {
|
||||
sequences: true,
|
||||
side_effects: true
|
||||
};
|
||||
input: {
|
||||
define(function() {
|
||||
function fn() {}
|
||||
if (fn()) {
|
||||
fn();
|
||||
return void 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
define(function() {
|
||||
function fn(){}
|
||||
if (fn()) return void fn();
|
||||
});
|
||||
}
|
||||
}
|
||||
22
test/compress/issue-637.js
Normal file
22
test/compress/issue-637.js
Normal file
@@ -0,0 +1,22 @@
|
||||
wrongly_optimized: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
booleans: true,
|
||||
evaluate: true
|
||||
};
|
||||
input: {
|
||||
function func() {
|
||||
foo();
|
||||
}
|
||||
if (func() || true) {
|
||||
bar();
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function func() {
|
||||
foo();
|
||||
}
|
||||
// TODO: optimize to `func(), bar()`
|
||||
(func(), 0) || bar();
|
||||
}
|
||||
}
|
||||
37
test/compress/issue-747.js
Normal file
37
test/compress/issue-747.js
Normal file
@@ -0,0 +1,37 @@
|
||||
dont_reuse_prop: {
|
||||
mangle_props = {
|
||||
regex: /asd/
|
||||
};
|
||||
|
||||
input: {
|
||||
var obj = {};
|
||||
obj.a = 123;
|
||||
obj.asd = 256;
|
||||
console.log(obj.a);
|
||||
}
|
||||
expect: {
|
||||
var obj = {};
|
||||
obj.a = 123;
|
||||
obj.b = 256;
|
||||
console.log(obj.a);
|
||||
}
|
||||
}
|
||||
|
||||
unmangleable_props_should_always_be_reserved: {
|
||||
mangle_props = {
|
||||
regex: /asd/
|
||||
};
|
||||
|
||||
input: {
|
||||
var obj = {};
|
||||
obj.asd = 256;
|
||||
obj.a = 123;
|
||||
console.log(obj.a);
|
||||
}
|
||||
expect: {
|
||||
var obj = {};
|
||||
obj.b = 256;
|
||||
obj.a = 123;
|
||||
console.log(obj.a);
|
||||
}
|
||||
}
|
||||
29
test/compress/issue-751.js
Normal file
29
test/compress/issue-751.js
Normal file
@@ -0,0 +1,29 @@
|
||||
negate_booleans_1: {
|
||||
options = {
|
||||
comparisons: true
|
||||
};
|
||||
input: {
|
||||
var a = !a || !b || !c || !d || !e || !f;
|
||||
}
|
||||
expect: {
|
||||
var a = !(a && b && c && d && e && f);
|
||||
}
|
||||
}
|
||||
|
||||
negate_booleans_2: {
|
||||
options = {
|
||||
comparisons: true
|
||||
};
|
||||
input: {
|
||||
var match = !x && // should not touch this one
|
||||
(!z || c) &&
|
||||
(!k || d) &&
|
||||
the_stuff();
|
||||
}
|
||||
expect: {
|
||||
var match = !x &&
|
||||
(!z || c) &&
|
||||
(!k || d) &&
|
||||
the_stuff();
|
||||
}
|
||||
}
|
||||
27
test/compress/issue-782.js
Normal file
27
test/compress/issue-782.js
Normal file
@@ -0,0 +1,27 @@
|
||||
remove_redundant_sequence_items: {
|
||||
options = { side_effects: true };
|
||||
input: {
|
||||
(0, 1, eval)();
|
||||
(0, 1, logThis)();
|
||||
(0, 1, _decorators.logThis)();
|
||||
}
|
||||
expect: {
|
||||
(0, eval)();
|
||||
logThis();
|
||||
(0, _decorators.logThis)();
|
||||
}
|
||||
}
|
||||
|
||||
dont_remove_this_binding_sequence: {
|
||||
options = { side_effects: true };
|
||||
input: {
|
||||
(0, eval)();
|
||||
(0, logThis)();
|
||||
(0, _decorators.logThis)();
|
||||
}
|
||||
expect: {
|
||||
(0, eval)();
|
||||
logThis();
|
||||
(0, _decorators.logThis)();
|
||||
}
|
||||
}
|
||||
32
test/compress/issue-892.js
Normal file
32
test/compress/issue-892.js
Normal file
@@ -0,0 +1,32 @@
|
||||
dont_mangle_arguments: {
|
||||
mangle = {
|
||||
};
|
||||
options = {
|
||||
sequences : true,
|
||||
properties : true,
|
||||
dead_code : true,
|
||||
drop_debugger : true,
|
||||
conditionals : true,
|
||||
comparisons : true,
|
||||
evaluate : true,
|
||||
booleans : true,
|
||||
loops : true,
|
||||
unused : true,
|
||||
hoist_funs : true,
|
||||
keep_fargs : true,
|
||||
keep_fnames : false,
|
||||
hoist_vars : true,
|
||||
if_return : true,
|
||||
join_vars : true,
|
||||
cascade : true,
|
||||
side_effects : true,
|
||||
negate_iife : false
|
||||
};
|
||||
input: {
|
||||
(function(){
|
||||
var arguments = arguments, not_arguments = 9;
|
||||
console.log(not_arguments, arguments);
|
||||
})(5,6,7);
|
||||
}
|
||||
expect_exact: "(function(){var arguments=arguments,o=9;console.log(o,arguments)})(5,6,7);"
|
||||
}
|
||||
20
test/compress/issue-913.js
Normal file
20
test/compress/issue-913.js
Normal file
@@ -0,0 +1,20 @@
|
||||
keep_var_for_in: {
|
||||
options = {
|
||||
hoist_vars: true,
|
||||
unused: true
|
||||
};
|
||||
input: {
|
||||
(function(obj){
|
||||
var foo = 5;
|
||||
for (var i in obj)
|
||||
return foo;
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function(obj){
|
||||
var i, foo = 5;
|
||||
for (i in obj)
|
||||
return foo;
|
||||
})();
|
||||
}
|
||||
}
|
||||
96
test/compress/issue-973.js
Normal file
96
test/compress/issue-973.js
Normal file
@@ -0,0 +1,96 @@
|
||||
this_binding_conditionals: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate : true
|
||||
};
|
||||
input: {
|
||||
(1 && a)();
|
||||
(0 || a)();
|
||||
(0 || 1 && a)();
|
||||
(1 ? a : 0)();
|
||||
|
||||
(1 && a.b)();
|
||||
(0 || a.b)();
|
||||
(0 || 1 && a.b)();
|
||||
(1 ? a.b : 0)();
|
||||
|
||||
(1 && a[b])();
|
||||
(0 || a[b])();
|
||||
(0 || 1 && a[b])();
|
||||
(1 ? a[b] : 0)();
|
||||
|
||||
(1 && eval)();
|
||||
(0 || eval)();
|
||||
(0 || 1 && eval)();
|
||||
(1 ? eval : 0)();
|
||||
}
|
||||
expect: {
|
||||
a();
|
||||
a();
|
||||
a();
|
||||
a();
|
||||
|
||||
(0, a.b)();
|
||||
(0, a.b)();
|
||||
(0, a.b)();
|
||||
(0, a.b)();
|
||||
|
||||
(0, a[b])();
|
||||
(0, a[b])();
|
||||
(0, a[b])();
|
||||
(0, a[b])();
|
||||
|
||||
(0, eval)();
|
||||
(0, eval)();
|
||||
(0, eval)();
|
||||
(0, eval)();
|
||||
}
|
||||
}
|
||||
|
||||
this_binding_collapse_vars: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
};
|
||||
input: {
|
||||
var c = a; c();
|
||||
var d = a.b; d();
|
||||
var e = eval; e();
|
||||
}
|
||||
expect: {
|
||||
a();
|
||||
(0, a.b)();
|
||||
(0, eval)();
|
||||
}
|
||||
}
|
||||
|
||||
this_binding_side_effects: {
|
||||
options = {
|
||||
side_effects : true
|
||||
};
|
||||
input: {
|
||||
(function (foo) {
|
||||
(0, foo)();
|
||||
(0, foo.bar)();
|
||||
(0, eval)('console.log(foo);');
|
||||
}());
|
||||
(function (foo) {
|
||||
var eval = console;
|
||||
(0, foo)();
|
||||
(0, foo.bar)();
|
||||
(0, eval)('console.log(foo);');
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
(function (foo) {
|
||||
foo();
|
||||
(0, foo.bar)();
|
||||
(0, eval)('console.log(foo);');
|
||||
}());
|
||||
(function (foo) {
|
||||
var eval = console;
|
||||
foo();
|
||||
(0, foo.bar)();
|
||||
(0, eval)('console.log(foo);');
|
||||
}());
|
||||
}
|
||||
}
|
||||
88
test/compress/issue-976.js
Normal file
88
test/compress/issue-976.js
Normal file
@@ -0,0 +1,88 @@
|
||||
eval_collapse_vars: {
|
||||
options = {
|
||||
collapse_vars:true, sequences:false, properties:true, dead_code:true, conditionals:true,
|
||||
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
|
||||
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
|
||||
};
|
||||
input: {
|
||||
function f1() {
|
||||
var e = 7;
|
||||
var s = "abcdef";
|
||||
var i = 2;
|
||||
var eval = console.log.bind(console);
|
||||
var x = s.charAt(i++);
|
||||
var y = s.charAt(i++);
|
||||
var z = s.charAt(i++);
|
||||
eval(x, y, z, e);
|
||||
}
|
||||
function p1() { var a = foo(), b = bar(), eval = baz(); return a + b + eval; }
|
||||
function p2() { var a = foo(), b = bar(), eval = baz; return a + b + eval(); }
|
||||
(function f2(eval) {
|
||||
var a = 2;
|
||||
console.log(a - 5);
|
||||
eval("console.log(a);");
|
||||
})(eval);
|
||||
}
|
||||
expect: {
|
||||
function f1() {
|
||||
var e = 7,
|
||||
s = "abcdef",
|
||||
i = 2,
|
||||
eval = console.log.bind(console),
|
||||
x = s.charAt(i++),
|
||||
y = s.charAt(i++),
|
||||
z = s.charAt(i++);
|
||||
eval(x, y, z, e);
|
||||
}
|
||||
function p1() { return foo() + bar() + baz(); }
|
||||
function p2() { var a = foo(), b = bar(), eval = baz; return a + b + eval(); }
|
||||
(function f2(eval) {
|
||||
var a = 2;
|
||||
console.log(a - 5);
|
||||
eval("console.log(a);");
|
||||
})(eval);
|
||||
}
|
||||
}
|
||||
|
||||
eval_unused: {
|
||||
options = { unused: true, keep_fargs: false };
|
||||
input: {
|
||||
function f1(a, eval, c, d, e) {
|
||||
return a('c') + eval;
|
||||
}
|
||||
function f2(a, b, c, d, e) {
|
||||
return a + eval('c');
|
||||
}
|
||||
function f3(a, eval, c, d, e) {
|
||||
return a + eval('c');
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f1(a, eval) {
|
||||
return a('c') + eval;
|
||||
}
|
||||
function f2(a, b, c, d, e) {
|
||||
return a + eval('c');
|
||||
}
|
||||
function f3(a, eval, c, d, e) {
|
||||
return a + eval('c');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eval_mangle: {
|
||||
mangle = {
|
||||
};
|
||||
input: {
|
||||
function f1(a, eval, c, d, e) {
|
||||
return a('c') + eval;
|
||||
}
|
||||
function f2(a, b, c, d, e) {
|
||||
return a + eval('c');
|
||||
}
|
||||
function f3(a, eval, c, d, e) {
|
||||
return a + eval('c');
|
||||
}
|
||||
}
|
||||
expect_exact: 'function f1(n,c,e,a,o){return n("c")+c}function f2(a,b,c,d,e){return a+eval("c")}function f3(a,eval,c,d,e){return a+eval("c")}'
|
||||
}
|
||||
89
test/compress/issue-979.js
Normal file
89
test/compress/issue-979.js
Normal file
@@ -0,0 +1,89 @@
|
||||
issue979_reported: {
|
||||
options = {
|
||||
sequences:true, properties:true, dead_code:true, conditionals:true,
|
||||
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
|
||||
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
|
||||
}
|
||||
input: {
|
||||
function f1() {
|
||||
if (a == 1 || b == 2) {
|
||||
foo();
|
||||
}
|
||||
}
|
||||
function f2() {
|
||||
if (!(a == 1 || b == 2)) {
|
||||
}
|
||||
else {
|
||||
foo();
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f1() {
|
||||
1!=a&&2!=b||foo();
|
||||
}
|
||||
function f2() {
|
||||
1!=a&&2!=b||foo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
issue979_test_negated_is_best: {
|
||||
options = {
|
||||
sequences:true, properties:true, dead_code:true, conditionals:true,
|
||||
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
|
||||
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
|
||||
}
|
||||
input: {
|
||||
function f3() {
|
||||
if (a == 1 | b == 2) {
|
||||
foo();
|
||||
}
|
||||
}
|
||||
function f4() {
|
||||
if (!(a == 1 | b == 2)) {
|
||||
}
|
||||
else {
|
||||
foo();
|
||||
}
|
||||
}
|
||||
function f5() {
|
||||
if (a == 1 && b == 2) {
|
||||
foo();
|
||||
}
|
||||
}
|
||||
function f6() {
|
||||
if (!(a == 1 && b == 2)) {
|
||||
}
|
||||
else {
|
||||
foo();
|
||||
}
|
||||
}
|
||||
function f7() {
|
||||
if (a == 1 || b == 2) {
|
||||
foo();
|
||||
}
|
||||
else {
|
||||
return bar();
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f3() {
|
||||
1==a|2==b&&foo();
|
||||
}
|
||||
function f4() {
|
||||
1==a|2==b&&foo();
|
||||
}
|
||||
function f5() {
|
||||
1==a&&2==b&&foo();
|
||||
}
|
||||
function f6() {
|
||||
1!=a||2!=b||foo();
|
||||
}
|
||||
function f7() {
|
||||
return 1!=a&&2!=b?bar():void foo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,3 +121,27 @@ drop_if_else_break_4: {
|
||||
for (; bar() && (x(), y(), foo());) baz(), z(), k();
|
||||
}
|
||||
}
|
||||
|
||||
parse_do_while_with_semicolon: {
|
||||
options = { loops: false };
|
||||
input: {
|
||||
do {
|
||||
x();
|
||||
} while (false);y()
|
||||
}
|
||||
expect: {
|
||||
do x(); while (false);y();
|
||||
}
|
||||
}
|
||||
|
||||
parse_do_while_without_semicolon: {
|
||||
options = { loops: false };
|
||||
input: {
|
||||
do {
|
||||
x();
|
||||
} while (false)y()
|
||||
}
|
||||
expect: {
|
||||
do x(); while (false);y();
|
||||
}
|
||||
}
|
||||
12
test/compress/new.js
Normal file
12
test/compress/new.js
Normal file
@@ -0,0 +1,12 @@
|
||||
new_statement: {
|
||||
input: {
|
||||
new x(1);
|
||||
new x(1)(2);
|
||||
new x(1)(2)(3);
|
||||
new new x(1);
|
||||
new new x(1)(2);
|
||||
new (new x(1))(2);
|
||||
(new new x(1))(2);
|
||||
}
|
||||
expect_exact: "new x(1);new x(1)(2);new x(1)(2)(3);new new x(1);new new x(1)(2);new new x(1)(2);(new new x(1))(2);"
|
||||
}
|
||||
@@ -52,3 +52,23 @@ dot_properties_es5: {
|
||||
a[""] = "whitespace";
|
||||
}
|
||||
}
|
||||
|
||||
evaluate_length: {
|
||||
options = {
|
||||
properties: true,
|
||||
unsafe: true,
|
||||
evaluate: true
|
||||
};
|
||||
input: {
|
||||
a = "foo".length;
|
||||
a = ("foo" + "bar")["len" + "gth"];
|
||||
a = b.length;
|
||||
a = ("foo" + b).length;
|
||||
}
|
||||
expect: {
|
||||
a = 3;
|
||||
a = 6;
|
||||
a = b.length;
|
||||
a = ("foo" + b).length;
|
||||
}
|
||||
}
|
||||
|
||||
124
test/compress/return_undefined.js
Normal file
124
test/compress/return_undefined.js
Normal file
@@ -0,0 +1,124 @@
|
||||
return_undefined: {
|
||||
options = {
|
||||
sequences : false,
|
||||
if_return : true,
|
||||
evaluate : true,
|
||||
dead_code : true,
|
||||
conditionals : true,
|
||||
comparisons : true,
|
||||
booleans : true,
|
||||
unused : true,
|
||||
side_effects : true,
|
||||
properties : true,
|
||||
drop_debugger : true,
|
||||
loops : true,
|
||||
hoist_funs : true,
|
||||
keep_fargs : true,
|
||||
keep_fnames : false,
|
||||
hoist_vars : true,
|
||||
join_vars : true,
|
||||
cascade : true,
|
||||
negate_iife : true
|
||||
};
|
||||
input: {
|
||||
function f0() {
|
||||
}
|
||||
function f1() {
|
||||
return undefined;
|
||||
}
|
||||
function f2() {
|
||||
return void 0;
|
||||
}
|
||||
function f3() {
|
||||
return void 123;
|
||||
}
|
||||
function f4() {
|
||||
return;
|
||||
}
|
||||
function f5(a, b) {
|
||||
console.log(a, b);
|
||||
baz(a);
|
||||
return;
|
||||
}
|
||||
function f6(a, b) {
|
||||
console.log(a, b);
|
||||
if (a) {
|
||||
foo(b);
|
||||
baz(a);
|
||||
return a + b;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function f7(a, b) {
|
||||
console.log(a, b);
|
||||
if (a) {
|
||||
foo(b);
|
||||
baz(a);
|
||||
return void 0;
|
||||
}
|
||||
return a + b;
|
||||
}
|
||||
function f8(a, b) {
|
||||
foo(a);
|
||||
bar(b);
|
||||
return void 0;
|
||||
}
|
||||
function f9(a, b) {
|
||||
foo(a);
|
||||
bar(b);
|
||||
return undefined;
|
||||
}
|
||||
function f10() {
|
||||
return false;
|
||||
}
|
||||
function f11() {
|
||||
return null;
|
||||
}
|
||||
function f12() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f0() {}
|
||||
function f1() {}
|
||||
function f2() {}
|
||||
function f3() {}
|
||||
function f4() {}
|
||||
function f5(a, b) {
|
||||
console.log(a, b);
|
||||
baz(a);
|
||||
}
|
||||
function f6(a, b) {
|
||||
console.log(a, b);
|
||||
if (a) {
|
||||
foo(b);
|
||||
baz(a);
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
function f7(a, b) {
|
||||
console.log(a, b);
|
||||
if (!a)
|
||||
return a + b;
|
||||
foo(b);
|
||||
baz(a);
|
||||
}
|
||||
function f8(a, b) {
|
||||
foo(a);
|
||||
bar(b);
|
||||
}
|
||||
function f9(a, b) {
|
||||
foo(a);
|
||||
bar(b);
|
||||
}
|
||||
function f10() {
|
||||
return !1;
|
||||
}
|
||||
function f11() {
|
||||
return null;
|
||||
}
|
||||
function f12() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
test/compress/screw-ie8.js
Normal file
18
test/compress/screw-ie8.js
Normal file
@@ -0,0 +1,18 @@
|
||||
do_screw: {
|
||||
options = { screw_ie8: true };
|
||||
beautify = {
|
||||
screw_ie8: true,
|
||||
ascii_only: true
|
||||
};
|
||||
|
||||
input: f("\v");
|
||||
expect_exact: 'f("\\v");';
|
||||
}
|
||||
|
||||
dont_screw: {
|
||||
options = { screw_ie8: false };
|
||||
beautify = { screw_ie8: false, ascii_only: true };
|
||||
|
||||
input: f("\v");
|
||||
expect_exact: 'f("\\x0B");';
|
||||
}
|
||||
@@ -91,9 +91,11 @@ make_sequences_4: {
|
||||
lift_sequences_1: {
|
||||
options = { sequences: true };
|
||||
input: {
|
||||
var foo, x, y, bar;
|
||||
foo = !(x(), y(), bar());
|
||||
}
|
||||
expect: {
|
||||
var foo, x, y, bar;
|
||||
x(), y(), foo = !bar();
|
||||
}
|
||||
}
|
||||
@@ -101,10 +103,12 @@ lift_sequences_1: {
|
||||
lift_sequences_2: {
|
||||
options = { sequences: true, evaluate: true };
|
||||
input: {
|
||||
var foo, bar;
|
||||
foo.x = (foo = {}, 10);
|
||||
bar = (bar = {}, 10);
|
||||
}
|
||||
expect: {
|
||||
var foo, bar;
|
||||
foo.x = (foo = {}, 10),
|
||||
bar = {}, bar = 10;
|
||||
}
|
||||
@@ -113,9 +117,11 @@ lift_sequences_2: {
|
||||
lift_sequences_3: {
|
||||
options = { sequences: true, conditionals: true };
|
||||
input: {
|
||||
var x, foo, bar, baz;
|
||||
x = (foo(), bar(), baz()) ? 10 : 20;
|
||||
}
|
||||
expect: {
|
||||
var x, foo, bar, baz;
|
||||
foo(), bar(), x = baz() ? 10 : 20;
|
||||
}
|
||||
}
|
||||
@@ -123,9 +129,11 @@ lift_sequences_3: {
|
||||
lift_sequences_4: {
|
||||
options = { side_effects: true };
|
||||
input: {
|
||||
var x, foo, bar, baz;
|
||||
x = (foo, bar, baz);
|
||||
}
|
||||
expect: {
|
||||
var x, foo, bar, baz;
|
||||
x = baz;
|
||||
}
|
||||
}
|
||||
|
||||
17
test/compress/unicode.js
Normal file
17
test/compress/unicode.js
Normal file
@@ -0,0 +1,17 @@
|
||||
unicode_parse_variables: {
|
||||
options = {};
|
||||
input: {
|
||||
var a = {};
|
||||
a.你好 = 456;
|
||||
|
||||
var ↂωↂ = 123;
|
||||
var l০ = 3; // 2nd char is a unicode digit
|
||||
}
|
||||
expect: {
|
||||
var a = {};
|
||||
a.你好 = 456;
|
||||
|
||||
var ↂωↂ = 123;
|
||||
var l০ = 3;
|
||||
}
|
||||
}
|
||||
29
test/mocha.js
Normal file
29
test/mocha.js
Normal file
@@ -0,0 +1,29 @@
|
||||
var Mocha = require('mocha'),
|
||||
fs = require('fs'),
|
||||
path = require('path');
|
||||
|
||||
// Instantiate a Mocha instance.
|
||||
var mocha = new Mocha({});
|
||||
|
||||
var testDir = __dirname + '/mocha/';
|
||||
|
||||
// Add each .js file to the mocha instance
|
||||
fs.readdirSync(testDir).filter(function(file){
|
||||
// Only keep the .js files
|
||||
return file.substr(-3) === '.js';
|
||||
|
||||
}).forEach(function(file){
|
||||
mocha.addFile(
|
||||
path.join(testDir, file)
|
||||
);
|
||||
});
|
||||
|
||||
module.exports = function() {
|
||||
mocha.run(function(failures) {
|
||||
if (failures !== 0) {
|
||||
process.on('exit', function () {
|
||||
process.exit(failures);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
22
test/mocha/arguments.js
Normal file
22
test/mocha/arguments.js
Normal file
@@ -0,0 +1,22 @@
|
||||
var UglifyJS = require('../../');
|
||||
var assert = require("assert");
|
||||
|
||||
describe("arguments", function() {
|
||||
it("Should known that arguments in functions are local scoped", function() {
|
||||
var ast = UglifyJS.parse("var arguments; var f = function() {arguments.length}");
|
||||
ast.figure_out_scope();
|
||||
|
||||
// Test scope of `var arguments`
|
||||
assert.strictEqual(ast.find_variable("arguments").global, true);
|
||||
|
||||
// Select arguments symbol in function
|
||||
var symbol = ast.body[1].definitions[0].value.find_variable("arguments");
|
||||
|
||||
assert.strictEqual(symbol.global, false);
|
||||
assert.strictEqual(symbol.scope, ast. // From ast
|
||||
body[1]. // Select 2nd statement (equals to `var f ...`)
|
||||
definitions[0]. // First definition of selected statement
|
||||
value // Select function as scope
|
||||
);
|
||||
});
|
||||
});
|
||||
45
test/mocha/comment-filter.js
Normal file
45
test/mocha/comment-filter.js
Normal file
@@ -0,0 +1,45 @@
|
||||
var UglifyJS = require('../../');
|
||||
var assert = require("assert");
|
||||
|
||||
describe("comment filters", function() {
|
||||
it("Should be able to filter comments by passing regex", function() {
|
||||
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
|
||||
assert.strictEqual(ast.print_to_string({comments: /^!/}), "/*!test1*/\n//!test3\n//!test6\n//!test8\n");
|
||||
});
|
||||
|
||||
it("Should be able to filter comments by passing a function", function() {
|
||||
var ast = UglifyJS.parse("/*TEST 123*/\n//An other comment\n//8 chars.");
|
||||
var f = function(node, comment) {
|
||||
return comment.value.length === 8;
|
||||
};
|
||||
|
||||
assert.strictEqual(ast.print_to_string({comments: f}), "/*TEST 123*/\n//8 chars.\n");
|
||||
});
|
||||
|
||||
it("Should be able to get the comment and comment type when using a function", function() {
|
||||
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
|
||||
var f = function(node, comment) {
|
||||
return comment.type == "comment1" || comment.type == "comment3";
|
||||
};
|
||||
|
||||
assert.strictEqual(ast.print_to_string({comments: f}), "//!test3\n//test4\n//test5\n//!test6\n");
|
||||
});
|
||||
|
||||
it("Should be able to filter comments by passing a boolean", function() {
|
||||
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
|
||||
|
||||
assert.strictEqual(ast.print_to_string({comments: true}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8\n");
|
||||
assert.strictEqual(ast.print_to_string({comments: false}), "");
|
||||
});
|
||||
|
||||
it("Should never be able to filter comment5 (shebangs)", function() {
|
||||
var ast = UglifyJS.parse("#!Random comment\n//test1\n/*test2*/");
|
||||
var f = function(node, comment) {
|
||||
assert.strictEqual(comment.type === "comment5", false);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
assert.strictEqual(ast.print_to_string({comments: f}), "#!Random comment\n//test1\n/*test2*/\n");
|
||||
});
|
||||
});
|
||||
89
test/mocha/getter-setter.js
Normal file
89
test/mocha/getter-setter.js
Normal file
@@ -0,0 +1,89 @@
|
||||
var UglifyJS = require('../../');
|
||||
var assert = require("assert");
|
||||
|
||||
describe("Getters and setters", function() {
|
||||
it("Should not accept operator symbols as getter/setter name", function() {
|
||||
var illegalOperators = [
|
||||
"++",
|
||||
"--",
|
||||
"+",
|
||||
"-",
|
||||
"!",
|
||||
"~",
|
||||
"&",
|
||||
"|",
|
||||
"^",
|
||||
"*",
|
||||
"/",
|
||||
"%",
|
||||
">>",
|
||||
"<<",
|
||||
">>>",
|
||||
"<",
|
||||
">",
|
||||
"<=",
|
||||
">=",
|
||||
"==",
|
||||
"===",
|
||||
"!=",
|
||||
"!==",
|
||||
"?",
|
||||
"=",
|
||||
"+=",
|
||||
"-=",
|
||||
"/=",
|
||||
"*=",
|
||||
"%=",
|
||||
">>=",
|
||||
"<<=",
|
||||
">>>=",
|
||||
"|=",
|
||||
"^=",
|
||||
"&=",
|
||||
"&&",
|
||||
"||"
|
||||
];
|
||||
var generator = function() {
|
||||
var results = [];
|
||||
|
||||
for (var i in illegalOperators) {
|
||||
results.push({
|
||||
code: "var obj = { get " + illegalOperators[i] + "() { return test; }};",
|
||||
operator: illegalOperators[i],
|
||||
method: "get"
|
||||
});
|
||||
results.push({
|
||||
code: "var obj = { set " + illegalOperators[i] + "(value) { test = value}};",
|
||||
operator: illegalOperators[i],
|
||||
method: "set"
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
var testCase = function(data) {
|
||||
return function() {
|
||||
UglifyJS.parse(data.code);
|
||||
};
|
||||
};
|
||||
|
||||
var fail = function(data) {
|
||||
return function (e) {
|
||||
return e instanceof UglifyJS.JS_Parse_Error &&
|
||||
e.message === "Invalid getter/setter name: " + data.operator;
|
||||
};
|
||||
};
|
||||
|
||||
var errorMessage = function(data) {
|
||||
return "Expected but didn't get a syntax error while parsing following line:\n" + data.code;
|
||||
};
|
||||
|
||||
var tests = generator();
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
var test = tests[i];
|
||||
assert.throws(testCase(test), fail(test), errorMessage(test));
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
34
test/mocha/string-literal.js
Normal file
34
test/mocha/string-literal.js
Normal file
@@ -0,0 +1,34 @@
|
||||
var UglifyJS = require('../../');
|
||||
var assert = require("assert");
|
||||
|
||||
describe("String literals", function() {
|
||||
it("Should throw syntax error if a string literal contains a newline", function() {
|
||||
var inputs = [
|
||||
"'\n'",
|
||||
"'\r'",
|
||||
'"\r\n"',
|
||||
"'\u2028'",
|
||||
'"\u2029"'
|
||||
];
|
||||
|
||||
var test = function(input) {
|
||||
return function() {
|
||||
var ast = UglifyJS.parse(input);
|
||||
};
|
||||
};
|
||||
|
||||
var error = function(e) {
|
||||
return e instanceof UglifyJS.JS_Parse_Error &&
|
||||
e.message === "Unterminated string constant";
|
||||
};
|
||||
|
||||
for (var input in inputs) {
|
||||
assert.throws(test(inputs[input]), error);
|
||||
}
|
||||
});
|
||||
|
||||
it("Should not throw syntax error if a string has a line continuation", function() {
|
||||
var output = UglifyJS.parse('var a = "a\\\nb";').print_to_string();
|
||||
assert.equal(output, 'var a="ab";');
|
||||
});
|
||||
});
|
||||
103
test/mozilla-ast.js
Normal file
103
test/mozilla-ast.js
Normal file
@@ -0,0 +1,103 @@
|
||||
// Testing UglifyJS <-> SpiderMonkey AST conversion
|
||||
// through generative testing.
|
||||
|
||||
var UglifyJS = require(".."),
|
||||
escodegen = require("escodegen"),
|
||||
esfuzz = require("esfuzz"),
|
||||
estraverse = require("estraverse"),
|
||||
prefix = Array(20).join("\b") + " ";
|
||||
|
||||
// Normalizes input AST for UglifyJS in order to get correct comparison.
|
||||
|
||||
function normalizeInput(ast) {
|
||||
return estraverse.replace(ast, {
|
||||
enter: function(node, parent) {
|
||||
switch (node.type) {
|
||||
// Internally mark all the properties with semi-standard type "Property".
|
||||
case "ObjectExpression":
|
||||
node.properties.forEach(function (property) {
|
||||
property.type = "Property";
|
||||
});
|
||||
break;
|
||||
|
||||
// Since UglifyJS doesn"t recognize different types of property keys,
|
||||
// decision on SpiderMonkey node type is based on check whether key
|
||||
// can be valid identifier or not - so we do in input AST.
|
||||
case "Property":
|
||||
var key = node.key;
|
||||
if (key.type === "Literal" && typeof key.value === "string" && UglifyJS.is_identifier(key.value)) {
|
||||
node.key = {
|
||||
type: "Identifier",
|
||||
name: key.value
|
||||
};
|
||||
} else if (key.type === "Identifier" && !UglifyJS.is_identifier(key.name)) {
|
||||
node.key = {
|
||||
type: "Literal",
|
||||
value: key.name
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
// UglifyJS internally flattens all the expression sequences - either
|
||||
// to one element (if sequence contains only one element) or flat list.
|
||||
case "SequenceExpression":
|
||||
node.expressions = node.expressions.reduce(function flatten(list, expr) {
|
||||
return list.concat(expr.type === "SequenceExpression" ? expr.expressions.reduce(flatten, []) : [expr]);
|
||||
}, []);
|
||||
if (node.expressions.length === 1) {
|
||||
return node.expressions[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = function(options) {
|
||||
console.log("--- UglifyJS <-> Mozilla AST conversion");
|
||||
|
||||
for (var counter = 0; counter < options.iterations; counter++) {
|
||||
process.stdout.write(prefix + counter + "/" + options.iterations);
|
||||
|
||||
var ast1 = normalizeInput(esfuzz.generate({
|
||||
maxDepth: options.maxDepth
|
||||
}));
|
||||
|
||||
var ast2 =
|
||||
UglifyJS
|
||||
.AST_Node
|
||||
.from_mozilla_ast(ast1)
|
||||
.to_mozilla_ast();
|
||||
|
||||
var astPair = [
|
||||
{name: 'expected', value: ast1},
|
||||
{name: 'actual', value: ast2}
|
||||
];
|
||||
|
||||
var jsPair = astPair.map(function(item) {
|
||||
return {
|
||||
name: item.name,
|
||||
value: escodegen.generate(item.value)
|
||||
}
|
||||
});
|
||||
|
||||
if (jsPair[0].value !== jsPair[1].value) {
|
||||
var fs = require("fs");
|
||||
var acorn = require("acorn");
|
||||
|
||||
fs.existsSync("tmp") || fs.mkdirSync("tmp");
|
||||
|
||||
jsPair.forEach(function (item) {
|
||||
var fileName = "tmp/dump_" + item.name;
|
||||
var ast = acorn.parse(item.value);
|
||||
fs.writeFileSync(fileName + ".js", item.value);
|
||||
fs.writeFileSync(fileName + ".json", JSON.stringify(ast, null, 2));
|
||||
});
|
||||
|
||||
process.stdout.write("\n");
|
||||
throw new Error("Got different outputs, check out tmp/dump_*.{js,json} for codes and ASTs.");
|
||||
}
|
||||
}
|
||||
|
||||
process.stdout.write(prefix + "Probability of error is less than " + (100 / options.iterations) + "%, stopping.\n");
|
||||
};
|
||||
@@ -4,7 +4,6 @@ var U = require("../tools/node");
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
var assert = require("assert");
|
||||
var sys = require("util");
|
||||
|
||||
var tests_dir = path.dirname(module.filename);
|
||||
var failures = 0;
|
||||
@@ -12,11 +11,23 @@ var failed_files = {};
|
||||
|
||||
run_compress_tests();
|
||||
if (failures) {
|
||||
sys.error("\n!!! Failed " + failures + " test cases.");
|
||||
sys.error("!!! " + Object.keys(failed_files).join(", "));
|
||||
console.error("\n!!! Failed " + failures + " test cases.");
|
||||
console.error("!!! " + Object.keys(failed_files).join(", "));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var mocha_tests = require("./mocha.js");
|
||||
mocha_tests();
|
||||
|
||||
var run_sourcemaps_tests = require('./sourcemaps');
|
||||
run_sourcemaps_tests();
|
||||
|
||||
var run_ast_conversion_tests = require("./mozilla-ast");
|
||||
|
||||
run_ast_conversion_tests({
|
||||
iterations: 1000
|
||||
});
|
||||
|
||||
/* -----[ utils ]----- */
|
||||
|
||||
function tmpl() {
|
||||
@@ -25,7 +36,7 @@ function tmpl() {
|
||||
|
||||
function log() {
|
||||
var txt = tmpl.apply(this, arguments);
|
||||
sys.puts(txt);
|
||||
console.log("%s", txt);
|
||||
}
|
||||
|
||||
function log_directory(dir) {
|
||||
@@ -78,12 +89,25 @@ function run_compress_tests() {
|
||||
warnings: false
|
||||
});
|
||||
var cmp = new U.Compressor(options, true);
|
||||
var expect = make_code(as_toplevel(test.expect), false);
|
||||
var output_options = test.beautify || {};
|
||||
var expect;
|
||||
if (test.expect) {
|
||||
expect = make_code(as_toplevel(test.expect), output_options);
|
||||
} else {
|
||||
expect = test.expect_exact;
|
||||
}
|
||||
var input = as_toplevel(test.input);
|
||||
var input_code = make_code(test.input);
|
||||
var input_code = make_code(test.input, { beautify: true });
|
||||
if (test.mangle_props) {
|
||||
input = U.mangle_properties(input, test.mangle_props);
|
||||
}
|
||||
var output = input.transform(cmp);
|
||||
output.figure_out_scope();
|
||||
output = make_code(output, false);
|
||||
if (test.mangle) {
|
||||
output.compute_char_frequency(test.mangle);
|
||||
output.mangle_names(test.mangle);
|
||||
}
|
||||
output = make_code(output, output_options);
|
||||
if (expect != output) {
|
||||
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
|
||||
input: input_code,
|
||||
@@ -127,7 +151,7 @@ function parse_test(file) {
|
||||
file: file,
|
||||
line: node.start.line,
|
||||
col: node.start.col,
|
||||
code: make_code(node, false)
|
||||
code: make_code(node, { beautify: false })
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -144,7 +168,7 @@ function parse_test(file) {
|
||||
}
|
||||
if (node instanceof U.AST_LabeledStatement) {
|
||||
assert.ok(
|
||||
node.label.name == "input" || node.label.name == "expect",
|
||||
node.label.name == "input" || node.label.name == "expect" || node.label.name == "expect_exact",
|
||||
tmpl("Unsupported label {name} [{line},{col}]", {
|
||||
name: node.label.name,
|
||||
line: node.label.start.line,
|
||||
@@ -156,7 +180,16 @@ function parse_test(file) {
|
||||
if (stat.body.length == 1) stat = stat.body[0];
|
||||
else if (stat.body.length == 0) stat = new U.AST_EmptyStatement();
|
||||
}
|
||||
if (node.label.name === "expect_exact") {
|
||||
if (!(stat.TYPE === "SimpleStatement" && stat.body.TYPE === "String")) {
|
||||
throw new Error(
|
||||
"The value of the expect_exact clause should be a string, " +
|
||||
"like `expect_exact: \"some.exact.javascript;\"`");
|
||||
}
|
||||
test[node.label.name] = stat.body.start.value
|
||||
} else {
|
||||
test[node.label.name] = stat;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -165,15 +198,15 @@ function parse_test(file) {
|
||||
};
|
||||
}
|
||||
|
||||
function make_code(ast, beautify) {
|
||||
if (arguments.length == 1) beautify = true;
|
||||
var stream = U.OutputStream({ beautify: beautify });
|
||||
function make_code(ast, options) {
|
||||
options.inline_script = true;
|
||||
var stream = U.OutputStream(options);
|
||||
ast.print(stream);
|
||||
return stream.get();
|
||||
}
|
||||
|
||||
function evaluate(code) {
|
||||
if (code instanceof U.AST_Node)
|
||||
code = make_code(code);
|
||||
code = make_code(code, { beautify: true });
|
||||
return new Function("return(" + code + ")")();
|
||||
}
|
||||
|
||||
40
test/sourcemaps.js
Normal file
40
test/sourcemaps.js
Normal file
@@ -0,0 +1,40 @@
|
||||
var UglifyJS = require("..");
|
||||
var ok = require("assert");
|
||||
|
||||
module.exports = function () {
|
||||
console.log("--- Sourcemaps tests");
|
||||
|
||||
var basic = source_map([
|
||||
'var x = 1 + 1;'
|
||||
].join('\n'));
|
||||
|
||||
ok.equal(basic.version, 3);
|
||||
ok.deepEqual(basic.names, ['x']);
|
||||
|
||||
var issue836 = source_map([
|
||||
"({",
|
||||
" get enabled() {",
|
||||
" return 3;",
|
||||
" },",
|
||||
" set enabled(x) {",
|
||||
" ;",
|
||||
" }",
|
||||
"});",
|
||||
].join("\n"));
|
||||
|
||||
ok.deepEqual(issue836.names, ['enabled', 'x']);
|
||||
}
|
||||
|
||||
function source_map(js) {
|
||||
var source_map = UglifyJS.SourceMap();
|
||||
var stream = UglifyJS.OutputStream({ source_map: source_map });
|
||||
var parsed = UglifyJS.parse(js);
|
||||
parsed.print(stream);
|
||||
return JSON.parse(source_map.toString());
|
||||
}
|
||||
|
||||
// Run standalone
|
||||
if (module.parent === null) {
|
||||
module.exports();
|
||||
}
|
||||
|
||||
5603
tools/domprops.json
Normal file
5603
tools/domprops.json
Normal file
File diff suppressed because it is too large
Load Diff
18
tools/exports.js
Normal file
18
tools/exports.js
Normal file
@@ -0,0 +1,18 @@
|
||||
exports["Compressor"] = Compressor;
|
||||
exports["DefaultsError"] = DefaultsError;
|
||||
exports["Dictionary"] = Dictionary;
|
||||
exports["JS_Parse_Error"] = JS_Parse_Error;
|
||||
exports["MAP"] = MAP;
|
||||
exports["OutputStream"] = OutputStream;
|
||||
exports["SourceMap"] = SourceMap;
|
||||
exports["TreeTransformer"] = TreeTransformer;
|
||||
exports["TreeWalker"] = TreeWalker;
|
||||
exports["base54"] = base54;
|
||||
exports["defaults"] = defaults;
|
||||
exports["mangle_properties"] = mangle_properties;
|
||||
exports["merge"] = merge;
|
||||
exports["parse"] = parse;
|
||||
exports["push_uniq"] = push_uniq;
|
||||
exports["string_template"] = string_template;
|
||||
exports["is_identifier"] = is_identifier;
|
||||
exports["SymbolDef"] = SymbolDef;
|
||||
144
tools/node.js
144
tools/node.js
@@ -1,26 +1,5 @@
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
var vm = require("vm");
|
||||
var sys = require("util");
|
||||
|
||||
var UglifyJS = vm.createContext({
|
||||
sys : sys,
|
||||
console : console,
|
||||
MOZ_SourceMap : require("source-map")
|
||||
});
|
||||
|
||||
function load_global(file) {
|
||||
file = path.resolve(path.dirname(module.filename), file);
|
||||
try {
|
||||
var code = fs.readFileSync(file, "utf8");
|
||||
return vm.runInContext(code, UglifyJS, file);
|
||||
} catch(ex) {
|
||||
// XXX: in case of a syntax error, the message is kinda
|
||||
// useless. (no location information).
|
||||
sys.debug("ERROR in file: " + file + " / " + ex);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
var FILES = exports.FILES = [
|
||||
"../lib/utils.js",
|
||||
@@ -31,24 +10,26 @@ var FILES = exports.FILES = [
|
||||
"../lib/output.js",
|
||||
"../lib/compress.js",
|
||||
"../lib/sourcemap.js",
|
||||
"../lib/mozilla-ast.js"
|
||||
"../lib/mozilla-ast.js",
|
||||
"../lib/propmangle.js",
|
||||
"./exports.js",
|
||||
].map(function(file){
|
||||
return path.join(path.dirname(fs.realpathSync(__filename)), file);
|
||||
return fs.realpathSync(path.join(path.dirname(__filename), file));
|
||||
});
|
||||
|
||||
FILES.forEach(load_global);
|
||||
var UglifyJS = exports;
|
||||
|
||||
new Function("MOZ_SourceMap", "exports", FILES.map(function(file){
|
||||
return fs.readFileSync(file, "utf8");
|
||||
}).join("\n\n"))(
|
||||
require("source-map"),
|
||||
UglifyJS
|
||||
);
|
||||
|
||||
UglifyJS.AST_Node.warn_function = function(txt) {
|
||||
sys.error("WARN: " + txt);
|
||||
console.error("WARN: %s", txt);
|
||||
};
|
||||
|
||||
// XXX: perhaps we shouldn't export everything but heck, I'm lazy.
|
||||
for (var i in UglifyJS) {
|
||||
if (UglifyJS.hasOwnProperty(i)) {
|
||||
exports[i] = UglifyJS[i];
|
||||
}
|
||||
}
|
||||
|
||||
exports.minify = function(files, options) {
|
||||
options = UglifyJS.defaults(options, {
|
||||
spidermonkey : false,
|
||||
@@ -58,8 +39,11 @@ exports.minify = function(files, options) {
|
||||
fromString : false,
|
||||
warnings : false,
|
||||
mangle : {},
|
||||
mangleProperties : false,
|
||||
nameCache : null,
|
||||
output : null,
|
||||
compress : {}
|
||||
compress : {},
|
||||
parse : {}
|
||||
});
|
||||
UglifyJS.base54.reset();
|
||||
|
||||
@@ -72,17 +56,21 @@ exports.minify = function(files, options) {
|
||||
} else {
|
||||
if (typeof files == "string")
|
||||
files = [ files ];
|
||||
files.forEach(function(file){
|
||||
files.forEach(function(file, i){
|
||||
var code = options.fromString
|
||||
? file
|
||||
: fs.readFileSync(file, "utf8");
|
||||
sourcesContent[file] = code;
|
||||
toplevel = UglifyJS.parse(code, {
|
||||
filename: options.fromString ? "?" : file,
|
||||
toplevel: toplevel
|
||||
filename: options.fromString ? i : file,
|
||||
toplevel: toplevel,
|
||||
bare_returns: options.parse ? options.parse.bare_returns : undefined
|
||||
});
|
||||
});
|
||||
}
|
||||
if (options.wrap) {
|
||||
toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll);
|
||||
}
|
||||
|
||||
// 2. compress
|
||||
if (options.compress) {
|
||||
@@ -93,14 +81,21 @@ exports.minify = function(files, options) {
|
||||
toplevel = toplevel.transform(sq);
|
||||
}
|
||||
|
||||
// 3. mangle
|
||||
// 3. mangle properties
|
||||
if (options.mangleProperties || options.nameCache) {
|
||||
options.mangleProperties.cache = UglifyJS.readNameCache(options.nameCache, "props");
|
||||
toplevel = UglifyJS.mangle_properties(toplevel, options.mangleProperties);
|
||||
UglifyJS.writeNameCache(options.nameCache, "props", options.mangleProperties.cache);
|
||||
}
|
||||
|
||||
// 4. mangle
|
||||
if (options.mangle) {
|
||||
toplevel.figure_out_scope();
|
||||
toplevel.compute_char_frequency();
|
||||
toplevel.figure_out_scope(options.mangle);
|
||||
toplevel.compute_char_frequency(options.mangle);
|
||||
toplevel.mangle_names(options.mangle);
|
||||
}
|
||||
|
||||
// 4. output
|
||||
// 5. output
|
||||
var inMap = options.inSourceMap;
|
||||
var output = {};
|
||||
if (typeof options.inSourceMap == "string") {
|
||||
@@ -127,13 +122,18 @@ exports.minify = function(files, options) {
|
||||
var stream = UglifyJS.OutputStream(output);
|
||||
toplevel.print(stream);
|
||||
|
||||
if(options.outSourceMap){
|
||||
if (options.outSourceMap && "string" === typeof options.outSourceMap) {
|
||||
stream += "\n//# sourceMappingURL=" + options.outSourceMap;
|
||||
}
|
||||
|
||||
var source_map = output.source_map;
|
||||
if (source_map) {
|
||||
source_map = source_map + "";
|
||||
}
|
||||
|
||||
return {
|
||||
code : stream + "",
|
||||
map : output.source_map + ""
|
||||
map : source_map
|
||||
};
|
||||
};
|
||||
|
||||
@@ -185,3 +185,63 @@ exports.describe_ast = function() {
|
||||
doitem(UglifyJS.AST_Node);
|
||||
return out + "";
|
||||
};
|
||||
|
||||
function readReservedFile(filename, reserved) {
|
||||
if (!reserved) {
|
||||
reserved = { vars: [], props: [] };
|
||||
}
|
||||
var data = fs.readFileSync(filename, "utf8");
|
||||
data = JSON.parse(data);
|
||||
if (data.vars) {
|
||||
data.vars.forEach(function(name){
|
||||
UglifyJS.push_uniq(reserved.vars, name);
|
||||
});
|
||||
}
|
||||
if (data.props) {
|
||||
data.props.forEach(function(name){
|
||||
UglifyJS.push_uniq(reserved.props, name);
|
||||
});
|
||||
}
|
||||
return reserved;
|
||||
}
|
||||
|
||||
exports.readReservedFile = readReservedFile;
|
||||
|
||||
exports.readDefaultReservedFile = function(reserved) {
|
||||
return readReservedFile(path.join(__dirname, "domprops.json"), reserved);
|
||||
};
|
||||
|
||||
exports.readNameCache = function(filename, key) {
|
||||
var cache = null;
|
||||
if (filename) {
|
||||
try {
|
||||
var cache = fs.readFileSync(filename, "utf8");
|
||||
cache = JSON.parse(cache)[key];
|
||||
if (!cache) throw "init";
|
||||
cache.props = UglifyJS.Dictionary.fromObject(cache.props);
|
||||
} catch(ex) {
|
||||
cache = {
|
||||
cname: -1,
|
||||
props: new UglifyJS.Dictionary()
|
||||
};
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
};
|
||||
|
||||
exports.writeNameCache = function(filename, key, cache) {
|
||||
if (filename) {
|
||||
var data;
|
||||
try {
|
||||
data = fs.readFileSync(filename, "utf8");
|
||||
data = JSON.parse(data);
|
||||
} catch(ex) {
|
||||
data = {};
|
||||
}
|
||||
data[key] = {
|
||||
cname: cache.cname,
|
||||
props: cache.props.toObject()
|
||||
};
|
||||
fs.writeFileSync(filename, JSON.stringify(data, null, 2), "utf8");
|
||||
}
|
||||
};
|
||||
|
||||
61
tools/props.html
Normal file
61
tools/props.html
Normal file
@@ -0,0 +1,61 @@
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<script>(function(){
|
||||
var props = {};
|
||||
|
||||
function addObject(obj) {
|
||||
if (obj == null) return;
|
||||
try {
|
||||
Object.getOwnPropertyNames(obj).forEach(add);
|
||||
} catch(ex) {}
|
||||
if (obj.prototype) {
|
||||
Object.getOwnPropertyNames(obj.prototype).forEach(add);
|
||||
}
|
||||
if (typeof obj == "function") {
|
||||
try {
|
||||
Object.getOwnPropertyNames(new obj).forEach(add);
|
||||
} catch(ex) {}
|
||||
}
|
||||
}
|
||||
|
||||
function add(name) {
|
||||
props[name] = true;
|
||||
}
|
||||
|
||||
Object.getOwnPropertyNames(window).forEach(function(thing){
|
||||
addObject(window[thing]);
|
||||
});
|
||||
|
||||
try {
|
||||
addObject(new Event("click"));
|
||||
addObject(new Event("contextmenu"));
|
||||
addObject(new Event("mouseup"));
|
||||
addObject(new Event("mousedown"));
|
||||
addObject(new Event("keydown"));
|
||||
addObject(new Event("keypress"));
|
||||
addObject(new Event("keyup"));
|
||||
} catch(ex) {}
|
||||
|
||||
var ta = document.createElement("textarea");
|
||||
ta.style.width = "100%";
|
||||
ta.style.height = "20em";
|
||||
ta.style.boxSizing = "border-box";
|
||||
<!-- ta.value = Object.keys(props).sort(cmp).map(function(name){ -->
|
||||
<!-- return JSON.stringify(name); -->
|
||||
<!-- }).join(",\n"); -->
|
||||
ta.value = JSON.stringify({
|
||||
vars: [],
|
||||
props: Object.keys(props).sort(cmp)
|
||||
}, null, 2);
|
||||
document.body.appendChild(ta);
|
||||
|
||||
function cmp(a, b) {
|
||||
a = a.toLowerCase();
|
||||
b = b.toLowerCase();
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
}
|
||||
})();</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user