Compare commits

..

31 Commits

Author SHA1 Message Date
Alex Lam S.L
75e2748b16 v2.8.27 2017-05-19 17:44:50 +08:00
Alex Lam S.L
a7c987ad2b Merge pull request #1971 from alexlamsl/v2.8.27 2017-05-19 17:21:39 +08:00
Alex Lam S.L
957c54bc87 introduce unsafe_regexp (#1970)
fixes #1964
2017-05-19 10:10:09 +08:00
Alex Lam S.L
4cbf5a7821 v2.8.26 2017-05-16 07:13:58 +08:00
Alex Lam S.L
93d4224072 Merge pull request #1947 from alexlamsl/v2.8.26 2017-05-16 07:12:28 +08:00
Alex Lam S.L
6cd580dc23 fix parsing of property access after new line (#1944)
Account for comments when detecting property access in `tokenizer`.

fixes #1943
2017-05-16 06:34:10 +08:00
Alex Lam S.L
ecb63ad8bc improve keyword-related parser errors (#1941)
fixes #1937
2017-05-16 06:33:57 +08:00
Alex Lam S.L
1ca43bcca1 v2.8.25 2017-05-15 19:45:33 +08:00
Alex Lam S.L
3ee1464aa4 Merge pull request #1938 from alexlamsl/v2.8.25 2017-05-15 19:44:44 +08:00
Alex Lam S.L
24967b8be8 fix & improve coverage of estree (#1935)
- fix `estree` conversion of getter/setter
- fix non-directive literal in `to_mozilla_ast()`
- revamp `test/mozilla-ast.js`
  - reuse `test/ufuzz.js` for code generation
  - use `acorn.parse()` for creating `estree`
- extend `test/ufuzz.js` for `acorn` workaround
  - catch variable redefinition
  - non-trivial literal as directive
  - adjust options for tolerance

Miscellaneous
- optional semi-colon when parsing directives

fixes #1914
closes #1915
2017-05-15 18:33:43 +08:00
Alex Lam S.L
a8c67ea353 fix bugs with getter/setter (#1926)
- `reduce_vars`
- `side_effects`
- property access for object
- `AST_SymbolAccessor` as key names

enhance `test/ufuzz.js`
- add object getter & setter
  - property assignment to setter
  - avoid infinite recursion in setter
- fix & adjust assignment operators
  - 50% `=`
  - 25% `+=`
  - 2.5% each for the rest
- avoid "Invalid array length"
- fix `console.log()`
  - bypass getter
  - curb recursive reference
- deprecate `-E`, always report runtime errors
2017-05-15 18:22:43 +08:00
olsonpm
d0b0aecfc5 document 3 max passes (#1928) 2017-05-15 18:18:38 +08:00
olsonpm
9f5a6029a3 clarify wording (#1931) 2017-05-15 18:18:27 +08:00
Alex Lam S.L
4027a0c962 fix parser bugs & CLI reporting (#1827)
fixes #1825
2017-05-15 18:18:04 +08:00
Alex Lam S.L
87f8a484e6 v2.8.24 2017-05-12 15:47:02 +08:00
Alex Lam S.L
c736834aa4 Merge pull request #1921 from alexlamsl/v2.8.24 2017-05-12 14:58:35 +08:00
olsonpm
9a98513981 add documentation for side_effects & [#@]__PURE__ (#1925) 2017-05-12 12:55:07 +08:00
Alex Lam S.L
f631d6437a avoid arguments and eval in reduce_vars (#1924)
fixes #1922
2017-05-12 12:45:38 +08:00
Alex Lam S.L
aa7e8783f8 fix invalid transform on const (#1919)
- preserve (re)assignment to `const` for runtime error
- suppress `cascade` on `const`, as runtime behaviour is ill-defined
2017-05-12 05:04:28 +08:00
Alex Lam S.L
13e5e33448 document known issues with const (#1916) 2017-05-12 03:36:54 +08:00
kzc
487ae8e3be change harmony references to uglify-es in README (#1902) 2017-05-10 16:38:10 +08:00
Alex Lam S.L
5dfda6e212 v2.8.23 2017-05-07 04:31:54 +08:00
Alex Lam S.L
d08c772eb3 Merge pull request #1871 from alexlamsl/v2.8.23 2017-05-07 04:06:51 +08:00
Alex Lam S.L
90ed54401b fix test for #1865 (#1873) 2017-05-07 03:04:17 +08:00
Alex Lam S.L
d8106b6c63 fix label-related bugs (#1835)
- deep cloning of `AST_LabeledStatement`
- `L:do{...}while(false)`
- empty statement with label within block

extend `test/ufuzz.js`
- generate labels for blocks & loops
- generate for-in statements
- skip suspicious option search if `minify()` errs

fixes #1833
2017-05-07 00:16:30 +08:00
alexlamsl
dda4eb96e1 backport test scripts 2017-05-06 23:48:28 +08:00
Alex Lam S.L
7305ba0296 fix unsafe on evaluate of reduce_vars (#1870)
Determine if variables with non-constant values can escape and be modified.

fixes #1865
2017-05-06 23:40:19 +08:00
Alex Lam S.L
2c21dc5e8e fix unused on for-in statements (#1843)
Only need to avoid `var` within the initialisation block.

fixes #1841
2017-05-06 23:34:21 +08:00
Alex Lam S.L
d0faa471db fix unused on labeled for-loop (#1831)
fixes #1830
2017-05-06 23:31:22 +08:00
Alex Lam S.L
6ad823d1e8 fix reduce_vars within try-block (#1818)
Possible partial execution due to exceptions.
2017-05-06 23:28:07 +08:00
Alex Lam S.L
43ad4e9775 fix variable substitution (#1816)
- let `collapse_vars` take care of value containing any symbols
- improve overhead accounting
2017-05-06 23:26:54 +08:00
85 changed files with 9418 additions and 9427 deletions

View File

@@ -1,19 +1,8 @@
**Bug report or feature request?** - Bug report or feature request? <!-- Note: sub-optimal but correct code is not a bug -->
- `uglify-js` version (`uglifyjs -V`)
<!-- Note: sub-optimal but correct code is not a bug --> - JavaScript input - ideally as small as possible.
- The `uglifyjs` CLI command executed or `minify()` options used.
**ES5 or ES6+ input?** - An example of JavaScript output produced and/or the error or warning.
<!-- Note: for ES6 see: https://github.com/mishoo/UglifyJS2/tree/harmony#harmony -->
**`uglify-js` version (`uglifyjs -V`)**
**JavaScript input - ideally as small as possible.**
**The `uglifyjs` CLI command executed or `minify()` options used.**
**JavaScript output produced and/or the error or warning.**
<!-- <!--
Note: the release version of uglify-js only supports ES5. Those wishing Note: the release version of uglify-js only supports ES5. Those wishing
to minify ES6 should use the experimental harmony branch. to minify ES6 should use the experimental harmony branch.

View File

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

614
README.md
View File

@@ -1,15 +1,20 @@
UglifyJS 3 UglifyJS 2
========== ==========
[![Build Status](https://travis-ci.org/mishoo/UglifyJS2.svg)](https://travis-ci.org/mishoo/UglifyJS2) [![Build Status](https://travis-ci.org/mishoo/UglifyJS2.svg)](https://travis-ci.org/mishoo/UglifyJS2)
UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit. UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit.
This page documents the command line utility. For
[API and internals documentation see my website](http://lisperator.net/uglifyjs/).
There's also an
[in-browser online demo](http://lisperator.net/uglifyjs/#demo) (for Firefox,
Chrome and probably Safari).
#### Note: #### Note:
- **`uglify-js@3.x` has a new API and CLI and is not backwards compatible with [`uglify-js@2.x`](https://github.com/mishoo/UglifyJS2/tree/v2.x)**. - `uglify-js` only supports ECMAScript 5 (ES5).
- **Documentation for UglifyJS `2.x` releases can be found [here](https://github.com/mishoo/UglifyJS2/tree/v2.x)**. - Support for `const` is [present but incomplete](#support-for-const), and may not be
- Release versions of `uglify-js` only support ECMAScript 5 (ES5). If you wish to minify transformed properly.
ES2015+ (ES6+) code then please use the [harmony](#harmony) development branch. - Those wishing to minify ES2015+ (ES6+) should use the `npm` package [**uglify-es**](https://github.com/mishoo/UglifyJS2/tree/harmony).
- Node 7 has a known performance regression and runs `uglify-js` twice as slow.
Install Install
------- -------
@@ -25,24 +30,19 @@ From NPM for programmatic use:
npm install uglify-js npm install uglify-js
From Git:
git clone git://github.com/mishoo/UglifyJS2.git
cd UglifyJS2
npm link .
Usage Usage
----- -----
uglifyjs [input files] [options] uglifyjs [input files] [options]
UglifyJS can take multiple input files. It's recommended that you pass the UglifyJS2 can take multiple input files. It's recommended that you pass the
input files first, then pass the options. UglifyJS will parse input files input files first, then pass the options. UglifyJS will parse input files
in sequence and apply any compression options. The files are parsed in the in sequence and apply any compression options. The files are parsed in the
same global scope, that is, a reference from a file to some same global scope, that is, a reference from a file to some
variable/function declared in another file will be matched properly. variable/function declared in another file will be matched properly.
If no input file is specified, UglifyJS will read from STDIN. 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 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: a double dash to prevent input files being used as option arguments:
@@ -52,53 +52,48 @@ a double dash to prevent input files being used as option arguments:
The available options are: The available options are:
``` ```
-h, --help Print usage information. --source-map Specify an output file where to generate source
-V, --version Print version number. map.
-p, --parse <options> Specify parser options: --source-map-root The path to the original source to be included
`acorn` Use Acorn for parsing. in the source map.
`bare_returns` Allow return outside of functions. --source-map-url The path to the source map to be added in //#
Useful when minifying CommonJS sourceMappingURL. Defaults to the value passed
modules and Userscripts that may with --source-map.
be anonymous function wrapped (IIFE) --source-map-include-sources Pass this flag if you want to include the
by the .user.js engine `caller`. content of source files in the source map as
`expression` Parse a single expression, rather than sourcesContent property.
a program (for parsing JSON). --source-map-inline Write base64-encoded source map to the end of js output.
`spidermonkey` Assume input files are SpiderMonkey --in-source-map Input source map, useful if you're compressing
AST format (as JSON). JS that was generated from some other original
-c, --compress [options] Enable compressor/specify compressor options: code. Specify "inline" if the source map is included
`pure_funcs` List of functions that can be safely inline with the sources.
removed when their return values are --screw-ie8 Use this flag if you don't wish to support
not used. Internet Explorer 6/7/8.
-m, --mangle [options] Mangle names/specify mangler options: By default UglifyJS will not try to be IE-proof.
`reserved` List of names that should not be mangled. --support-ie8 Use this flag to support Internet Explorer 6/7/8.
--mangle-props [options] Mangle properties/specify mangler options: Equivalent to setting `screw_ie8: false` in `minify()`
`builtins` Mangle property names that overlaps for `compress`, `mangle` and `output` options.
with standard JavaScript globals. --expr Parse a single expression, rather than a
`debug` Add debug prefix and suffix. program (for parsing JSON)
`domprops` Mangle property names that overlaps -p, --prefix Skip prefix for original filenames that appear
with DOM properties. in source maps. For example -p 3 will drop 3
`keep_quoted` Only mangle unquoted properies. directories from file names and ensure they are
`regex` Only mangle matched property names. relative paths. You can also specify -p
`reserved` List of names that should not be mangled. relative, which will make UglifyJS figure out
-b, --beautify [options] Beautify output/specify output options: itself the relative paths between original
`beautify` Enabled with `--beautify` by default. sources, the source map and the output file.
`preamble` Preamble to prepend to the output. You -o, --output Output file (default STDOUT).
can use this to insert a comment, for -b, --beautify Beautify output/specify output options.
example for licensing information. -m, --mangle Mangle names/pass mangler options.
This will not be parsed, but the source -r, --reserved Reserved names to exclude from mangling.
map will adjust for its presence. -c, --compress Enable compressor/pass compressor options, e.g.
`quote_style` Quote style: `-c 'if_return=false,pure_funcs=["Math.pow","console.log"]'`
0 - auto Use `-c` with no argument to enable default compression
1 - single options.
2 - double -d, --define Global definitions
3 - original -e, --enclose Embed everything in a big function, with a
`wrap_iife` Wrap IIFEs in parenthesis. Note: you may configurable parameter/argument list.
want to disable `negate_iife` under --comments Preserve copyright comments in the output. By
compressor options.
-o, --output <file> Output file path (default STDOUT). Specify `ast` or
`spidermonkey` to write UglifyJS or SpiderMonkey AST
as JSON to STDOUT respectively.
--comments [filter] Preserve copyright comments in the output. By
default this works like Google Closure, keeping default this works like Google Closure, keeping
JSDoc-style comments that contain "@license" or JSDoc-style comments that contain "@license" or
"@preserve". You can optionally pass one of the "@preserve". You can optionally pass one of the
@@ -110,39 +105,54 @@ The available options are:
kept when compression is on, because of dead kept when compression is on, because of dead
code removal or cascading statements into code removal or cascading statements into
sequences. sequences.
--config-file <file> Read `minify()` options from JSON file. --preamble Preamble to prepend to the output. You can use
-d, --define <expr>[=value] Global definitions. this to insert a comment, for example for
--ie8 Support non-standard Internet Explorer 8. licensing information. This will not be
Equivalent to setting `ie8: true` in `minify()` parsed, but the source map will adjust for its
for `compress`, `mangle` and `output` options. presence.
By default UglifyJS will not try to be IE-proof. --stats Display operations run time on STDERR.
--keep-fnames Do not mangle/drop function names. Useful for --acorn Use Acorn for parsing.
code relying on Function.prototype.name. --spidermonkey Assume input files are SpiderMonkey AST format
--name-cache File to hold mangled name mappings. (as JSON).
--self Build UglifyJS as a library (implies --wrap UglifyJS) --self Build itself (UglifyJS2) as a library (implies
--source-map [options] Enable source map/specify source map options: --wrap=UglifyJS --export-all)
`base` Path to compute relative paths from input files. --wrap Embed everything in a big function, making the
`content` Input source map, useful if you're compressing
JS that was generated from some other original
code. Specify "inline" if the source map is
included within the sources.
`filename` Name and/or location of the output source.
`includeSources` Pass this flag if you want to include
the content of source files in the
source map as sourcesContent property.
`root` Path to the original source to be included in
the source map.
`url` If specified, path to the source map to append in
`//# sourceMappingURL`.
--stats Display operations run time on STDERR.
--toplevel Compress and/or mangle variables in toplevel scope.
--verbose Print diagnostic messages.
--warn Print warning messages.
--wrap <name> Embed everything in a big function, making the
“exports” and “global” variables available. You “exports” and “global” variables available. You
need to pass an argument to this option to need to pass an argument to this option to
specify the name that your module will take specify the name that your module will take
when included in, say, a browser. 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 (default `0`). Set to
`true` or `1` to mangle all property names. Set
to `unquoted` or `2` to only mangle unquoted
property names. Mode `2` also enables the
`keep_quoted_props` beautifier option to
preserve the quotes around property names and
disables the `properties` compressor option to
prevent rewriting quoted properties with dot
notation. You can override these by setting
them explicitly on the command line.
--mangle-regex Only mangle property names matching the regex
--name-cache File to hold mangled names mappings
--pure-funcs Functions that can be safely removed if their
return value is not used, e.g.
`--pure-funcs Math.floor console.info`
(requires `--compress`)
``` ```
Specify `--output` (`-o`) to declare the output file. Otherwise the output Specify `--output` (`-o`) to declare the output file. Otherwise the output
@@ -150,21 +160,25 @@ goes to STDOUT.
## Source map options ## Source map options
UglifyJS can generate a source map file, which is highly useful for UglifyJS2 can generate a source map file, which is highly useful for
debugging your compressed JavaScript. To get a source map, pass debugging your compressed JavaScript. To get a source map, pass
`--source-map --output output.js` (source map will be written out to `--source-map output.js.map` (full path to the file where you want the
`output.js.map`). source map dumped).
Additionally you might need `--source-map root=<URL>` to pass the URL where Additionally you might need `--source-map-root` to pass the URL where the
the original files can be found. Use `--source-map url=<URL>` to specify original files can be found. In case you are passing full paths to input
the URL where the source map can be found. files to UglifyJS, you can use `--prefix` (`-p`) to specify the number of
directories to drop from the path prefix when declaring files in the source
map.
For example: For example:
uglifyjs /home/doe/work/foo/src/js/file1.js \ uglifyjs /home/doe/work/foo/src/js/file1.js \
/home/doe/work/foo/src/js/file2.js \ /home/doe/work/foo/src/js/file2.js \
-o foo.min.js -c -m \ -o foo.min.js \
--source-map base="/home/doe/work/foo/src",root="http://foo.com/src" --source-map foo.min.js.map \
--source-map-root http://foo.com/src \
-p 5 -c -m
The above will compress and mangle `file1.js` and `file2.js`, will drop the The above will compress and mangle `file1.js` and `file2.js`, will drop the
output in `foo.min.js` and the source map in `foo.min.js.map`. The source output in `foo.min.js` and the source map in `foo.min.js.map`. The source
@@ -201,10 +215,10 @@ To enable the mangler you need to pass `--mangle` (`-m`). The following
(disabled by default). (disabled by default).
When mangling is enabled but you want to prevent certain names from being When mangling is enabled but you want to prevent certain names from being
mangled, you can declare those names with `--mangle reserved` — pass a mangled, you can declare those names with `--reserved` (`-r`) — pass a
comma-separated list of names. For example: comma-separated list of names. For example:
uglifyjs ... -m reserved=[$,require,exports] uglifyjs ... -m -r '$,require,exports'
to prevent the `require`, `exports` and `$` names from being changed. to prevent the `require`, `exports` and `$` names from being changed.
@@ -229,22 +243,39 @@ console.log(x.something());
In the above code, `foo`, `bar`, `baz`, `moo` and `boo` will be replaced In the above code, `foo`, `bar`, `baz`, `moo` and `boo` will be replaced
with single characters, while `something()` will be left as is. with single characters, while `something()` will be left as is.
In order for this to be of any use, we avoid mangling standard JS names by In order for this to be of any use, we should avoid mangling standard JS
default (`--mangle-props builtins` to override). 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 A default exclusion file is provided in `tools/domprops.json` which should
cover most standard JS and DOM properties defined in various browsers. Pass cover most standard JS and DOM properties defined in various browsers. Pass
`--mangle-props domprops` to disable this feature. `--reserve-domprops` to read that in.
You can also use a regular expression to define which property names should be You can also use a regular expression to define which property names should be
mangled. For example, `--mangle-props regex=/^_/` will only mangle property mangled. For example, `--mangle-regex="/^_/"` will only mangle property names
names that start with an underscore. that start with an underscore.
When you compress multiple files using this option, in order for them to 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 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` mangled to the same name in all of them. For this, pass `--name-cache
and UglifyJS will maintain these mappings in a file which can then be reused. filename.json` and UglifyJS will maintain these mappings in a file which can
It should be initially empty. Example: then be reused. It should be initially empty. Example:
``` ```
rm -f /tmp/cache.json # start fresh rm -f /tmp/cache.json # start fresh
@@ -258,26 +289,26 @@ of mangled property names.
Using the name cache is not necessary if you compress all your files in a Using the name cache is not necessary if you compress all your files in a
single call to UglifyJS. single call to UglifyJS.
#### Mangling unquoted names (`--mangle-props keep_quoted`) #### Mangling unquoted names (`--mangle-props=unquoted` or `--mangle-props=2`)
Using quoted property name (`o["foo"]`) reserves the property name (`foo`) Using quoted property name (`o["foo"]`) reserves the property name (`foo`)
so that it is not mangled throughout the entire script even when used in an so that it is not mangled throughout the entire script even when used in an
unquoted style (`o.foo`). Example: unquoted style (`o.foo`). Example:
``` ```
$ echo 'var o={"foo":1, bar:3}; o.foo += o.bar; console.log(o.foo);' | uglifyjs --mangle-props keep_quoted -mc $ echo 'var o={"foo":1, bar:3}; o.foo += o.bar; console.log(o.foo);' | uglifyjs --mangle-props=2 -mc
var o={foo:1,a:3};o.foo+=o.a,console.log(o.foo); var o={"foo":1,a:3};o.foo+=o.a,console.log(o.foo);
``` ```
#### Debugging property name mangling #### Debugging property name mangling
You can also pass `--mangle-props debug` in order to mangle property names You can also pass `--mangle-props-debug` in order to mangle property names
without completely obscuring them. For example the property `o.foo` without completely obscuring them. For example the property `o.foo`
would mangle to `o._$foo$_` with this option. This allows property mangling would mangle to `o._$foo$_` with this option. This allows property mangling
of a large codebase while still being able to debug the code and identify of a large codebase while still being able to debug the code and identify
where mangling is breaking things. where mangling is breaking things.
You can also pass a custom suffix using `--mangle-props debug=XYZ`. This would then You can also pass a custom suffix using `--mangle-props-debug=XYZ`. This would then
mangle `o.foo` to `o._$foo$XYZ_`. You can change this each time you compile a mangle `o.foo` to `o._$foo$XYZ_`. You can change this each time you compile a
script to identify how a property got mangled. One technique is to pass a script to identify how a property got mangled. One technique is to pass a
random number on every compile to simulate mangling changing with different random number on every compile to simulate mangling changing with different
@@ -322,6 +353,9 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `unsafe_proto` (default: false) -- optimize expressions like - `unsafe_proto` (default: false) -- optimize expressions like
`Array.prototype.slice.call(a)` into `[].slice.call(a)` `Array.prototype.slice.call(a)` into `[].slice.call(a)`
- `unsafe_regexp` (default: false) -- enable substitutions of variables with
`RegExp` values the same way as if they are constants.
- `conditionals` -- apply optimizations for `if`-s and conditional - `conditionals` -- apply optimizations for `if`-s and conditional
expressions expressions
@@ -405,13 +439,19 @@ to set `true`; it's effectively a shortcut for `foo=true`).
compressor from discarding function names. Useful for code relying on compressor from discarding function names. Useful for code relying on
`Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle). `Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle).
- `passes` -- default `1`. Number of times to run compress. Use an - `passes` -- default `1`. Number of times to run compress with a maximum of 3.
integer argument larger than 1 to further reduce code size in some cases. In some cases more than one pass leads to further compressed code. Keep in
Note: raising the number of passes will increase uglify compress time. mind more passes will take more time.
- `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from - `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome. being compressed into `1/0`, which may cause performance issues on Chrome.
- `side_effects` -- default `true`. Pass `false` to disable potentially dropping
functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();`
### The `unsafe` option ### The `unsafe` option
It enables some transformations that *might* break code logic in certain It enables some transformations that *might* break code logic in certain
@@ -465,6 +505,8 @@ code as usual. The build will contain the `const` declarations if you use
them. If you are targeting < ES6 environments which does not support `const`, them. If you are targeting < ES6 environments which does not support `const`,
using `var` with `reduce_vars` (enabled by default) should suffice. using `var` with `reduce_vars` (enabled by default) should suffice.
<a name="codegen-options"></a>
#### Conditional compilation, API #### Conditional compilation, API
You can also use conditional compilation via the programmatic API. With the difference that the You can also use conditional compilation via the programmatic API. With the difference that the
property name is `global_defs` and is a compressor property: property name is `global_defs` and is a compressor property:
@@ -532,8 +574,8 @@ You can pass `--comments` to retain certain comments in the output. By
default it will keep JSDoc-style comments that contain "@preserve", default it will keep JSDoc-style comments that contain "@preserve",
"@license" or "@cc_on" (conditional compilation for IE). You can pass "@license" or "@cc_on" (conditional compilation for IE). You can pass
`--comments all` to keep all the comments, or a valid JavaScript regexp to `--comments all` to keep all the comments, or a valid JavaScript regexp to
keep only comments that match this regexp. For example `--comments /^!/` keep only comments that match this regexp. For example `--comments
will keep comments like `/*! Copyright Notice */`. '/foo|bar/'` will keep only comments that contain "foo" or "bar".
Note, however, that there might be situations where comments are lost. For Note, however, that there might be situations where comments are lost. For
example: example:
@@ -556,7 +598,7 @@ needs to be kept in the output) are comments attached to toplevel nodes.
## Support for the SpiderMonkey AST ## Support for the SpiderMonkey AST
UglifyJS has its own abstract syntax tree format; for UglifyJS2 has its own abstract syntax tree format; for
[practical reasons](http://lisperator.net/blog/uglifyjs-why-not-switching-to-spidermonkey-ast/) [practical reasons](http://lisperator.net/blog/uglifyjs-why-not-switching-to-spidermonkey-ast/)
we can't easily change to using the SpiderMonkey AST internally. However, we can't easily change to using the SpiderMonkey AST internally. However,
UglifyJS now has a converter which can import a SpiderMonkey AST. UglifyJS now has a converter which can import a SpiderMonkey AST.
@@ -566,22 +608,54 @@ SpiderMonkey AST. It has a small CLI utility that parses one file and dumps
the AST in JSON on the standard output. To use UglifyJS to mangle and the AST in JSON on the standard output. To use UglifyJS to mangle and
compress that: compress that:
acorn file.js | uglifyjs -p spidermonkey -m -c acorn file.js | uglifyjs --spidermonkey -m -c
The `-p spidermonkey` option tells UglifyJS that all input files are not The `--spidermonkey` option tells UglifyJS that all input files are not
JavaScript, but JS code described in SpiderMonkey AST in JSON. Therefore we JavaScript, but JS code described in SpiderMonkey AST in JSON. Therefore we
don't use our own parser in this case, but just transform that AST into our don't use our own parser in this case, but just transform that AST into our
internal AST. internal AST.
### Use Acorn for parsing ### Use Acorn for parsing
More for fun, I added the `-p acorn` option which will use Acorn to do all More for fun, I added the `--acorn` option which will use Acorn to do all
the parsing. If you pass this option, UglifyJS will `require("acorn")`. the parsing. If you pass this option, UglifyJS will `require("acorn")`.
Acorn is really fast (e.g. 250ms instead of 380ms on some 650K code), but 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 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. 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 = UglifyJS.Compressor(options).compress(uAST);
// 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 API Reference
------------- -------------
@@ -591,72 +665,107 @@ like this:
var UglifyJS = require("uglify-js"); var UglifyJS = require("uglify-js");
``` ```
There is a single toplevel function, `minify(files, options)`, which will It exports a lot of names, but I'll discuss here the basics that are needed
performs all the steps in a configurable manner. for parsing, mangling and compressing a piece of code. The sequence is (1)
parse, (2) compress, (3) mangle, (4) generate output code.
### The simple way
There's a single toplevel function which combines all the steps. If you
don't need additional customization, you might want to go with `minify`.
Example: Example:
```javascript ```javascript
var result = UglifyJS.minify("var b = function() {};"); var result = UglifyJS.minify("/path/to/file.js");
console.log(result.code); // minified output console.log(result.code); // minified output
// if you need to pass code instead of file name
var result = UglifyJS.minify("var b = function () {};", {fromString: true});
``` ```
You can also compress multiple files: You can also compress multiple files:
```javascript ```javascript
var result = UglifyJS.minify({ var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ]);
"file1.js": "var a = function() {};",
"file2.js": "var b = function() {};"
});
console.log(result.code); console.log(result.code);
``` ```
To generate a source map: To generate a source map:
```javascript ```javascript
var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, { var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
sourceMap: { outSourceMap: "out.js.map"
filename: "out.js",
url: "out.js.map"
}
}); });
console.log(result.code); // minified output console.log(result.code); // minified output
console.log(result.map); // source map console.log(result.map);
```
To generate a source map with the fromString option, you can also use an object:
```javascript
var result = UglifyJS.minify({"file1.js": "var a = function () {};"}, {
outSourceMap: "out.js.map",
outFileName: "out.js",
fromString: true
});
``` ```
Note that the source map is not saved in a file, it's just returned in Note that the source map is not saved in a file, it's just returned in
`result.map`. The value passed for `sourceMap.url` is only used to set `result.map`. The value passed for `outSourceMap` is only used to set
`//# sourceMappingURL=out.js.map` in `result.code`. The value of `//# sourceMappingURL=out.js.map` in `result.code`. The value of
`filename` is only used to set `file` attribute (see [the spec][sm-spec]) `outFileName` is only used to set `file` attribute in source map file.
in source map file.
You can set option `sourceMap.url` to be `"inline"` and source map will The `file` attribute in the source map (see [the spec][sm-spec]) will
use `outFileName` firstly, if it's falsy, then will be deduced from
`outSourceMap` (by removing `'.map'`).
You can set option `sourceMapInline` to be `true` and source map will
be appended to code. be appended to code.
You can also specify sourceRoot property to be included in source map: You can also specify sourceRoot property to be included in source map:
```javascript ```javascript
var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, { var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
sourceMap: { outSourceMap: "out.js.map",
root: "http://example.com/src", sourceRoot: "http://example.com/src"
url: "out.js.map"
}
}); });
``` ```
If you're compressing compiled JavaScript and have a source map for it, you If you're compressing compiled JavaScript and have a source map for it, you
can use `sourceMap.content`: can use the `inSourceMap` argument:
```javascript ```javascript
var result = UglifyJS.minify({"compiled.js": "compiled code"}, { var result = UglifyJS.minify("compiled.js", {
sourceMap: { inSourceMap: "compiled.js.map",
content: "content from compiled.js.map", outSourceMap: "minified.js.map"
url: "minified.js.map"
}
}); });
// same as before, it returns `code` and `map` // same as before, it returns `code` and `map`
``` ```
If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.url`. 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).
To set the source map url, use the `sourceMapUrl` option.
If you're using the X-SourceMap header instead, you can just set the `sourceMapUrl` option to false.
Defaults to outSourceMap:
```javascript
var result = UglifyJS.minify([ "file1.js" ], {
outSourceMap: "out.js.map",
sourceMapUrl: "localhost/out.js.map"
});
```
Other options: Other options:
- `warnings` (default `false`) — pass `true` to display compressor warnings. - `warnings` (default `false`) — pass `true` to display compressor warnings.
- `fromString` (default `false`) — if you pass `true` then you can pass
JavaScript source code, rather than file names.
- `mangle` (default `true`) — pass `false` to skip mangling names, or pass - `mangle` (default `true`) — pass `false` to skip mangling names, or pass
an object to specify mangling options (see below). an object to specify mangling options (see below).
@@ -664,18 +773,18 @@ Other options:
mangle property options. mangle property options.
- `output` (default `null`) — pass an object if you wish to specify - `output` (default `null`) — pass an object if you wish to specify
additional [output options](#beautifier-options). The defaults are optimized additional [output options][codegen]. The defaults are optimized
for best compression. for best compression.
- `compress` (default `{}`) — pass `false` to skip compressing entirely. - `compress` (default `{}`) — pass `false` to skip compressing entirely.
Pass an object to specify custom [compressor options](#compressor-options). Pass an object to specify custom [compressor options][compressor].
- `parse` (default {}) — pass an object if you wish to specify some - `parse` (default {}) — pass an object if you wish to specify some
additional [parser options](#the-parser). additional [parser options][parser]. (not all options available... see below)
##### mangle ##### mangle
- `reserved` - pass an array of identifiers that should be excluded from mangling - `except` - pass an array of identifiers that should be excluded from mangling
- `toplevel` — mangle names declared in the toplevel scope (disabled by - `toplevel` — mangle names declared in the toplevel scope (disabled by
default). default).
@@ -700,36 +809,187 @@ Other options:
UglifyJS.minify("tst.js").code; UglifyJS.minify("tst.js").code;
// 'function funcName(a,n){}var globalVar;' // 'function funcName(a,n){}var globalVar;'
UglifyJS.minify("tst.js", { mangle: { reserved: ['firstLongName'] } }).code; UglifyJS.minify("tst.js", { mangle: { except: ['firstLongName'] } }).code;
// 'function funcName(firstLongName,a){}var globalVar;' // 'function funcName(firstLongName,a){}var globalVar;'
UglifyJS.minify("tst.js", { mangle: { toplevel: true } }).code; UglifyJS.minify("tst.js", { mangle: { toplevel: true } }).code;
// 'function n(n,a){}var a;' // 'function n(n,a){}var a;'
``` ```
##### mangle.properties options ##### mangleProperties options
- `regex` — Pass a RegExp to only mangle certain names - `regex` — Pass a RegExp to only mangle certain names (maps to the `--mangle-regex` CLI arguments option)
- `keep_quoted` Only mangle unquoted property names - `ignore_quoted` Only mangle unquoted property names (maps to the `--mangle-props 2` CLI arguments option)
- `debug` Mangle names with the original name still present. Defaults to `false`. - `debug` Mangle names with the original name still present (maps to the `--mangle-props-debug` CLI arguments option). Defaults to `false`. Pass an empty string to enable, or a non-empty string to set the suffix.
Pass an empty string to enable, or a non-empty string to set the suffix.
We could add more options to `UglifyJS.minify` — if you need additional
functionality please suggest!
### The hard way
Following there's more detailed API info, in case the `minify` function is
too simple for your needs.
#### The parser
```javascript
var toplevel_ast = UglifyJS.parse(code, options);
```
`options` is optional and if present it must be an object. The following
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`)
The last two options are useful when you'd like to minify multiple files and
get a single file as the output and a proper source map. Our CLI tool does
something like this:
```javascript
var toplevel = null;
files.forEach(function(file){
var code = fs.readFileSync(file, "utf8");
toplevel = UglifyJS.parse(code, {
filename: file,
toplevel: toplevel
});
});
```
After this, we have in `toplevel` a big AST containing all our files, with
each token having proper information about where it came from.
#### Scope information
UglifyJS contains a scope analyzer that you need to call manually before
compressing or mangling. Basically it augments various nodes in the AST
with information about where is a name defined, how many times is a name
referenced, if it is a global or not, if a function is using `eval` or the
`with` statement etc. I will discuss this some place else, for now what's
important to know is that you need to call the following before doing
anything with the tree:
```javascript
toplevel.figure_out_scope()
```
#### Compression
Like this:
```javascript
var compressor = UglifyJS.Compressor(options);
var compressed_ast = compressor.compress(toplevel);
```
The `options` can be missing. Available options are discussed above in
“Compressor options”. Defaults should lead to best compression in most
scripts.
The compressor is destructive, so don't rely that `toplevel` remains the
original tree.
#### Mangling
After compression it is a good idea to call again `figure_out_scope` (since
the compressor might drop unused variables / unreachable code and this might
change the number of identifiers or their position). Optionally, you can
call a trick that helps after Gzip (counting character frequency in
non-mangleable words). Example:
```javascript
compressed_ast.figure_out_scope();
compressed_ast.compute_char_frequency();
compressed_ast.mangle_names();
```
#### Generating output
AST nodes have a `print` method that takes an output stream. Essentially,
to generate code you do this:
```javascript
var stream = UglifyJS.OutputStream(options);
compressed_ast.print(stream);
var code = stream.toString(); // this is your minified code
```
or, for a shortcut you can do:
```javascript
var code = compressed_ast.print_to_string(options);
```
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`.
#### Keeping comments in the output
In order to keep certain comments in the output you need to pass the
`comments` option. Pass a RegExp (as string starting and closing with `/`
or pass a RegExp object), a boolean or a function. Stringified options
`all` and `some` can be passed too, where `some` behaves like it's cli
equivalent `--comments` without passing a value. If you pass a RegExp,
only those comments whose body matches the RegExp will be kept. Note that body
means without the initial `//` or `/*`. If you pass a function, it will be
called for every comment in the tree and will receive two arguments: the
node that the comment is attached to, and the comment token itself.
The comment token has these properties:
- `type`: "comment1" for single-line comments or "comment2" for multi-line
comments
- `value`: the comment body
- `pos` and `endpos`: the start/end positions (zero-based indexes) in the
original code where this comment appears
- `line` and `col`: the line and column where this comment appears in the
original code
- `file` — the file name of the original file
- `nlb` — true if there was a newline before this comment in the original
code, or if this comment contains a newline.
Your function should return `true` to keep the comment, or a falsy value
otherwise.
#### Generating a source mapping
You need to pass the `source_map` argument when calling `print`. It needs
to be a `SourceMap` object (which is a thin wrapper on top of the
[source-map][source-map] library).
Example:
```javascript
var source_map = UglifyJS.SourceMap(source_map_options);
var stream = UglifyJS.OutputStream({
...
source_map: source_map
});
compressed_ast.print(stream);
var code = stream.toString();
var map = source_map.toString(); // json output for your source map
```
The `source_map_options` (optional) can contain the following properties:
- `file`: the name of the JavaScript output file that this mapping refers to
- `root`: the `sourceRoot` property (see the [spec][sm-spec])
- `orig`: the "original source map", handy when you compress generated JS
and want to map the minified output back to the original code where it
came from. It can be simply a string in JSON, or a JSON object containing
the original source map.
[acorn]: https://github.com/ternjs/acorn [acorn]: https://github.com/ternjs/acorn
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k [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
#### Harmony #### Support for `const`
If you wish to use the experimental [harmony](https://github.com/mishoo/UglifyJS2/commits/harmony) `const` in `uglify-js@2.x` has function scope and as such behaves much like
branch to minify ES2015+ (ES6+) code please use the following in your `package.json` file: `var` - unlike `const` in ES2015 (ES6) which has block scope. It is recommended
to avoid using `const` for this reason as it will have undefined behavior when
``` run on an ES2015 compatible browser.
"uglify-js": "git+https://github.com/mishoo/UglifyJS2.git#harmony"
```
or to directly install the experimental harmony version of uglify:
```
npm install --save-dev uglify-js@github:mishoo/UglifyJS2#harmony
```
See [#448](https://github.com/mishoo/UglifyJS2/issues/448) for additional details.

77
bin/extract-props.js Executable file
View 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;
}
}

View File

@@ -3,382 +3,633 @@
"use strict"; "use strict";
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
if (stream._handle && stream._handle.setBlocking)
stream._handle.setBlocking(true);
});
var fs = require("fs");
var info = require("../package.json");
var path = require("path");
var program = require("commander");
var UglifyJS = require("../tools/node"); var UglifyJS = require("../tools/node");
var sys = require("util");
var yargs = require("yargs");
var fs = require("fs");
var path = require("path");
var acorn;
var screw_ie8 = true;
var ARGS = yargs
.usage("$0 input1.js [input2.js ...] [options]\n\
Use a single dash to read input from the standard input.\
\n\n\
NOTE: by default there is no mangling/compression.\n\
Without [options] it will simply parse input files and dump the AST\n\
with whitespace and comments discarded. To achieve compression and\n\
mangling you need to use `-c` and `-m`.\
")
.describe("source-map", "Specify an output file where to generate source map.")
.describe("source-map-root", "The path to the original source to be included in the source map.")
.describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL. Defaults to the value passed with --source-map.")
.describe("source-map-inline", "Write base64-encoded source map to the end of js output. Disabled by default")
.describe("source-map-include-sources", "Pass this flag if you want to include the content of source files in the source map as sourcesContent property.")
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
.describe("screw-ie8", "Do not support Internet Explorer 6/7/8. This flag is enabled by default.")
.describe("support-ie8", "Support non-standard Internet Explorer 6/7/8 javascript.")
.describe("expr", "Parse a single expression, rather than a program (for parsing JSON)")
.describe("p", "Skip prefix for original filenames that appear in source maps. \
For example -p 3 will drop 3 directories from file names and ensure they are relative paths. \
You can also specify -p relative, which will make UglifyJS figure out itself the relative paths between original sources, \
the source map and the output file.")
.describe("o", "Output file (default STDOUT).")
.describe("b", "Beautify output/specify output options.")
.describe("m", "Mangle names/pass mangler options.")
.describe("r", "Reserved names to exclude from mangling.")
.describe("c", "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.")
.describe("d", "Global definitions")
.describe("e", "Embed everything in a big function, with a configurable parameter/argument list.")
var skip_keys = [ "cname", "enclosed", "parent_scope", "scope", "thedef", "uses_eval", "uses_with" ]; .describe("comments", "Preserve copyright comments in the output. \
var files = {}; By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \
var options = { You can optionally pass one of the following arguments to this flag:\n\
compress: false, - \"all\" to keep all comments\n\
mangle: false - a valid JS RegExp like `/foo/`or `/^!/` to keep only matching comments.\n\
}; \
program._name = info.name; Note that currently not *all* comments can be kept when compression is on, \
program.version(info.version); because of dead code removal or cascading statements into sequences.")
program.parseArgv = program.parse;
program.parse = undefined; .describe("preamble", "Preamble to prepend to the output. You can use this to insert a \
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true)); comment, for example for licensing information. This will not be \
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true)); parsed, but the source map will adjust for its presence.")
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true));
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js("mangle-props", true)); .describe("stats", "Display operations run time on STDERR.")
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js("beautify", true)); .describe("acorn", "Use Acorn for parsing.")
program.option("-o, --output <file>", "Output file (default STDOUT)."); .describe("spidermonkey", "Assume input files are SpiderMonkey AST format (as JSON).")
program.option("--comments [filter]", "Preserve copyright comments in the output."); .describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)")
program.option("--config-file <file>", "Read minify() options from JSON file."); .describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \
program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("define")); You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.")
program.option("--ie8", "Support non-standard Internet Explorer 8."); .describe("export-all", "Only used when --wrap, this tells UglifyJS to add code to automatically export all globals.")
program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name."); .describe("lint", "Display some scope warnings")
program.option("--name-cache <file>", "File to hold mangled name mappings."); .describe("v", "Verbose")
program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)"); .describe("V", "Print version number and exit.")
program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map()); .describe("noerr", "Don't throw an error for unknown options in -c, -b or -m.")
program.option("--stats", "Display operations run time on STDERR.") .describe("bare-returns", "Allow return outside of functions. Useful when minifying CommonJS modules.")
program.option("--toplevel", "Compress and/or mangle variables in toplevel scope."); .describe("keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.")
program.option("--verbose", "Print diagnostic messages."); .describe("quotes", "Quote style (0 - auto, 1 - single, 2 - double, 3 - original)")
program.option("--warn", "Print warning messages."); .describe("reserved-file", "File containing reserved names")
program.option("--wrap <name>", "Embed everything as a function with “exports” corresponding to “name” globally."); .describe("reserve-domprops", "Make (most?) DOM properties reserved for --mangle-props")
program.arguments("[files...]").parseArgv(process.argv); .describe("mangle-props", "Mangle property names (0 - disabled, 1 - mangle all properties, 2 - mangle unquoted properies)")
if (program.configFile) { .describe("mangle-regex", "Only mangle property names matching the regex")
options = JSON.parse(read_file(program.configFile)); .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.")
.describe("wrap-iife", "Wrap IIFEs in parenthesis. Note: this disables the negate_iife compression option")
.alias("p", "prefix")
.alias("o", "output")
.alias("v", "verbose")
.alias("b", "beautify")
.alias("m", "mangle")
.alias("c", "compress")
.alias("d", "define")
.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("mangle-props-debug")
.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-inline")
.boolean("source-map-include-sources")
.boolean("screw-ie8")
.boolean("support-ie8")
.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("reserve-domprops")
.boolean("wrap-iife")
.wrap(80)
.argv
;
normalize(ARGS);
if (ARGS.noerr) {
UglifyJS.DefaultsError.croak = function(msg, defs) {
print_error("WARN: " + msg);
};
} }
if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
fatal("ERROR: cannot write source map to STDOUT"); if (ARGS.version || ARGS.V) {
var json = require("../package.json");
print(json.name + ' ' + json.version);
process.exit(0);
} }
[
"compress", if (ARGS.ast_help) {
"ie8", var desc = UglifyJS.describe_ast();
"mangle", print(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
"sourceMap", process.exit(0);
"toplevel", }
"wrap"
].forEach(function(name) { if (ARGS.h || ARGS.help) {
if (name in program) { print(yargs.help());
options[name] = program[name]; process.exit(0);
} }
if (ARGS.acorn) {
acorn = require("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 (program.beautify) {
options.output = typeof program.beautify == "object" ? program.beautify : {}; if (ARGS.reserve_domprops) {
if (!("beautify" in options.output)) { RESERVED = UglifyJS.readDefaultReservedFile(RESERVED);
options.output.beautify = true;
}
} }
if (program.comments) {
if (typeof options.output != "object") options.output = {}; if (ARGS.d) {
options.output.comments = typeof program.comments == "string" ? program.comments : "some"; if (COMPRESS) COMPRESS.global_defs = getOptions("d");
} }
if (program.define) {
if (typeof options.compress != "object") options.compress = {}; if (ARGS.pure_funcs) {
if (typeof options.compress.global_defs != "object") options.compress.global_defs = {}; if (COMPRESS) COMPRESS.pure_funcs = ARGS.pure_funcs;
for (var expr in program.define) {
options.compress.global_defs[expr] = program.define[expr];
}
} }
if (program.keepFnames) {
options.keep_fnames = true; if (ARGS.r) {
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
} }
if (program.mangleProps) {
if (program.mangleProps.domprops) { if (RESERVED && MANGLE) {
delete program.mangleProps.domprops; if (!MANGLE.except) MANGLE.except = RESERVED.vars;
} else { else MANGLE.except = MANGLE.except.concat(RESERVED.vars);
if (typeof program.mangleProps != "object") program.mangleProps = {};
if (!Array.isArray(program.mangleProps.reserved)) program.mangleProps.reserved = [];
require("../tools/domprops").forEach(function(name) {
UglifyJS._push_uniq(program.mangleProps.reserved, name);
});
}
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.properties = program.mangleProps;
} }
var cache;
if (program.nameCache) { function readNameCache(key) {
cache = JSON.parse(read_file(program.nameCache, "{}")); return UglifyJS.readNameCache(ARGS.name_cache, key);
if (options.mangle) {
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.cache = to_cache("vars");
if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") options.mangle.properties = {};
options.mangle.properties.cache = to_cache("props");
}
}
} }
if (program.output == "ast") {
options.output = { function writeNameCache(key, cache) {
ast: true, return UglifyJS.writeNameCache(ARGS.name_cache, key, cache);
code: false
};
} }
if (program.parse) {
if (program.parse.acorn || program.parse.spidermonkey) { function extractRegex(str) {
if (program.sourceMap) fatal("ERROR: inline source map only works with built-in parser"); if (/^\/.*\/[a-zA-Z]*$/.test(str)) {
} else { var regex_pos = str.lastIndexOf("/");
options.parse = program.parse; return new RegExp(str.substr(1, regex_pos - 1), str.substr(regex_pos + 1));
} } else {
throw new Error("Invalid regular expression: " + str);
}
} }
var convert_path = function(name) {
return name; if (ARGS.quotes === true) {
ARGS.quotes = 3;
}
if (ARGS.mangle_props === true) {
ARGS.mangle_props = 1;
} else if (ARGS.mangle_props === "unquoted") {
ARGS.mangle_props = 2;
}
var OUTPUT_OPTIONS = {
beautify : BEAUTIFY ? true : false,
max_line_len : 32000,
preamble : ARGS.preamble || null,
quote_style : ARGS.quotes != null ? ARGS.quotes : 0,
}; };
if (typeof program.sourceMap == "object" && "base" in program.sourceMap) {
convert_path = function() { if (ARGS.mangle_props == 2) {
var base = program.sourceMap.base; OUTPUT_OPTIONS.keep_quoted_props = true;
delete options.sourceMap.base; if (COMPRESS && !("properties" in COMPRESS))
return function(name) { COMPRESS.properties = false;
return path.relative(base, name);
};
}();
} }
if (program.verbose) {
options.warnings = "verbose"; if (ARGS.support_ie8 === true && ARGS.screw_ie8 !== true) {
} else if (program.warn) { screw_ie8 = false;
options.warnings = true;
} }
if (program.self) {
if (program.args.length) { if (COMPRESS) COMPRESS.screw_ie8 = screw_ie8;
console.error("WARN: Ignoring input files since --self was passed"); if (MANGLE) MANGLE.screw_ie8 = screw_ie8;
} OUTPUT_OPTIONS.screw_ie8 = screw_ie8;
if (!options.wrap) options.wrap = "UglifyJS";
simple_glob(UglifyJS.FILES).forEach(function(name) { if (ARGS.keep_fnames) {
files[convert_path(name)] = read_file(name); if (COMPRESS) COMPRESS.keep_fnames = true;
}); if (MANGLE) MANGLE.keep_fnames = true;
run(); }
} else if (program.args.length) {
simple_glob(program.args).forEach(function(name) { if (ARGS.wrap_iife) {
files[convert_path(name)] = read_file(name); if (COMPRESS) COMPRESS.negate_iife = false;
}); OUTPUT_OPTIONS.wrap_iife = true;
run(); }
if (BEAUTIFY)
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
if (ARGS.comments === "") {
OUTPUT_OPTIONS.comments = "some";
} else { } else {
var chunks = []; OUTPUT_OPTIONS.comments = ARGS.comments;
process.stdin.setEncoding("utf8");
process.stdin.on("data", function(chunk) {
chunks.push(chunk);
}).on("end", function() {
files = [ chunks.join("") ];
run();
});
process.stdin.resume();
} }
function convert_ast(fn) { var files = ARGS._.slice();
return UglifyJS.AST_Node.from_mozilla_ast(Object.keys(files).reduce(fn, null));
if (process.platform === "win32")
files = UglifyJS.simple_glob(files);
if (ARGS.self) {
if (files.length > 0) {
print_error("WARN: Ignoring input files since --self was passed");
}
files = UglifyJS.FILES;
if (!ARGS.wrap) ARGS.wrap = "UglifyJS";
} }
function run() { var ORIG_MAP = ARGS.in_source_map;
UglifyJS.AST_Node.warn_function = function(msg) {
console.error("WARN:", msg); if (ORIG_MAP && ORIG_MAP != "inline") {
}; ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP));
if (program.stats) program.stats = Date.now(); if (files.length == 0) {
try { print_error("INFO: Using file from the input source map: " + ORIG_MAP.file);
if (program.parse) { files = [ ORIG_MAP.file ];
if (program.parse.acorn) {
files = convert_ast(function(toplevel, name) {
return require("acorn").parse(files[name], {
locations: true,
program: toplevel,
sourceFile: name
});
});
} else if (program.parse.spidermonkey) {
files = convert_ast(function(toplevel, name) {
var obj = JSON.parse(files[name]);
if (!toplevel) return obj;
toplevel.body = toplevel.body.concat(obj.body);
return toplevel;
});
}
}
} catch (ex) {
fatal("ERROR: " + ex.message);
} }
var result = UglifyJS.minify(files, options);
if (result.error) {
var ex = result.error;
if (ex.name == "SyntaxError") {
console.error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
var col = ex.col;
var lines = files[ex.filename].split(/\r?\n/);
var line = lines[ex.line - 1];
if (!line && !col) {
line = lines[ex.line - 2];
col = line.length;
}
if (line) {
if (col > 40) {
line = line.slice(col - 40);
col = 40;
}
console.error(line.slice(0, 80));
console.error(line.slice(0, col).replace(/\S/g, " ") + "^");
}
}
if (ex.defs) {
console.error("Supported options:");
console.error(ex.defs);
}
fatal("ERROR: " + ex.message);
} else if (program.output == "ast") {
console.log(JSON.stringify(result.ast, function(key, value) {
if (skip_key(key)) return;
if (value instanceof UglifyJS.AST_Token) return;
if (value instanceof UglifyJS.Dictionary) return;
if (value instanceof UglifyJS.AST_Node) {
var result = {
_class: "AST_" + value.TYPE
};
value.CTOR.PROPS.forEach(function(prop) {
result[prop] = value[prop];
});
return result;
}
return value;
}, 2));
} else if (program.output == "spidermonkey") {
console.log(JSON.stringify(UglifyJS.minify(result.code, {
compress: false,
mangle: false,
output: {
ast: true,
code: false
}
}).ast.to_mozilla_ast(), null, 2));
} else if (program.output) {
fs.writeFileSync(program.output, result.code);
if (result.map) {
fs.writeFileSync(program.output + ".map", result.map);
}
} else {
console.log(result.code);
}
if (program.nameCache) {
fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) {
return value instanceof UglifyJS.Dictionary ? value.toObject() : value;
}));
}
if (program.stats) console.error("Elapsed:", Date.now() - program.stats);
} }
function fatal(message) { if (files.length == 0) {
console.error(message); files = [ "-" ];
}
if (ORIG_MAP == "inline") {
if (files.length > 1) {
print_error("ERROR: Inline source map only works with singular input");
process.exit(1);
}
if (ARGS.acorn || ARGS.spidermonkey) {
print_error("ERROR: Inline source map only works with built-in parser");
process.exit(1);
}
}
if (files.indexOf("-") >= 0 && ARGS.source_map) {
print_error("ERROR: Source map doesn't work with input from STDIN");
process.exit(1); process.exit(1);
} }
// A file glob function that only supports "*" and "?" wildcards in the basename. if (files.filter(function(el){ return el == "-" }).length > 1) {
// Example: "foo/bar/*baz??.*.js" print_error("ERROR: Can read a single file from STDIN (two or more dashes specified)");
// Argument `glob` may be a string or an array of strings. process.exit(1);
// Returns an array of strings. Garbage in, garbage out. }
function simple_glob(glob) {
if (Array.isArray(glob)) { var STATS = {};
return [].concat.apply([], glob.map(simple_glob)); var TOPLEVEL = null;
} var P_RELATIVE = ARGS.p && ARGS.p == "relative";
if (glob.match(/\*|\?/)) { var SOURCES_CONTENT = {};
var dir = path.dirname(glob); var index = 0;
try {
var entries = fs.readdirSync(dir); !function cb() {
} catch (ex) {} if (index == files.length) return done();
if (entries) { var file = files[index++];
var pattern = "^" + path.basename(glob) read_whole_file(file, function (err, code) {
.replace(/[.+^$[\]\\(){}]/g, "\\$&") if (err) {
.replace(/\*/g, "[^/\\\\]*") print_error("ERROR: can't read file: " + file);
.replace(/\?/g, "[^/\\\\]") + "$"; process.exit(1);
var mod = process.platform === "win32" ? "i" : "";
var rx = new RegExp(pattern, mod);
var results = entries.filter(function(name) {
return rx.test(name);
}).map(function(name) {
return path.join(dir, name);
});
if (results.length) return results;
} }
} if (ORIG_MAP == "inline") {
return [ glob ]; ORIG_MAP = read_source_map(code);
} }
if (ARGS.p != null) {
function read_file(path, default_value) { if (P_RELATIVE) {
try { file = path.relative(path.dirname(ARGS.source_map), file).replace(/\\/g, '/');
return fs.readFileSync(path, "utf8"); } else {
} catch (ex) { var p = parseInt(ARGS.p, 10);
if (ex.code == "ENOENT" && default_value != null) return default_value; if (!isNaN(p)) {
fatal("ERROR: " + ex.message); file = file.replace(/^\/+/, "").split(/\/+/).slice(ARGS.p).join("/");
}
}
function parse_js(flag, constants) {
return function(value, options) {
options = options || {};
try {
UglifyJS.minify(value, {
parse: {
expression: true
},
compress: false,
mangle: false,
output: {
ast: true,
code: false
} }
}).ast.walk(new UglifyJS.TreeWalker(function(node) { }
if (node instanceof UglifyJS.AST_Assign) { }
var name = node.left.print_to_string(); SOURCES_CONTENT[file] = code;
var value = node.right; time_it("parse", function(){
if (!constants) { if (ARGS.spidermonkey) {
options[name] = value; var program = JSON.parse(code);
} else if (value instanceof UglifyJS.AST_Array) { if (!TOPLEVEL) TOPLEVEL = program;
options[name] = value.elements.map(to_string); else TOPLEVEL.body = TOPLEVEL.body.concat(program.body);
} else { }
options[name] = to_string(value); else if (ARGS.acorn) {
} TOPLEVEL = acorn.parse(code, {
return true; locations : true,
} sourceFile : file,
if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_PropAccess) { program : TOPLEVEL
var name = node.print_to_string(); });
options[name] = true; }
return true; else {
} try {
if (!(node instanceof UglifyJS.AST_Sequence)) throw node; TOPLEVEL = UglifyJS.parse(code, {
filename : file,
function to_string(value) { toplevel : TOPLEVEL,
return value instanceof UglifyJS.AST_Constant ? value.getValue() : value.print_to_string({ expression : ARGS.expr,
quote_keys: true bare_returns : ARGS.bare_returns,
}); });
} catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Parse error at " + file + ":" + ex.line + "," + ex.col);
var col = ex.col;
var lines = code.split(/\r?\n/);
var line = lines[ex.line - 1];
if (!line && !col) {
line = lines[ex.line - 2];
col = line.length;
}
if (line) {
if (col > 40) {
line = line.slice(col - 40);
col = 40;
}
print_error(line.slice(0, 80));
print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
}
print_error(ex.stack);
process.exit(1);
}
throw ex;
} }
})); };
} catch(ex) { });
fatal("Error parsing arguments for '" + flag + "': " + value); cb();
} });
return options; }();
}
}
function parse_source_map() { function done() {
var parse = parse_js("sourceMap", true); var OUTPUT_FILE = ARGS.o;
return function(value, options) {
var hasContent = options && options.sourceMap && "content" in options.sourceMap;
var settings = parse(value, options);
if (!hasContent && settings.content && settings.content != "inline") {
console.error("INFO: Using input source map:", settings.content);
settings.content = read_file(settings.content, settings.content);
}
return settings;
}
}
function to_cache(key) { var SOURCE_MAP = (ARGS.source_map || ARGS.source_map_inline) ? UglifyJS.SourceMap({
if (cache[key]) { file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
cache[key].props = UglifyJS.Dictionary.fromObject(cache[key].props); root: ARGS.source_map_root || ORIG_MAP && ORIG_MAP.sourceRoot,
orig: ORIG_MAP,
}) : null;
OUTPUT_OPTIONS.source_map = SOURCE_MAP;
try {
var output = UglifyJS.OutputStream(OUTPUT_OPTIONS);
var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
} catch(ex) {
if (ex instanceof UglifyJS.DefaultsError) {
print_error(ex.message);
print_error("Supported options:");
print_error(sys.inspect(ex.defs));
process.exit(1);
}
}
if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){
TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
});
if (ARGS.wrap != null) {
TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all);
}
if (ARGS.enclose != null) {
var arg_parameter_list = ARGS.enclose;
if (arg_parameter_list === true) {
arg_parameter_list = [];
}
else if (!(arg_parameter_list instanceof Array)) {
arg_parameter_list = [arg_parameter_list];
}
TOPLEVEL = TOPLEVEL.wrap_enclose(arg_parameter_list);
}
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,
ignore_quoted : ARGS.mangle_props == 2,
debug : typeof ARGS.mangle_props_debug === "undefined" ? false : ARGS.mangle_props_debug
});
writeNameCache("props", cache);
})();
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint
var TL_CACHE = readNameCache("vars");
if (MANGLE) MANGLE.cache = TL_CACHE;
if (SCOPE_IS_NEEDED) {
time_it("scope", function(){
TOPLEVEL.figure_out_scope(MANGLE || { screw_ie8: screw_ie8, cache: TL_CACHE });
if (ARGS.lint) {
TOPLEVEL.scope_warnings();
}
});
}
if (COMPRESS) {
time_it("squeeze", function(){
TOPLEVEL = compressor.compress(TOPLEVEL);
});
}
if (SCOPE_IS_NEEDED) {
time_it("scope", function(){
TOPLEVEL.figure_out_scope(MANGLE || { screw_ie8: screw_ie8, cache: TL_CACHE });
if (MANGLE && !TL_CACHE) {
TOPLEVEL.compute_char_frequency(MANGLE);
}
});
}
if (MANGLE) time_it("mangle", function(){
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)) {
SOURCE_MAP.get().setSourceContent(file, SOURCES_CONTENT[file]);
}
}
}
if (ARGS.dump_spidermonkey_ast) {
print(JSON.stringify(TOPLEVEL.to_mozilla_ast(), null, 2));
} else { } else {
cache[key] = { time_it("generate", function(){
cname: -1, TOPLEVEL.print(output);
props: new UglifyJS.Dictionary() });
};
output = output.get();
if (SOURCE_MAP) {
if (ARGS.source_map_inline) {
var base64_string = new Buffer(SOURCE_MAP.toString()).toString('base64');
output += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + base64_string;
} else {
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
var source_map_url = ARGS.source_map_url || (
P_RELATIVE
? path.relative(path.dirname(OUTPUT_FILE), ARGS.source_map)
: ARGS.source_map
);
output += "\n//# sourceMappingURL=" + source_map_url;
}
}
if (OUTPUT_FILE) {
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
} else {
print(output);
}
}
if (ARGS.stats) {
print_error(UglifyJS.string_template("Timing information (compressed {count} files):", {
count: files.length
}));
for (var i in STATS) if (STATS.hasOwnProperty(i)) {
print_error(UglifyJS.string_template("- {name}: {time}s", {
name: i,
time: (STATS[i] / 1000).toFixed(3)
}));
}
} }
return cache[key];
} }
function skip_key(key) { /* -----[ functions ]----- */
return skip_keys.indexOf(key) >= 0;
function normalize(o) {
for (var i in o) if (o.hasOwnProperty(i) && /-/.test(i)) {
o[i.replace(/-/g, "_")] = o[i];
delete o[i];
}
}
function getOptions(flag, constants) {
var x = ARGS[flag];
if (x == null || x === false) return null;
var ret = {};
if (x !== "") {
if (Array.isArray(x)) x = x.map(function (v) { return "(" + v + ")"; }).join(", ");
var ast;
try {
ast = UglifyJS.parse(x, { cli: true, expression: true });
} catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Error parsing arguments for flag `" + flag + "': " + x);
process.exit(1);
}
}
ast.walk(new UglifyJS.TreeWalker(function(node){
if (node instanceof UglifyJS.AST_Seq) return; // descend
if (node instanceof UglifyJS.AST_Assign) {
var name = node.left.print_to_string().replace(/-/g, "_");
var value = node.right;
if (constants)
value = new Function("return (" + value.print_to_string() + ")")();
ret[name] = value;
return true; // no descend
}
if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_Binary) {
var name = node.print_to_string().replace(/-/g, "_");
ret[name] = true;
return true; // no descend
}
print_error(node.TYPE)
print_error("Error parsing arguments for flag `" + flag + "': " + x);
process.exit(1);
}));
}
return ret;
}
function read_whole_file(filename, cb) {
if (filename == "-") {
var chunks = [];
process.stdin.setEncoding('utf-8');
process.stdin.on('data', function (chunk) {
chunks.push(chunk);
}).on('end', function () {
cb(null, chunks.join(""));
});
process.openStdin();
} else {
fs.readFile(filename, "utf-8", cb);
}
}
function read_source_map(code) {
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code);
if (!match) {
print_error("WARN: inline source map not found");
return null;
}
return JSON.parse(new Buffer(match[2], "base64"));
}
function time_it(name, cont) {
var t1 = new Date().getTime();
var ret = cont();
if (ARGS.stats) {
var spent = new Date().getTime() - t1;
if (STATS[name]) STATS[name] += spent;
else STATS[name] = spent;
}
return ret;
}
function print_error(msg) {
console.error("%s", msg);
}
function print(txt) {
console.log("%s", txt);
} }

View File

@@ -182,13 +182,21 @@ var AST_BlockStatement = DEFNODE("BlockStatement", null, {
}, AST_Block); }, AST_Block);
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, { var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
$documentation: "The empty statement (empty block or simply a semicolon)" $documentation: "The empty statement (empty block or simply a semicolon)",
_walk: function(visitor) {
return visitor._visit(this);
}
}, AST_Statement); }, AST_Statement);
var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", { var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
$documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`", $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
$propdoc: { $propdoc: {
body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement" body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.body._walk(visitor);
});
} }
}, AST_Statement); }, AST_Statement);
@@ -318,13 +326,62 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
$propdoc: { $propdoc: {
globals: "[Object/S] a map of name -> SymbolDef for all undeclared names", globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
}, },
wrap_commonjs: function(name) { wrap_enclose: function(arg_parameter_pairs) {
var body = this.body; var self = this;
var wrapped_tl = "(function(exports){'$ORIG';})(typeof " + name + "=='undefined'?(" + name + "={}):" + name + ");"; var args = [];
var parameters = [];
arg_parameter_pairs.forEach(function(pair) {
var splitAt = pair.lastIndexOf(":");
args.push(pair.substr(0, splitAt));
parameters.push(pair.substr(splitAt + 1));
});
var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
wrapped_tl = parse(wrapped_tl); wrapped_tl = parse(wrapped_tl);
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
if (node instanceof AST_Directive && node.value == "$ORIG") { if (node instanceof AST_Directive && node.value == "$ORIG") {
return MAP.splice(body); return MAP.splice(self.body);
}
}));
return wrapped_tl;
},
wrap_commonjs: function(name, export_all) {
var self = this;
var to_export = [];
if (export_all) {
self.figure_out_scope();
self.walk(new TreeWalker(function(node){
if (node instanceof AST_SymbolDeclaration && node.definition().global) {
if (!find_if(function(n){ return n.name == node.name }, to_export))
to_export.push(node);
}
}));
}
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_Directive) {
switch (node.value) {
case "$ORIG":
return MAP.splice(self.body);
case "$EXPORTS":
var body = [];
to_export.forEach(function(sym){
body.push(new AST_SimpleStatement({
body: new AST_Assign({
left: new AST_Sub({
expression: new AST_SymbolRef({ name: "exports" }),
property: new AST_String({ value: sym.name }),
}),
operator: "=",
right: new AST_SymbolRef(sym),
}),
}));
});
return MAP.splice(body);
}
} }
})); }));
return wrapped_tl; return wrapped_tl;
@@ -544,11 +601,11 @@ var AST_Call = DEFNODE("Call", "expression args", {
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
this.expression._walk(visitor);
var args = this.args; var args = this.args;
for (var i = 0, len = args.length; i < len; i++) { for (var i = 0, len = args.length; i < len; i++) {
args[i]._walk(visitor); args[i]._walk(visitor);
} }
this.expression._walk(visitor);
}); });
} }
}); });
@@ -557,16 +614,68 @@ var AST_New = DEFNODE("New", null, {
$documentation: "An object instantiation. Derives from a function call since it has exactly the same properties" $documentation: "An object instantiation. Derives from a function call since it has exactly the same properties"
}, AST_Call); }, AST_Call);
var AST_Sequence = DEFNODE("Sequence", "expressions", { var AST_Seq = DEFNODE("Seq", "car cdr", {
$documentation: "A sequence expression (comma-separated expressions)", $documentation: "A sequence expression (two comma-separated expressions)",
$propdoc: { $propdoc: {
expressions: "[AST_Node*] array of expressions (at least two)" car: "[AST_Node] first element in sequence",
cdr: "[AST_Node] second element in sequence"
},
$cons: function(x, y) {
var seq = new AST_Seq(x);
seq.car = x;
seq.cdr = y;
return seq;
},
$from_array: function(array) {
if (array.length == 0) return null;
if (array.length == 1) return array[0].clone();
var list = null;
for (var i = array.length; --i >= 0;) {
list = AST_Seq.cons(array[i], list);
}
var p = list;
while (p) {
if (p.cdr && !p.cdr.cdr) {
p.cdr = p.cdr.car;
break;
}
p = p.cdr;
}
return list;
},
to_array: function() {
var p = this, a = [];
while (p) {
a.push(p.car);
if (p.cdr && !(p.cdr instanceof AST_Seq)) {
a.push(p.cdr);
break;
}
p = p.cdr;
}
return a;
},
add: function(node) {
var p = this;
while (p) {
if (!(p.cdr instanceof AST_Seq)) {
var cell = AST_Seq.cons(p.cdr, node);
return p.cdr = cell;
}
p = p.cdr;
}
},
len: function() {
if (this.cdr instanceof AST_Seq) {
return this.cdr.len() + 1;
} else {
return 2;
}
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
this.expressions.forEach(function(node) { this.car._walk(visitor);
node._walk(visitor); if (this.cdr) this.cdr._walk(visitor);
});
}); });
} }
}); });
@@ -619,7 +728,7 @@ var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
$documentation: "Unary postfix expression, i.e. `i++`" $documentation: "Unary postfix expression, i.e. `i++`"
}, AST_Unary); }, AST_Unary);
var AST_Binary = DEFNODE("Binary", "operator left right", { var AST_Binary = DEFNODE("Binary", "left operator right", {
$documentation: "Binary expression, i.e. `a + b`", $documentation: "Binary expression, i.e. `a + b`",
$propdoc: { $propdoc: {
left: "[AST_Node] left-hand side expression", left: "[AST_Node] left-hand side expression",
@@ -689,8 +798,8 @@ var AST_Object = DEFNODE("Object", "properties", {
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", { var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
$documentation: "Base class for literal object properties", $documentation: "Base class for literal object properties",
$propdoc: { $propdoc: {
key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an arbitrary AST_Node.", key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an AST_SymbolAccessor.",
value: "[AST_Node] property value. For setters and getters this is an AST_Function." value: "[AST_Node] property value. For setters and getters this is an AST_Accessor."
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
@@ -873,7 +982,7 @@ TreeWalker.prototype = {
parent: function(n) { parent: function(n) {
return this.stack[this.stack.length - 2 - (n || 0)]; return this.stack[this.stack.length - 2 - (n || 0)];
}, },
push: function(node) { push: function (node) {
if (node instanceof AST_Lambda) { if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives); this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive && !this.directives[node.value]) { } else if (node instanceof AST_Directive && !this.directives[node.value]) {

File diff suppressed because it is too large Load Diff

View File

@@ -1,154 +0,0 @@
"use strict";
var to_ascii = typeof atob == "undefined" ? function(b64) {
return new Buffer(b64, "base64").toString();
} : atob;
var to_base64 = typeof btoa == "undefined" ? function(str) {
return new Buffer(str).toString("base64");
} : btoa;
function read_source_map(code) {
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code);
if (!match) {
AST_Node.warn("inline source map not found");
return null;
}
return to_ascii(match[2]);
}
function set_shorthand(name, options, keys) {
if (options[name]) {
keys.forEach(function(key) {
if (options[key]) {
if (typeof options[key] != "object") options[key] = {};
if (!(name in options[key])) options[key][name] = options[name];
}
});
}
}
function minify(files, options) {
var warn_function = AST_Node.warn_function;
try {
if (typeof files == "string") {
files = [ files ];
}
options = defaults(options, {
compress: {},
ie8: false,
keep_fnames: false,
mangle: {},
output: {},
parse: {},
sourceMap: false,
toplevel: false,
warnings: false,
wrap: false,
}, true);
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]);
set_shorthand("warnings", options, [ "compress" ]);
if (options.mangle) {
options.mangle = defaults(options.mangle, {
cache: null,
eval: false,
ie8: false,
keep_fnames: false,
properties: false,
reserved: [],
toplevel: false,
}, true);
}
if (options.sourceMap) {
options.sourceMap = defaults(options.sourceMap, {
content: null,
filename: null,
includeSources: false,
root: null,
url: null,
}, true);
}
var warnings = [];
if (options.warnings && !AST_Node.warn_function) {
AST_Node.warn_function = function(warning) {
warnings.push(warning);
};
}
var toplevel;
if (files instanceof AST_Toplevel) {
toplevel = files;
} else {
options.parse = options.parse || {};
options.parse.toplevel = null;
for (var name in files) {
options.parse.filename = name;
options.parse.toplevel = parse(files[name], options.parse);
if (options.sourceMap && options.sourceMap.content == "inline") {
if (Object.keys(files).length > 1)
throw new Error("inline source map only works with singular input");
options.sourceMap.content = read_source_map(files[name]);
}
}
toplevel = options.parse.toplevel;
}
if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap);
}
if (options.compress) {
toplevel.figure_out_scope(options.mangle);
toplevel = new Compressor(options.compress).compress(toplevel);
}
if (options.mangle) {
toplevel.figure_out_scope(options.mangle);
base54.reset();
toplevel.compute_char_frequency(options.mangle);
toplevel.mangle_names(options.mangle);
if (options.mangle.properties) {
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
}
var result = {};
if (options.output.ast) {
result.ast = toplevel;
}
if (!HOP(options.output, "code") || options.output.code) {
if (options.sourceMap) {
if (typeof options.sourceMap.content == "string") {
options.sourceMap.content = JSON.parse(options.sourceMap.content);
}
options.output.source_map = SourceMap({
file: options.sourceMap.filename,
orig: options.sourceMap.content,
root: options.sourceMap.root
});
if (options.sourceMap.includeSources) {
for (var name in files) {
options.output.source_map.get().setSourceContent(name, files[name]);
}
}
}
delete options.output.ast;
delete options.output.code;
var stream = OutputStream(options.output);
toplevel.print(stream);
result.code = stream.get();
if (options.sourceMap) {
result.map = options.output.source_map.toString();
if (options.sourceMap.url == "inline") {
result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(result.map);
} else if (options.sourceMap.url) {
result.code += "\n//# sourceMappingURL=" + options.sourceMap.url;
}
}
}
if (warnings.length) {
result.warnings = warnings;
}
return result;
} catch (ex) {
return { error: ex };
} finally {
AST_Node.warn_function = warn_function;
}
}

View File

@@ -111,23 +111,19 @@
}, },
Property: function(M) { Property: function(M) {
var key = M.key; var key = M.key;
var name = key.type == "Identifier" ? key.name : key.value;
var args = { var args = {
start : my_start_token(key), start : my_start_token(key),
end : my_end_token(M.value), end : my_end_token(M.value),
key : name, key : key.type == "Identifier" ? key.name : key.value,
value : from_moz(M.value) value : from_moz(M.value)
}; };
switch (M.kind) { if (M.kind == "init") return new AST_ObjectKeyVal(args);
case "init": args.key = new AST_SymbolAccessor({
return new AST_ObjectKeyVal(args); name: args.key
case "set": });
args.value.name = from_moz(key); args.value = new AST_Accessor(args.value);
return new AST_ObjectSetter(args); if (M.kind == "get") return new AST_ObjectGetter(args);
case "get": if (M.kind == "set") return new AST_ObjectSetter(args);
args.value.name = from_moz(key);
return new AST_ObjectGetter(args);
}
}, },
ArrayExpression: function(M) { ArrayExpression: function(M) {
return new AST_Array({ return new AST_Array({
@@ -149,11 +145,7 @@
}); });
}, },
SequenceExpression: function(M) { SequenceExpression: function(M) {
return new AST_Sequence({ return AST_Seq.from_array(M.expressions.map(from_moz));
start : my_start_token(M),
end : my_end_token(M),
expressions: M.expressions.map(from_moz)
});
}, },
MemberExpression: function(M) { MemberExpression: function(M) {
return new (M.computed ? AST_Sub : AST_Dot)({ return new (M.computed ? AST_Sub : AST_Dot)({
@@ -260,10 +252,7 @@
map("CallExpression", AST_Call, "callee>expression, arguments@args"); map("CallExpression", AST_Call, "callee>expression, arguments@args");
def_to_moz(AST_Toplevel, function To_Moz_Program(M) { def_to_moz(AST_Toplevel, function To_Moz_Program(M) {
return { return to_moz_scope("Program", M);
type: "Program",
body: M.body.map(to_moz)
};
}); });
def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) { def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) {
@@ -271,7 +260,7 @@
type: "FunctionDeclaration", type: "FunctionDeclaration",
id: to_moz(M.name), id: to_moz(M.name),
params: M.argnames.map(to_moz), params: M.argnames.map(to_moz),
body: to_moz_block(M) body: to_moz_scope("BlockStatement", M)
} }
}); });
@@ -280,7 +269,7 @@
type: "FunctionExpression", type: "FunctionExpression",
id: to_moz(M.name), id: to_moz(M.name),
params: M.argnames.map(to_moz), params: M.argnames.map(to_moz),
body: to_moz_block(M) body: to_moz_scope("BlockStatement", M)
} }
}); });
@@ -336,10 +325,10 @@
}; };
}); });
def_to_moz(AST_Sequence, function To_Moz_SequenceExpression(M) { def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) {
return { return {
type: "SequenceExpression", type: "SequenceExpression",
expressions: M.expressions.map(to_moz) expressions: M.to_array().map(to_moz)
}; };
}); });
@@ -386,11 +375,10 @@
}); });
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) { def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
var key = ( var key = {
is_identifier(M.key) type: "Literal",
? {type: "Identifier", name: M.key} value: M.key instanceof AST_SymbolAccessor ? M.key.name : M.key
: {type: "Literal", value: M.key} };
);
var kind; var kind;
if (M instanceof AST_ObjectKeyVal) { if (M instanceof AST_ObjectKeyVal) {
kind = "init"; kind = "init";
@@ -551,8 +539,8 @@
moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + 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 exports, my_start_token, my_end_token, from_moz
); );
me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")( me_to_moz = new Function("to_moz", "to_moz_block", "to_moz_scope", "return(" + me_to_moz + ")")(
to_moz, to_moz_block to_moz, to_moz_block, to_moz_scope
); );
MOZ_TO_ME[moztype] = moz_to_me; MOZ_TO_ME[moztype] = moz_to_me;
def_to_moz(mytype, me_to_moz); def_to_moz(mytype, me_to_moz);
@@ -610,4 +598,14 @@
}; };
}; };
function to_moz_scope(type, node) {
var body = node.body.map(to_moz);
if (node.body[0] instanceof AST_SimpleStatement && node.body[0].body instanceof AST_String) {
body.unshift(to_moz(new AST_EmptyStatement(node.body[0])));
}
return {
type: type,
body: body
};
};
})(); })();

View File

@@ -57,7 +57,6 @@ function OutputStream(options) {
beautify : false, beautify : false,
bracketize : false, bracketize : false,
comments : false, comments : false,
ie8 : false,
indent_level : 4, indent_level : 4,
indent_start : 0, indent_start : 0,
inline_script : true, inline_script : true,
@@ -67,6 +66,7 @@ function OutputStream(options) {
preserve_line : false, preserve_line : false,
quote_keys : false, quote_keys : false,
quote_style : 0, quote_style : 0,
screw_ie8 : true,
semicolons : true, semicolons : true,
shebang : true, shebang : true,
source_map : null, source_map : null,
@@ -136,7 +136,7 @@ function OutputStream(options) {
case "\t": return "\\t"; case "\t": return "\\t";
case "\b": return "\\b"; case "\b": return "\\b";
case "\f": return "\\f"; case "\f": return "\\f";
case "\x0B": return options.ie8 ? "\\x0B" : "\\v"; case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B";
case "\u2028": return "\\u2028"; case "\u2028": return "\\u2028";
case "\u2029": return "\\u2029"; case "\u2029": return "\\u2029";
case "\ufeff": return "\\ufeff"; case "\ufeff": return "\\ufeff";
@@ -588,7 +588,7 @@ function OutputStream(options) {
|| p instanceof AST_Call && p.expression === this; || p instanceof AST_Call && p.expression === this;
}); });
PARENS(AST_Sequence, function(output){ PARENS(AST_Seq, function(output){
var p = output.parent(); var p = output.parent();
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4) return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
|| p instanceof AST_Unary // !(foo, bar, baz) || p instanceof AST_Unary // !(foo, bar, baz)
@@ -677,7 +677,7 @@ function OutputStream(options) {
} }
}); });
PARENS([ AST_Assign, AST_Conditional ], function(output){ PARENS([ AST_Assign, AST_Conditional ], function (output){
var p = output.parent(); var p = output.parent();
// !(a = false) → true // !(a = false) → true
if (p instanceof AST_Unary) if (p instanceof AST_Unary)
@@ -902,7 +902,7 @@ function OutputStream(options) {
function make_then(self, output) { function make_then(self, output) {
var b = self.body; var b = self.body;
if (output.option("bracketize") if (output.option("bracketize")
|| output.option("ie8") && b instanceof AST_Do) || !output.option("screw_ie8") && b instanceof AST_Do)
return make_block(b, output); return make_block(b, output);
// The squeezer replaces "block"-s that contain only a single // The squeezer replaces "block"-s that contain only a single
// statement with the statement itself; technically, the AST // statement with the statement itself; technically, the AST
@@ -1083,19 +1083,18 @@ function OutputStream(options) {
AST_Call.prototype._codegen(self, output); AST_Call.prototype._codegen(self, output);
}); });
AST_Sequence.DEFMETHOD("_do_print", function(output){ AST_Seq.DEFMETHOD("_do_print", function(output){
this.expressions.forEach(function(node, index) { this.car.print(output);
if (index > 0) { if (this.cdr) {
output.comma(); output.comma();
if (output.should_break()) { if (output.should_break()) {
output.newline(); output.newline();
output.indent(); output.indent();
}
} }
node.print(output); this.cdr.print(output);
}); }
}); });
DEFPRINT(AST_Sequence, function(self, output){ DEFPRINT(AST_Seq, function(self, output){
self._do_print(output); self._do_print(output);
// var p = output.parent(); // var p = output.parent();
// if (p instanceof AST_Statement) { // if (p instanceof AST_Statement) {
@@ -1218,7 +1217,7 @@ function OutputStream(options) {
&& +key + "" == key) && +key + "" == key)
&& parseFloat(key) >= 0) { && parseFloat(key) >= 0) {
output.print(make_num(key)); output.print(make_num(key));
} else if (RESERVED_WORDS(key) ? !output.option("ie8") : is_identifier_string(key)) { } else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
if (quote && output.option("keep_quoted_props")) { if (quote && output.option("keep_quoted_props")) {
output.print_string(key, quote); output.print_string(key, quote);
} else { } else {

View File

@@ -285,7 +285,11 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) || S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) ||
(type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) || (type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
(type == "punc" && PUNC_BEFORE_EXPRESSION(value))); (type == "punc" && PUNC_BEFORE_EXPRESSION(value)));
prev_was_dot = (type == "punc" && value == "."); if (type == "punc" && value == ".") {
prev_was_dot = true;
} else if (!is_comment) {
prev_was_dot = false;
}
var ret = { var ret = {
type : type, type : type,
value : value, value : value,
@@ -629,7 +633,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
} }
next_token.has_directive = function(directive) { next_token.has_directive = function(directive) {
return S.directives[directive] > 0; return S.directives[directive] !== undefined &&
S.directives[directive] > 0;
} }
return next_token; return next_token;
@@ -688,6 +693,7 @@ function parse($TEXT, options) {
options = defaults(options, { options = defaults(options, {
bare_returns : false, bare_returns : false,
cli : false,
expression : false, expression : false,
filename : null, filename : null,
html5_comments : true, html5_comments : true,
@@ -801,28 +807,23 @@ function parse($TEXT, options) {
}; };
var statement = embed_tokens(function() { var statement = embed_tokens(function() {
var tmp;
handle_regexp(); handle_regexp();
switch (S.token.type) { switch (S.token.type) {
case "string": case "string":
var dir = false; if (S.in_directives) {
if (S.in_directives === true) { var token = peek();
if ((is_token(peek(), "punc", ";") || peek().nlb) && S.token.raw.indexOf("\\") === -1) { if (S.token.raw.indexOf("\\") == -1
&& (token.nlb
|| is_token(token, "eof")
|| is_token(token, "punc", ";")
|| is_token(token, "punc", "}"))) {
S.input.add_directive(S.token.value); S.input.add_directive(S.token.value);
} else { } else {
S.in_directives = false; S.in_directives = false;
} }
} }
var dir = S.in_directives, stat = simple_statement(); var dir = S.in_directives, stat = simple_statement();
if (dir) { return dir ? new AST_Directive(stat.body) : stat;
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 "num":
case "regexp": case "regexp":
case "operator": case "operator":
@@ -854,75 +855,103 @@ function parse($TEXT, options) {
} }
case "keyword": case "keyword":
switch (tmp = S.token.value, next(), tmp) { switch (S.token.value) {
case "break": case "break":
next();
return break_cont(AST_Break); return break_cont(AST_Break);
case "continue": case "continue":
next();
return break_cont(AST_Continue); return break_cont(AST_Continue);
case "debugger": case "debugger":
next();
semicolon(); semicolon();
return new AST_Debugger(); return new AST_Debugger();
case "do": case "do":
next();
var body = in_loop(statement);
expect_token("keyword", "while");
var condition = parenthesised();
semicolon(true);
return new AST_Do({ return new AST_Do({
body : in_loop(statement), body : body,
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(true), tmp) condition : condition
}); });
case "while": case "while":
next();
return new AST_While({ return new AST_While({
condition : parenthesised(), condition : parenthesised(),
body : in_loop(statement) body : in_loop(statement)
}); });
case "for": case "for":
next();
return for_(); return for_();
case "function": case "function":
next();
return function_(AST_Defun); return function_(AST_Defun);
case "if": case "if":
next();
return if_(); return if_();
case "return": case "return":
if (S.in_function == 0 && !options.bare_returns) if (S.in_function == 0 && !options.bare_returns)
croak("'return' outside of function"); croak("'return' outside of function");
next();
var value = null;
if (is("punc", ";")) {
next();
} else if (!can_insert_semicolon()) {
value = expression(true);
semicolon();
}
return new AST_Return({ return new AST_Return({
value: ( is("punc", ";") value: value
? (next(), null)
: can_insert_semicolon()
? null
: (tmp = expression(true), semicolon(), tmp) )
}); });
case "switch": case "switch":
next();
return new AST_Switch({ return new AST_Switch({
expression : parenthesised(), expression : parenthesised(),
body : in_loop(switch_body_) body : in_loop(switch_body_)
}); });
case "throw": case "throw":
next();
if (S.token.nlb) if (S.token.nlb)
croak("Illegal newline after 'throw'"); croak("Illegal newline after 'throw'");
var value = expression(true);
semicolon();
return new AST_Throw({ return new AST_Throw({
value: (tmp = expression(true), semicolon(), tmp) value: value
}); });
case "try": case "try":
next();
return try_(); return try_();
case "var": case "var":
return tmp = var_(), semicolon(), tmp; next();
var node = var_();
semicolon();
return node;
case "const": case "const":
return tmp = const_(), semicolon(), tmp; next();
var node = const_();
semicolon();
return node;
case "with": case "with":
if (S.input.has_directive("use strict")) { if (S.input.has_directive("use strict")) {
croak("Strict mode may not include a with statement"); croak("Strict mode may not include a with statement");
} }
next();
return new AST_With({ return new AST_With({
expression : parenthesised(), expression : parenthesised(),
body : statement() body : statement()
@@ -1032,32 +1061,29 @@ function parse($TEXT, options) {
if (in_statement && !name) if (in_statement && !name)
unexpected(); unexpected();
expect("("); expect("(");
var argnames = [];
for (var first = true; !is("punc", ")");) {
if (first) first = false; else expect(",");
argnames.push(as_symbol(AST_SymbolFunarg));
}
next();
var loop = S.in_loop;
var labels = S.labels;
++S.in_function;
S.in_directives = true;
S.input.push_directives_stack();
S.in_loop = 0;
S.labels = [];
var body = block_();
if (S.input.has_directive("use strict")) {
if (name) strict_verify_symbol(name);
argnames.forEach(strict_verify_symbol);
}
S.input.pop_directives_stack();
--S.in_function;
S.in_loop = loop;
S.labels = labels;
return new ctor({ return new ctor({
name: name, name: name,
argnames: argnames, argnames: (function(first, a){
body: body while (!is("punc", ")")) {
if (first) first = false; else expect(",");
a.push(as_symbol(AST_SymbolFunarg));
}
next();
return a;
})(true, []),
body: (function(loop, labels){
++S.in_function;
S.in_directives = true;
S.input.push_directives_stack();
S.in_loop = 0;
S.labels = [];
var a = block_();
S.input.pop_directives_stack();
--S.in_function;
S.in_loop = loop;
S.labels = labels;
return a;
})(S.in_loop, S.labels)
}); });
}; };
@@ -1159,10 +1185,7 @@ function parse($TEXT, options) {
a.push(new AST_VarDef({ a.push(new AST_VarDef({
start : S.token, start : S.token,
name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar), name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar),
value : is("operator", "=") value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
? (next(), expression(false, no_in))
: in_const && S.input.has_directive("use strict")
? croak("Missing initializer in const declaration") : null,
end : prev() end : prev()
})); }));
if (!is("punc", ",")) if (!is("punc", ","))
@@ -1324,10 +1347,15 @@ function parse($TEXT, options) {
var type = start.type; var type = start.type;
var name = as_property_name(); var name = as_property_name();
if (type == "name" && !is("punc", ":")) { if (type == "name" && !is("punc", ":")) {
var key = new AST_SymbolAccessor({
start: S.token,
name: as_property_name(),
end: prev()
});
if (name == "get") { if (name == "get") {
a.push(new AST_ObjectGetter({ a.push(new AST_ObjectGetter({
start : start, start : start,
key : as_atom_node(), key : key,
value : create_accessor(), value : create_accessor(),
end : prev() end : prev()
})); }));
@@ -1336,7 +1364,7 @@ function parse($TEXT, options) {
if (name == "set") { if (name == "set") {
a.push(new AST_ObjectSetter({ a.push(new AST_ObjectSetter({
start : start, start : start,
key : as_atom_node(), key : key,
value : create_accessor(), value : create_accessor(),
end : prev() end : prev()
})); }));
@@ -1389,20 +1417,12 @@ function parse($TEXT, options) {
}); });
}; };
function strict_verify_symbol(sym) {
if (sym.name == "arguments" || sym.name == "eval")
croak("Unexpected " + sym.name + " in strict mode", sym.start.line, sym.start.col, sym.start.pos);
}
function as_symbol(type, noerror) { function as_symbol(type, noerror) {
if (!is("name")) { if (!is("name")) {
if (!noerror) croak("Name expected"); if (!noerror) croak("Name expected");
return null; return null;
} }
var sym = _make_symbol(type); var sym = _make_symbol(type);
if (S.input.has_directive("use strict") && sym instanceof AST_SymbolDeclaration) {
strict_verify_symbol(sym);
}
next(); next();
return sym; return sym;
}; };
@@ -1463,17 +1483,8 @@ function parse($TEXT, options) {
function make_unary(ctor, token, expr) { function make_unary(ctor, token, expr) {
var op = token.value; var op = token.value;
switch (op) { if ((op == "++" || op == "--") && !is_assignable(expr))
case "++": croak("Invalid use of " + op + " operator", token.line, token.col, token.pos);
case "--":
if (!is_assignable(expr))
croak("Invalid use of " + op + " operator", token.line, token.col, token.pos);
break;
case "delete":
if (expr instanceof AST_SymbolRef && S.input.has_directive("use strict"))
croak("Calling delete on expression not allowed in strict mode", expr.start.line, expr.start.col, expr.start.pos);
break;
}
return new ctor({ operator: op, expression: expr }); return new ctor({ operator: op, expression: expr });
}; };
@@ -1518,6 +1529,7 @@ function parse($TEXT, options) {
}; };
function is_assignable(expr) { function is_assignable(expr) {
if (options.cli) return true;
return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef; return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef;
}; };
@@ -1542,18 +1554,17 @@ function parse($TEXT, options) {
var expression = function(commas, no_in) { var expression = function(commas, no_in) {
var start = S.token; var start = S.token;
var exprs = []; var expr = maybe_assign(no_in);
while (true) { if (commas && is("punc", ",")) {
exprs.push(maybe_assign(no_in));
if (!commas || !is("punc", ",")) break;
next(); next();
commas = true; return new AST_Seq({
start : start,
car : expr,
cdr : expression(true, no_in),
end : peek()
});
} }
return exprs.length == 1 ? exprs[0] : new AST_Sequence({ return expr;
start : start,
expressions : exprs,
end : peek()
});
}; };
function in_loop(cont) { function in_loop(cont) {

View File

@@ -43,16 +43,16 @@
"use strict"; "use strict";
function find_builtins(reserved) { function find_builtins() {
// NaN will be included due to Number.NaN // NaN will be included due to Number.NaN
[ var a = [
"null", "null",
"true", "true",
"false", "false",
"Infinity", "Infinity",
"-Infinity", "-Infinity",
"undefined", "undefined",
].forEach(add); ];
[ Object, Array, Function, Number, [ Object, Array, Function, Number,
String, Boolean, Error, Math, String, Boolean, Error, Math,
Date, RegExp Date, RegExp
@@ -63,23 +63,24 @@ function find_builtins(reserved) {
} }
}); });
function add(name) { function add(name) {
push_uniq(reserved, name); push_uniq(a, name);
} }
return a;
} }
function mangle_properties(ast, options) { function mangle_properties(ast, options) {
options = defaults(options, { options = defaults(options, {
builtins: false,
cache: null, cache: null,
debug: false, debug: false,
keep_quoted: false, ignore_quoted: false,
only_cache: false, only_cache: false,
regex: null, regex: null,
reserved: null, reserved: null,
}); });
var reserved = options.reserved || []; var reserved = options.reserved;
if (!options.builtins) find_builtins(reserved); if (reserved == null)
reserved = find_builtins();
var cache = options.cache; var cache = options.cache;
if (cache == null) { if (cache == null) {
@@ -90,12 +91,12 @@ function mangle_properties(ast, options) {
} }
var regex = options.regex; var regex = options.regex;
var keep_quoted = options.keep_quoted; var ignore_quoted = options.ignore_quoted;
// note debug is either false (disabled), or a string of the debug suffix to use (enabled). // note debug is either false (disabled), or a string of the debug suffix to use (enabled).
// note debug may be enabled as an empty string, which is falsey. Also treat passing 'true' // note debug may be enabled as an empty string, which is falsey. Also treat passing 'true'
// the same as passing an empty string. // the same as passing an empty string.
var debug = options.debug !== false; var debug = (options.debug !== false);
var debug_name_suffix; var debug_name_suffix;
if (debug) { if (debug) {
debug_name_suffix = (options.debug === true ? "" : options.debug); debug_name_suffix = (options.debug === true ? "" : options.debug);
@@ -103,12 +104,12 @@ function mangle_properties(ast, options) {
var names_to_mangle = []; var names_to_mangle = [];
var unmangleable = []; var unmangleable = [];
var to_keep = {}; var ignored = {};
// step 1: find candidates to mangle // step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){ ast.walk(new TreeWalker(function(node){
if (node instanceof AST_ObjectKeyVal) { if (node instanceof AST_ObjectKeyVal) {
add(node.key, keep_quoted && node.quote); add(node.key, ignore_quoted && node.quote);
} }
else if (node instanceof AST_ObjectProperty) { else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above // setter or getter, since KeyVal is handled above
@@ -118,14 +119,14 @@ function mangle_properties(ast, options) {
add(node.property); add(node.property);
} }
else if (node instanceof AST_Sub) { else if (node instanceof AST_Sub) {
addStrings(node.property, keep_quoted); addStrings(node.property, ignore_quoted);
} }
})); }));
// step 2: transform the tree, renaming properties // step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(function(node){ return ast.transform(new TreeTransformer(function(node){
if (node instanceof AST_ObjectKeyVal) { if (node instanceof AST_ObjectKeyVal) {
if (!(keep_quoted && node.quote)) if (!(ignore_quoted && node.quote))
node.key = mangle(node.key); node.key = mangle(node.key);
} }
else if (node instanceof AST_ObjectProperty) { else if (node instanceof AST_ObjectProperty) {
@@ -136,7 +137,7 @@ function mangle_properties(ast, options) {
node.property = mangle(node.property); node.property = mangle(node.property);
} }
else if (node instanceof AST_Sub) { else if (node instanceof AST_Sub) {
if (!keep_quoted) if (!ignore_quoted)
node.property = mangleStrings(node.property); node.property = mangleStrings(node.property);
} }
// else if (node instanceof AST_String) { // else if (node instanceof AST_String) {
@@ -166,16 +167,16 @@ function mangle_properties(ast, options) {
} }
function should_mangle(name) { function should_mangle(name) {
if (keep_quoted && name in to_keep) return false; if (ignore_quoted && name in ignored) return false;
if (regex && !regex.test(name)) return false; if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false; if (reserved.indexOf(name) >= 0) return false;
return cache.props.has(name) return cache.props.has(name)
|| names_to_mangle.indexOf(name) >= 0; || names_to_mangle.indexOf(name) >= 0;
} }
function add(name, keep) { function add(name, ignore) {
if (keep) { if (ignore) {
to_keep[name] = true; ignored[name] = true;
return; return;
} }
@@ -198,19 +199,19 @@ function mangle_properties(ast, options) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_. // debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_"; var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_";
if (can_mangle(debug_mangled) && !(keep_quoted && debug_mangled in to_keep)) { if (can_mangle(debug_mangled) && !(ignore_quoted && debug_mangled in ignored)) {
mangled = debug_mangled; mangled = debug_mangled;
} }
} }
// either debug mode is off, or it is on and we could not use the mangled name // either debug mode is off, or it is on and we could not use the mangled name
if (!mangled) { if (!mangled) {
// Note: `can_mangle()` does not check if the name collides with the `to_keep` set // note can_mangle() does not check if the name collides with the 'ignored' set
// (filled with quoted properties when `keep_quoted` is set). Make sure we add this // (filled with quoted properties when ignore_quoted set). Make sure we add this
// check so we don't collide with a quoted name. // check so we don't collide with a quoted name.
do { do {
mangled = base54(++cache.cname); mangled = base54(++cache.cname);
} while (!can_mangle(mangled) || keep_quoted && mangled in to_keep); } while (!can_mangle(mangled) || (ignore_quoted && mangled in ignored));
} }
cache.props.set(name, mangled); cache.props.set(name, mangled);
@@ -218,17 +219,17 @@ function mangle_properties(ast, options) {
return mangled; return mangled;
} }
function addStrings(node, keep) { function addStrings(node, ignore) {
var out = {}; var out = {};
try { try {
(function walk(node){ (function walk(node){
node.walk(new TreeWalker(function(node){ node.walk(new TreeWalker(function(node){
if (node instanceof AST_Sequence) { if (node instanceof AST_Seq) {
walk(node.expressions[node.expressions.length - 1]); walk(node.cdr);
return true; return true;
} }
if (node instanceof AST_String) { if (node instanceof AST_String) {
add(node.value, keep); add(node.value, ignore);
return true; return true;
} }
if (node instanceof AST_Conditional) { if (node instanceof AST_Conditional) {
@@ -246,9 +247,8 @@ function mangle_properties(ast, options) {
function mangleStrings(node) { function mangleStrings(node) {
return node.transform(new TreeTransformer(function(node){ return node.transform(new TreeTransformer(function(node){
if (node instanceof AST_Sequence) { if (node instanceof AST_Seq) {
var last = node.expressions.length - 1; node.cdr = mangleStrings(node.cdr);
node.expressions[last] = mangleStrings(node.expressions[last]);
} }
else if (node instanceof AST_String) { else if (node instanceof AST_String) {
node.value = mangle(node.value); node.value = mangle(node.value);
@@ -260,4 +260,5 @@ function mangle_properties(ast, options) {
return node; return node;
})); }));
} }
} }

View File

@@ -76,7 +76,7 @@ SymbolDef.prototype = {
else if (!this.mangled_name && !this.unmangleable(options)) { else if (!this.mangled_name && !this.unmangleable(options)) {
var s = this.scope; var s = this.scope;
var sym = this.orig[0]; var sym = this.orig[0];
if (options.ie8 && sym instanceof AST_SymbolLambda) if (!options.screw_ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope; s = s.parent_scope;
var def; var def;
if (this.defun && (def = this.defun.variables.get(this.name))) { if (this.defun && (def = this.defun.variables.get(this.name))) {
@@ -93,7 +93,7 @@ SymbolDef.prototype = {
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
options = defaults(options, { options = defaults(options, {
cache: null, cache: null,
ie8: false, screw_ie8: true,
}); });
// pass 1: setup scope chaining and handle definitions // pass 1: setup scope chaining and handle definitions
@@ -220,7 +220,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
self.walk(tw); self.walk(tw);
// pass 3: fix up any scoping issue with IE8 // pass 3: fix up any scoping issue with IE8
if (options.ie8) { if (!options.screw_ie8) {
self.walk(new TreeWalker(function(node, descend) { self.walk(new TreeWalker(function(node, descend) {
if (node instanceof AST_SymbolCatch) { if (node instanceof AST_SymbolCatch) {
var name = node.name; var name = node.name;
@@ -268,7 +268,7 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){
AST_Lambda.DEFMETHOD("init_scope_vars", function(){ AST_Lambda.DEFMETHOD("init_scope_vars", function(){
AST_Scope.prototype.init_scope_vars.apply(this, arguments); AST_Scope.prototype.init_scope_vars.apply(this, arguments);
this.uses_arguments = false; this.uses_arguments = false;
this.def_variable(new AST_SymbolConst({ this.def_variable(new AST_SymbolVar({
name: "arguments", name: "arguments",
start: this.start, start: this.start,
end: this.end end: this.end
@@ -325,8 +325,8 @@ AST_Scope.DEFMETHOD("next_mangled", function(options){
if (!is_identifier(m)) continue; // skip over "do" if (!is_identifier(m)) continue; // skip over "do"
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not // https://github.com/mishoo/UglifyJS2/issues/242 -- do not
// shadow a name reserved from mangling. // shadow a name excepted from mangling.
if (options.reserved.indexOf(m) >= 0) continue; if (options.except.indexOf(m) >= 0) continue;
// we must ensure that the mangled name does not shadow a name // we must ensure that the mangled name does not shadow a name
// from some parent scope that is referenced in this or in // from some parent scope that is referenced in this or in
@@ -361,11 +361,6 @@ AST_Symbol.DEFMETHOD("unmangleable", function(options){
return this.definition().unmangleable(options); return this.definition().unmangleable(options);
}); });
// property accessors are not mangleable
AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){
return true;
});
// labels are always mangleable // labels are always mangleable
AST_Label.DEFMETHOD("unmangleable", function(){ AST_Label.DEFMETHOD("unmangleable", function(){
return false; return false;
@@ -399,9 +394,10 @@ AST_Symbol.DEFMETHOD("global", function(){
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, { return defaults(options, {
eval : false, eval : false,
ie8 : false, except : [],
keep_fnames : false, keep_fnames : false,
reserved : [], screw_ie8 : true,
sort : false, // Ignored. Flag retained for backwards compatibility.
toplevel : false, toplevel : false,
}); });
}); });
@@ -410,7 +406,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = this._default_mangler_options(options); options = this._default_mangler_options(options);
// Never mangle arguments // Never mangle arguments
options.reserved.push('arguments'); options.except.push('arguments');
// We only need to mangle declaration nodes. Special logic wired // We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's // into the code generator will display the mangled name if it's
@@ -421,7 +417,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
if (options.cache) { if (options.cache) {
this.globals.each(function(symbol){ this.globals.each(function(symbol){
if (options.reserved.indexOf(symbol.name) < 0) { if (options.except.indexOf(symbol.name) < 0) {
to_mangle.push(symbol); to_mangle.push(symbol);
} }
}); });
@@ -438,7 +434,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
if (node instanceof AST_Scope) { if (node instanceof AST_Scope) {
var p = tw.parent(), a = []; var p = tw.parent(), a = [];
node.variables.each(function(symbol){ node.variables.each(function(symbol){
if (options.reserved.indexOf(symbol.name) < 0) { if (options.except.indexOf(symbol.name) < 0) {
a.push(symbol); a.push(symbol);
} }
}); });
@@ -451,7 +447,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
node.mangled_name = name; node.mangled_name = name;
return true; return true;
} }
if (!options.ie8 && node instanceof AST_SymbolCatch) { if (options.screw_ie8 && node instanceof AST_SymbolCatch) {
to_mangle.push(node.definition()); to_mangle.push(node.definition());
return; return;
} }
@@ -572,3 +568,89 @@ var base54 = (function() {
}; };
return base54; return base54;
})(); })();
AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
options = defaults(options, {
assign_to_global : true,
eval : true,
func_arguments : true,
nested_defuns : true,
undeclared : false, // this makes a lot of noise
unreferenced : true,
});
var tw = new TreeWalker(function(node){
if (options.undeclared
&& node instanceof AST_SymbolRef
&& node.undeclared())
{
// XXX: this also warns about JS standard names,
// i.e. Object, Array, parseInt etc. Should add a list of
// exceptions.
AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", {
name: node.name,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.assign_to_global)
{
var sym = null;
if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef)
sym = node.left;
else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef)
sym = node.init;
if (sym
&& (sym.undeclared()
|| (sym.global() && sym.scope !== sym.definition().scope))) {
AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", {
msg: sym.undeclared() ? "Accidental global?" : "Assignment to global",
name: sym.name,
file: sym.start.file,
line: sym.start.line,
col: sym.start.col
});
}
}
if (options.eval
&& node instanceof AST_SymbolRef
&& node.undeclared()
&& node.name == "eval") {
AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start);
}
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",
name: node.name,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.func_arguments
&& node instanceof AST_Lambda
&& node.uses_arguments) {
AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", {
name: node.name ? node.name.name : "anonymous",
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
if (options.nested_defuns
&& node instanceof AST_Defun
&& !(tw.parent() instanceof AST_Scope)) {
AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", {
name: node.name.name,
type: tw.parent().TYPE,
file: node.start.file,
line: node.start.line,
col: node.start.col
});
}
});
this.walk(tw);
});

View File

@@ -174,8 +174,9 @@ TreeTransformer.prototype = new TreeWalker;
self.args = do_list(self.args, tw); self.args = do_list(self.args, tw);
}); });
_(AST_Sequence, function(self, tw){ _(AST_Seq, function(self, tw){
self.expressions = do_list(self.expressions, tw); self.car = self.car.transform(tw);
self.cdr = self.cdr.transform(tw);
}); });
_(AST_Dot, function(self, tw){ _(AST_Dot, function(self, tw){

View File

@@ -346,7 +346,7 @@ function first_in_statement(stack) {
for (var i = 0, p; p = stack.parent(i); i++) { for (var i = 0, p; p = stack.parent(i); i++) {
if (p instanceof AST_Statement && p.body === node) if (p instanceof AST_Statement && p.body === node)
return true; return true;
if ((p instanceof AST_Sequence && p.expressions[0] === node) || if ((p instanceof AST_Seq && p.car === node ) ||
(p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) || (p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
(p instanceof AST_Dot && p.expression === node ) || (p instanceof AST_Dot && p.expression === node ) ||
(p instanceof AST_Sub && p.expression === node ) || (p instanceof AST_Sub && p.expression === node ) ||

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs", "homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "3.0.1", "version": "2.8.27",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -29,14 +29,11 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"commander": "~2.9.0", "source-map": "~0.5.1",
"source-map": "~0.5.1" "yargs": "~3.10.0"
}, },
"devDependencies": { "devDependencies": {
"acorn": "~0.6.0", "acorn": "~5.0.3",
"escodegen": "~1.3.3",
"esfuzz": "~0.3.1",
"estraverse": "~1.5.1",
"mocha": "~2.3.4" "mocha": "~2.3.4"
}, },
"optionalDependencies": { "optionalDependencies": {

View File

@@ -7,7 +7,7 @@ var createHash = require("crypto").createHash;
var fork = require("child_process").fork; var fork = require("child_process").fork;
var args = process.argv.slice(2); var args = process.argv.slice(2);
if (!args.length) { if (!args.length) {
args.push("-mc"); args.push("-mc", "warnings=false");
} }
args.push("--stats"); args.push("--stats");
var urls = [ var urls = [
@@ -29,11 +29,11 @@ function done() {
var info = results[url]; var info = results[url];
console.log(); console.log();
console.log(url); console.log(url);
console.log(info.log);
var elapsed = 0; var elapsed = 0;
console.log(info.log.replace(/Elapsed: ([0-9]+)\s*/g, function(match, time) { info.log.replace(/: ([0-9]+\.[0-9]{3})s/g, function(match, time) {
elapsed += 1e-3 * parseInt(time); elapsed += parseFloat(time);
return ""; });
}));
console.log("Run-time:", elapsed.toFixed(3), "s"); console.log("Run-time:", elapsed.toFixed(3), "s");
console.log("Original:", info.input, "bytes"); console.log("Original:", info.input, "bytes");
console.log("Uglified:", info.output, "bytes"); console.log("Uglified:", info.output, "bytes");

67
test/compress/angular-inject.js vendored Normal file
View 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;
}])
}
}

View File

@@ -2,7 +2,7 @@ ascii_only_true: {
options = {} options = {}
beautify = { beautify = {
ascii_only : true, ascii_only : true,
ie8 : false, screw_ie8 : true,
beautify : false, beautify : false,
} }
input: { input: {
@@ -20,7 +20,7 @@ ascii_only_false: {
options = {} options = {}
beautify = { beautify = {
ascii_only : false, ascii_only : false,
ie8 : false, screw_ie8 : true,
beautify : false, beautify : false,
} }
input: { input: {
@@ -33,3 +33,4 @@ ascii_only_false: {
} }
expect_exact: 'function f(){return"\\x000\\x001\\x007\\08\\0"+"\\0\x01\x02\x03\x04\x05\x06\x07\\b\\t\\n\\v\\f\\r\x0e\x0f"+"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"+\' !"# ... }~\x7f\x80\x81 ... \xfe\xff\u0fff\uffff\'}' expect_exact: 'function f(){return"\\x000\\x001\\x007\\08\\0"+"\\0\x01\x02\x03\x04\x05\x06\x07\\b\\t\\n\\v\\f\\r\x0e\x0f"+"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"+\' !"# ... }~\x7f\x80\x81 ... \xfe\xff\u0fff\uffff\'}'
} }

View File

@@ -2,7 +2,7 @@ collapse_vars_side_effects_1: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs: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, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f1() { function f1() {
@@ -68,10 +68,11 @@ collapse_vars_side_effects_1: {
log(x, s.charAt(i++), y, 7); log(x, s.charAt(i++), y, 7);
} }
function f4() { function f4() {
var i = 10, var log = console.log.bind(console),
i = 10,
x = i += 2, x = i += 2,
y = i += 3; y = i += 3;
console.log.bind(console)(x, i += 4, y, i); log(x, i += 4, y, i);
} }
f1(), f2(), f3(), f4(); f1(), f2(), f3(), f4();
} }
@@ -150,7 +151,7 @@ collapse_vars_issue_721: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs: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, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
define(["require", "exports", 'handlebars'], function (require, exports, hb) { define(["require", "exports", 'handlebars'], function (require, exports, hb) {
@@ -216,7 +217,7 @@ collapse_vars_properties: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs: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, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f1(obj) { function f1(obj) {
@@ -243,7 +244,7 @@ collapse_vars_if: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs: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, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f1() { function f1() {
@@ -293,7 +294,7 @@ collapse_vars_while: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:false, unused:true, hoist_funs:true, comparisons:true, evaluate:true, booleans:true, loops:false, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f1(y) { function f1(y) {
@@ -392,9 +393,9 @@ collapse_vars_do_while: {
} }
function f3(y) { function f3(y) {
function fn(n) { console.log(n); } function fn(n) { console.log(n); }
var a = 2, x = 7; var a = 2;
do { do {
fn(a = x); fn(a = 7);
break; break;
} while (y); } while (y);
} }
@@ -467,9 +468,8 @@ collapse_vars_do_while_drop_assign: {
} }
function f3(y) { function f3(y) {
function fn(n) { console.log(n); } function fn(n) { console.log(n); }
var x = 7;
do { do {
fn(x); fn(7);
break; break;
} while (y); } while (y);
} }
@@ -670,8 +670,8 @@ collapse_vars_lvalues: {
function f4(x) { var a = (x -= 3); return x + a; } function f4(x) { var a = (x -= 3); return x + a; }
function f5(x) { var w = e1(), v = e2(), c = v = --x; return (w = x) - c; } function f5(x) { var w = e1(), v = e2(), c = v = --x; return (w = x) - c; }
function f6(x) { var w = e1(), v = e2(); return (v = --x) - (w = x); } function f6(x) { var w = e1(), v = e2(); return (v = --x) - (w = x); }
function f7(x) { var w = e1(); return (w = x) - (e2() - x); } function f7(x) { var w = e1(), v = e2(), c = v - x; return (w = x) - c; }
function f8(x) { var w = e1(); return (w = x) - (e2() - x); } function f8(x) { var w = e1(), v = e2(); return (w = x) - (v - x); }
function f9(x) { var w = e1(); return e2() - x - (w = x); } function f9(x) { var w = e1(); return e2() - x - (w = x); }
} }
} }
@@ -700,10 +700,10 @@ collapse_vars_lvalues_drop_assign: {
function f2(x) { var z = x, a = ++z; return z += a; } function f2(x) { var z = x, a = ++z; return z += a; }
function f3(x) { var a = (x -= 3); return x + a; } function f3(x) { var a = (x -= 3); return x + a; }
function f4(x) { var a = (x -= 3); return x + a; } function f4(x) { var a = (x -= 3); return x + a; }
function f5(x) { e1(); var v = e2(), c = v = --x; return x - c; } function f5(x) { var v = (e1(), e2()), c = v = --x; return x - c; }
function f6(x) { e1(), e2(); return --x - x; } function f6(x) { e1(), e2(); return --x - x; }
function f7(x) { e1(); return x - (e2() - x); } function f7(x) { var v = (e1(), e2()), c = v - x; return x - c; }
function f8(x) { e1(); return x - (e2() - x); } function f8(x) { var v = (e1(), e2()); return x - (v - x); }
function f9(x) { e1(); return e2() - x - x; } function f9(x) { e1(); return e2() - x - x; }
} }
} }
@@ -712,7 +712,7 @@ collapse_vars_misc1: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs: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, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f0(o, a, h) { function f0(o, a, h) {
@@ -789,7 +789,7 @@ collapse_vars_repeated: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs: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, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f1() { function f1() {
@@ -812,17 +812,19 @@ collapse_vars_repeated: {
} }
expect: { expect: {
function f1() { function f1() {
return -3; return -3
} }
function f2(x) { function f2(x) {
return x; return x
} }
(function(x){ (function(x){
console.log("GOOD!!"); var a = "GOOD" + x, e = "BAD", e = a;
})(), console.log(e + "!");
})("!"),
(function(x){ (function(x){
console.log("GOOD!!"); var a = "GOOD" + x, e = "BAD" + x, e = a;
})(); console.log(e + "!");
})("!");
} }
expect_stdout: true expect_stdout: true
} }
@@ -831,7 +833,7 @@ collapse_vars_closures: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs: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, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function constant_vars_can_be_replaced_in_any_scope() { function constant_vars_can_be_replaced_in_any_scope() {
@@ -921,7 +923,7 @@ collapse_vars_try: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs: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, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f1() { function f1() {
@@ -1046,9 +1048,10 @@ collapse_vars_object: {
} }
expect: { expect: {
function f0(x, y) { function f0(x, y) {
var z = x + y;
return { return {
get b() { return 7; }, get b() { return 7; },
r: x + y r: z
}; };
} }
function f1(x, y) { function f1(x, y) {
@@ -1118,7 +1121,7 @@ collapse_vars_constants: {
options = { options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs: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, reduce_vars:true keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
} }
input: { input: {
function f1(x) { function f1(x) {
@@ -1156,7 +1159,7 @@ collapse_vars_arguments: {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true, collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs: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, keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
toplevel:true, reduce_vars:true toplevel:true
} }
input: { input: {
var outer = function() { var outer = function() {
@@ -1283,7 +1286,6 @@ collapse_vars_regexp: {
join_vars: true, join_vars: true,
cascade: true, cascade: true,
side_effects: true, side_effects: true,
reduce_vars: true,
} }
input: { input: {
function f1() { function f1() {
@@ -1317,8 +1319,8 @@ collapse_vars_regexp: {
}; };
} }
(function(){ (function(){
var result, rx = /ab*/g; var result, s = "acdabcdeabbb", rx = /ab*/g;
while (result = rx.exec("acdabcdeabbb")) while (result = rx.exec(s))
console.log(result[0]); console.log(result[0]);
})(); })();
} }
@@ -1342,10 +1344,7 @@ issue_1537: {
issue_1562: { issue_1562: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true,
reduce_vars: true,
toplevel: true, toplevel: true,
unused: true,
} }
input: { input: {
var v = 1, B = 2; var v = 1, B = 2;
@@ -1364,11 +1363,14 @@ issue_1562: {
var v = 1; var v = 1;
for (v in objs) f(2); for (v in objs) f(2);
while(5) bar(10); var x = 3;
while(x + 2) bar(10);
do bar(20); while(6); var y = 4;
do bar(20); while(y + 2);
for (; f(7) ;) bar(30); var z = 5;
for (; f(z + 2) ;) bar(30);
} }
} }
@@ -1591,598 +1593,48 @@ var_side_effects_3: {
expect_stdout: true expect_stdout: true
} }
reduce_vars_assign: { reassign_const_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
reduce_vars: true,
}
input: {
!function() {
var a = 1;
a = [].length,
console.log(a);
}();
}
expect: {
!function() {
var a = 1;
a = [].length,
console.log(a);
}();
}
expect_stdout: "0"
}
iife_1: {
options = {
collapse_vars: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var log = function(x) {
console.log(x);
}, foo = bar();
log(foo);
}
expect: {
(function(x) {
console.log(x);
})(bar());
}
}
iife_2: {
options = {
collapse_vars: true,
reduce_vars: false,
toplevel: true,
unused: false,
}
input: {
var foo = bar();
!function(x) {
console.log(x);
}(foo);
}
expect: {
!function(x) {
console.log(x);
}(bar());
}
}
var_defs: {
options = {
collapse_vars:true, 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: {
var f1 = function(x, y) {
var a, b, r = x + y, q = r * r, z = q - r, a = z, b = 7;
console.log(a + b);
};
f1("1", 0);
}
expect: {
var f1 = function(x, y) {
var r = x + y, a = r * r - r, b = 7;
console.log(a + b);
};
f1("1", 0);
}
expect_stdout: "97"
}
assignment: {
options = {
collapse_vars: true,
unused: true,
} }
input: { input: {
function f() { function f() {
var a; const a = 1;
a = x; a = 2;
return a; return a;
} }
console.log(f());
} }
expect: { expect: {
function f() { function f() {
return x; const a = 1;
a = 2;
return a;
} }
} console.log(f());
}
for_init: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f(x, y) {
var a = x;
var b = y;
for (a; b;);
}
}
expect: {
function f(x, y) {
var b = y;
for (x; b;);
}
}
}
switch_case: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f(x, y, z) {
var a = x();
var b = y();
var c = z;
switch (a) {
default: d();
case b: e();
case c: f();
}
}
}
expect: {
function f(x, y, z) {
var c = z;
switch (x()) {
default: d();
case y(): e();
case c: f();
}
}
}
}
issue_27: {
options = {
collapse_vars: true,
unused: true,
}
input: {
(function(jQuery) {
var $;
$ = jQuery;
$("body").addClass("foo");
})(jQuery);
}
expect: {
(function(jQuery) {
jQuery("body").addClass("foo");
})(jQuery);
}
}
modified: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f1(b) {
var a = b;
return b + a;
}
function f2(b) {
var a = b;
return b++ + a;
}
function f3(b) {
var a = b++;
return b + a;
}
function f4(b) {
var a = b++;
return b++ + a;
}
function f5(b) {
var a = function() {
return b;
}();
return b++ + a;
}
console.log(f1(1), f2(1), f3(1), f4(1), f5(1));
}
expect: {
function f1(b) {
return b + b;
}
function f2(b) {
var a = b;
return b++ + a;
}
function f3(b) {
var a = b++;
return b + a;
}
function f4(b) {
var a = b++;
return b++ + a;
}
function f5(b) {
var a = function() {
return b;
}();
return b++ + a;
}
console.log(f1(1), f2(1), f3(1), f4(1), f5(1));
}
expect_stdout: "2 2 3 3 2"
}
issue_1858: {
options = {
collapse_vars: true,
pure_getters: true,
unused: true,
}
input: {
console.log(function(x) {
var a = {}, b = a.b = x;
return a.b + b;
}(1));
}
expect: {
console.log(function(x) {
var a = {}, b = a.b = x;
return a.b + b;
}(1));
}
expect_stdout: "2"
}
anonymous_function: {
options = {
collapse_vars: true,
}
input: {
console.log(function f(a) {
f ^= 0;
return f * a;
}(1));
}
expect: {
console.log(function f(a) {
f ^= 0;
return f * a;
}(1));
} }
expect_stdout: true expect_stdout: true
} }
side_effects_property: { reassign_const_2: {
options = { options = {
collapse_vars: true, collapse_vars: true,
} }
input: { input: {
var a = []; function f() {
var b = 0; const a = 1;
a[b++] = function() { return 42;}; ++a;
var c = a[b++](); return a;
console.log(c);
}
expect: {
var a = [];
var b = 0;
a[b++] = function() { return 42;};
var c = a[b++]();
console.log(c);
}
expect_stdout: true
}
undeclared: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f(x, y) {
var a;
a = x;
b = y;
return b + a;
} }
console.log(f());
} }
expect: { expect: {
function f(x, y) { function f() {
var a; const a = 1;
a = x; ++a;
b = y; return a;
return b + a;
} }
} console.log(f());
}
ref_scope: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(function() {
var a = 1, b = 2, c = 3;
var a = c++, b = b /= a;
return function() {
return a;
}() + b;
}());
}
expect: {
console.log(function() {
var a = 1, b = 2, c = 3;
b = b /= a = c++;
return function() {
return a;
}() + b;
}());
} }
expect_stdout: true expect_stdout: true
} }
chained_1: {
options = {
collapse_vars: true,
}
input: {
var a = 2;
var a = 3 / a;
console.log(a);
}
expect: {
var a = 3 / (a = 2);
console.log(a);
}
expect_stdout: true
}
chained_2: {
options = {
collapse_vars: true,
}
input: {
var a;
var a = 2;
a = 3 / a;
console.log(a);
}
expect: {
var a;
a = 3 / (a = 2);
console.log(a);
}
expect_stdout: true
}
chained_3: {
options = {
collapse_vars: true,
unused: true,
}
input: {
console.log(function(a, b) {
var c = a, c = b;
b++;
return c;
}(1, 2));
}
expect: {
console.log(function(a, b) {
var c = a, c = b;
b++;
return c;
}(1, 2));
}
expect_stdout: "2"
}
boolean_binary_1: {
options = {
collapse_vars: true,
}
input: {
var a = 1;
a++;
(function() {} || a || 3).toString();
console.log(a);
}
expect: {
var a = 1;
a++;
(function() {} || a || 3).toString();
console.log(a);
}
expect_stdout: true
}
boolean_binary_2: {
options = {
collapse_vars: true,
}
input: {
var c = 0;
c += 1;
(function() {
c = 1 + c;
} || 9).toString();
console.log(c);
}
expect: {
var c = 0;
c += 1;
(function() {
c = 1 + c;
} || 9).toString();
console.log(c);
}
expect_stdout: true
}
inner_lvalues: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a, b = 10;
var a = (--b || a || 3).toString(), c = --b + -a;
console.log(null, a, b);
}
expect: {
var a, b = 10;
var a = (--b || a || 3).toString(), c = --b + -a;
console.log(null, a, b);
}
expect_stdout: true
}
double_def: {
options = {
collapse_vars: true,
}
input: {
var a = x, a = a && y;
a();
}
expect: {
var a = x;
(a = a && y)();
}
}
toplevel_single_reference: {
options = {
collapse_vars: true,
}
input: {
var a;
for (var b in x) {
var a = b;
b(a);
}
}
expect: {
var a;
for (var b in x)
b(a = b);
}
}
unused_orig: {
options = {
collapse_vars: true,
passes: 2,
reduce_vars: true,
unused: true,
}
input: {
var a = 1;
console.log(function(b) {
var a;
var c = b;
for (var d in c) {
var a = c[0];
return --b + a;
}
try {
} catch (e) {
--b + a;
}
a && a.NaN;
}([2]), a);
}
expect: {
var a = 1;
console.log(function(b) {
var c = b;
for (var d in c) {
var a = c[0];
return --b + a;
}
a && a.NaN;
}([2]), a);
}
expect_stdout: "3 1"
}
issue_315: {
options = {
collapse_vars: true,
evaluate: true,
keep_fargs: false,
reduce_vars: true,
sequences: true,
unused: true,
}
input: {
console.log(function(s) {
var w, _i, _len, _ref, _results;
_ref = s.trim().split(" ");
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
w = _ref[_i];
_results.push(w.toLowerCase());
}
return _results;
}("test"));
}
expect: {
console.log(function() {
var w, _i, _len, _ref, _results;
for (_results = [], _i = 0, _len = (_ref = "test".trim().split(" ")).length; _i < _len ; _i++)
w = _ref[_i], _results.push(w.toLowerCase());
return _results;
}());
}
expect_stdout: true
}
lvalues_def: {
options = {
collapse_vars: true,
side_effects: true,
unused: true,
}
input: {
var a = 0, b = 1;
var a = b++, b = +function() {}();
a && a[a++];
console.log(a, b);
}
expect: {
var a = 0, b = 1;
var a = b++, b = +void 0;
a && a[a++];
console.log(a, b);
}
expect_stdout: true
}
compound_assignment: {
options = {
collapse_vars: true,
}
input: {
var a;
a = 1;
a += a + 2;
console.log(a);
}
expect: {
var a;
a = 1;
a += a + 2;
console.log(a);
}
expect_stdout: "4"
}

View File

@@ -979,12 +979,12 @@ delete_conditional_1: {
console.log(delete (1 ? 0 / 0 : x)); console.log(delete (1 ? 0 / 0 : x));
} }
expect: { expect: {
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((NaN, !0));
console.log(!0); console.log((NaN, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -1006,12 +1006,12 @@ delete_conditional_2: {
console.log(delete (0 ? x : 0 / 0)); console.log(delete (0 ? x : 0 / 0));
} }
expect: { expect: {
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((Infinity, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((NaN, !0));
console.log(!0); console.log((NaN, !0));
} }
expect_stdout: true expect_stdout: true
} }

View File

@@ -256,3 +256,19 @@ try_catch_finally: {
"1", "1",
] ]
} }
accessor: {
options = {
side_effects: true,
}
input: {
({
get a() {},
set a(v){
this.b = 2;
},
b: 1
});
}
expect: {}
}

View File

@@ -935,8 +935,7 @@ issue_1715_3: {
try { try {
console; console;
} catch (a) { } catch (a) {
var a; var a = x();
x();
} }
} }
f(); f();
@@ -1031,33 +1030,6 @@ delete_assign_2: {
expect_stdout: true expect_stdout: true
} }
drop_var: {
options = {
toplevel: true,
unused: true,
}
input: {
var a;
console.log(a, b);
var a = 1, b = 2;
console.log(a, b);
var a = 3;
console.log(a, b);
}
expect: {
console.log(a, b);
var a = 1, b = 2;
console.log(a, b);
a = 3;
console.log(a, b);
}
expect_stdout: [
"undefined undefined",
"1 2",
"3 2",
]
}
issue_1830_1: { issue_1830_1: {
options = { options = {
unused: true, unused: true,
@@ -1093,57 +1065,27 @@ issue_1830_2: {
expect_stdout: "1" expect_stdout: "1"
} }
issue_1838: { reassign_const: {
options = { options = {
join_vars: true, cascade: true,
loops: true, sequences: true,
unused: true,
}
beautify = {
beautify: true,
}
input: {
function f() {
var b = a;
while (c);
}
}
expect_exact: [
"function f() {",
" for (a; c; ) ;",
"}",
]
}
var_catch_toplevel: {
options = {
conditionals: true,
negate_iife: true,
reduce_vars: true,
side_effects: true, side_effects: true,
toplevel: true,
unused: true, unused: true,
} }
input: { input: {
function f() { function f() {
a--; const a = 1;
try { a = 2;
a++; return a;
} catch(a) {
if (a) var a;
var a = 10;
}
} }
f(); console.log(f());
} }
expect: { expect: {
!function() { function f() {
a--; const a = 1;
try { return a = 2, a;
a++; }
} catch(a) { console.log(f());
var a;
}
}();
} }
expect_stdout: true
} }

View File

@@ -922,12 +922,12 @@ delete_binary_1: {
console.log(delete (true && (0 / 0))); console.log(delete (true && (0 / 0)));
} }
expect: { expect: {
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((NaN, !0));
console.log(!0); console.log((NaN, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -948,12 +948,12 @@ delete_binary_2: {
console.log(delete (false || (0 / 0))); console.log(delete (false || (0 / 0)));
} }
expect: { expect: {
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((Infinity, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((NaN, !0));
console.log(!0); console.log((NaN, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -989,3 +989,50 @@ Infinity_NaN_undefined_LHS: {
"}", "}",
] ]
} }
issue_1964_1: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_regexp: false,
unused: true,
}
input: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect_stdout: "b"
}
issue_1964_2: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_regexp: true,
unused: true,
}
input: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
return "a b c".split(/\s/)[1];
}
console.log(f());
}
expect_stdout: "b"
}

View File

@@ -112,8 +112,9 @@ issue_1841_1: {
expect: { expect: {
var b = 10; var b = 10;
!function() { !function() {
for (var key in "hi") for (var key in "hi") {
b = 42; b = 42;
}
}(--b); }(--b);
console.log(b); console.log(b);
} }
@@ -138,32 +139,11 @@ issue_1841_2: {
expect: { expect: {
var b = 10; var b = 10;
!function(arg) { !function(arg) {
for (var key in "hi") for (var key in "hi") {
arg.baz, b = 42; arg.baz, b = 42;
}
}(--b); }(--b);
console.log(b); console.log(b);
} }
expect_exact: "42" expect_exact: "42"
} }
function_returning_constant_literal: {
options = {
reduce_vars: true,
unsafe: true,
toplevel: true,
evaluate: true,
cascade: true,
unused: true,
}
input: {
function greeter() {
return { message: 'Hello there' };
}
var greeting = greeter();
console.log(greeting.message);
}
expect: {
console.log("Hello there");
}
expect_stdout: "Hello there"
}

View File

@@ -1,6 +1,6 @@
issue_1321_no_debug: { issue_1321_no_debug: {
mangle_props = { mangle_props = {
keep_quoted: true ignore_quoted: true
} }
input: { input: {
var x = {}; var x = {};
@@ -19,7 +19,7 @@ issue_1321_no_debug: {
issue_1321_debug: { issue_1321_debug: {
mangle_props = { mangle_props = {
keep_quoted: true, ignore_quoted: true,
debug: "" debug: ""
} }
input: { input: {
@@ -39,7 +39,7 @@ issue_1321_debug: {
issue_1321_with_quoted: { issue_1321_with_quoted: {
mangle_props = { mangle_props = {
keep_quoted: false ignore_quoted: false
} }
input: { input: {
var x = {}; var x = {};

View File

@@ -23,7 +23,7 @@ typeof_eq_undefined: {
typeof_eq_undefined_ie8: { typeof_eq_undefined_ie8: {
options = { options = {
comparisons: true, comparisons: true,
ie8: true, screw_ie8: false
} }
input: { input: {
var a = typeof b != "undefined"; var a = typeof b != "undefined";

View File

@@ -1,9 +1,9 @@
screw_ie8: { screw_ie8: {
options = { options = {
ie8: false, screw_ie8: true,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
} }
input: { input: {
try { throw "foo"; } catch (x) { console.log(x); } try { throw "foo"; } catch (x) { console.log(x); }
@@ -16,10 +16,10 @@ screw_ie8: {
support_ie8: { support_ie8: {
options = { options = {
ie8: true, screw_ie8: false,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
} }
input: { input: {
try { throw "foo"; } catch (x) { console.log(x); } try { throw "foo"; } catch (x) { console.log(x); }

View File

@@ -18,7 +18,9 @@ chained_evaluation_1: {
expect: { expect: {
(function() { (function() {
(function() { (function() {
f(1).bar = 1; var c;
c = f(1);
c.bar = 1;
})(); })();
})(); })();
} }
@@ -44,8 +46,9 @@ chained_evaluation_2: {
expect: { expect: {
(function() { (function() {
(function() { (function() {
var b = "long piece of string"; var c, b = "long piece of string";
f(b).bar = b; c = f(b);
c.bar = b;
})(); })();
})(); })();
} }

View File

@@ -1,10 +1,10 @@
mangle_catch: { mangle_catch: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -22,11 +22,11 @@ mangle_catch: {
mangle_catch_ie8: { mangle_catch_ie8: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -44,11 +44,11 @@ mangle_catch_ie8: {
mangle_catch_var: { mangle_catch_var: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -66,11 +66,11 @@ mangle_catch_var: {
mangle_catch_var_ie8: { mangle_catch_var_ie8: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -88,11 +88,11 @@ mangle_catch_var_ie8: {
mangle_catch_toplevel: { mangle_catch_toplevel: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
input: { input: {
@@ -110,11 +110,11 @@ mangle_catch_toplevel: {
mangle_catch_ie8_toplevel: { mangle_catch_ie8_toplevel: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
input: { input: {
@@ -132,11 +132,11 @@ mangle_catch_ie8_toplevel: {
mangle_catch_var_toplevel: { mangle_catch_var_toplevel: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
input: { input: {
@@ -154,11 +154,11 @@ mangle_catch_var_toplevel: {
mangle_catch_var_ie8_toplevel: { mangle_catch_var_ie8_toplevel: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
input: { input: {
@@ -176,11 +176,11 @@ mangle_catch_var_ie8_toplevel: {
mangle_catch_redef_1: { mangle_catch_redef_1: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -198,11 +198,11 @@ mangle_catch_redef_1: {
mangle_catch_redef_1_ie8: { mangle_catch_redef_1_ie8: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -220,11 +220,11 @@ mangle_catch_redef_1_ie8: {
mangle_catch_redef_1_toplevel: { mangle_catch_redef_1_toplevel: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
input: { input: {
@@ -242,11 +242,11 @@ mangle_catch_redef_1_toplevel: {
mangle_catch_redef_1_ie8_toplevel: { mangle_catch_redef_1_ie8_toplevel: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
input: { input: {
@@ -264,11 +264,11 @@ mangle_catch_redef_1_ie8_toplevel: {
mangle_catch_redef_2: { mangle_catch_redef_2: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -285,11 +285,11 @@ mangle_catch_redef_2: {
mangle_catch_redef_2_ie8: { mangle_catch_redef_2_ie8: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: false, toplevel: false,
} }
input: { input: {
@@ -306,11 +306,11 @@ mangle_catch_redef_2_ie8: {
mangle_catch_redef_2_toplevel: { mangle_catch_redef_2_toplevel: {
options = { options = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
toplevel: true, toplevel: true,
} }
input: { input: {
@@ -327,11 +327,11 @@ mangle_catch_redef_2_toplevel: {
mangle_catch_redef_2_ie8_toplevel: { mangle_catch_redef_2_ie8_toplevel: {
options = { options = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
toplevel: true, toplevel: true,
} }
input: { input: {

View File

@@ -1,6 +1,6 @@
function_iife_catch: { function_iife_catch: {
mangle = { mangle = {
ie8: false, screw_ie8: true,
} }
input: { input: {
function f(n) { function f(n) {
@@ -21,7 +21,7 @@ function_iife_catch: {
function_iife_catch_ie8: { function_iife_catch_ie8: {
mangle = { mangle = {
ie8: true, screw_ie8: false,
} }
input: { input: {
function f(n) { function f(n) {
@@ -42,7 +42,7 @@ function_iife_catch_ie8: {
function_catch_catch: { function_catch_catch: {
mangle = { mangle = {
ie8: false, screw_ie8: true,
} }
input: { input: {
var o = 0; var o = 0;
@@ -70,7 +70,7 @@ function_catch_catch: {
function_catch_catch_ie8: { function_catch_catch_ie8: {
mangle = { mangle = {
ie8: true, screw_ie8: false,
} }
input: { input: {
var o = 0; var o = 0;

View File

@@ -10,6 +10,10 @@ unary_prefix: {
return x; return x;
}()); }());
} }
expect_exact: "console.log(-2/3);" expect: {
console.log(function() {
return -2 / 3;
}());
}
expect_stdout: true expect_stdout: true
} }

View File

@@ -0,0 +1,31 @@
operator: {
input: {
a. //comment
typeof
}
expect_exact: "a.typeof;"
}
name: {
input: {
a. //comment
b
}
expect_exact: "a.b;"
}
keyword: {
input: {
a. //comment
default
}
expect_exact: "a.default;"
}
atom: {
input: {
a. //comment
true
}
expect_exact: "a.true;"
}

View File

@@ -159,7 +159,7 @@ negate_iife_4: {
})(); })();
} }
expect: { expect: {
!function(){ return t }() ? console.log(false) : console.log(true), function(){ (function(){ return t })() ? console.log(true) : console.log(false), function(){
console.log("something"); console.log("something");
}(); }();
} }
@@ -183,7 +183,7 @@ negate_iife_5: {
})(); })();
} }
expect: { expect: {
!function(){ return t }() ? bar(false) : foo(true), function(){ (function(){ return t })() ? foo(true) : bar(false), function(){
console.log("something"); console.log("something");
}(); }();
} }
@@ -207,7 +207,7 @@ negate_iife_5_off: {
})(); })();
} }
expect: { expect: {
!function(){ return t }() ? bar(false) : foo(true), function(){ (function(){ return t })() ? foo(true) : bar(false), function(){
console.log("something"); console.log("something");
}(); }();
} }

View File

@@ -245,7 +245,7 @@ issue_1532: {
issue_186: { issue_186: {
beautify = { beautify = {
beautify: false, beautify: false,
ie8: false, screw_ie8: true,
} }
input: { input: {
var x = 3; var x = 3;
@@ -264,7 +264,7 @@ issue_186: {
issue_186_ie8: { issue_186_ie8: {
beautify = { beautify = {
beautify: false, beautify: false,
ie8: true, screw_ie8: false,
} }
input: { input: {
var x = 3; var x = 3;
@@ -283,7 +283,7 @@ issue_186_ie8: {
issue_186_beautify: { issue_186_beautify: {
beautify = { beautify = {
beautify: true, beautify: true,
ie8: false, screw_ie8: true,
} }
input: { input: {
var x = 3; var x = 3;
@@ -310,7 +310,7 @@ issue_186_beautify: {
issue_186_beautify_ie8: { issue_186_beautify_ie8: {
beautify = { beautify = {
beautify: true, beautify: true,
ie8: true, screw_ie8: false,
} }
input: { input: {
var x = 3; var x = 3;
@@ -340,7 +340,7 @@ issue_186_bracketize: {
beautify = { beautify = {
beautify: false, beautify: false,
bracketize: true, bracketize: true,
ie8: false, screw_ie8: true,
} }
input: { input: {
var x = 3; var x = 3;
@@ -360,7 +360,7 @@ issue_186_bracketize_ie8: {
beautify = { beautify = {
beautify: false, beautify: false,
bracketize: true, bracketize: true,
ie8: true, screw_ie8: false,
} }
input: { input: {
var x = 3; var x = 3;
@@ -380,7 +380,7 @@ issue_186_beautify_bracketize: {
beautify = { beautify = {
beautify: true, beautify: true,
bracketize: true, bracketize: true,
ie8: false, screw_ie8: true,
} }
input: { input: {
var x = 3; var x = 3;
@@ -412,7 +412,7 @@ issue_186_beautify_bracketize_ie8: {
beautify = { beautify = {
beautify: true, beautify: true,
bracketize: true, bracketize: true,
ie8: true, screw_ie8: false,
} }
input: { input: {
var x = 3; var x = 3;

View File

@@ -25,9 +25,11 @@ negate_iife_2: {
negate_iife: true negate_iife: true
}; };
input: { input: {
(function(){ return {} })().x = 10; // should not transform this one
}
expect: {
(function(){ return {} })().x = 10; (function(){ return {} })().x = 10;
} }
expect_exact: "({}).x=10;"
} }
negate_iife_2_side_effects: { negate_iife_2_side_effects: {
@@ -36,9 +38,11 @@ negate_iife_2_side_effects: {
side_effects: true, side_effects: true,
} }
input: { input: {
(function(){ return {} })().x = 10; // should not transform this one
}
expect: {
(function(){ return {} })().x = 10; (function(){ return {} })().x = 10;
} }
expect_exact: "({}).x=10;"
} }
negate_iife_3: { negate_iife_3: {

View File

@@ -13,7 +13,7 @@ keep_properties: {
dot_properties: { dot_properties: {
options = { options = {
properties: true, properties: true,
ie8: true, screw_ie8: false
}; };
input: { input: {
a["foo"] = "bar"; a["foo"] = "bar";
@@ -36,7 +36,7 @@ dot_properties: {
dot_properties_es5: { dot_properties_es5: {
options = { options = {
properties: true, properties: true,
ie8: false, screw_ie8: true
}; };
input: { input: {
a["foo"] = "bar"; a["foo"] = "bar";
@@ -125,7 +125,7 @@ evaluate_string_length: {
mangle_properties: { mangle_properties: {
mangle_props = { mangle_props = {
keep_quoted: false ignore_quoted: false
}; };
input: { input: {
a["foo"] = "bar"; a["foo"] = "bar";
@@ -148,7 +148,7 @@ mangle_unquoted_properties: {
properties: false properties: false
} }
mangle_props = { mangle_props = {
keep_quoted: true ignore_quoted: true
} }
beautify = { beautify = {
beautify: false, beautify: false,
@@ -233,12 +233,12 @@ mangle_debug_suffix: {
} }
} }
mangle_debug_suffix_keep_quoted: { mangle_debug_suffix_ignore_quoted: {
options = { options = {
properties: false properties: false
} }
mangle_props = { mangle_props = {
keep_quoted: true, ignore_quoted: true,
debug: "XYZ", debug: "XYZ",
reserved: [] reserved: []
} }

View File

@@ -119,3 +119,62 @@ chained: {
a.b.c; a.b.c;
} }
} }
impure_getter_1: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
({
get a() {
console.log(1);
},
b: 1
}).a;
({
get a() {
console.log(1);
},
b: 1
}).b;
}
expect: {
({
get a() {
console.log(1);
},
b: 1
}).a;
({
get a() {
console.log(1);
},
b: 1
}).b;
}
expect_stdout: "1"
}
impure_getter_2: {
options = {
pure_getters: true,
side_effects: true,
}
input: {
// will produce incorrect output because getter is not pure
({
get a() {
console.log(1);
},
b: 1
}).a;
({
get a() {
console.log(1);
},
b: 1
}).b;
}
expect: {}
}

View File

@@ -41,20 +41,22 @@ reduce_vars: {
var A = 1; var A = 1;
(function() { (function() {
console.log(-3); console.log(-3);
console.log(-4); console.log(A - 5);
})(); })();
(function f1() { (function f1() {
var a = 2; var a = 2;
console.log(-3); console.log(a - 5);
eval("console.log(a);"); eval("console.log(a);");
})(); })();
(function f2(eval) { (function f2(eval) {
var a = 2; var a = 2;
console.log(-3); console.log(a - 5);
eval("console.log(a);"); eval("console.log(a);");
})(eval); })(eval);
"yes"; (function() {
console.log(2); return "yes";
})();
console.log(A + 1);
} }
expect_stdout: true expect_stdout: true
} }
@@ -64,7 +66,7 @@ modified: {
conditionals : true, conditionals : true,
evaluate : true, evaluate : true,
reduce_vars : true, reduce_vars : true,
unused : true, unused : true
} }
input: { input: {
function f0() { function f0() {
@@ -134,11 +136,12 @@ modified: {
} }
function f2() { function f2() {
3; var b = 2;
b = 3;
console.log(1 + b);
console.log(b + 3);
console.log(4); console.log(4);
console.log(6); console.log(1 + b + 3);
console.log(4);
console.log(7);
} }
function f3() { function f3() {
@@ -387,11 +390,12 @@ passes: {
} }
expect: { expect: {
function f() { function f() {
3; var b = 2;
b = 3;
console.log(1 + b);
console.log(b + 3);
console.log(4); console.log(4);
console.log(6); console.log(1 + b + 3);
console.log(4);
console.log(7);
} }
} }
} }
@@ -584,7 +588,7 @@ inner_var_label: {
} }
} }
inner_var_for_1: { inner_var_for: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true, reduce_vars: true,
@@ -613,29 +617,6 @@ inner_var_for_1: {
} }
} }
inner_var_for_2: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
var a = 1;
for (var b = 1; --b;) var a = 2;
console.log(a);
}();
}
expect: {
!function() {
a = 1;
for (var b = 1; --b;) var a = 2;
console.log(a);
}();
}
expect_stdout: "1"
}
inner_var_for_in_1: { inner_var_for_in_1: {
options = { options = {
evaluate: true, evaluate: true,
@@ -1673,7 +1654,7 @@ redefine_arguments_1: {
return typeof arguments; return typeof arguments;
} }
function g() { function g() {
return "number"; return"number";
} }
function h(x) { function h(x) {
var arguments = x; var arguments = x;
@@ -1712,7 +1693,9 @@ redefine_arguments_2: {
console.log(function() { console.log(function() {
var arguments; var arguments;
return typeof arguments; return typeof arguments;
}(), "number", function(x) { }(), function() {
return"number";
}(), function(x) {
var arguments = x; var arguments = x;
return typeof arguments; return typeof arguments;
}()); }());
@@ -1749,7 +1732,10 @@ redefine_arguments_3: {
console.log(function() { console.log(function() {
var arguments; var arguments;
return typeof arguments; return typeof arguments;
}(), "number", "undefined"); }(), "number", function(x) {
var arguments = x;
return typeof arguments;
}());
} }
expect_stdout: "object number undefined" expect_stdout: "object number undefined"
} }
@@ -1821,7 +1807,9 @@ redefine_farg_2: {
console.log(function(a) { console.log(function(a) {
var a; var a;
return typeof a; return typeof a;
}([]), "number",function(a, b) { }([]), function() {
return "number";
}(),function(a, b) {
var a = b; var a = b;
return typeof a; return typeof a;
}([])); }([]));
@@ -1858,7 +1846,10 @@ redefine_farg_3: {
console.log(function(a) { console.log(function(a) {
var a; var a;
return typeof a; return typeof a;
}([]), "number", "undefined"); }([]), "number", function(a) {
var a = void 0;
return typeof a;
}([]));
} }
expect_stdout: "object number undefined" expect_stdout: "object number undefined"
} }
@@ -1978,6 +1969,7 @@ pure_getters_2: {
var a = a && a.b; var a = a && a.b;
} }
expect: { expect: {
var a;
var a = a && a.b; var a = a && a.b;
} }
} }
@@ -2022,167 +2014,6 @@ catch_var: {
expect_stdout: "true" expect_stdout: "true"
} }
var_assign_1: {
options = {
evaluate: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
!function() {
var a;
a = 2;
console.log(a);
}();
}
expect: {
!function() {
console.log(2);
}();
}
expect_stdout: "2"
}
var_assign_2: {
options = {
evaluate: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
!function() {
var a;
if (a = 2) console.log(a);
}();
}
expect: {
!function() {
if (2) console.log(2);
}();
}
expect_stdout: "2"
}
var_assign_3: {
options = {
evaluate: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
!function() {
var a;
while (a = 2);
console.log(a);
}();
}
expect: {
!function() {
var a;
while (a = 2);
console.log(a);
}();
}
}
var_assign_4: {
options = {
evaluate: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
!function a() {
a = 2;
console.log(a);
}();
}
expect: {
!function a() {
a = 2,
console.log(a);
}();
}
}
var_assign_5: {
options = {
evaluate: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
!function() {
var a;
!function(b) {
a = 2;
console.log(a, b);
}(a);
}();
}
expect: {
!function() {
var a;
!function(b) {
a = 2,
console.log(a, b);
}(a);
}();
}
expect_stdout: "2 undefined"
}
var_assign_6: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
var a = function(){}(a = 1);
console.log(a);
}();
}
expect: {
!function() {
var a = function(){}(a = 1);
console.log(a);
}();
}
expect_stdout: "undefined"
}
immutable: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
var a = "test";
console.log(a.indexOf("e"));
}();
}
expect: {
!function() {
console.log("test".indexOf("e"));
}();
}
expect_stdout: "1"
}
issue_1814_1: { issue_1814_1: {
options = { options = {
evaluate: true, evaluate: true,
@@ -2266,173 +2097,6 @@ try_abort: {
expect_stdout: "1 undefined" expect_stdout: "1 undefined"
} }
boolean_binary_assign: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
var a;
void 0 && (a = 1);
console.log(a);
}();
}
expect: {
!function() {
var a;
void 0;
console.log(a);
}();
}
expect_stdout: "undefined"
}
cond_assign: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
var a;
void 0 ? (a = 1) : 0;
console.log(a);
}();
}
expect: {
!function() {
var a;
void 0 ? (a = 1) : 0;
console.log(a);
}();
}
expect_stdout: "undefined"
}
iife_assign: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
var a = 1, b = 0;
!function() {
b++;
return;
a = 2;
}();
console.log(a);
}();
}
expect: {
!function() {
var a = 1, b = 0;
!function() {
b++;
return;
a = 2;
}();
console.log(a);
}();
}
expect_stdout: "1"
}
issue_1850_1: {
options = {
reduce_vars: true,
toplevel: false,
unused: true,
}
input: {
function f() {
console.log(a, a, a);
}
var a = 1;
f();
}
expect: {
function f() {
console.log(a, a, a);
}
var a = 1;
f();
}
expect_stdout: true
}
issue_1850_2: {
options = {
reduce_vars: true,
toplevel: "funcs",
unused: true,
}
input: {
function f() {
console.log(a, a, a);
}
var a = 1;
f();
}
expect: {
var a = 1;
(function() {
console.log(a, a, a);
})();
}
expect_stdout: true
}
issue_1850_3: {
options = {
reduce_vars: true,
toplevel: "vars",
unused: true,
}
input: {
function f() {
console.log(a, a, a);
}
var a = 1;
f();
}
expect: {
function f() {
console.log(a, a, a);
}
var a = 1;
f();
}
expect_stdout: true
}
issue_1850_4: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
console.log(a, a, a);
}
var a = 1;
f();
}
expect: {
var a = 1;
(function() {
console.log(a, a, a);
})();
}
expect_stdout: true
}
issue_1865: { issue_1865: {
options = { options = {
evaluate: true, evaluate: true,
@@ -2461,3 +2125,76 @@ issue_1865: {
} }
expect_stdout: true expect_stdout: true
} }
issue_1922_1: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function(a) {
arguments[0] = 2;
return a;
}(1));
}
expect: {
console.log(function(a) {
arguments[0] = 2;
return a;
}(1));
}
expect_stdout: "2"
}
issue_1922_2: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function() {
var a;
eval("a = 1");
return a;
}(1));
}
expect: {
console.log(function() {
var a;
eval("a = 1");
return a;
}(1));
}
expect_stdout: "1"
}
accessor: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 1;
console.log({
get a() {
a = 2;
return a;
},
b: 1
}.b, a);
}
expect: {
var a = 1;
console.log({
get a() {
a = 2;
return a;
},
b: 1
}.b, a);
}
expect_stdout: "1 1"
}

View File

@@ -1,9 +1,9 @@
do_screw: { do_screw: {
options = { options = {
ie8: false, screw_ie8: true,
} }
beautify = { beautify = {
ie8: false, screw_ie8: true,
ascii_only: true, ascii_only: true,
} }
input: { input: {
@@ -14,10 +14,10 @@ do_screw: {
dont_screw: { dont_screw: {
options = { options = {
ie8: true, screw_ie8: false,
} }
beautify = { beautify = {
ie8: true, screw_ie8: false,
ascii_only: true, ascii_only: true,
} }
input: { input: {
@@ -28,7 +28,7 @@ dont_screw: {
do_screw_constants: { do_screw_constants: {
options = { options = {
ie8: false, screw_ie8: true,
} }
input: { input: {
f(undefined, Infinity); f(undefined, Infinity);
@@ -38,7 +38,7 @@ do_screw_constants: {
dont_screw_constants: { dont_screw_constants: {
options = { options = {
ie8: true, screw_ie8: false,
} }
input: { input: {
f(undefined, Infinity); f(undefined, Infinity);
@@ -47,15 +47,9 @@ dont_screw_constants: {
} }
do_screw_try_catch: { do_screw_try_catch: {
options = { options = { screw_ie8: true };
ie8: false, mangle = { screw_ie8: true };
} beautify = { screw_ie8: true };
mangle = {
ie8: false,
}
beautify = {
ie8: false,
}
input: { input: {
good = function(e){ good = function(e){
return function(error){ return function(error){
@@ -81,15 +75,9 @@ do_screw_try_catch: {
} }
dont_screw_try_catch: { dont_screw_try_catch: {
options = { options = { screw_ie8: false };
ie8: true, mangle = { screw_ie8: false };
} beautify = { screw_ie8: false };
mangle = {
ie8: true,
}
beautify = {
ie8: true,
}
input: { input: {
bad = function(e){ bad = function(e){
return function(error){ return function(error){
@@ -115,15 +103,9 @@ dont_screw_try_catch: {
} }
do_screw_try_catch_undefined: { do_screw_try_catch_undefined: {
options = { options = { screw_ie8: true };
ie8: false, mangle = { screw_ie8: true };
} beautify = { screw_ie8: true };
mangle = {
ie8: false,
}
beautify = {
ie8: false,
}
input: { input: {
function a(b){ function a(b){
try { try {
@@ -150,15 +132,9 @@ do_screw_try_catch_undefined: {
} }
dont_screw_try_catch_undefined: { dont_screw_try_catch_undefined: {
options = { options = { screw_ie8: false };
ie8: true, mangle = { screw_ie8: false };
} beautify = { screw_ie8: false };
mangle = {
ie8: true,
}
beautify = {
ie8: true,
}
input: { input: {
function a(b){ function a(b){
try { try {
@@ -188,11 +164,11 @@ reduce_vars: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true, reduce_vars: true,
ie8: true, screw_ie8: false,
unused: true, unused: true,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
} }
input: { input: {
function f() { function f() {
@@ -220,10 +196,10 @@ reduce_vars: {
issue_1586_1: { issue_1586_1: {
options = { options = {
ie8: true, screw_ie8: false,
} }
mangle = { mangle = {
ie8: true, screw_ie8: false,
} }
input: { input: {
function f() { function f() {
@@ -239,10 +215,10 @@ issue_1586_1: {
issue_1586_2: { issue_1586_2: {
options = { options = {
ie8: false, screw_ie8: true,
} }
mangle = { mangle = {
ie8: false, screw_ie8: true,
} }
input: { input: {
function f() { function f() {

View File

@@ -460,7 +460,7 @@ issue_1758: {
console.log(function(c) { console.log(function(c) {
var undefined = 42; var undefined = 42;
return function() { return function() {
return c--, c--, void c.toString(); return c--, c--, c.toString(), void 0;
}(); }();
}()); }());
} }
@@ -481,12 +481,12 @@ delete_seq_1: {
console.log(delete (1, 0 / 0)); console.log(delete (1, 0 / 0));
} }
expect: { expect: {
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((NaN, !0));
console.log(!0); console.log((0 / 0, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -505,12 +505,12 @@ delete_seq_2: {
console.log(delete (1, 2, 0 / 0)); console.log(delete (1, 2, 0 / 0));
} }
expect: { expect: {
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((NaN, !0));
console.log(!0); console.log((0 / 0, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -530,12 +530,12 @@ delete_seq_3: {
console.log(delete (1, 2, 0 / 0)); console.log(delete (1, 2, 0 / 0));
} }
expect: { expect: {
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((void 0, !0));
console.log(!0); console.log((Infinity, !0));
console.log(!0); console.log((1 / 0, !0));
console.log(!0); console.log((NaN, !0));
console.log(!0); console.log((0 / 0, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -606,107 +606,31 @@ delete_seq_6: {
} }
expect: { expect: {
var a; var a;
console.log(!0); console.log((a, !0));
} }
expect_stdout: true expect_stdout: true
} }
side_effects: { reassign_const: {
options = { options = {
cascade: true,
sequences: true, sequences: true,
side_effects: true, side_effects: true,
} }
input: { input: {
0, a(), 1, b(), 2, c(), 3; function f() {
const a = 1;
a++;
return a;
}
console.log(f());
} }
expect: { expect: {
a(), b(), c(); function f() {
} const a = 1;
} return a++, a;
}
side_effects_cascade_1: { console.log(f());
options = {
cascade: true,
conditionals: true,
sequences: true,
side_effects: true,
}
input: {
function f(a, b) {
a -= 42;
if (a < 0) a = 0;
b.a = a;
}
}
expect: {
function f(a, b) {
(a -= 42) < 0 && (a = 0), b.a = a;
}
}
}
side_effects_cascade_2: {
options = {
cascade: true,
side_effects: true,
}
input: {
function f(a, b) {
b = a,
!a + (b += a) || (b += a),
b = a,
b;
}
}
expect: {
function f(a, b) {
b = a,
!a + (b += a) || (b += a),
b = a;
}
}
}
side_effects_cascade_3: {
options = {
cascade: true,
conditionals: true,
side_effects: true,
}
input: {
function f(a, b) {
"foo" ^ (b += a),
b ? false : (b = a) ? -1 : (b -= a) - (b ^= a),
a-- || !a,
a;
}
}
expect: {
function f(a, b) {
!(b += a) && ((b = a) || (b -= a, b ^= a)),
--a;
}
}
}
issue_27: {
options = {
cascade: true,
passes: 2,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function(jQuery) {
var $;
$ = jQuery;
$("body").addClass("foo");
})(jQuery);
}
expect: {
(function(jQuery) {
jQuery("body").addClass("foo");
})(jQuery);
} }
expect_stdout: true
} }

View File

@@ -1,13 +0,0 @@
exports["Compressor"] = Compressor;
exports["JS_Parse_Error"] = JS_Parse_Error;
exports["OutputStream"] = OutputStream;
exports["SourceMap"] = SourceMap;
exports["TreeWalker"] = TreeWalker;
exports["base54"] = base54;
exports["defaults"] = defaults;
exports["mangle_properties"] = mangle_properties;
exports["minify"] = minify;
exports["parse"] = parse;
exports["string_template"] = string_template;
exports["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier;

View File

@@ -1,8 +0,0 @@
function f() {
const a;
}
function g() {
"use strict";
const a;
}

View File

@@ -1,14 +0,0 @@
function f(x) {
delete 42;
delete (0, x);
delete null;
delete x;
}
function g(x) {
"use strict";
delete 42;
delete (0, x);
delete null;
delete x;
}

View File

@@ -0,0 +1 @@
if (0) else 1;

View File

@@ -1,6 +0,0 @@
function f(arguments) {
}
function g(arguments) {
"use strict";
}

View File

@@ -1,6 +0,0 @@
function arguments() {
}
function eval() {
"use strict";
}

View File

@@ -1,6 +0,0 @@
!function eval() {
}();
!function arguments() {
"use strict";
}();

View File

@@ -0,0 +1 @@
return 42;

View File

@@ -1,8 +0,0 @@
function f() {
try {} catch (eval) {}
}
function g() {
"use strict";
try {} catch (eval) {}
}

View File

@@ -1,8 +0,0 @@
function f() {
var eval;
}
function g() {
"use strict";
var eval;
}

View File

@@ -12,7 +12,7 @@ if (typeof phantom == "undefined") {
}); });
var args = process.argv.slice(2); var args = process.argv.slice(2);
if (!args.length) { if (!args.length) {
args.push("-mc"); args.push("-mc", "warnings=false");
} }
args.push("--stats"); args.push("--stats");
var child_process = require("child_process"); var child_process = require("child_process");

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("../node"); var UglifyJS = require('../../');
var assert = require("assert"); var assert = require("assert");
describe("Accessor tokens", function() { describe("Accessor tokens", function() {
@@ -16,7 +16,7 @@ describe("Accessor tokens", function() {
assert.equal(node.start.pos, 12); assert.equal(node.start.pos, 12);
assert.equal(node.end.endpos, 46); assert.equal(node.end.endpos, 46);
assert(node.key instanceof UglifyJS.AST_SymbolRef); assert(node.key instanceof UglifyJS.AST_SymbolAccessor);
assert.equal(node.key.start.pos, 16); assert.equal(node.key.start.pos, 16);
assert.equal(node.key.end.endpos, 22); assert.equal(node.key.end.endpos, 22);

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("../node"); var UglifyJS = require('../../');
var assert = require("assert"); var assert = require("assert");
describe("arguments", function() { describe("arguments", function() {

View File

@@ -2,10 +2,6 @@ var assert = require("assert");
var exec = require("child_process").exec; var exec = require("child_process").exec;
var readFileSync = require("fs").readFileSync; var readFileSync = require("fs").readFileSync;
function read(path) {
return readFileSync(path, "utf8");
}
describe("bin/uglifyjs", function () { describe("bin/uglifyjs", function () {
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs'; var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
it("should produce a functional build when using --self", function (done) { it("should produce a functional build when using --self", function (done) {
@@ -19,12 +15,12 @@ describe("bin/uglifyjs", function () {
eval(stdout); eval(stdout);
assert.strictEqual(typeof WrappedUglifyJS, 'object'); assert.strictEqual(typeof WrappedUglifyJS, 'object');
assert.strictEqual(WrappedUglifyJS.minify("foo([true,,2+3]);").code, "foo([!0,,5]);"); assert.strictEqual(true, WrappedUglifyJS.parse('foo;') instanceof WrappedUglifyJS.AST_Node);
done(); done();
}); });
}); });
it("Should be able to filter comments correctly with `--comments all`", function (done) { it("Should be able to filter comments correctly with `--comment all`", function (done) {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments all'; var command = uglifyjscmd + ' test/input/comments/filter.js --comments all';
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
@@ -54,18 +50,18 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should append source map to output when using --source-map url=inline", function (done) { it("Should append source map to output when using --source-map-inline", function (done) {
var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map url=inline"; var command = uglifyjscmd + ' test/input/issue-1323/sample.js --source-map-inline';
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" + assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=\n"); "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=\n");
done(); done();
}); });
}); });
it("should not append source map to output when not using --source-map url=inline", function (done) { it("should not append source map to output when not using --source-map-inline", function (done) {
var command = uglifyjscmd + ' test/input/issue-1323/sample.js'; var command = uglifyjscmd + ' test/input/issue-1323/sample.js';
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
@@ -76,134 +72,130 @@ describe("bin/uglifyjs", function () {
}); });
}); });
it("Should work with --keep-fnames (mangle only)", function (done) { it("Should work with --keep-fnames (mangle only)", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m'; var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done();
});
});
it("Should work with --keep-fnames (mangle & compress)", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m -c unused=false';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(5==f(g)());\n");
done();
});
});
it("Should work with keep_fnames under mangler options", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js -m keep_fnames=true';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done();
});
});
it("Should work with --define (simple)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/simple.js --define D=5 -c';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "console.log(5);\n");
done();
});
});
it("Should work with --define (nested)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/nested.js --define C.D=5,C.V=3 -c';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "console.log(3,5);\n");
done();
});
});
it("Should work with --define (AST_Node)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/simple.js --define console.log=stdout.println -c';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "stdout.println(D);\n");
done();
});
});
it("Should work with `--beautify`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-1482/default.js"));
done();
});
});
it("Should work with `--beautify bracketize`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b bracketize';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-1482/bracketize.js"));
done();
});
});
it("Should process inline source map", function(done) {
var command = uglifyjscmd + " test/input/issue-520/input.js -mc toplevel --source-map content=inline,url=inline";
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-520/output.js")); assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done();
});
});
it("Should work with --keep-fnames (mangle & compress)", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js --keep-fnames -m -c unused=false';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(5==f(g)());\n");
done();
});
});
it("Should work with keep_fnames under mangler options", function (done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js -m keep_fnames=true';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done();
});
});
it("Should work with --define (simple)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/simple.js --define D=5 -c';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "console.log(5);\n");
done();
});
});
it("Should work with --define (nested)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/nested.js --define C.D=5,C.V=3 -c';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "console.log(3,5);\n");
done();
});
});
it("Should work with --define (AST_Node)", function (done) {
var command = uglifyjscmd + ' test/input/global_defs/simple.js --define console.log=stdout.println -c';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "stdout.println(D);\n");
done();
});
});
it("Should work with `--beautify`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, readFileSync("test/input/issue-1482/default.js", "utf8"));
done();
});
});
it("Should work with `--beautify bracketize`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b bracketize';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, readFileSync("test/input/issue-1482/bracketize.js", "utf8"));
done();
});
});
it("Should process inline source map", function(done) {
var command = uglifyjscmd + ' test/input/issue-520/input.js -mc toplevel --in-source-map inline --source-map-inline';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, readFileSync("test/input/issue-520/output.js", "utf8"));
done(); done();
}); });
}); });
it("Should warn for missing inline source map", function(done) { it("Should warn for missing inline source map", function(done) {
var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map content=inline,url=inline"; var command = uglifyjscmd + ' test/input/issue-1323/sample.js --in-source-map inline';
exec(command, function (err, stdout, stderr) { exec(command, function (err, stdout, stderr) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, [ assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n");
"var bar=function(){function foo(bar){return bar}return foo}();",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=",
"",
].join("\n"));
assert.strictEqual(stderr, "WARN: inline source map not found\n"); assert.strictEqual(stderr, "WARN: inline source map not found\n");
done(); done();
}); });
}); });
it("Should fail with multiple input and inline source map", function(done) { it("Should fail with multiple input and inline source map", function(done) {
var command = uglifyjscmd + " test/input/issue-520/input.js test/input/issue-520/output.js --source-map content=inline,url=inline"; var command = uglifyjscmd + ' test/input/issue-520/input.js test/input/issue-520/output.js --in-source-map inline --source-map-inline';
exec(command, function (err, stdout, stderr) { exec(command, function (err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stderr, "ERROR: inline source map only works with singular input\n"); assert.strictEqual(stderr, "ERROR: Inline source map only works with singular input\n");
done(); done();
}); });
}); });
it("Should fail with acorn and inline source map", function(done) { it("Should fail with acorn and inline source map", function(done) {
var command = uglifyjscmd + " test/input/issue-520/input.js --source-map content=inline,url=inline -p acorn"; var command = uglifyjscmd + ' test/input/issue-520/input.js --in-source-map inline --source-map-inline --acorn';
exec(command, function (err, stdout, stderr) { exec(command, function (err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stderr, "ERROR: inline source map only works with built-in parser\n"); assert.strictEqual(stderr, "ERROR: Inline source map only works with built-in parser\n");
done(); done();
}); });
}); });
it("Should fail with SpiderMonkey and inline source map", function(done) { it("Should fail with SpiderMonkey and inline source map", function(done) {
var command = uglifyjscmd + " test/input/issue-520/input.js --source-map content=inline,url=inline -p spidermonkey"; var command = uglifyjscmd + ' test/input/issue-520/input.js --in-source-map inline --source-map-inline --spidermonkey';
exec(command, function (err, stdout, stderr) { exec(command, function (err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stderr, "ERROR: inline source map only works with built-in parser\n"); assert.strictEqual(stderr, "ERROR: Inline source map only works with built-in parser\n");
done(); done();
}); });
}); });
@@ -216,7 +208,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(lines[0], "Parse error at test/input/invalid/simple.js:1,12"); assert.strictEqual(lines[0], "Parse error at test/input/invalid/simple.js:1,12");
assert.strictEqual(lines[1], "function f(a{}"); assert.strictEqual(lines[1], "function f(a{}");
assert.strictEqual(lines[2], " ^"); assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "ERROR: Unexpected token punc «{», expected punc «,»"); assert.strictEqual(lines[3], "SyntaxError: Unexpected token punc «{», expected punc «,»");
done(); done();
}); });
}); });
@@ -229,7 +221,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(lines[0], "Parse error at test/input/invalid/tab.js:1,12"); assert.strictEqual(lines[0], "Parse error at test/input/invalid/tab.js:1,12");
assert.strictEqual(lines[1], "\t\tfoo(\txyz, 0abc);"); assert.strictEqual(lines[1], "\t\tfoo(\txyz, 0abc);");
assert.strictEqual(lines[2], "\t\t \t ^"); assert.strictEqual(lines[2], "\t\t \t ^");
assert.strictEqual(lines[3], "ERROR: Invalid syntax: 0abc"); assert.strictEqual(lines[3], "SyntaxError: Invalid syntax: 0abc");
done(); done();
}); });
}); });
@@ -242,7 +234,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(lines[0], "Parse error at test/input/invalid/eof.js:2,0"); assert.strictEqual(lines[0], "Parse error at test/input/invalid/eof.js:2,0");
assert.strictEqual(lines[1], "foo, bar("); assert.strictEqual(lines[1], "foo, bar(");
assert.strictEqual(lines[2], " ^"); assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "ERROR: Unexpected token: eof (undefined)"); assert.strictEqual(lines[3], "SyntaxError: Unexpected token: eof (undefined)");
done(); done();
}); });
}); });
@@ -255,268 +247,167 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(lines[0], "Parse error at test/input/invalid/loop-no-body.js:2,0"); assert.strictEqual(lines[0], "Parse error at test/input/invalid/loop-no-body.js:2,0");
assert.strictEqual(lines[1], "for (var i = 0; i < 1; i++) "); assert.strictEqual(lines[1], "for (var i = 0; i < 1; i++) ");
assert.strictEqual(lines[2], " ^"); assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "ERROR: Unexpected token: eof (undefined)"); assert.strictEqual(lines[3], "SyntaxError: Unexpected token: eof (undefined)");
done();
});
});
it("Should support hyphen as shorthand", function(done) {
var command = uglifyjscmd + ' test/input/issue-1431/sample.js -m keep-fnames=true';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n");
done(); done();
}); });
}); });
it("Should throw syntax error (5--)", function(done) { it("Should throw syntax error (5--)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_1.js'; var command = uglifyjscmd + ' test/input/invalid/assign_1.js';
exec(command, function (err, stdout, stderr) { exec(command, function (err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_1.js:1,18", "Parse error at test/input/invalid/assign_1.js:1,18",
"console.log(1 || 5--);", "console.log(1 || 5--);",
" ^", " ^",
"ERROR: Invalid use of -- operator" "SyntaxError: Invalid use of -- operator"
].join("\n"));
done();
});
});
it("Should throw syntax error (Math.random() /= 2)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_2.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_2.js:1,32",
"console.log(2 || (Math.random() /= 2));",
" ^",
"ERROR: Invalid assignment"
].join("\n"));
done();
});
});
it("Should throw syntax error (++this)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_3.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_3.js:1,17",
"console.log(3 || ++this);",
" ^",
"ERROR: Invalid use of ++ operator"
].join("\n"));
done();
});
});
it("Should throw syntax error (++null)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_4.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_4.js:1,0",
"++null",
"^",
"ERROR: Invalid use of ++ operator"
].join("\n"));
done();
});
});
it("Should throw syntax error (a.=)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_1.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/dot_1.js:1,2",
"a.=",
" ^",
"ERROR: Unexpected token: operator (=)"
].join("\n"));
done();
});
});
it("Should throw syntax error (%.a)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_2.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/dot_2.js:1,0",
"%.a;",
"^",
"ERROR: Unexpected token: operator (%)"
].join("\n"));
done();
});
});
it("Should throw syntax error (a./();)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_3.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/dot_3.js:1,2",
"a./();",
" ^",
"ERROR: Unexpected token: operator (/)"
].join("\n"));
done();
});
});
it("Should throw syntax error ({%: 1})", function(done) {
var command = uglifyjscmd + ' test/input/invalid/object.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/object.js:1,13",
"console.log({%: 1});",
" ^",
"ERROR: Unexpected token: operator (%)"
].join("\n"));
done();
});
});
it("Should throw syntax error (const a)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/const.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/const.js:7,11",
" const a;",
" ^",
"ERROR: Missing initializer in const declaration"
].join("\n"));
done();
});
});
it("Should throw syntax error (delete x)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/delete.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/delete.js:13,11",
" delete x;",
" ^",
"ERROR: Calling delete on expression not allowed in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (function g(arguments))", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_1.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/function_1.js:4,11",
"function g(arguments) {",
" ^",
"ERROR: Unexpected arguments in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (function eval())", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_2.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/function_2.js:4,9",
"function eval() {",
" ^",
"ERROR: Unexpected eval in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (iife arguments())", function(done) {
var command = uglifyjscmd + ' test/input/invalid/function_3.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/function_3.js:4,10",
"!function arguments() {",
" ^",
"ERROR: Unexpected arguments in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (catch(eval))", function(done) {
var command = uglifyjscmd + ' test/input/invalid/try.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/try.js:7,18",
" try {} catch (eval) {}",
" ^",
"ERROR: Unexpected eval in strict mode"
].join("\n"));
done();
});
});
it("Should throw syntax error (var eval)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/var.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/var.js:7,8",
" var eval;",
" ^",
"ERROR: Unexpected eval in strict mode"
].join("\n"));
done();
});
});
it("Should handle literal string as source map input", function(done) {
var command = [
uglifyjscmd,
"test/input/issue-1236/simple.js",
"--source-map",
'content="' + read_map() + '",url=inline'
].join(" ");
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, [
'"use strict";var foo=function foo(x){return"foo "+x};console.log(foo("bar"));',
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmpzIl0sIm5hbWVzIjpbImZvbyIsIngiLCJjb25zb2xlIiwibG9nIl0sIm1hcHBpbmdzIjoiWUFBQSxJQUFJQSxLQUFNLFFBQU5BLEtBQU1DLEdBQUEsTUFBSyxPQUFTQSxFQUN4QkMsU0FBUUMsSUFBSUgsSUFBSSJ9",
""
].join("\n")); ].join("\n"));
done(); done();
}); });
function read_map() {
var map = JSON.parse(read("./test/input/issue-1236/simple.js.map"));
delete map.sourcesContent;
return JSON.stringify(map).replace(/"/g, '\\"');
}
}); });
it("Should dump AST as JSON", function(done) { it("Should throw syntax error (Math.random() /= 2)", function(done) {
var command = uglifyjscmd + " test/input/global_defs/simple.js -mco ast"; var command = uglifyjscmd + ' test/input/invalid/assign_2.js';
exec(command, function (err, stdout) {
if (err) throw err;
var ast = JSON.parse(stdout); exec(command, function (err, stdout, stderr) {
assert.strictEqual(ast._class, "AST_Toplevel"); assert.ok(err);
assert.ok(Array.isArray(ast.body)); assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_2.js:1,32",
"console.log(2 || (Math.random() /= 2));",
" ^",
"SyntaxError: Invalid assignment"
].join("\n"));
done();
});
});
it("Should throw syntax error (++this)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_3.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_3.js:1,17",
"console.log(3 || ++this);",
" ^",
"SyntaxError: Invalid use of ++ operator"
].join("\n"));
done();
});
});
it("Should throw syntax error (++null)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/assign_4.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_4.js:1,0",
"++null",
"^",
"SyntaxError: Invalid use of ++ operator"
].join("\n"));
done();
});
});
it("Should throw syntax error (a.=)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_1.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/dot_1.js:1,2",
"a.=",
" ^",
"SyntaxError: Unexpected token: operator (=)"
].join("\n"));
done();
});
});
it("Should throw syntax error (%.a)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_2.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/dot_2.js:1,0",
"%.a;",
"^",
"SyntaxError: Unexpected token: operator (%)"
].join("\n"));
done();
});
});
it("Should throw syntax error (a./();)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/dot_3.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/dot_3.js:1,2",
"a./();",
" ^",
"SyntaxError: Unexpected token: operator (/)"
].join("\n"));
done();
});
});
it("Should throw syntax error ({%: 1})", function(done) {
var command = uglifyjscmd + ' test/input/invalid/object.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/object.js:1,13",
"console.log({%: 1});",
" ^",
"SyntaxError: Unexpected token: operator (%)"
].join("\n"));
done();
});
});
it("Should throw syntax error (else)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/else.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/else.js:1,7",
"if (0) else 1;",
" ^",
"SyntaxError: Unexpected token: keyword (else)"
].join("\n"));
done();
});
});
it("Should throw syntax error (return)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/return.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/return.js:1,0",
"return 42;",
"^",
"SyntaxError: 'return' outside of function"
].join("\n"));
done(); done();
}); });
}); });

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("../node"); var UglifyJS = require('../../');
var assert = require("assert"); var assert = require("assert");
describe("comment filters", function() { describe("comment filters", function() {
@@ -75,6 +75,7 @@ describe("comment filters", function() {
it("Should handle shebang and preamble correctly", function() { it("Should handle shebang and preamble correctly", function() {
var code = UglifyJS.minify("#!/usr/bin/node\nvar x = 10;", { var code = UglifyJS.minify("#!/usr/bin/node\nvar x = 10;", {
fromString: true,
output: { preamble: "/* Build */" } output: { preamble: "/* Build */" }
}).code; }).code;
assert.strictEqual(code, "#!/usr/bin/node\n/* Build */\nvar x=10;"); assert.strictEqual(code, "#!/usr/bin/node\n/* Build */\nvar x=10;");
@@ -82,6 +83,7 @@ describe("comment filters", function() {
it("Should handle preamble without shebang correctly", function() { it("Should handle preamble without shebang correctly", function() {
var code = UglifyJS.minify("var x = 10;", { var code = UglifyJS.minify("var x = 10;", {
fromString: true,
output: { preamble: "/* Build */" } output: { preamble: "/* Build */" }
}).code; }).code;
assert.strictEqual(code, "/* Build */\nvar x=10;"); assert.strictEqual(code, "/* Build */\nvar x=10;");

View File

@@ -1,5 +1,5 @@
var assert = require("assert"); var assert = require("assert");
var uglify = require("../node"); var uglify = require("../../");
describe("Comment", function() { describe("Comment", function() {
it("Should recognize eol of single line comments", function() { it("Should recognize eol of single line comments", function() {
@@ -20,7 +20,7 @@ describe("Comment", function() {
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.throws(function() { assert.throws(function() {
uglify.parse(tests[i]); uglify.parse(tests[i], {fromString: true})
}, fail, tests[i]); }, fail, tests[i]);
} }
}); });
@@ -43,7 +43,7 @@ describe("Comment", function() {
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.throws(function() { assert.throws(function() {
uglify.parse(tests[i]); uglify.parse(tests[i], {fromString: true})
}, fail, tests[i]); }, fail, tests[i]);
} }
}); });

View File

@@ -6,7 +6,9 @@ describe("comment before constant", function() {
it("Should test comment before constant is retained and output after mangle.", function() { it("Should test comment before constant is retained and output after mangle.", function() {
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
fromString: true,
compress: { collapse_vars: false, reduce_vars: false }, compress: { collapse_vars: false, reduce_vars: false },
mangle: {},
output: { comments: true }, output: { comments: true },
}); });
assert.strictEqual(result.code, 'function f(){/*c1*/var/*c2*/n=/*c3*/!1;return n}'); assert.strictEqual(result.code, 'function f(){/*c1*/var/*c2*/n=/*c3*/!1;return n}');
@@ -14,9 +16,12 @@ describe("comment before constant", function() {
it("Should test code works when comments disabled.", function() { it("Should test code works when comments disabled.", function() {
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
fromString: true,
compress: { collapse_vars: false, reduce_vars: false }, compress: { collapse_vars: false, reduce_vars: false },
mangle: {},
output: { comments: false }, output: { comments: false },
}); });
assert.strictEqual(result.code, 'function f(){var n=!1;return n}'); assert.strictEqual(result.code, 'function f(){var n=!1;return n}');
}); });
}); });

View File

@@ -1,5 +1,5 @@
var assert = require("assert"); var assert = require("assert");
var uglify = require("../node"); var uglify = require("../../");
describe("Directives", function() { describe("Directives", function() {
it ("Should allow tokenizer to store directives state", function() { it ("Should allow tokenizer to store directives state", function() {
@@ -197,7 +197,7 @@ describe("Directives", function() {
assert.strictEqual( assert.strictEqual(
uglify.minify( uglify.minify(
'"use strict";\'use strict\';"use strict";"use strict";;\'use strict\';console.log(\'use strict\');', '"use strict";\'use strict\';"use strict";"use strict";;\'use strict\';console.log(\'use strict\');',
{output: {beautify: true, quote_style: 3}, compress: false} {fromString: true, output: {beautify: true, quote_style: 3}, compress: false}
).code, ).code,
'"use strict";\n\n\'use strict\';\n\n"use strict";\n\n"use strict";\n\n;\'use strict\';\n\nconsole.log(\'use strict\');' '"use strict";\n\n\'use strict\';\n\n"use strict";\n\n"use strict";\n\n;\'use strict\';\n\nconsole.log(\'use strict\');'
); );
@@ -225,7 +225,7 @@ describe("Directives", function() {
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.strictEqual( assert.strictEqual(
uglify.minify(tests[i][0], {compress: false, mangle: false}).code, uglify.minify(tests[i][0], {fromString: true, quote_style: 3, compress: false, mangle: false}).code,
tests[i][1], tests[i][1],
tests[i][0] tests[i][0]
); );
@@ -234,7 +234,7 @@ describe("Directives", function() {
it("Should add double semicolon when relying on automatic semicolon insertion", function() { it("Should add double semicolon when relying on automatic semicolon insertion", function() {
var code = uglify.minify('"use strict";"use\\x20strict";', var code = uglify.minify('"use strict";"use\\x20strict";',
{output: {semicolons: false}, compress: false} {fromString: true, output: {semicolons: false}, compress: false}
).code; ).code;
assert.strictEqual(code, '"use strict";;"use strict"\n'); assert.strictEqual(code, '"use strict";;"use strict"\n');
}); });
@@ -340,7 +340,7 @@ describe("Directives", function() {
]; ];
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.strictEqual( assert.strictEqual(
uglify.minify(tests[i][0], {output:{quote_style: tests[i][1]}, compress: false}).code, uglify.minify(tests[i][0], {fromString: true, output:{quote_style: tests[i][1]}, compress: false}).code,
tests[i][2], tests[i][2],
tests[i][0] + " using mode " + tests[i][1] tests[i][0] + " using mode " + tests[i][1]
); );
@@ -351,18 +351,28 @@ describe("Directives", function() {
var tests = [ var tests = [
[ [
'"use strict";"use strict";"use strict";"use foo";"use strict";;"use sloppy";doSomething("foo");', '"use strict";"use strict";"use strict";"use foo";"use strict";;"use sloppy";doSomething("foo");',
'"use strict";"use foo";doSomething("foo");' '"use strict";"use foo";doSomething("foo");',
'function f(){ "use strict" }',
'function f(){ "use asm" }',
'function f(){ "use nondirective" }',
'function f(){ ;"use strict" }',
'function f(){ "use \n"; }',
], ],
[ [
// Nothing gets optimised in the compressor because "use asm" is the first statement // Nothing gets optimised in the compressor because "use asm" is the first statement
'"use asm";"use\\x20strict";1+1;', '"use asm";"use\\x20strict";1+1;',
'"use asm";;"use strict";1+1;' // Yet, the parser noticed that "use strict" wasn't a directive '"use asm";;"use strict";1+1;', // Yet, the parser noticed that "use strict" wasn't a directive
'function f(){"use strict"}',
'function f(){"use asm"}',
'function f(){"use nondirective"}',
'function f(){}',
'function f(){}',
] ]
]; ];
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.strictEqual( assert.strictEqual(
uglify.minify(tests[i][0], {compress: {collapse_vars: true, side_effects: true}}).code, uglify.minify(tests[i][0], {fromString: true}).code,
tests[i][1], tests[i][1],
tests[i][0] tests[i][0]
); );

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("../node"); var UglifyJS = require('../../');
var assert = require("assert"); var assert = require("assert");
describe("Getters and setters", function() { describe("Getters and setters", function() {
@@ -71,7 +71,7 @@ describe("Getters and setters", function() {
var fail = function(data) { var fail = function(data) {
return function (e) { return function (e) {
return e instanceof UglifyJS.JS_Parse_Error && return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "Invalid getter/setter name: " + data.operator; e.message === "Unexpected token: operator (" + data.operator + ")";
}; };
}; };

View File

@@ -1,80 +1,58 @@
var Uglify = require('../../');
var assert = require("assert"); var assert = require("assert");
var exec = require("child_process").exec;
var path = require("path"); var path = require("path");
var readFileSync = require("fs").readFileSync;
describe("bin/uglifyjs with input file globs", function() { describe("minify() with input file globs", function() {
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs'; it("minify() with one input file glob string.", function() {
it("bin/uglifyjs with one input file extension glob.", function(done) { var result = Uglify.minify("test/input/issue-1242/foo.*");
var command = uglifyjscmd + ' "test/input/issue-1242/foo.*" -cm'; assert.strictEqual(result.code, 'function foo(o){print("Foo:",2*o)}var print=console.log.bind(console);');
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, 'function foo(o){print("Foo:",2*o)}var print=console.log.bind(console);\n');
done();
});
}); });
it("bin/uglifyjs with one input file name glob.", function(done) { it("minify() with an array of one input file glob.", function() {
var command = uglifyjscmd + ' "test/input/issue-1242/b*.es5" -cm'; var result = Uglify.minify([
"test/input/issue-1242/b*.es5",
exec(command, function(err, stdout) { ]);
if (err) throw err; assert.strictEqual(result.code, 'function bar(n){return 3*n}function baz(n){return n/2}');
assert.strictEqual(stdout, 'function bar(n){return 3*n}function baz(n){return n/2}\n');
done();
});
}); });
it("bin/uglifyjs with multiple input file globs.", function(done) { it("minify() with an array of multiple input file globs.", function() {
var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel'; var result = Uglify.minify([
"test/input/issue-1242/???.es5",
exec(command, function(err, stdout) { "test/input/issue-1242/*.js",
if (err) throw err; ], {
compress: { toplevel: true }
assert.strictEqual(stdout, 'var print=console.log.bind(console),a=function(n){return 3*n}(3),b=function(n){return n/2}(12);print("qux",a,b),function(n){print("Foo:",2*n)}(11);\n');
done();
}); });
assert.strictEqual(result.code, 'var print=console.log.bind(console),a=function(n){return 3*n}(3),b=function(n){return n/2}(12);print("qux",a,b),function(n){print("Foo:",2*n)}(11);');
}); });
it("should throw with non-matching glob string", function(done) { it("should throw with non-matching glob string", function() {
var command = uglifyjscmd + ' "test/input/issue-1242/blah.*"'; var glob = "test/input/issue-1242/blah.*";
assert.strictEqual(Uglify.simple_glob(glob).length, 1);
exec(command, function(err, stdout, stderr) { assert.strictEqual(Uglify.simple_glob(glob)[0], glob);
assert.ok(err); assert.throws(function() {
assert.ok(/^ERROR: ENOENT/.test(stderr)); Uglify.minify(glob);
done(); }, "should throw file not found");
});
}); });
it('"?" in glob string should not match "/"', function(done) { it('"?" in glob string should not match "/"', function() {
var command = uglifyjscmd + ' "test/input?issue-1242/foo.*"'; var glob = "test/input?issue-1242/foo.*";
assert.strictEqual(Uglify.simple_glob(glob).length, 1);
exec(command, function(err, stdout, stderr) { assert.strictEqual(Uglify.simple_glob(glob)[0], glob);
assert.ok(err); assert.throws(function() {
assert.ok(/^ERROR: ENOENT/.test(stderr)); Uglify.minify(glob);
done(); }, "should throw file not found");
});
}); });
it("should handle special characters in glob string", function(done) { it("should handle special characters in glob string", function() {
var command = uglifyjscmd + ' "test/input/issue-1632/^{*}[???](*)+$.??" -cm'; var result = Uglify.minify("test/input/issue-1632/^{*}[???](*)+$.??");
assert.strictEqual(result.code, "console.log(x);");
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "console.log(x);\n");
done();
});
}); });
it("should handle array of glob strings - matching and otherwise", function(done) { it("should handle array of glob strings - matching and otherwise", function() {
var dir = "test/input/issue-1242"; var dir = "test/input/issue-1242";
var command = uglifyjscmd + ' "' + [ var matches = Uglify.simple_glob([
path.join(dir, "b*.es5"), path.join(dir, "b*.es5"),
path.join(dir, "z*.es5"), path.join(dir, "z*.es5"),
path.join(dir, "*.js") path.join(dir, "*.js"),
].join('" "') + '"'; ]);
assert.strictEqual(matches.length, 4);
exec(command, function(err, stdout, stderr) { assert.strictEqual(matches[0], path.join(dir, "bar.es5"));
assert.ok(err); assert.strictEqual(matches[1], path.join(dir, "baz.es5"));
assert.ok(/^ERROR: ENOENT.*?z\*\.es5/.test(stderr)); assert.strictEqual(matches[2], path.join(dir, "z*.es5"));
done(); assert.strictEqual(matches[3], path.join(dir, "qux.js"));
});
}); });
}); });

View File

@@ -8,7 +8,12 @@ describe("Huge number of comments.", function() {
for (i = 1; i <= 5000; ++i) { js += "// " + i + "\n"; } for (i = 1; i <= 5000; ++i) { js += "// " + i + "\n"; }
for (; i <= 10000; ++i) { js += "/* " + i + " */ /**/"; } for (; i <= 10000; ++i) { js += "/* " + i + " */ /**/"; }
js += "x; }"; js += "x; }";
var result = Uglify.minify(js, { mangle: false }); var result = Uglify.minify(js, {
fromString: true,
mangle: false,
compress: {}
});
assert.strictEqual(result.code, "function lots_of_comments(x){return 7-x}"); assert.strictEqual(result.code, "function lots_of_comments(x){return 7-x}");
}); });
}); });

View File

@@ -25,9 +25,9 @@ describe("input sourcemaps", function() {
transpilemap = sourceMap || getMap(); transpilemap = sourceMap || getMap();
var result = Uglify.minify(transpiled, { var result = Uglify.minify(transpiled, {
sourceMap: { fromString: true,
content: transpilemap inSourceMap: transpilemap,
} outSourceMap: true
}); });
map = new SourceMapConsumer(result.map); map = new SourceMapConsumer(result.map);

View File

@@ -11,7 +11,7 @@ describe("let", function() {
s += "var v" + i + "=0;"; s += "var v" + i + "=0;";
} }
s += '}'; s += '}';
var result = Uglify.minify(s, {compress: false}); var result = Uglify.minify(s, {fromString: true, compress: false});
// Verify that select keywords and reserved keywords not produced // Verify that select keywords and reserved keywords not produced
assert.strictEqual(result.code.indexOf("var let="), -1); assert.strictEqual(result.code.indexOf("var let="), -1);

View File

@@ -1,10 +1,11 @@
var Uglify = require("../node"); var Uglify = require('../../');
var assert = require("assert"); var assert = require("assert");
describe("line-endings", function() { describe("line-endings", function() {
var options = { var options = {
compress: false, fromString: true,
mangle: false, mangle: false,
compress: false,
output: { output: {
beautify: false, beautify: false,
comments: /^!/, comments: /^!/,

View File

@@ -6,41 +6,43 @@ describe("Input file as map", function() {
var jsMap = { var jsMap = {
'/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};' '/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};'
}; };
var result = Uglify.minify(jsMap, {sourceMap: true}); var result = Uglify.minify(jsMap, {fromString: true, outSourceMap: true});
var map = JSON.parse(result.map); var map = JSON.parse(result.map);
assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3};'); assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3};');
assert.deepEqual(map.sources, ['/scripts/foo.js']); assert.deepEqual(map.sources, ['/scripts/foo.js']);
assert.strictEqual(map.file, undefined); assert.strictEqual(map.file, undefined);
result = Uglify.minify(jsMap); result = Uglify.minify(jsMap, {fromString: true, outFileName: 'out.js'});
assert.strictEqual(result.map, undefined); assert.strictEqual(result.map, null);
result = Uglify.minify(jsMap, {sourceMap: {filename: 'out.js'}}); result = Uglify.minify(jsMap, {fromString: true, outFileName: 'out.js', outSourceMap: true});
map = JSON.parse(result.map); map = JSON.parse(result.map);
assert.strictEqual(map.file, 'out.js'); assert.strictEqual(map.file, 'out.js');
}); });
it("Should accept array of strings", function() { it("Should accept array of objects and strings", function() {
var jsSeq = [ var jsSeq = [
'var foo = {"x": 1, y: 2, \'z\': 3};', {'/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};'},
'var bar = 15;' 'var bar = 15;'
]; ];
var result = Uglify.minify(jsSeq, {sourceMap: true}); var result = Uglify.minify(jsSeq, {fromString: true, outSourceMap: true});
var map = JSON.parse(result.map); var map = JSON.parse(result.map);
assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3},bar=15;'); assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3},bar=15;');
assert.deepEqual(map.sources, ['0', '1']); assert.strictEqual(map.sources[0], '/scripts/foo.js');
}); });
it("Should correctly include source", function() { it("Should correctly include source", function() {
var jsMap = { var jsSeq = [
'/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};' {'/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};'},
}; 'var bar = 15;'
var result = Uglify.minify(jsMap, {sourceMap: {includeSources: true}}); ];
var result = Uglify.minify(jsSeq, {fromString: true, outSourceMap: true, sourceMapIncludeSources: true});
var map = JSON.parse(result.map); var map = JSON.parse(result.map);
assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3};'); assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3},bar=15;');
assert.deepEqual(map.sourcesContent, ['var foo = {"x": 1, y: 2, \'z\': 3};']); assert.deepEqual(map.sourcesContent, ['var foo = {"x": 1, y: 2, \'z\': 3};', 'var bar = 15;']);
}); });
}); });

View File

@@ -2,14 +2,10 @@ var Uglify = require('../../');
var assert = require("assert"); var assert = require("assert");
var readFileSync = require("fs").readFileSync; var readFileSync = require("fs").readFileSync;
function read(path) {
return readFileSync(path, "utf8");
}
describe("minify", function() { describe("minify", function() {
it("Should test basic sanity of minify with default options", function() { it("Should test basic sanity of minify with default options", function() {
var js = 'function foo(bar) { if (bar) return 3; else return 7; var u = not_called(); }'; var js = 'function foo(bar) { if (bar) return 3; else return 7; var u = not_called(); }';
var result = Uglify.minify(js); var result = Uglify.minify(js, {fromString: true});
assert.strictEqual(result.code, 'function foo(n){return n?3:7}'); assert.strictEqual(result.code, 'function foo(n){return n?3:7}');
}); });
@@ -17,7 +13,7 @@ describe("minify", function() {
it("Should preserve quotes in object literals", function() { it("Should preserve quotes in object literals", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
output: { fromString: true, output: {
keep_quoted_props: true keep_quoted_props: true
}}); }});
assert.strictEqual(result.code, 'var foo={"x":1,y:2,"z":3};'); assert.strictEqual(result.code, 'var foo={"x":1,y:2,"z":3};');
@@ -26,7 +22,7 @@ describe("minify", function() {
it("Should preserve quote styles when quote_style is 3", function() { it("Should preserve quote styles when quote_style is 3", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
output: { fromString: true, output: {
keep_quoted_props: true, keep_quoted_props: true,
quote_style: 3 quote_style: 3
}}); }});
@@ -36,7 +32,7 @@ describe("minify", function() {
it("Should not preserve quotes in object literals when disabled", function() { it("Should not preserve quotes in object literals when disabled", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
output: { fromString: true, output: {
keep_quoted_props: false, keep_quoted_props: false,
quote_style: 3 quote_style: 3
}}); }});
@@ -48,13 +44,12 @@ describe("minify", function() {
it("Shouldn't mangle quoted properties", function() { it("Shouldn't mangle quoted properties", function() {
var js = 'a["foo"] = "bar"; a.color = "red"; x = {"bar": 10};'; var js = 'a["foo"] = "bar"; a.color = "red"; x = {"bar": 10};';
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
fromString: true,
compress: { compress: {
properties: false properties: false
}, },
mangle: { mangleProperties: {
properties: { ignore_quoted: true
keep_quoted: true
}
}, },
output: { output: {
keep_quoted_props: true, keep_quoted_props: true,
@@ -68,12 +63,10 @@ describe("minify", function() {
describe("inSourceMap", function() { describe("inSourceMap", function() {
it("Should read the given string filename correctly when sourceMapIncludeSources is enabled (#1236)", function() { it("Should read the given string filename correctly when sourceMapIncludeSources is enabled (#1236)", function() {
var result = Uglify.minify(read("./test/input/issue-1236/simple.js"), { var result = Uglify.minify('./test/input/issue-1236/simple.js', {
sourceMap: { outSourceMap: "simple.min.js.map",
content: read("./test/input/issue-1236/simple.js.map"), inSourceMap: "./test/input/issue-1236/simple.js.map",
filename: "simple.min.js", sourceMapIncludeSources: true
includeSources: true
}
}); });
var map = JSON.parse(result.map); var map = JSON.parse(result.map);
@@ -84,12 +77,10 @@ describe("minify", function() {
'let foo = x => "foo " + x;\nconsole.log(foo("bar"));'); 'let foo = x => "foo " + x;\nconsole.log(foo("bar"));');
}); });
it("Should process inline source map", function() { it("Should process inline source map", function() {
var code = Uglify.minify(read("./test/input/issue-520/input.js"), { var code = Uglify.minify("./test/input/issue-520/input.js", {
compress: { toplevel: true }, compress: { toplevel: true },
sourceMap: { inSourceMap: "inline",
content: "inline", sourceMapInline: true
url: "inline"
}
}).code + "\n"; }).code + "\n";
assert.strictEqual(code, readFileSync("test/input/issue-520/output.js", "utf8")); assert.strictEqual(code, readFileSync("test/input/issue-520/output.js", "utf8"));
}); });
@@ -100,11 +91,9 @@ describe("minify", function() {
warnings.push(txt); warnings.push(txt);
}; };
try { try {
var result = Uglify.minify(read("./test/input/issue-1323/sample.js"), { var result = Uglify.minify("./test/input/issue-1323/sample.js", {
inSourceMap: "inline",
mangle: false, mangle: false,
sourceMap: {
content: "inline"
}
}); });
assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();"); assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();");
assert.strictEqual(warnings.length, 1); assert.strictEqual(warnings.length, 1);
@@ -114,34 +103,41 @@ describe("minify", function() {
} }
}); });
it("Should fail with multiple input and inline source map", function() { it("Should fail with multiple input and inline source map", function() {
var result = Uglify.minify([ assert.throws(function() {
read("./test/input/issue-520/input.js"), Uglify.minify([
read("./test/input/issue-520/output.js") "./test/input/issue-520/input.js",
], { "./test/input/issue-520/output.js"
sourceMap: { ], {
content: "inline", inSourceMap: "inline",
url: "inline" sourceMapInline: true
} });
});
});
it("Should fail with SpiderMonkey and inline source map", function() {
assert.throws(function() {
Uglify.minify("./test/input/issue-520/input.js", {
inSourceMap: "inline",
sourceMapInline: true,
spidermonkey: true
});
}); });
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: inline source map only works with singular input");
}); });
}); });
describe("sourceMapInline", function() { describe("sourceMapInline", function() {
it("should append source map to output js when sourceMapInline is enabled", function() { it("should append source map to output js when sourceMapInline is enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };', { var result = Uglify.minify('var a = function(foo) { return foo; };', {
sourceMap: { fromString: true,
url: "inline" sourceMapInline: true
}
}); });
var code = result.code; var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};\n" + assert.strictEqual(code, "var a=function(n){return n};\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsR0FBSUEsR0FBSSxTQUFTQyxHQUFPLE1BQU9BIn0="); "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIj8iXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsR0FBSUEsR0FBSSxTQUFTQyxHQUFPLE1BQU9BIn0=");
}); });
it("should not append source map to output js when sourceMapInline is not enabled", function() { it("should not append source map to output js when sourceMapInline is not enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };'); var result = Uglify.minify('var a = function(foo) { return foo; };', {
fromString: true
});
var code = result.code; var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};"); assert.strictEqual(code, "var a=function(n){return n};");
}); });
@@ -150,6 +146,7 @@ describe("minify", function() {
describe("#__PURE__", function() { describe("#__PURE__", function() {
it("should drop #__PURE__ hint after use", function() { it("should drop #__PURE__ hint after use", function() {
var result = Uglify.minify('//@__PURE__ comment1 #__PURE__ comment2\n foo(), bar();', { var result = Uglify.minify('//@__PURE__ comment1 #__PURE__ comment2\n foo(), bar();', {
fromString: true,
output: { output: {
comments: "all", comments: "all",
beautify: false, beautify: false,
@@ -160,6 +157,7 @@ describe("minify", function() {
}); });
it("should not drop #__PURE__ hint if function is retained", function() { it("should not drop #__PURE__ hint if function is retained", function() {
var result = Uglify.minify("var a = /*#__PURE__*/(function(){ foo(); })();", { var result = Uglify.minify("var a = /*#__PURE__*/(function(){ foo(); })();", {
fromString: true,
output: { output: {
comments: "all", comments: "all",
beautify: false, beautify: false,
@@ -171,14 +169,27 @@ describe("minify", function() {
}); });
describe("JS_Parse_Error", function() { describe("JS_Parse_Error", function() {
it("should return syntax error", function() { it("should throw syntax error", function() {
var result = Uglify.minify("function f(a{}"); assert.throws(function() {
var err = result.error; Uglify.minify("function f(a{}", { fromString: true });
assert.ok(err instanceof Error); }, function(err) {
assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token punc «{», expected punc «,»"); assert.ok(err instanceof Error);
assert.strictEqual(err.filename, "0"); assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token punc «{», expected punc «,»");
assert.strictEqual(err.line, 1); assert.strictEqual(err.filename, 0);
assert.strictEqual(err.col, 12); assert.strictEqual(err.line, 1);
assert.strictEqual(err.col, 12);
return true;
});
}); });
}); });
describe("Compressor", function() {
it("should be backward compatible with ast.transform(compressor)", function() {
var ast = Uglify.parse("function f(a){for(var i=0;i<a;i++)console.log(i)}");
ast.figure_out_scope();
ast = ast.transform(Uglify.Compressor());
assert.strictEqual(ast.print_to_string(), "function f(a){for(var i=0;i<a;i++)console.log(i)}");
});
})
}); });

View File

@@ -34,6 +34,7 @@ describe("New", function() {
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.strictEqual( assert.strictEqual(
uglify.minify(tests[i], { uglify.minify(tests[i], {
fromString: true,
output: {beautify: true}, output: {beautify: true},
compress: false, compress: false,
mangle: false mangle: false
@@ -75,6 +76,7 @@ describe("New", function() {
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.strictEqual( assert.strictEqual(
uglify.minify(tests[i], { uglify.minify(tests[i], {
fromString: true,
output: {beautify: false}, output: {beautify: false},
compress: false, compress: false,
mangle: false mangle: false

View File

@@ -1,5 +1,5 @@
var assert = require("assert"); var assert = require("assert");
var uglify = require("../node"); var uglify = require("../../");
describe("Number literals", function () { describe("Number literals", function () {
it("Should not allow legacy octal literals in strict mode", function() { it("Should not allow legacy octal literals in strict mode", function() {

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("../node"); var UglifyJS = require("../../");
var assert = require("assert"); var assert = require("assert");
describe("operator", function() { describe("operator", function() {

View File

@@ -42,8 +42,8 @@ describe("test/jetstream.js", function() {
run("npm", ["install", "phantomjs-prebuilt@2.1.14"], done); run("npm", ["install", "phantomjs-prebuilt@2.1.14"], done);
}); });
[ [
"-mc", "-mc warnings=false",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto", "-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto,warnings=false",
].forEach(function(options) { ].forEach(function(options) {
it("Should pass with options " + options, function(done) { it("Should pass with options " + options, function(done) {
var args = options.split(/ /); var args = options.split(/ /);

View File

@@ -13,7 +13,9 @@ describe("screw-ie8", function () {
}\ }\
console.log('undefined is ' + undefined);\ console.log('undefined is ' + undefined);\
return b === undefined;\ return b === undefined;\
};" };", {
fromString: true
}
).code, ).code,
'function a(o){try{throw"Stuff"}catch(o){console.log("caught: "+o)}return console.log("undefined is "+void 0),void 0===o}' 'function a(o){try{throw"Stuff"}catch(o){console.log("caught: "+o)}return console.log("undefined is "+void 0),void 0===o}'
); );

View File

@@ -1,21 +1,32 @@
var assert = require("assert"); var assert = require("assert");
var exec = require("child_process").exec; var exec = require("child_process").exec;
var uglify = require("../node"); var uglify = require("../../");
describe("spidermonkey export/import sanity test", function() { describe("spidermonkey export/import sanity test", function() {
it("should produce a functional build when using --self with spidermonkey", function(done) { it("should produce a functional build when using --self with spidermonkey", function (done) {
this.timeout(20000); this.timeout(20000);
var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs'; var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs';
var command = uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey | " + var command = uglifyjs + " --self -cm --wrap SpiderUglify --dump-spidermonkey-ast | " +
uglifyjs + " -p spidermonkey -cm"; uglifyjs + " --spidermonkey -cm";
exec(command, function(err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;
eval(stdout); eval(stdout);
assert.strictEqual(typeof SpiderUglify, "object"); assert.strictEqual(typeof SpiderUglify, "object");
assert.strictEqual(SpiderUglify.minify("foo([true,,2+3]);").code, "foo([!0,,5]);");
var ast = SpiderUglify.parse("foo([true,,2+3]);");
assert.strictEqual(true, ast instanceof SpiderUglify.AST_Node);
ast.figure_out_scope();
ast = SpiderUglify.Compressor({}).compress(ast);
assert.strictEqual(true, ast instanceof SpiderUglify.AST_Node);
var stream = SpiderUglify.OutputStream({});
ast.print(stream);
var code = stream.toString();
assert.strictEqual(code, "foo([!0,,5]);");
done(); done();
}); });

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("../node"); var UglifyJS = require('../../');
var assert = require("assert"); var assert = require("assert");
describe("String literals", function() { describe("String literals", function() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert"); var assert = require("assert");
var uglify = require("../node"); var uglify = require("../../");
describe("With", function() { describe("With", function() {
it("Should throw syntaxError when using with statement in strict mode", function() { it("Should throw syntaxError when using with statement in strict mode", function() {

View File

@@ -1,103 +1,87 @@
// Testing UglifyJS <-> SpiderMonkey AST conversion // Testing UglifyJS <-> SpiderMonkey AST conversion
// through generative testing. "use strict";
var UglifyJS = require("./node"), var acorn = require("acorn");
escodegen = require("escodegen"), var ufuzz = require("./ufuzz");
esfuzz = require("esfuzz"), var UglifyJS = require("..");
estraverse = require("estraverse"),
prefix = "\r ";
// Normalizes input AST for UglifyJS in order to get correct comparison. function try_beautify(code) {
var beautified;
function normalizeInput(ast) { try {
return estraverse.replace(ast, { beautified = UglifyJS.minify(code, {
enter: function(node, parent) { fromString: true,
switch (node.type) { compress: false,
// Internally mark all the properties with semi-standard type "Property". mangle: false,
case "ObjectExpression": output: {
node.properties.forEach(function (property) { beautify: true,
property.type = "Property"; bracketize: true
});
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)
} }
}); });
} catch (ex) {
if (jsPair[0].value !== jsPair[1].value) { beautified = { error: ex };
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.");
}
} }
if (beautified.error) {
console.log("// !!! beautify failed !!!");
console.log(beautified.error.stack);
console.log(code);
} else {
console.log("// (beautified)");
console.log(beautified.code);
}
}
process.stdout.write(prefix + "Probability of error is less than " + (100 / options.iterations) + "%, stopping.\n"); function test(original, estree, description) {
}; var transformed;
try {
transformed = UglifyJS.minify(estree, {
fromString: true,
compress: false,
mangle: false,
spidermonkey: true
});
} catch (ex) {
transformed = { error: ex };
}
if (transformed.error || original !== transformed.code) {
console.log("//=============================================================");
console.log("// !!!!!! Failed... round", round);
console.log("// original code");
try_beautify(original);
console.log();
console.log();
console.log("//-------------------------------------------------------------");
console.log("//", description);
if (transformed.error) {
console.log(transformed.error.stack);
} else {
try_beautify(transformed.code);
}
console.log("!!!!!! Failed... round", round);
process.exit(1);
}
}
var num_iterations = ufuzz.num_iterations;
for (var round = 1; round <= num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r");
var code = ufuzz.createTopLevelCode();
var uglified = {
ast: UglifyJS.parse(code),
code: UglifyJS.minify(code, {
fromString: true,
compress: false,
mangle: false
}).code
};
test(uglified.code, uglified.ast.to_mozilla_ast(), "AST_Node.to_mozilla_ast()");
try {
test(uglified.code, acorn.parse(code), "acorn.parse()");
} catch (e) {
console.log("//=============================================================");
console.log("// acorn parser failed... round", round);
console.log(e);
console.log("// original code");
console.log(code);
}
}
console.log();

View File

@@ -1,6 +0,0 @@
var fs = require("fs");
new Function("MOZ_SourceMap", "exports", require("../tools/node").FILES.map(function(file) {
if (/exports\.js$/.test(file)) file = require.resolve("./exports");
return fs.readFileSync(file, "utf8");
}).join("\n\n"))(require("source-map"), exports);

View File

@@ -1,6 +1,6 @@
#! /usr/bin/env node #! /usr/bin/env node
var U = require("./node"); var U = require("../tools/node");
var path = require("path"); var path = require("path");
var fs = require("fs"); var fs = require("fs");
var assert = require("assert"); var assert = require("assert");
@@ -23,12 +23,6 @@ mocha_tests();
var run_sourcemaps_tests = require('./sourcemaps'); var run_sourcemaps_tests = require('./sourcemaps');
run_sourcemaps_tests(); run_sourcemaps_tests();
var run_ast_conversion_tests = require("./mozilla-ast");
run_ast_conversion_tests({
iterations: 1000
});
/* -----[ utils ]----- */ /* -----[ utils ]----- */
function tmpl() { function tmpl() {

View File

@@ -1,14 +1,16 @@
var vm = require("vm"); var vm = require("vm");
function safe_log(arg) { function safe_log(arg, level) {
if (arg) switch (typeof arg) { if (arg) switch (typeof arg) {
case "function": case "function":
return arg.toString(); return arg.toString();
case "object": case "object":
if (/Error$/.test(arg.name)) return arg.toString(); if (/Error$/.test(arg.name)) return arg.toString();
arg.constructor.toString(); arg.constructor.toString();
for (var key in arg) { if (level--) for (var key in arg) {
arg[key] = safe_log(arg[key]); if (!Object.getOwnPropertyDescriptor(arg, key).get) {
arg[key] = safe_log(arg[key], level);
}
} }
} }
return arg; return arg;
@@ -48,7 +50,9 @@ exports.run_code = function(code) {
].join("\n"), { ].join("\n"), {
console: { console: {
log: function() { log: function() {
return console.log.apply(console, [].map.call(arguments, safe_log)); return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));
} }
} }
}, { timeout: 5000 }); }, { timeout: 5000 });

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("./node"); var UglifyJS = require("..");
var ok = require("assert"); var ok = require("assert");
module.exports = function () { module.exports = function () {

View File

@@ -12,7 +12,7 @@
stream._handle.setBlocking(true); stream._handle.setBlocking(true);
}); });
var UglifyJS = require("./node"); var UglifyJS = require("..");
var randomBytes = require("crypto").randomBytes; var randomBytes = require("crypto").randomBytes;
var sandbox = require("./sandbox"); var sandbox = require("./sandbox");
@@ -48,8 +48,9 @@ var STMT_COUNT_FROM_GLOBAL = true; // count statement depth from nearest functio
var num_iterations = +process.argv[2] || 1/0; var num_iterations = +process.argv[2] || 1/0;
var verbose = false; // log every generated test var verbose = false; // log every generated test
var verbose_interval = false; // log every 100 generated tests var verbose_interval = false; // log every 100 generated tests
var verbose_error = false;
var use_strict = false; var use_strict = false;
var catch_redef = require.main === module;
var generate_directive = require.main === module;
for (var i = 2; i < process.argv.length; ++i) { for (var i = 2; i < process.argv.length; ++i) {
switch (process.argv[i]) { switch (process.argv[i]) {
case '-v': case '-v':
@@ -58,9 +59,6 @@ for (var i = 2; i < process.argv.length; ++i) {
case '-V': case '-V':
verbose_interval = true; verbose_interval = true;
break; break;
case '-E':
verbose_error = true;
break;
case '-t': case '-t':
MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i]; MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i];
if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error('Must generate at least one toplevel per run'); if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error('Must generate at least one toplevel per run');
@@ -79,6 +77,12 @@ for (var i = 2; i < process.argv.length; ++i) {
STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name]; STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name];
if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list'); if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list');
break; break;
case '--no-catch-redef':
catch_redef = false;
break;
case '--no-directive':
generate_directive = false;
break;
case '--use-strict': case '--use-strict':
use_strict = true; use_strict = true;
break; break;
@@ -103,11 +107,12 @@ for (var i = 2; i < process.argv.length; ++i) {
console.log('<number>: generate this many cases (if used must be first arg)'); console.log('<number>: generate this many cases (if used must be first arg)');
console.log('-v: print every generated test case'); console.log('-v: print every generated test case');
console.log('-V: print every 100th generated test case'); console.log('-V: print every 100th generated test case');
console.log('-E: print generated test case with runtime error');
console.log('-t <int>: generate this many toplevels per run (more take longer)'); console.log('-t <int>: generate this many toplevels per run (more take longer)');
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)'); console.log('-r <int>: maximum recursion depth for generator (higher takes longer)');
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)'); console.log('-s1 <statement name>: force the first level statement to be this one (see list below)');
console.log('-s2 <statement name>: force the second level statement to be this one (see list below)'); console.log('-s2 <statement name>: force the second level statement to be this one (see list below)');
console.log('--no-catch-redef: do not redefine catch variables');
console.log('--no-directive: do not generate directives');
console.log('--use-strict: generate "use strict"'); console.log('--use-strict: generate "use strict"');
console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise'); console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated'); console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
@@ -192,12 +197,33 @@ var ASSIGNMENTS = [
'=', '=',
'=', '=',
'=', '=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'==',
'!=',
'===',
'!==',
'+=', '+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'-=', '-=',
'*=', '*=',
'/=', '/=',
@@ -207,7 +233,8 @@ var ASSIGNMENTS = [
'<<=', '<<=',
'>>=', '>>=',
'>>>=', '>>>=',
'%=' ]; '%=',
];
var UNARY_SAFE = [ var UNARY_SAFE = [
'+', '+',
@@ -276,6 +303,7 @@ var TYPEOF_OUTCOMES = [
'symbol', 'symbol',
'crap' ]; 'crap' ];
var unique_vars = [];
var loops = 0; var loops = 0;
var funcs = 0; var funcs = 0;
var labels = 10000; var labels = 10000;
@@ -290,6 +318,10 @@ function strictMode() {
} }
function createTopLevelCode() { function createTopLevelCode() {
VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list
unique_vars.length = 0;
loops = 0;
funcs = 0;
return [ return [
strictMode(), strictMode(),
'var a = 100, b = 10, c = 0;', 'var a = 100, b = 10, c = 0;',
@@ -325,33 +357,36 @@ function createArgs() {
return args.join(', '); return args.join(', ');
} }
function filterDirective(s) {
if (!generate_directive && !s[1] && /\("/.test(s[2])) s[2] = ';' + s[2];
return s;
}
function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) { function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
if (--recurmax < 0) { return ';'; } if (--recurmax < 0) { return ';'; }
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0; if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
var func = funcs++; var func = funcs++;
var namesLenBefore = VAR_NAMES.length; var namesLenBefore = VAR_NAMES.length;
var name = (inGlobal || rng(5) > 0) ? 'f' + func : createVarName(MANDATORY, noDecl); var name;
if (name === 'a' || name === 'b' || name === 'c') name = 'f' + func; // quick hack to prevent assignment to func names of being called if (inGlobal || rng(5) > 0) name = 'f' + func;
var s = ''; else {
unique_vars.push('a', 'b', 'c');
name = createVarName(MANDATORY, noDecl);
unique_vars.length -= 3;
}
var s = [
'function ' + name + '(' + createParams() + '){',
strictMode()
];
if (rng(5) === 0) { if (rng(5) === 0) {
// functions with functions. lower the recursion to prevent a mess. // functions with functions. lower the recursion to prevent a mess.
s = [ s.push(createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth));
'function ' + name + '(' + createParams() + '){',
strictMode(),
createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth),
'}',
''
].join('\n');
} else { } else {
// functions with statements // functions with statements
s = [ s.push(createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth));
'function ' + name + '(' + createParams() + '){',
strictMode(),
createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
'}',
''
].join('\n');
} }
s.push('}', '');
s = filterDirective(s).join('\n');
VAR_NAMES.length = namesLenBefore; VAR_NAMES.length = namesLenBefore;
@@ -359,7 +394,6 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
// avoid "function statements" (decl inside statements) // avoid "function statements" (decl inside statements)
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');'; else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');';
return s; return s;
} }
@@ -406,7 +440,7 @@ function getLabel(label) {
return label && " L" + label; return label && " L" + label;
} }
function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) { function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, target) {
++stmtDepth; ++stmtDepth;
var loop = ++loops; var loop = ++loops;
if (--recurmax < 0) { if (--recurmax < 0) {
@@ -414,10 +448,11 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
} }
// allow to forcefully generate certain structures at first or second recursion level // allow to forcefully generate certain structures at first or second recursion level
var target = 0; if (target === undefined) {
if (stmtDepth === 1 && STMT_FIRST_LEVEL_OVERRIDE >= 0) target = STMT_FIRST_LEVEL_OVERRIDE; if (stmtDepth === 1 && STMT_FIRST_LEVEL_OVERRIDE >= 0) target = STMT_FIRST_LEVEL_OVERRIDE;
else if (stmtDepth === 2 && STMT_SECOND_LEVEL_OVERRIDE >= 0) target = STMT_SECOND_LEVEL_OVERRIDE; else if (stmtDepth === 2 && STMT_SECOND_LEVEL_OVERRIDE >= 0) target = STMT_SECOND_LEVEL_OVERRIDE;
else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)]; else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)];
}
switch (target) { switch (target) {
case STMT_BLOCK: case STMT_BLOCK:
@@ -460,20 +495,22 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
case STMT_VAR: case STMT_VAR:
switch (rng(3)) { switch (rng(3)) {
case 0: case 0:
unique_vars.push('c');
var name = createVarName(MANDATORY); var name = createVarName(MANDATORY);
if (name === 'c') name = 'a'; unique_vars.pop();
return 'var ' + name + ';'; return 'var ' + name + ';';
case 1: case 1:
// initializer can only have one expression // initializer can only have one expression
unique_vars.push('c');
var name = createVarName(MANDATORY); var name = createVarName(MANDATORY);
if (name === 'c') name = 'b'; unique_vars.pop();
return 'var ' + name + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';'; return 'var ' + name + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
default: default:
// initializer can only have one expression // initializer can only have one expression
unique_vars.push('c');
var n1 = createVarName(MANDATORY); var n1 = createVarName(MANDATORY);
if (n1 === 'c') n1 = 'b';
var n2 = createVarName(MANDATORY); var n2 = createVarName(MANDATORY);
if (n2 === 'c') n2 = 'b'; unique_vars.pop();
return 'var ' + n1 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ', ' + n2 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';'; return 'var ' + n1 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ', ' + n2 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
} }
case STMT_RETURN_ETC: case STMT_RETURN_ETC:
@@ -514,8 +551,11 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
var nameLenBefore = VAR_NAMES.length; var nameLenBefore = VAR_NAMES.length;
var catchName = createVarName(MANDATORY); var catchName = createVarName(MANDATORY);
var freshCatchName = VAR_NAMES.length !== nameLenBefore; var freshCatchName = VAR_NAMES.length !== nameLenBefore;
if (!catch_redef) unique_vars.push(catchName);
s += ' catch (' + catchName + ') { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }'; s += ' catch (' + catchName + ') { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }';
if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1); // remove catch name // remove catch name
if (!catch_redef) unique_vars.pop();
if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1);
} }
if (n !== 0) s += ' finally { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }'; if (n !== 0) s += ' finally { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }';
return s; return s;
@@ -593,8 +633,9 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++: case p++:
case p++: case p++:
var nameLenBefore = VAR_NAMES.length; var nameLenBefore = VAR_NAMES.length;
unique_vars.push('c');
var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that. var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that.
if (name == 'c') name = 'a'; unique_vars.pop();
var s = []; var s = [];
switch (rng(5)) { switch (rng(5)) {
case 0: case 0:
@@ -636,7 +677,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
strictMode() strictMode()
); );
if (instantiate) for (var i = rng(4); --i >= 0;) { if (instantiate) for (var i = rng(4); --i >= 0;) {
if (rng(2)) s.push('this.' + getDotKey() + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';'); if (rng(2)) s.push('this.' + getDotKey(true) + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';');
else s.push('this[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']' + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';'); else s.push('this[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']' + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';');
} }
s.push( s.push(
@@ -646,7 +687,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
break; break;
} }
VAR_NAMES.length = nameLenBefore; VAR_NAMES.length = nameLenBefore;
return s.join('\n'); return filterDirective(s).join('\n');
case p++: case p++:
case p++: case p++:
return createTypeofExpr(recurmax, stmtDepth, canThrow); return createTypeofExpr(recurmax, stmtDepth, canThrow);
@@ -689,19 +730,19 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
") || " + rng(10) + ").toString()[" + ") || " + rng(10) + ").toString()[" +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] "; createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] ";
case p++: case p++:
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow); return createArrayLiteral(recurmax, stmtDepth, canThrow);
case p++: case p++:
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow); return createObjectLiteral(recurmax, stmtDepth, canThrow);
case p++: case p++:
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' + return createArrayLiteral(recurmax, stmtDepth, canThrow) + '[' +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
case p++: case p++:
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' + return createObjectLiteral(recurmax, stmtDepth, canThrow) + '[' +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
case p++: case p++:
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey(); return createArrayLiteral(recurmax, stmtDepth, canThrow) + '.' + getDotKey();
case p++: case p++:
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey(); return createObjectLiteral(recurmax, stmtDepth, canThrow) + '.' + getDotKey();
case p++: case p++:
var name = getVarName(); var name = getVarName();
return name + ' && ' + name + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; return name + ' && ' + name + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
@@ -713,7 +754,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
return _createExpression(recurmax, noComma, stmtDepth, canThrow); return _createExpression(recurmax, noComma, stmtDepth, canThrow);
} }
function createArrayLiteral(recurmax, noComma, stmtDepth, canThrow) { function createArrayLiteral(recurmax, stmtDepth, canThrow) {
recurmax--; recurmax--;
var arr = "["; var arr = "[";
for (var i = rng(6); --i >= 0;) { for (var i = rng(6); --i >= 0;) {
@@ -746,18 +787,56 @@ var KEYS = [
"3", "3",
].concat(SAFE_KEYS); ].concat(SAFE_KEYS);
function getDotKey() { function getDotKey(assign) {
return SAFE_KEYS[rng(SAFE_KEYS.length)]; var key;
do {
key = SAFE_KEYS[rng(SAFE_KEYS.length)];
} while (assign && key == "length");
return key;
} }
function createObjectLiteral(recurmax, noComma, stmtDepth, canThrow) { function createAccessor(recurmax, stmtDepth, canThrow) {
recurmax--; var namesLenBefore = VAR_NAMES.length;
var obj = "({"; var s;
for (var i = rng(6); --i >= 0;) { var prop1 = getDotKey();
var key = KEYS[rng(KEYS.length)]; if (rng(2) == 0) {
obj += key + ":(" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "), "; s = [
'get ' + prop1 + '(){',
strictMode(),
createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC),
'},'
];
} else {
var prop2;
do {
prop2 = getDotKey();
} while (prop1 == prop2);
s = [
'set ' + prop1 + '(' + createVarName(MANDATORY) + '){',
strictMode(),
createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
'this.' + prop2 + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ';',
'},'
];
} }
return obj + "})"; VAR_NAMES.length = namesLenBefore;
return filterDirective(s).join('\n');
}
function createObjectLiteral(recurmax, stmtDepth, canThrow) {
recurmax--;
var obj = ['({'];
for (var i = rng(6); --i >= 0;) {
if (rng(20) == 0) {
obj.push(createAccessor(recurmax, stmtDepth, canThrow));
} else {
var key = KEYS[rng(KEYS.length)];
obj.push(key + ':(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + '),');
}
}
obj.push('})');
return obj.join('\n');
} }
function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
@@ -787,7 +866,7 @@ function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')'; return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
case 4: case 4:
assignee = getVarName(); assignee = getVarName();
expr = '(' + assignee + '.' + getDotKey() + createAssignment() expr = '(' + assignee + '.' + getDotKey(true) + createAssignment()
+ _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')'; return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
default: default:
@@ -844,20 +923,28 @@ function getVarName() {
function createVarName(maybe, dontStore) { function createVarName(maybe, dontStore) {
if (!maybe || rng(2)) { if (!maybe || rng(2)) {
var name = VAR_NAMES[rng(VAR_NAMES.length)];
var suffix = rng(3); var suffix = rng(3);
if (suffix) { var name;
name += '_' + suffix; do {
if (!dontStore) VAR_NAMES.push(name); name = VAR_NAMES[rng(VAR_NAMES.length)];
} if (suffix) name += '_' + suffix;
} while (unique_vars.indexOf(name) >= 0);
if (suffix && !dontStore) VAR_NAMES.push(name);
return name; return name;
} }
return ''; return '';
} }
if (require.main !== module) {
exports.createTopLevelCode = createTopLevelCode;
exports.num_iterations = num_iterations;
return;
}
function try_beautify(code, result) { function try_beautify(code, result) {
try { try {
var beautified = UglifyJS.minify(code, { var beautified = UglifyJS.minify(code, {
fromString: true,
compress: false, compress: false,
mangle: false, mangle: false,
output: { output: {
@@ -968,16 +1055,15 @@ var fallback_options = [ JSON.stringify({
compress: false, compress: false,
mangle: false mangle: false
}) ]; }) ];
var minify_options = require("./ufuzz.json").map(JSON.stringify); var minify_options = require("./ufuzz.json").map(function(options) {
options.fromString = true;
return JSON.stringify(options);
});
var original_code, original_result; var original_code, original_result;
var uglify_code, uglify_result, ok; var uglify_code, uglify_result, ok;
for (var round = 1; round <= num_iterations; round++) { for (var round = 1; round <= num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r"); process.stdout.write(round + " of " + num_iterations + "\r");
VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list
loops = 0;
funcs = 0;
original_code = createTopLevelCode(); original_code = createTopLevelCode();
original_result = sandbox.run_code(original_code); original_result = sandbox.run_code(original_code);
(typeof original_result != "string" ? fallback_options : minify_options).forEach(function(options) { (typeof original_result != "string" ? fallback_options : minify_options).forEach(function(options) {
@@ -995,7 +1081,7 @@ for (var round = 1; round <= num_iterations; round++) {
ok = uglify_code.name == original_result.name; ok = uglify_code.name == original_result.name;
} }
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options); if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
else if (verbose_error && typeof original_result != "string") { else if (typeof original_result != "string") {
console.log("//============================================================="); console.log("//=============================================================");
console.log("// original code"); console.log("// original code");
try_beautify(original_code, original_result); try_beautify(original_code, original_result);

View File

@@ -11,12 +11,20 @@
"compress": false "compress": false
}, },
{ {
"compress": {
"warnings": false
},
"mangle": false "mangle": false
}, },
{},
{ {
"compress": { "compress": {
"toplevel": true "warnings": false
}
},
{
"compress": {
"toplevel": true,
"warnings": false
}, },
"mangle": { "mangle": {
"toplevel": true "toplevel": true
@@ -25,7 +33,8 @@
{ {
"compress": { "compress": {
"keep_fargs": false, "keep_fargs": false,
"passes": 3 "passes": 3,
"warnings": false
} }
} }
] ]

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,19 @@
exports["Compressor"] = Compressor;
exports["DefaultsError"] = DefaultsError;
exports["Dictionary"] = Dictionary; 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["TreeWalker"] = TreeWalker;
exports["minify"] = minify; exports["base54"] = base54;
exports["_push_uniq"] = push_uniq; 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["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier;
exports["SymbolDef"] = SymbolDef;

View File

@@ -1,3 +1,10 @@
// workaround for tty output truncation upon process.exit()
[process.stdout, process.stderr].forEach(function(stream){
if (stream._handle && stream._handle.setBlocking)
stream._handle.setBlocking(true);
});
var path = require("path");
var fs = require("fs"); var fs = require("fs");
var UglifyJS = exports; var UglifyJS = exports;
@@ -12,25 +19,182 @@ var FILES = UglifyJS.FILES = [
"../lib/sourcemap.js", "../lib/sourcemap.js",
"../lib/mozilla-ast.js", "../lib/mozilla-ast.js",
"../lib/propmangle.js", "../lib/propmangle.js",
"../lib/minify.js",
"./exports.js", "./exports.js",
].map(function(file){ ].map(function(file){
return require.resolve(file); return require.resolve(file);
}); });
new Function("MOZ_SourceMap", "exports", function() { new Function("MOZ_SourceMap", "exports", FILES.map(function(file){
var code = FILES.map(function(file) { return fs.readFileSync(file, "utf8");
return fs.readFileSync(file, "utf8"); }).join("\n\n"))(
});
code.push("exports.describe_ast = " + describe_ast.toString());
return code.join("\n\n");
}())(
require("source-map"), require("source-map"),
UglifyJS UglifyJS
); );
function describe_ast() { UglifyJS.AST_Node.warn_function = function(txt) {
var out = OutputStream({ beautify: true }); console.error("WARN: %s", txt);
};
function read_source_map(code) {
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code);
if (!match) {
UglifyJS.AST_Node.warn("inline source map not found");
return null;
}
return JSON.parse(new Buffer(match[2], "base64"));
}
UglifyJS.minify = function(files, options) {
options = UglifyJS.defaults(options, {
compress : {},
fromString : false,
inSourceMap : null,
mangle : {},
mangleProperties : false,
nameCache : null,
outFileName : null,
output : null,
outSourceMap : null,
parse : {},
sourceMapInline : false,
sourceMapUrl : null,
sourceRoot : null,
spidermonkey : false,
warnings : false,
});
UglifyJS.base54.reset();
var inMap = options.inSourceMap;
if (typeof inMap == "string" && inMap != "inline") {
inMap = JSON.parse(fs.readFileSync(inMap, "utf8"));
}
// 1. parse
var toplevel = null,
sourcesContent = {};
if (options.spidermonkey) {
if (inMap == "inline") {
throw new Error("inline source map only works with built-in parser");
}
toplevel = UglifyJS.AST_Node.from_mozilla_ast(files);
} else {
function addFile(file, fileUrl) {
var code = options.fromString
? file
: fs.readFileSync(file, "utf8");
if (inMap == "inline") {
inMap = read_source_map(code);
}
sourcesContent[fileUrl] = code;
toplevel = UglifyJS.parse(code, {
filename: fileUrl,
toplevel: toplevel,
bare_returns: options.parse ? options.parse.bare_returns : undefined
});
}
if (!options.fromString) {
files = UglifyJS.simple_glob(files);
if (inMap == "inline" && files.length > 1) {
throw new Error("inline source map only works with singular input");
}
}
[].concat(files).forEach(function (files, i) {
if (typeof files === 'string') {
addFile(files, options.fromString ? i : files);
} else {
for (var fileUrl in files) {
addFile(files[fileUrl], fileUrl);
}
}
});
}
if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll);
}
// 2. compress
if (options.compress) {
var compress = { warnings: options.warnings };
UglifyJS.merge(compress, options.compress);
toplevel.figure_out_scope(options.mangle);
var sq = UglifyJS.Compressor(compress);
toplevel = sq.compress(toplevel);
}
// 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(options.mangle);
toplevel.compute_char_frequency(options.mangle);
toplevel.mangle_names(options.mangle);
}
// 5. output
var output = { max_line_len: 32000 };
if (options.outSourceMap || options.sourceMapInline) {
output.source_map = UglifyJS.SourceMap({
// prefer outFileName, otherwise use outSourceMap without .map suffix
file: options.outFileName || (typeof options.outSourceMap === 'string' ? options.outSourceMap.replace(/\.map$/i, '') : null),
orig: inMap,
root: options.sourceRoot
});
if (options.sourceMapIncludeSources) {
for (var file in sourcesContent) {
if (sourcesContent.hasOwnProperty(file)) {
output.source_map.get().setSourceContent(file, sourcesContent[file]);
}
}
}
}
if (options.output) {
UglifyJS.merge(output, options.output);
}
var stream = UglifyJS.OutputStream(output);
toplevel.print(stream);
var source_map = output.source_map;
if (source_map) {
source_map = source_map + "";
}
var mappingUrlPrefix = "\n//# sourceMappingURL=";
if (options.sourceMapInline) {
stream += mappingUrlPrefix + "data:application/json;charset=utf-8;base64," + new Buffer(source_map).toString("base64");
} else if (options.outSourceMap && typeof options.outSourceMap === "string" && options.sourceMapUrl !== false) {
stream += mappingUrlPrefix + (typeof options.sourceMapUrl === "string" ? options.sourceMapUrl : options.outSourceMap);
}
return {
code : stream + "",
map : source_map
};
};
// UglifyJS.describe_ast = function() {
// function doitem(ctor) {
// var sub = {};
// ctor.SUBCLASSES.forEach(function(ctor){
// sub[ctor.TYPE] = doitem(ctor);
// });
// var ret = {};
// if (ctor.SELF_PROPS.length > 0) ret.props = ctor.SELF_PROPS;
// if (ctor.SUBCLASSES.length > 0) ret.sub = sub;
// return ret;
// }
// return doitem(UglifyJS.AST_Node).sub;
// }
UglifyJS.describe_ast = function() {
var out = UglifyJS.OutputStream({ beautify: true });
function doitem(ctor) { function doitem(ctor) {
out.print("AST_" + ctor.TYPE); out.print("AST_" + ctor.TYPE);
var props = ctor.SELF_PROPS.filter(function(prop){ var props = ctor.SELF_PROPS.filter(function(prop){
@@ -60,6 +224,97 @@ function describe_ast() {
}); });
} }
}; };
doitem(AST_Node); doitem(UglifyJS.AST_Node);
return out + ""; 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;
} }
UglifyJS.readReservedFile = readReservedFile;
UglifyJS.readDefaultReservedFile = function(reserved) {
return readReservedFile(require.resolve("./domprops.json"), reserved);
};
UglifyJS.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;
};
UglifyJS.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");
}
};
// A file glob function that only supports "*" and "?" wildcards in the basename.
// Example: "foo/bar/*baz??.*.js"
// Argument `glob` may be a string or an array of strings.
// Returns an array of strings. Garbage in, garbage out.
UglifyJS.simple_glob = function simple_glob(glob) {
if (Array.isArray(glob)) {
return [].concat.apply([], glob.map(simple_glob));
}
if (glob.match(/\*|\?/)) {
var dir = path.dirname(glob);
try {
var entries = fs.readdirSync(dir);
} catch (ex) {}
if (entries) {
var pattern = "^" + path.basename(glob)
.replace(/[.+^$[\]\\(){}]/g, "\\$&")
.replace(/\*/g, "[^/\\\\]*")
.replace(/\?/g, "[^/\\\\]") + "$";
var mod = process.platform === "win32" ? "i" : "";
var rx = new RegExp(pattern, mod);
var results = entries.filter(function(name) {
return rx.test(name);
}).map(function(name) {
return path.join(dir, name);
});
if (results.length) return results;
}
}
return [ glob ];
};