unify CLI & API under minify() (#1811)
- rename `screw_ie8` to `ie8` - rename `mangle.except` to `mangle.reserved` - rename `mangle.properties.ignore_quoted` to `mangle.properties.keep_quoted` - compact `sourceMap` options - more stringent verification on input `options` - toplevel shorthands - `ie8` - `keep_fnames` - `toplevel` - `warnings` - support arrays and unquoted string values on CLI - drop `fromString` from `minify()` - `minify()` no longer handles any `fs` operations - unify order of operations for `mangle_properties()` on CLI & API - `bin/uglifyjs` used to `mangle_properties()` before even `Compressor` - `minify()` used to `mangle_properties()` after `Compressor` but before `mangle_names()` - both will now do `Compressor`, `mangle_names()` then `mangle_properties()` - `options.parse` / `--parse` for parser options beyond `bare_returns` - add `mangle.properties.builtins` to disable built-in reserved list - disable with `--mangle-props builtins` on CLI - `warnings` now off by default - add `--warn` and `--verbose` on CLI - drop `--enclose` - drop `--export-all` - drop `--reserved-file` - use `--mangle reserved` instead - drop `--reserve-domprops` - enabled by default, disable with `--mangle-props domprops` - drop `--prefix` - use `--source-map base` instead - drop `--lint` - remove `bin/extract-props.js` - limit exposure of internal APIs - update documentations closes #96 closes #102 closes #136 closes #166 closes #243 closes #254 closes #261 closes #311 closes #700 closes #748 closes #912 closes #1072 closes #1366 fixes #101 fixes #123 fixes #124 fixes #263 fixes #379 fixes #419 fixes #423 fixes #461 fixes #465 fixes #576 fixes #737 fixes #772 fixes #958 fixes #1036 fixes #1142 fixes #1175 fixes #1220 fixes #1223 fixes #1280 fixes #1359 fixes #1368
This commit is contained in:
517
README.md
517
README.md
@@ -4,12 +4,6 @@ UglifyJS 2
|
||||
|
||||
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:
|
||||
- release versions of `uglify-js` only support ECMAScript 5 (ES5). If you wish to minify
|
||||
ES2015+ (ES6+) code then please use the [harmony](#harmony) development branch.
|
||||
@@ -57,48 +51,52 @@ a double dash to prevent input files being used as option arguments:
|
||||
The available options are:
|
||||
|
||||
```
|
||||
--source-map Specify an output file where to generate source
|
||||
map.
|
||||
--source-map-root The path to the original source to be included
|
||||
in the source map.
|
||||
--source-map-url The path to the source map to be added in //#
|
||||
sourceMappingURL. Defaults to the value passed
|
||||
with --source-map.
|
||||
--source-map-include-sources Pass this flag if you want to include the
|
||||
content of source files in the source map as
|
||||
sourcesContent property.
|
||||
--source-map-inline Write base64-encoded source map to the end of js output.
|
||||
--in-source-map 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
|
||||
inline with the sources.
|
||||
--screw-ie8 Use this flag if you don't wish to support
|
||||
Internet Explorer 6/7/8.
|
||||
By default UglifyJS will not try to be IE-proof.
|
||||
--support-ie8 Use this flag to support Internet Explorer 6/7/8.
|
||||
Equivalent to setting `screw_ie8: false` in `minify()`
|
||||
for `compress`, `mangle` and `output` options.
|
||||
--expr Parse a single expression, rather than a
|
||||
program (for parsing JSON)
|
||||
-p, --prefix Skip prefix for original filenames that appear
|
||||
in source maps. For example -p 3 will drop 3
|
||||
directories from file names and ensure they are
|
||||
relative paths. You can also specify -p
|
||||
relative, which will make UglifyJS figure out
|
||||
itself the relative paths between original
|
||||
sources, the source map and the output file.
|
||||
-o, --output Output file (default STDOUT).
|
||||
-b, --beautify Beautify output/specify output options.
|
||||
-m, --mangle Mangle names/pass mangler options.
|
||||
-r, --reserved Reserved names to exclude from mangling.
|
||||
-c, --compress Enable compressor/pass compressor options, e.g.
|
||||
`-c 'if_return=false,pure_funcs=["Math.pow","console.log"]'`
|
||||
Use `-c` with no argument to enable default compression
|
||||
options.
|
||||
-d, --define Global definitions
|
||||
-e, --enclose Embed everything in a big function, with a
|
||||
configurable parameter/argument list.
|
||||
--comments Preserve copyright comments in the output. By
|
||||
-h, --help Print usage information.
|
||||
-V, --version Print version number.
|
||||
-p, --parse <options> Specify parser options:
|
||||
`acorn` Use Acorn for parsing.
|
||||
`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`.
|
||||
`expression` Parse a single expression, rather than
|
||||
a program (for parsing JSON).
|
||||
`spidermonkey` Assume input files are SpiderMonkey
|
||||
AST format (as JSON).
|
||||
-c, --compress [options] Enable compressor/specify compressor options:
|
||||
`pure_funcs` List of functions that can be safely
|
||||
removed when their return values are
|
||||
not used.
|
||||
-m, --mangle [options] Mangle names/specify mangler options:
|
||||
`reserved` List of names that should not be mangled.
|
||||
--mangle-props [options] Mangle properties/specify mangler options:
|
||||
`builtins` Mangle property names that overlaps
|
||||
with standard JavaScript globals.
|
||||
`debug` Add debug prefix and suffix.
|
||||
`domprops` Mangle property names that overlaps
|
||||
with DOM properties.
|
||||
`keep_quoted` Only mangle unquoted properies.
|
||||
`regex` Only mangle matched property names.
|
||||
`reserved` List of names that should not be mangled.
|
||||
-b, --beautify [options] Beautify output/specify output options:
|
||||
`beautify` Enabled with `--beautify` by default.
|
||||
`preamble` Preamble to prepend to the output. You
|
||||
can use this to insert a comment, for
|
||||
example for licensing information.
|
||||
This will not be parsed, but the source
|
||||
map will adjust for its presence.
|
||||
`quote_style` Quote style:
|
||||
0 - auto
|
||||
1 - single
|
||||
2 - double
|
||||
3 - original
|
||||
`wrap_iife` Wrap IIFEs in parenthesis. Note: you may
|
||||
want to disable `negate_iife` under
|
||||
compressor options.
|
||||
-o, --output <file> Output file (default STDOUT). Specify "spidermonkey"
|
||||
to dump SpiderMonkey AST format (as JSON) to STDOUT.
|
||||
--comments [filter] Preserve copyright comments in the output. By
|
||||
default this works like Google Closure, keeping
|
||||
JSDoc-style comments that contain "@license" or
|
||||
"@preserve". You can optionally pass one of the
|
||||
@@ -110,54 +108,39 @@ The available options are:
|
||||
kept when compression is on, because of dead
|
||||
code removal or cascading statements into
|
||||
sequences.
|
||||
--preamble Preamble to prepend to the output. You can use
|
||||
this to insert a comment, for example for
|
||||
licensing information. This will not be
|
||||
parsed, but the source map will adjust for its
|
||||
presence.
|
||||
--config-file <file> Read `minify()` options from JSON file.
|
||||
-d, --define <expr>[=value] Global definitions.
|
||||
--ie8 Support non-standard Internet Explorer 8.
|
||||
Equivalent to setting `ie8: true` in `minify()`
|
||||
for `compress`, `mangle` and `output` options.
|
||||
By default UglifyJS will not try to be IE-proof.
|
||||
--keep-fnames Do not mangle/drop function names. Useful for
|
||||
code relying on Function.prototype.name.
|
||||
--name-cache File to hold mangled name mappings.
|
||||
--self Build UglifyJS2 as a library (implies --wrap UglifyJS)
|
||||
--source-map [options] Enable source map/specify source map options:
|
||||
`base` Path to compute relative paths from input files.
|
||||
`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.
|
||||
--acorn Use Acorn for parsing.
|
||||
--spidermonkey Assume input files are SpiderMonkey AST format
|
||||
(as JSON).
|
||||
--self Build itself (UglifyJS2) as a library (implies
|
||||
--wrap=UglifyJS --export-all)
|
||||
--wrap Embed everything in a big function, making the
|
||||
--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
|
||||
need to pass an argument to this option to
|
||||
specify the name that your module will take
|
||||
when included in, say, a browser.
|
||||
--export-all Only used when --wrap, this tells UglifyJS to
|
||||
add code to automatically export all globals.
|
||||
--lint Display some scope warnings
|
||||
-v, --verbose Verbose
|
||||
-V, --version Print version number and exit.
|
||||
--noerr Don't throw an error for unknown options in -c,
|
||||
-b or -m.
|
||||
--bare-returns Allow return outside of functions. Useful when
|
||||
minifying CommonJS modules and Userscripts that
|
||||
may be anonymous function wrapped (IIFE) by the
|
||||
.user.js engine `caller`.
|
||||
--keep-fnames Do not mangle/drop function names. Useful for
|
||||
code relying on Function.prototype.name.
|
||||
--reserved-file File containing reserved names
|
||||
--reserve-domprops Make (most?) DOM properties reserved for
|
||||
--mangle-props
|
||||
--mangle-props Mangle property names (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
|
||||
@@ -167,23 +150,19 @@ goes to STDOUT.
|
||||
|
||||
UglifyJS2 can generate a source map file, which is highly useful for
|
||||
debugging your compressed JavaScript. To get a source map, pass
|
||||
`--source-map output.js.map` (full path to the file where you want the
|
||||
source map dumped).
|
||||
`--source-map --output output.js` (source map will be written out to
|
||||
`output.js.map`).
|
||||
|
||||
Additionally you might need `--source-map-root` to pass the URL where the
|
||||
original files can be found. In case you are passing full paths to input
|
||||
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.
|
||||
Additionally you might need `--source-map root=<URL>` to pass the URL where
|
||||
the original files can be found. Use `--source-map url=<URL>` to specify
|
||||
the URL where the source map can be found.
|
||||
|
||||
For example:
|
||||
|
||||
uglifyjs /home/doe/work/foo/src/js/file1.js \
|
||||
/home/doe/work/foo/src/js/file2.js \
|
||||
-o foo.min.js \
|
||||
--source-map foo.min.js.map \
|
||||
--source-map-root http://foo.com/src \
|
||||
-p 5 -c -m
|
||||
-o foo.min.js -c -m \
|
||||
--source-map base="/home/doe/work/foo/src",root="http://foo.com/src"
|
||||
|
||||
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
|
||||
@@ -220,10 +199,10 @@ To enable the mangler you need to pass `--mangle` (`-m`). The following
|
||||
(disabled by default).
|
||||
|
||||
When mangling is enabled but you want to prevent certain names from being
|
||||
mangled, you can declare those names with `--reserved` (`-r`) — pass a
|
||||
mangled, you can declare those names with `--mangle reserved` — pass a
|
||||
comma-separated list of names. For example:
|
||||
|
||||
uglifyjs ... -m -r '$,require,exports'
|
||||
uglifyjs ... -m reserved=[$,require,exports]
|
||||
|
||||
to prevent the `require`, `exports` and `$` names from being changed.
|
||||
|
||||
@@ -248,39 +227,22 @@ console.log(x.something());
|
||||
In the above code, `foo`, `bar`, `baz`, `moo` and `boo` will be replaced
|
||||
with single characters, while `something()` will be left as is.
|
||||
|
||||
In order for this to be of any use, we should avoid mangling standard JS
|
||||
names. For instance, if your code would contain `x.length = 10`, then
|
||||
`length` becomes a candidate for mangling and it will be mangled throughout
|
||||
the code, regardless if it's being used as part of your own objects or
|
||||
accessing an array's length. To avoid that, you can use `--reserved-file`
|
||||
to pass a filename that should contain the names to be excluded from
|
||||
mangling. This file can be used both for excluding variable names and
|
||||
property names. It could look like this, for example:
|
||||
|
||||
```js
|
||||
{
|
||||
"vars": [ "define", "require", ... ],
|
||||
"props": [ "length", "prototype", ... ]
|
||||
}
|
||||
```
|
||||
|
||||
`--reserved-file` can be an array of file names (either a single
|
||||
comma-separated argument, or you can pass multiple `--reserved-file`
|
||||
arguments) — in this case it will exclude names from all those files.
|
||||
In order for this to be of any use, we avoid mangling standard JS names by
|
||||
default (`--mangle-props builtins` to override).
|
||||
|
||||
A default exclusion file is provided in `tools/domprops.json` which should
|
||||
cover most standard JS and DOM properties defined in various browsers. Pass
|
||||
`--reserve-domprops` to read that in.
|
||||
`--mangle-props domprops` to disable this feature.
|
||||
|
||||
You can also use a regular expression to define which property names should be
|
||||
mangled. For example, `--mangle-regex="/^_/"` will only mangle property names
|
||||
that start with an underscore.
|
||||
mangled. For example, `--mangle-props regex=/^_/` will only mangle property
|
||||
names that start with an underscore.
|
||||
|
||||
When you compress multiple files using this option, in order for them to
|
||||
work together in the end we need to ensure somehow that one property gets
|
||||
mangled to the same name in all of them. For this, pass `--name-cache
|
||||
filename.json` and UglifyJS will maintain these mappings in a file which can
|
||||
then be reused. It should be initially empty. Example:
|
||||
mangled to the same name in all of them. For this, pass `--name-cache filename.json`
|
||||
and UglifyJS will maintain these mappings in a file which can then be reused.
|
||||
It should be initially empty. Example:
|
||||
|
||||
```
|
||||
rm -f /tmp/cache.json # start fresh
|
||||
@@ -294,26 +256,26 @@ of mangled property names.
|
||||
Using the name cache is not necessary if you compress all your files in a
|
||||
single call to UglifyJS.
|
||||
|
||||
#### Mangling unquoted names (`--mangle-props=unquoted` or `--mangle-props=2`)
|
||||
#### Mangling unquoted names (`--mangle-props keep_quoted`)
|
||||
|
||||
Using quoted property name (`o["foo"]`) reserves the property name (`foo`)
|
||||
so that it is not mangled throughout the entire script even when used in an
|
||||
unquoted style (`o.foo`). Example:
|
||||
|
||||
```
|
||||
$ 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);
|
||||
$ echo 'var o={"foo":1, bar:3}; o.foo += o.bar; console.log(o.foo);' | uglifyjs --mangle-props keep_quoted -mc
|
||||
var o={foo:1,a:3};o.foo+=o.a,console.log(o.foo);
|
||||
```
|
||||
|
||||
#### 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`
|
||||
would mangle to `o._$foo$_` with this option. This allows property mangling
|
||||
of a large codebase while still being able to debug the code and identify
|
||||
where mangling is breaking things.
|
||||
|
||||
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
|
||||
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
|
||||
@@ -501,8 +463,6 @@ 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`,
|
||||
using `var` with `reduce_vars` (enabled by default) should suffice.
|
||||
|
||||
<a name="codegen-options"></a>
|
||||
|
||||
#### Conditional compilation, API
|
||||
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:
|
||||
@@ -570,8 +530,8 @@ You can pass `--comments` to retain certain comments in the output. By
|
||||
default it will keep JSDoc-style comments that contain "@preserve",
|
||||
"@license" or "@cc_on" (conditional compilation for IE). You can pass
|
||||
`--comments all` to keep all the comments, or a valid JavaScript regexp to
|
||||
keep only comments that match this regexp. For example `--comments
|
||||
'/foo|bar/'` will keep only comments that contain "foo" or "bar".
|
||||
keep only comments that match this regexp. For example `--comments /^!/`
|
||||
will keep comments like `/*! Copyright Notice */`.
|
||||
|
||||
Note, however, that there might be situations where comments are lost. For
|
||||
example:
|
||||
@@ -604,16 +564,16 @@ 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
|
||||
compress that:
|
||||
|
||||
acorn file.js | uglifyjs --spidermonkey -m -c
|
||||
acorn file.js | uglifyjs -p spidermonkey -m -c
|
||||
|
||||
The `--spidermonkey` option tells UglifyJS that all input files are not
|
||||
The `-p spidermonkey` option tells UglifyJS that all input files are not
|
||||
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
|
||||
internal AST.
|
||||
|
||||
### Use Acorn for parsing
|
||||
|
||||
More for fun, I added the `--acorn` option which will use Acorn to do all
|
||||
More for fun, I added the `-p acorn` option which will use Acorn to do all
|
||||
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
|
||||
@@ -661,107 +621,71 @@ like this:
|
||||
var UglifyJS = require("uglify-js");
|
||||
```
|
||||
|
||||
It exports a lot of names, but I'll discuss here the basics that are needed
|
||||
for parsing, mangling and compressing a piece of code. The sequence is (1)
|
||||
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`.
|
||||
There is a single toplevel function, `minify(files, options)`, which will
|
||||
performs all the steps in a configurable manner.
|
||||
Example:
|
||||
```javascript
|
||||
var result = UglifyJS.minify("/path/to/file.js");
|
||||
var result = UglifyJS.minify("var b = function() {};");
|
||||
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:
|
||||
```javascript
|
||||
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ]);
|
||||
var result = UglifyJS.minify({
|
||||
"file1.js": "var a = function() {};",
|
||||
"file2.js": "var b = function() {};"
|
||||
});
|
||||
console.log(result.code);
|
||||
```
|
||||
|
||||
To generate a source map:
|
||||
```javascript
|
||||
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
||||
outSourceMap: "out.js.map"
|
||||
var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, {
|
||||
sourceMap: {
|
||||
filename: "out.js",
|
||||
url: "out.js.map"
|
||||
}
|
||||
});
|
||||
console.log(result.code); // minified output
|
||||
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
|
||||
});
|
||||
console.log(result.map); // source map
|
||||
```
|
||||
|
||||
Note that the source map is not saved in a file, it's just returned in
|
||||
`result.map`. The value passed for `outSourceMap` is only used to set
|
||||
`result.map`. The value passed for `sourceMap.url` is only used to set
|
||||
`//# sourceMappingURL=out.js.map` in `result.code`. The value of
|
||||
`outFileName` is only used to set `file` attribute in source map file.
|
||||
`filename` is only used to set `file` attribute (see [the spec][sm-spec])
|
||||
in source map file.
|
||||
|
||||
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
|
||||
You can set option `sourceMap.url` to be `"inline"` and source map will
|
||||
be appended to code.
|
||||
|
||||
You can also specify sourceRoot property to be included in source map:
|
||||
```javascript
|
||||
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
||||
outSourceMap: "out.js.map",
|
||||
sourceRoot: "http://example.com/src"
|
||||
var result = UglifyJS.minify({"file1.js": "var a = function() {};"}, {
|
||||
sourceMap: {
|
||||
root: "http://example.com/src",
|
||||
url: "out.js.map"
|
||||
});
|
||||
```
|
||||
|
||||
If you're compressing compiled JavaScript and have a source map for it, you
|
||||
can use the `inSourceMap` argument:
|
||||
can use `sourceMap.content`:
|
||||
```javascript
|
||||
var result = UglifyJS.minify("compiled.js", {
|
||||
inSourceMap: "compiled.js.map",
|
||||
outSourceMap: "minified.js.map"
|
||||
var result = UglifyJS.minify({"compiled.js": "compiled code"}, {
|
||||
sourceMap: {
|
||||
content: "content from compiled.js.map",
|
||||
url: "minified.js.map"
|
||||
}
|
||||
});
|
||||
// same as before, it returns `code` and `map`
|
||||
```
|
||||
|
||||
If your input source map is not in a file, you can pass it in as an object
|
||||
using the `inSourceMap` argument:
|
||||
|
||||
```javascript
|
||||
var result = UglifyJS.minify("compiled.js", {
|
||||
inSourceMap: JSON.parse(my_source_map_string),
|
||||
outSourceMap: "minified.js.map"
|
||||
});
|
||||
```
|
||||
|
||||
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
|
||||
no sense otherwise).
|
||||
|
||||
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"
|
||||
});
|
||||
```
|
||||
If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.url`.
|
||||
|
||||
Other options:
|
||||
|
||||
- `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
|
||||
an object to specify mangling options (see below).
|
||||
|
||||
@@ -769,18 +693,18 @@ Other options:
|
||||
mangle property options.
|
||||
|
||||
- `output` (default `null`) — pass an object if you wish to specify
|
||||
additional [output options][codegen]. The defaults are optimized
|
||||
additional [output options](#beautifier-options). The defaults are optimized
|
||||
for best compression.
|
||||
|
||||
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
|
||||
Pass an object to specify custom [compressor options][compressor].
|
||||
Pass an object to specify custom [compressor options](#compressor-options).
|
||||
|
||||
- `parse` (default {}) — pass an object if you wish to specify some
|
||||
additional [parser options][parser]. (not all options available... see below)
|
||||
additional [parser options](#the-parser).
|
||||
|
||||
##### mangle
|
||||
|
||||
- `except` - pass an array of identifiers that should be excluded from mangling
|
||||
- `reserved` - pass an array of identifiers that should be excluded from mangling
|
||||
|
||||
- `toplevel` — mangle names declared in the toplevel scope (disabled by
|
||||
default).
|
||||
@@ -805,183 +729,22 @@ Other options:
|
||||
UglifyJS.minify("tst.js").code;
|
||||
// 'function funcName(a,n){}var globalVar;'
|
||||
|
||||
UglifyJS.minify("tst.js", { mangle: { except: ['firstLongName'] } }).code;
|
||||
UglifyJS.minify("tst.js", { mangle: { reserved: ['firstLongName'] } }).code;
|
||||
// 'function funcName(firstLongName,a){}var globalVar;'
|
||||
|
||||
UglifyJS.minify("tst.js", { mangle: { toplevel: true } }).code;
|
||||
// 'function n(n,a){}var a;'
|
||||
```
|
||||
|
||||
##### mangleProperties options
|
||||
##### mangle.properties options
|
||||
|
||||
- `regex` — Pass a RegExp to only mangle certain names (maps to the `--mangle-regex` CLI arguments option)
|
||||
- `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 (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.
|
||||
|
||||
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.
|
||||
- `regex` — Pass a RegExp to only mangle certain names
|
||||
- `keep_quoted` — Only mangle unquoted property names
|
||||
- `debug` — Mangle names with the original name still present. Defaults to `false`.
|
||||
Pass an empty string to enable, or a non-empty string to set the suffix.
|
||||
|
||||
[acorn]: https://github.com/ternjs/acorn
|
||||
[source-map]: https://github.com/mozilla/source-map
|
||||
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
|
||||
[codegen]: http://lisperator.net/uglifyjs/codegen
|
||||
[compressor]: http://lisperator.net/uglifyjs/compress
|
||||
[parser]: http://lisperator.net/uglifyjs/parser
|
||||
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k
|
||||
|
||||
#### Harmony
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
#! /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_Sequence) {
|
||||
walk(node.expressions[node.expressions.length - 1]);
|
||||
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;
|
||||
}
|
||||
}
|
||||
862
bin/uglifyjs
862
bin/uglifyjs
@@ -3,633 +3,333 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
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.")
|
||||
|
||||
.describe("comments", "Preserve copyright comments in the output. \
|
||||
By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \
|
||||
You can optionally pass one of the following arguments to this flag:\n\
|
||||
- \"all\" to keep all comments\n\
|
||||
- a valid JS RegExp like `/foo/`or `/^!/` to keep only matching comments.\n\
|
||||
\
|
||||
Note that currently not *all* comments can be kept when compression is on, \
|
||||
because of dead code removal or cascading statements into sequences.")
|
||||
|
||||
.describe("preamble", "Preamble to prepend to the output. You can use this to insert a \
|
||||
comment, for example for licensing information. This will not be \
|
||||
parsed, but the source map will adjust for its presence.")
|
||||
|
||||
.describe("stats", "Display operations run time on STDERR.")
|
||||
.describe("acorn", "Use Acorn for parsing.")
|
||||
.describe("spidermonkey", "Assume input files are SpiderMonkey AST format (as JSON).")
|
||||
.describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)")
|
||||
.describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \
|
||||
You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.")
|
||||
.describe("export-all", "Only used when --wrap, this tells UglifyJS to add code to automatically export all globals.")
|
||||
.describe("lint", "Display some scope warnings")
|
||||
.describe("v", "Verbose")
|
||||
.describe("V", "Print version number and exit.")
|
||||
.describe("noerr", "Don't throw an error for unknown options in -c, -b or -m.")
|
||||
.describe("bare-returns", "Allow return outside of functions. Useful when minifying CommonJS modules.")
|
||||
.describe("keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.")
|
||||
.describe("quotes", "Quote style (0 - auto, 1 - single, 2 - double, 3 - original)")
|
||||
.describe("reserved-file", "File containing reserved names")
|
||||
.describe("reserve-domprops", "Make (most?) DOM properties reserved for --mangle-props")
|
||||
.describe("mangle-props", "Mangle property names (0 - disabled, 1 - mangle all properties, 2 - mangle unquoted properies)")
|
||||
.describe("mangle-regex", "Only mangle property names matching the regex")
|
||||
.describe("name-cache", "File to hold mangled names mappings")
|
||||
.describe("pure-funcs", "List of functions that can be safely removed if their return value is not used")
|
||||
.describe("dump-spidermonkey-ast", "Dump SpiderMonkey AST to stdout.")
|
||||
.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 (ARGS.version || ARGS.V) {
|
||||
var json = require("../package.json");
|
||||
print(json.name + ' ' + json.version);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (ARGS.ast_help) {
|
||||
var desc = UglifyJS.describe_ast();
|
||||
print(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (ARGS.h || ARGS.help) {
|
||||
print(yargs.help());
|
||||
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);
|
||||
// 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);
|
||||
});
|
||||
|
||||
if (ARGS.reserve_domprops) {
|
||||
RESERVED = UglifyJS.readDefaultReservedFile(RESERVED);
|
||||
}
|
||||
var fs = require("fs");
|
||||
var info = require("../package.json");
|
||||
var path = require("path");
|
||||
var program = require("commander");
|
||||
var UglifyJS = require("../tools/node");
|
||||
|
||||
if (ARGS.d) {
|
||||
if (COMPRESS) COMPRESS.global_defs = getOptions("d");
|
||||
}
|
||||
|
||||
if (ARGS.pure_funcs) {
|
||||
if (COMPRESS) COMPRESS.pure_funcs = ARGS.pure_funcs;
|
||||
}
|
||||
|
||||
if (ARGS.r) {
|
||||
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
|
||||
}
|
||||
|
||||
if (RESERVED && MANGLE) {
|
||||
if (!MANGLE.except) MANGLE.except = RESERVED.vars;
|
||||
else MANGLE.except = MANGLE.except.concat(RESERVED.vars);
|
||||
}
|
||||
|
||||
function readNameCache(key) {
|
||||
return UglifyJS.readNameCache(ARGS.name_cache, key);
|
||||
}
|
||||
|
||||
function writeNameCache(key, cache) {
|
||||
return UglifyJS.writeNameCache(ARGS.name_cache, key, cache);
|
||||
}
|
||||
|
||||
function extractRegex(str) {
|
||||
if (/^\/.*\/[a-zA-Z]*$/.test(str)) {
|
||||
var regex_pos = str.lastIndexOf("/");
|
||||
return new RegExp(str.substr(1, regex_pos - 1), str.substr(regex_pos + 1));
|
||||
} else {
|
||||
throw new Error("Invalid regular expression: " + str);
|
||||
}
|
||||
}
|
||||
|
||||
if (ARGS.quotes === true) {
|
||||
ARGS.quotes = 3;
|
||||
}
|
||||
|
||||
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,
|
||||
var files = {};
|
||||
var options = {
|
||||
compress: false,
|
||||
mangle: false
|
||||
};
|
||||
|
||||
if (ARGS.mangle_props == 2) {
|
||||
OUTPUT_OPTIONS.keep_quoted_props = true;
|
||||
if (COMPRESS && !("properties" in COMPRESS))
|
||||
COMPRESS.properties = false;
|
||||
program._name = info.name;
|
||||
program.version(info.version);
|
||||
program.parseArgv = program.parse;
|
||||
program.parse = undefined;
|
||||
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true));
|
||||
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true));
|
||||
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true));
|
||||
program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js("mangle-props", true));
|
||||
program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js("beautify", true));
|
||||
program.option("-o, --output <file>", "Output file (default STDOUT).");
|
||||
program.option("--comments [filter]", "Preserve copyright comments in the output.");
|
||||
program.option("--config-file <file>", "Read minify() options from JSON file.");
|
||||
program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("define"));
|
||||
program.option("--ie8", "Support non-standard Internet Explorer 8.");
|
||||
program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.");
|
||||
program.option("--name-cache <file>", "File to hold mangled name mappings.");
|
||||
program.option("--self", "Build UglifyJS2 as a library (implies --wrap UglifyJS)");
|
||||
program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map());
|
||||
program.option("--stats", "Display operations run time on STDERR.")
|
||||
program.option("--toplevel", "Compress and/or mangle variables in toplevel scope.");
|
||||
program.option("--verbose", "Print diagnostic messages.");
|
||||
program.option("--warn", "Print warning messages.");
|
||||
program.option("--wrap <name>", "Embed everything as a function with “exports” corresponding to “name” globally.");
|
||||
program.arguments("[files...]").parseArgv(process.argv);
|
||||
if (program.configFile) {
|
||||
options = JSON.parse(read_file(program.configFile));
|
||||
}
|
||||
|
||||
if (ARGS.support_ie8 === true && ARGS.screw_ie8 !== true) {
|
||||
screw_ie8 = false;
|
||||
if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
|
||||
fatal("ERROR: cannot write source map to STDOUT");
|
||||
}
|
||||
|
||||
if (COMPRESS) COMPRESS.screw_ie8 = screw_ie8;
|
||||
if (MANGLE) MANGLE.screw_ie8 = screw_ie8;
|
||||
OUTPUT_OPTIONS.screw_ie8 = screw_ie8;
|
||||
|
||||
if (ARGS.keep_fnames) {
|
||||
if (COMPRESS) COMPRESS.keep_fnames = true;
|
||||
if (MANGLE) MANGLE.keep_fnames = true;
|
||||
}
|
||||
|
||||
if (ARGS.wrap_iife) {
|
||||
if (COMPRESS) COMPRESS.negate_iife = false;
|
||||
OUTPUT_OPTIONS.wrap_iife = true;
|
||||
}
|
||||
|
||||
if (BEAUTIFY)
|
||||
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
|
||||
|
||||
if (ARGS.comments === "") {
|
||||
OUTPUT_OPTIONS.comments = "some";
|
||||
} else {
|
||||
OUTPUT_OPTIONS.comments = ARGS.comments;
|
||||
}
|
||||
|
||||
var files = ARGS._.slice();
|
||||
|
||||
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");
|
||||
[
|
||||
"compress",
|
||||
"ie8",
|
||||
"mangle",
|
||||
"sourceMap",
|
||||
"toplevel",
|
||||
"wrap"
|
||||
].forEach(function(name) {
|
||||
if (name in program) {
|
||||
options[name] = program[name];
|
||||
}
|
||||
files = UglifyJS.FILES;
|
||||
if (!ARGS.wrap) ARGS.wrap = "UglifyJS";
|
||||
}
|
||||
|
||||
var ORIG_MAP = ARGS.in_source_map;
|
||||
|
||||
if (ORIG_MAP && ORIG_MAP != "inline") {
|
||||
ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP));
|
||||
if (files.length == 0) {
|
||||
print_error("INFO: Using file from the input source map: " + ORIG_MAP.file);
|
||||
files = [ ORIG_MAP.file ];
|
||||
});
|
||||
if (program.beautify) {
|
||||
options.output = typeof program.beautify == "object" ? program.beautify : {};
|
||||
if (!("beautify" in options.output)) {
|
||||
options.output.beautify = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (files.length == 0) {
|
||||
files = [ "-" ];
|
||||
if (program.comments) {
|
||||
if (typeof options.output != "object") options.output = {};
|
||||
options.output.comments = typeof program.comments == "string" ? program.comments : "some";
|
||||
}
|
||||
|
||||
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 (program.define) {
|
||||
if (typeof options.compress != "object") options.compress = {};
|
||||
if (typeof options.compress.global_defs != "object") options.compress.global_defs = {};
|
||||
for (var expr in program.define) {
|
||||
options.compress.global_defs[expr] = program.define[expr];
|
||||
}
|
||||
}
|
||||
|
||||
if (files.indexOf("-") >= 0 && ARGS.source_map) {
|
||||
print_error("ERROR: Source map doesn't work with input from STDIN");
|
||||
process.exit(1);
|
||||
if (program.keepFnames) {
|
||||
options.keep_fnames = true;
|
||||
}
|
||||
|
||||
if (files.filter(function(el){ return el == "-" }).length > 1) {
|
||||
print_error("ERROR: Can read a single file from STDIN (two or more dashes specified)");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var STATS = {};
|
||||
var TOPLEVEL = null;
|
||||
var P_RELATIVE = ARGS.p && ARGS.p == "relative";
|
||||
var SOURCES_CONTENT = {};
|
||||
var index = 0;
|
||||
|
||||
!function cb() {
|
||||
if (index == files.length) return done();
|
||||
var file = files[index++];
|
||||
read_whole_file(file, function (err, code) {
|
||||
if (err) {
|
||||
print_error("ERROR: can't read file: " + file);
|
||||
process.exit(1);
|
||||
}
|
||||
if (ORIG_MAP == "inline") {
|
||||
ORIG_MAP = read_source_map(code);
|
||||
}
|
||||
if (ARGS.p != null) {
|
||||
if (P_RELATIVE) {
|
||||
file = path.relative(path.dirname(ARGS.source_map), file).replace(/\\/g, '/');
|
||||
if (program.mangleProps) {
|
||||
if (program.mangleProps.domprops) {
|
||||
delete program.mangleProps.domprops;
|
||||
} else {
|
||||
var p = parseInt(ARGS.p, 10);
|
||||
if (!isNaN(p)) {
|
||||
file = file.replace(/^\/+/, "").split(/\/+/).slice(ARGS.p).join("/");
|
||||
}
|
||||
}
|
||||
}
|
||||
SOURCES_CONTENT[file] = code;
|
||||
time_it("parse", function(){
|
||||
if (ARGS.spidermonkey) {
|
||||
var program = JSON.parse(code);
|
||||
if (!TOPLEVEL) TOPLEVEL = program;
|
||||
else TOPLEVEL.body = TOPLEVEL.body.concat(program.body);
|
||||
}
|
||||
else if (ARGS.acorn) {
|
||||
TOPLEVEL = acorn.parse(code, {
|
||||
locations : true,
|
||||
sourceFile : file,
|
||||
program : TOPLEVEL
|
||||
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);
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (typeof options.mangle != "object") options.mangle = {};
|
||||
options.mangle.properties = program.mangleProps;
|
||||
}
|
||||
var cache;
|
||||
if (program.nameCache) {
|
||||
cache = JSON.parse(read_file(program.nameCache, "{}"));
|
||||
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.parse) {
|
||||
if (program.parse.acorn || program.parse.spidermonkey) {
|
||||
if (program.sourceMap) fatal("ERROR: inline source map only works with built-in parser");
|
||||
} else {
|
||||
options.parse = program.parse;
|
||||
}
|
||||
}
|
||||
var convert_path = function(name) {
|
||||
return name;
|
||||
};
|
||||
if (typeof program.sourceMap == "object" && "base" in program.sourceMap) {
|
||||
convert_path = function() {
|
||||
var base = program.sourceMap.base;
|
||||
delete options.sourceMap.base;
|
||||
return function(name) {
|
||||
return path.relative(base, name);
|
||||
};
|
||||
}();
|
||||
}
|
||||
if (program.verbose) {
|
||||
options.warnings = "verbose";
|
||||
} else if (program.warn) {
|
||||
options.warnings = true;
|
||||
}
|
||||
if (program.self) {
|
||||
if (program.args.length) {
|
||||
console.error("WARN: Ignoring input files since --self was passed");
|
||||
}
|
||||
if (!options.wrap) options.wrap = "UglifyJS";
|
||||
simple_glob(UglifyJS.FILES).forEach(function(name) {
|
||||
files[convert_path(name)] = read_file(name);
|
||||
});
|
||||
run();
|
||||
} else if (program.args.length) {
|
||||
simple_glob(program.args).forEach(function(name) {
|
||||
files[convert_path(name)] = read_file(name);
|
||||
});
|
||||
run();
|
||||
} else {
|
||||
var chunks = [];
|
||||
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) {
|
||||
return UglifyJS.AST_Node.from_mozilla_ast(Object.keys(files).reduce(fn, null));
|
||||
}
|
||||
|
||||
function run() {
|
||||
UglifyJS.AST_Node.warn_function = function(msg) {
|
||||
console.error("WARN:", msg);
|
||||
};
|
||||
if (program.stats) program.stats = Date.now();
|
||||
try {
|
||||
TOPLEVEL = UglifyJS.parse(code, {
|
||||
filename : file,
|
||||
toplevel : TOPLEVEL,
|
||||
expression : ARGS.expr,
|
||||
bare_returns : ARGS.bare_returns,
|
||||
if (program.parse) {
|
||||
if (program.parse.acorn) {
|
||||
files = convert_ast(function(toplevel, name) {
|
||||
return require("acorn").parse(files[name], {
|
||||
locations: true,
|
||||
program: toplevel,
|
||||
sourceFile: name
|
||||
});
|
||||
} catch(ex) {
|
||||
});
|
||||
} 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
var result = UglifyJS.minify(files, options);
|
||||
} catch (ex) {
|
||||
if (ex instanceof UglifyJS.JS_Parse_Error) {
|
||||
print_error("Parse error at " + file + ":" + ex.line + "," + ex.col);
|
||||
console.error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
|
||||
var col = ex.col;
|
||||
var line = code.split(/\r?\n/)[ex.line - (col ? 1 : 2)];
|
||||
var line = files[ex.filename].split(/\r?\n/)[ex.line - (col ? 1 : 2)];
|
||||
if (line) {
|
||||
if (col > 40) {
|
||||
line = line.slice(col - 40);
|
||||
col = 40;
|
||||
}
|
||||
if (col) {
|
||||
print_error(line.slice(0, 80));
|
||||
print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
|
||||
console.error(line.slice(0, 80));
|
||||
console.error(line.slice(0, col).replace(/\S/g, " ") + "^");
|
||||
} else {
|
||||
print_error(line.slice(-40));
|
||||
print_error(line.slice(-40).replace(/\S/g, " ") + "^");
|
||||
}
|
||||
}
|
||||
print_error(ex.stack);
|
||||
process.exit(1);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
};
|
||||
});
|
||||
cb();
|
||||
});
|
||||
}();
|
||||
|
||||
function done() {
|
||||
var OUTPUT_FILE = ARGS.o;
|
||||
|
||||
var SOURCE_MAP = (ARGS.source_map || ARGS.source_map_inline) ? UglifyJS.SourceMap({
|
||||
file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
|
||||
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]);
|
||||
console.error(line.slice(-40));
|
||||
console.error(line.slice(-40).replace(/\S/g, " ") + "^");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ARGS.dump_spidermonkey_ast) {
|
||||
print(JSON.stringify(TOPLEVEL.to_mozilla_ast(), null, 2));
|
||||
fatal("ERROR: " + ex.message);
|
||||
}
|
||||
if (program.output == "spidermonkey") {
|
||||
console.log(JSON.stringify(UglifyJS.parse(result.code).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 {
|
||||
time_it("generate", function(){
|
||||
TOPLEVEL.print(output);
|
||||
});
|
||||
|
||||
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;
|
||||
console.log(result.code);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
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) {
|
||||
console.error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 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.
|
||||
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 ];
|
||||
}
|
||||
|
||||
function read_file(path, default_value) {
|
||||
try {
|
||||
return fs.readFileSync(path, "utf8");
|
||||
} catch (ex) {
|
||||
if (ex.code == "ENOENT" && default_value != null) return default_value;
|
||||
fatal("ERROR: " + ex.message);
|
||||
}
|
||||
}
|
||||
|
||||
/* -----[ functions ]----- */
|
||||
|
||||
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;
|
||||
function parse_js(flag, constants) {
|
||||
return function(value, options) {
|
||||
options = options || {};
|
||||
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_Sequence) return; // descend
|
||||
UglifyJS.parse(value, {
|
||||
expression: true
|
||||
}).walk(new UglifyJS.TreeWalker(function(node) {
|
||||
if (node instanceof UglifyJS.AST_Assign) {
|
||||
var name = node.left.print_to_string().replace(/-/g, "_");
|
||||
var name = node.left.print_to_string();
|
||||
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();
|
||||
if (!constants) {
|
||||
options[name] = value;
|
||||
} else if (value instanceof UglifyJS.AST_Array) {
|
||||
options[name] = value.elements.map(to_string);
|
||||
} else {
|
||||
fs.readFile(filename, "utf-8", cb);
|
||||
options[name] = to_string(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_PropAccess) {
|
||||
var name = node.print_to_string();
|
||||
options[name] = true;
|
||||
return true;
|
||||
}
|
||||
if (!(node instanceof UglifyJS.AST_Sequence)) throw node;
|
||||
|
||||
function to_string(value) {
|
||||
return value instanceof UglifyJS.AST_Constant ? value.getValue() : value.print_to_string({
|
||||
quote_keys: true
|
||||
});
|
||||
}
|
||||
}));
|
||||
} catch(ex) {
|
||||
fatal("Error parsing arguments for '" + flag + "': " + value);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
function parse_source_map() {
|
||||
var parse = parse_js("sourceMap", true);
|
||||
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 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 settings;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function print_error(msg) {
|
||||
console.error("%s", msg);
|
||||
}
|
||||
|
||||
function print(txt) {
|
||||
console.log("%s", txt);
|
||||
function to_cache(key) {
|
||||
if (cache[key]) {
|
||||
cache[key].props = UglifyJS.Dictionary.fromObject(cache[key].props);
|
||||
} else {
|
||||
cache[key] = {
|
||||
cname: -1,
|
||||
props: new UglifyJS.Dictionary()
|
||||
};
|
||||
}
|
||||
return cache[key];
|
||||
}
|
||||
|
||||
57
lib/ast.js
57
lib/ast.js
@@ -325,63 +325,14 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
$propdoc: {
|
||||
globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
|
||||
},
|
||||
wrap_enclose: function(arg_parameter_pairs) {
|
||||
var self = this;
|
||||
var args = [];
|
||||
var parameters = [];
|
||||
|
||||
arg_parameter_pairs.forEach(function(pair) {
|
||||
var splitAt = pair.lastIndexOf(":");
|
||||
|
||||
args.push(pair.substr(0, splitAt));
|
||||
parameters.push(pair.substr(splitAt + 1));
|
||||
});
|
||||
|
||||
var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
|
||||
wrap_commonjs: function(name) {
|
||||
var body = this.body;
|
||||
var wrapped_tl = "(function(exports){'$ORIG';})(typeof " + name + "=='undefined'?(" + name + "={}):" + name + ");";
|
||||
wrapped_tl = parse(wrapped_tl);
|
||||
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
|
||||
if (node instanceof AST_Directive && node.value == "$ORIG") {
|
||||
return MAP.splice(self.body);
|
||||
}
|
||||
}));
|
||||
return wrapped_tl;
|
||||
},
|
||||
wrap_commonjs: function(name, export_all) {
|
||||
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;
|
||||
}
|
||||
@@ -929,7 +880,7 @@ TreeWalker.prototype = {
|
||||
parent: function(n) {
|
||||
return this.stack[this.stack.length - 2 - (n || 0)];
|
||||
},
|
||||
push: function (node) {
|
||||
push: function(node) {
|
||||
if (node instanceof AST_Lambda) {
|
||||
this.directives = Object.create(this.directives);
|
||||
} else if (node instanceof AST_Directive && !this.directives[node.value]) {
|
||||
|
||||
@@ -61,6 +61,7 @@ function Compressor(options, false_by_default) {
|
||||
global_defs : {},
|
||||
hoist_funs : !false_by_default,
|
||||
hoist_vars : false,
|
||||
ie8 : false,
|
||||
if_return : !false_by_default,
|
||||
join_vars : !false_by_default,
|
||||
keep_fargs : true,
|
||||
@@ -73,7 +74,6 @@ function Compressor(options, false_by_default) {
|
||||
pure_getters : !false_by_default && "strict",
|
||||
pure_funcs : null,
|
||||
reduce_vars : !false_by_default,
|
||||
screw_ie8 : true,
|
||||
sequences : !false_by_default,
|
||||
side_effects : !false_by_default,
|
||||
switches : !false_by_default,
|
||||
@@ -84,7 +84,7 @@ function Compressor(options, false_by_default) {
|
||||
unsafe_math : false,
|
||||
unsafe_proto : false,
|
||||
unused : !false_by_default,
|
||||
warnings : true,
|
||||
warnings : false,
|
||||
}, true);
|
||||
var pure_funcs = this.options["pure_funcs"];
|
||||
if (typeof pure_funcs == "function") {
|
||||
@@ -1138,7 +1138,7 @@ merge(Compressor.prototype, {
|
||||
/* -----[ boolean/negation helpers ]----- */
|
||||
|
||||
// methods to determine whether an expression has a boolean result type
|
||||
(function (def){
|
||||
(function(def){
|
||||
var unary_bool = [ "!", "delete" ];
|
||||
var binary_bool = [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ];
|
||||
def(AST_Node, return_false);
|
||||
@@ -1166,7 +1166,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
// methods to determine if an expression has a numeric result type
|
||||
(function (def){
|
||||
(function(def){
|
||||
def(AST_Node, return_false);
|
||||
def(AST_Number, return_true);
|
||||
var unary = makePredicate("+ - ~ ++ --");
|
||||
@@ -1194,7 +1194,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
// methods to determine if an expression has a string result type
|
||||
(function (def){
|
||||
(function(def){
|
||||
def(AST_Node, return_false);
|
||||
def(AST_String, return_true);
|
||||
def(AST_UnaryPrefix, function(){
|
||||
@@ -1224,7 +1224,7 @@ merge(Compressor.prototype, {
|
||||
if (parent instanceof AST_Assign && parent.left === node) return node;
|
||||
}
|
||||
|
||||
(function (def){
|
||||
(function(def){
|
||||
AST_Node.DEFMETHOD("resolve_defines", function(compressor) {
|
||||
if (!compressor.option("global_defs")) return;
|
||||
var def = this._find_defs(compressor, "");
|
||||
@@ -1305,7 +1305,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
// methods to evaluate a constant expression
|
||||
(function (def){
|
||||
(function(def){
|
||||
// If the node has been successfully reduced to a constant,
|
||||
// then its value is returned; otherwise the element itself
|
||||
// is returned.
|
||||
@@ -2767,10 +2767,11 @@ merge(Compressor.prototype, {
|
||||
return arg.value;
|
||||
}).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
|
||||
var ast = parse(code);
|
||||
ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
|
||||
var mangle = { ie8: compressor.option("ie8") };
|
||||
ast.figure_out_scope(mangle);
|
||||
var comp = new Compressor(compressor.options);
|
||||
ast = ast.transform(comp);
|
||||
ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
|
||||
ast.figure_out_scope(mangle);
|
||||
ast.mangle_names();
|
||||
var fun;
|
||||
try {
|
||||
@@ -3216,7 +3217,7 @@ merge(Compressor.prototype, {
|
||||
&& self.right.operator == "typeof") {
|
||||
var expr = self.right.expression;
|
||||
if (expr instanceof AST_SymbolRef ? !expr.undeclared()
|
||||
: !(expr instanceof AST_PropAccess) || compressor.option("screw_ie8")) {
|
||||
: !(expr instanceof AST_PropAccess && compressor.option("ie8"))) {
|
||||
self.right = expr;
|
||||
self.left = make_node(AST_Undefined, self.left).optimize(compressor);
|
||||
if (self.operator.length == 2) self.operator += "=";
|
||||
@@ -3540,7 +3541,7 @@ merge(Compressor.prototype, {
|
||||
return def.optimize(compressor);
|
||||
}
|
||||
// testing against !self.scope.uses_with first is an optimization
|
||||
if (compressor.option("screw_ie8")
|
||||
if (!compressor.option("ie8")
|
||||
&& self.undeclared()
|
||||
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
|
||||
switch (self.name) {
|
||||
@@ -3860,7 +3861,7 @@ merge(Compressor.prototype, {
|
||||
var prop = self.property;
|
||||
if (prop instanceof AST_String && compressor.option("properties")) {
|
||||
prop = prop.getValue();
|
||||
if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) {
|
||||
if (RESERVED_WORDS(prop) ? !compressor.option("ie8") : is_identifier_string(prop)) {
|
||||
return make_node(AST_Dot, self, {
|
||||
expression : self.expression,
|
||||
property : prop
|
||||
@@ -3887,7 +3888,7 @@ merge(Compressor.prototype, {
|
||||
return def.optimize(compressor);
|
||||
}
|
||||
var prop = self.property;
|
||||
if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) {
|
||||
if (RESERVED_WORDS(prop) && compressor.option("ie8")) {
|
||||
return make_node(AST_Sub, self, {
|
||||
expression : self.expression,
|
||||
property : make_node(AST_String, self, {
|
||||
|
||||
146
lib/minify.js
Normal file
146
lib/minify.js
Normal file
@@ -0,0 +1,146 @@
|
||||
"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);
|
||||
}
|
||||
}
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
var stream = OutputStream(options.output);
|
||||
toplevel.print(stream);
|
||||
var 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;
|
||||
} finally {
|
||||
AST_Node.warn_function = warn_function;
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,7 @@ function OutputStream(options) {
|
||||
beautify : false,
|
||||
bracketize : false,
|
||||
comments : false,
|
||||
ie8 : false,
|
||||
indent_level : 4,
|
||||
indent_start : 0,
|
||||
inline_script : true,
|
||||
@@ -66,7 +67,6 @@ function OutputStream(options) {
|
||||
preserve_line : false,
|
||||
quote_keys : false,
|
||||
quote_style : 0,
|
||||
screw_ie8 : true,
|
||||
semicolons : true,
|
||||
shebang : true,
|
||||
source_map : null,
|
||||
@@ -136,7 +136,7 @@ function OutputStream(options) {
|
||||
case "\t": return "\\t";
|
||||
case "\b": return "\\b";
|
||||
case "\f": return "\\f";
|
||||
case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B";
|
||||
case "\x0B": return options.ie8 ? "\\x0B" : "\\v";
|
||||
case "\u2028": return "\\u2028";
|
||||
case "\u2029": return "\\u2029";
|
||||
case "\ufeff": return "\\ufeff";
|
||||
@@ -681,7 +681,7 @@ function OutputStream(options) {
|
||||
}
|
||||
});
|
||||
|
||||
PARENS([ AST_Assign, AST_Conditional ], function (output){
|
||||
PARENS([ AST_Assign, AST_Conditional ], function(output){
|
||||
var p = output.parent();
|
||||
// !(a = false) → true
|
||||
if (p instanceof AST_Unary)
|
||||
@@ -906,7 +906,7 @@ function OutputStream(options) {
|
||||
function make_then(self, output) {
|
||||
var b = self.body;
|
||||
if (output.option("bracketize")
|
||||
|| !output.option("screw_ie8") && b instanceof AST_Do)
|
||||
|| output.option("ie8") && b instanceof AST_Do)
|
||||
return make_block(b, output);
|
||||
// The squeezer replaces "block"-s that contain only a single
|
||||
// statement with the statement itself; technically, the AST
|
||||
@@ -1222,7 +1222,7 @@ function OutputStream(options) {
|
||||
&& +key + "" == key)
|
||||
&& parseFloat(key) >= 0) {
|
||||
output.print(make_num(key));
|
||||
} else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
|
||||
} else if (RESERVED_WORDS(key) ? !output.option("ie8") : is_identifier_string(key)) {
|
||||
if (quote && output.option("keep_quoted_props")) {
|
||||
output.print_string(key, quote);
|
||||
} else {
|
||||
|
||||
@@ -689,7 +689,6 @@ function parse($TEXT, options) {
|
||||
|
||||
options = defaults(options, {
|
||||
bare_returns : false,
|
||||
cli : false,
|
||||
expression : false,
|
||||
filename : null,
|
||||
html5_comments : true,
|
||||
@@ -1502,7 +1501,6 @@ function parse($TEXT, options) {
|
||||
};
|
||||
|
||||
function is_assignable(expr) {
|
||||
if (options.cli) return true;
|
||||
return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef;
|
||||
};
|
||||
|
||||
|
||||
@@ -43,16 +43,16 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
function find_builtins() {
|
||||
function find_builtins(reserved) {
|
||||
// NaN will be included due to Number.NaN
|
||||
var a = [
|
||||
[
|
||||
"null",
|
||||
"true",
|
||||
"false",
|
||||
"Infinity",
|
||||
"-Infinity",
|
||||
"undefined",
|
||||
];
|
||||
].forEach(add);
|
||||
[ Object, Array, Function, Number,
|
||||
String, Boolean, Error, Math,
|
||||
Date, RegExp
|
||||
@@ -63,24 +63,23 @@ function find_builtins() {
|
||||
}
|
||||
});
|
||||
function add(name) {
|
||||
push_uniq(a, name);
|
||||
push_uniq(reserved, name);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function mangle_properties(ast, options) {
|
||||
options = defaults(options, {
|
||||
builtins: false,
|
||||
cache: null,
|
||||
debug: false,
|
||||
ignore_quoted: false,
|
||||
keep_quoted: false,
|
||||
only_cache: false,
|
||||
regex: null,
|
||||
reserved: null,
|
||||
});
|
||||
|
||||
var reserved = options.reserved;
|
||||
if (reserved == null)
|
||||
reserved = find_builtins();
|
||||
var reserved = options.reserved || [];
|
||||
if (!options.builtins) find_builtins(reserved);
|
||||
|
||||
var cache = options.cache;
|
||||
if (cache == null) {
|
||||
@@ -91,12 +90,12 @@ function mangle_properties(ast, options) {
|
||||
}
|
||||
|
||||
var regex = options.regex;
|
||||
var ignore_quoted = options.ignore_quoted;
|
||||
var keep_quoted = options.keep_quoted;
|
||||
|
||||
// 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'
|
||||
// the same as passing an empty string.
|
||||
var debug = (options.debug !== false);
|
||||
var debug = options.debug !== false;
|
||||
var debug_name_suffix;
|
||||
if (debug) {
|
||||
debug_name_suffix = (options.debug === true ? "" : options.debug);
|
||||
@@ -104,12 +103,12 @@ function mangle_properties(ast, options) {
|
||||
|
||||
var names_to_mangle = [];
|
||||
var unmangleable = [];
|
||||
var ignored = {};
|
||||
var to_keep = {};
|
||||
|
||||
// step 1: find candidates to mangle
|
||||
ast.walk(new TreeWalker(function(node){
|
||||
if (node instanceof AST_ObjectKeyVal) {
|
||||
add(node.key, ignore_quoted && node.quote);
|
||||
add(node.key, keep_quoted && node.quote);
|
||||
}
|
||||
else if (node instanceof AST_ObjectProperty) {
|
||||
// setter or getter, since KeyVal is handled above
|
||||
@@ -119,14 +118,14 @@ function mangle_properties(ast, options) {
|
||||
add(node.property);
|
||||
}
|
||||
else if (node instanceof AST_Sub) {
|
||||
addStrings(node.property, ignore_quoted);
|
||||
addStrings(node.property, keep_quoted);
|
||||
}
|
||||
}));
|
||||
|
||||
// step 2: transform the tree, renaming properties
|
||||
return ast.transform(new TreeTransformer(function(node){
|
||||
if (node instanceof AST_ObjectKeyVal) {
|
||||
if (!(ignore_quoted && node.quote))
|
||||
if (!(keep_quoted && node.quote))
|
||||
node.key = mangle(node.key);
|
||||
}
|
||||
else if (node instanceof AST_ObjectProperty) {
|
||||
@@ -137,7 +136,7 @@ function mangle_properties(ast, options) {
|
||||
node.property = mangle(node.property);
|
||||
}
|
||||
else if (node instanceof AST_Sub) {
|
||||
if (!ignore_quoted)
|
||||
if (!keep_quoted)
|
||||
node.property = mangleStrings(node.property);
|
||||
}
|
||||
// else if (node instanceof AST_String) {
|
||||
@@ -167,16 +166,16 @@ function mangle_properties(ast, options) {
|
||||
}
|
||||
|
||||
function should_mangle(name) {
|
||||
if (ignore_quoted && name in ignored) return false;
|
||||
if (keep_quoted && name in to_keep) return false;
|
||||
if (regex && !regex.test(name)) return false;
|
||||
if (reserved.indexOf(name) >= 0) return false;
|
||||
return cache.props.has(name)
|
||||
|| names_to_mangle.indexOf(name) >= 0;
|
||||
}
|
||||
|
||||
function add(name, ignore) {
|
||||
if (ignore) {
|
||||
ignored[name] = true;
|
||||
function add(name, keep) {
|
||||
if (keep) {
|
||||
to_keep[name] = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -199,19 +198,19 @@ function mangle_properties(ast, options) {
|
||||
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
|
||||
var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_";
|
||||
|
||||
if (can_mangle(debug_mangled) && !(ignore_quoted && debug_mangled in ignored)) {
|
||||
if (can_mangle(debug_mangled) && !(keep_quoted && debug_mangled in to_keep)) {
|
||||
mangled = debug_mangled;
|
||||
}
|
||||
}
|
||||
|
||||
// either debug mode is off, or it is on and we could not use the mangled name
|
||||
if (!mangled) {
|
||||
// note can_mangle() does not check if the name collides with the 'ignored' set
|
||||
// (filled with quoted properties when ignore_quoted set). Make sure we add this
|
||||
// Note: `can_mangle()` does not check if the name collides with the `to_keep` set
|
||||
// (filled with quoted properties when `keep_quoted` is set). Make sure we add this
|
||||
// check so we don't collide with a quoted name.
|
||||
do {
|
||||
mangled = base54(++cache.cname);
|
||||
} while (!can_mangle(mangled) || (ignore_quoted && mangled in ignored));
|
||||
} while (!can_mangle(mangled) || keep_quoted && mangled in to_keep);
|
||||
}
|
||||
|
||||
cache.props.set(name, mangled);
|
||||
@@ -219,7 +218,7 @@ function mangle_properties(ast, options) {
|
||||
return mangled;
|
||||
}
|
||||
|
||||
function addStrings(node, ignore) {
|
||||
function addStrings(node, keep) {
|
||||
var out = {};
|
||||
try {
|
||||
(function walk(node){
|
||||
@@ -229,7 +228,7 @@ function mangle_properties(ast, options) {
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_String) {
|
||||
add(node.value, ignore);
|
||||
add(node.value, keep);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Conditional) {
|
||||
@@ -261,5 +260,4 @@ function mangle_properties(ast, options) {
|
||||
return node;
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
109
lib/scope.js
109
lib/scope.js
@@ -76,7 +76,7 @@ SymbolDef.prototype = {
|
||||
else if (!this.mangled_name && !this.unmangleable(options)) {
|
||||
var s = this.scope;
|
||||
var sym = this.orig[0];
|
||||
if (!options.screw_ie8 && sym instanceof AST_SymbolLambda)
|
||||
if (options.ie8 && sym instanceof AST_SymbolLambda)
|
||||
s = s.parent_scope;
|
||||
var def;
|
||||
if (this.defun && (def = this.defun.variables.get(this.name))) {
|
||||
@@ -93,7 +93,7 @@ SymbolDef.prototype = {
|
||||
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
options = defaults(options, {
|
||||
cache: null,
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
});
|
||||
|
||||
// pass 1: setup scope chaining and handle definitions
|
||||
@@ -220,7 +220,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
self.walk(tw);
|
||||
|
||||
// pass 3: fix up any scoping issue with IE8
|
||||
if (!options.screw_ie8) {
|
||||
if (options.ie8) {
|
||||
self.walk(new TreeWalker(function(node, descend) {
|
||||
if (node instanceof AST_SymbolCatch) {
|
||||
var name = node.name;
|
||||
@@ -325,8 +325,8 @@ AST_Scope.DEFMETHOD("next_mangled", function(options){
|
||||
if (!is_identifier(m)) continue; // skip over "do"
|
||||
|
||||
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not
|
||||
// shadow a name excepted from mangling.
|
||||
if (options.except.indexOf(m) >= 0) continue;
|
||||
// shadow a name reserved from mangling.
|
||||
if (options.reserved.indexOf(m) >= 0) continue;
|
||||
|
||||
// we must ensure that the mangled name does not shadow a name
|
||||
// from some parent scope that is referenced in this or in
|
||||
@@ -399,10 +399,9 @@ AST_Symbol.DEFMETHOD("global", function(){
|
||||
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
|
||||
return defaults(options, {
|
||||
eval : false,
|
||||
except : [],
|
||||
ie8 : false,
|
||||
keep_fnames : false,
|
||||
screw_ie8 : true,
|
||||
sort : false, // Ignored. Flag retained for backwards compatibility.
|
||||
reserved : [],
|
||||
toplevel : false,
|
||||
});
|
||||
});
|
||||
@@ -411,7 +410,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||
options = this._default_mangler_options(options);
|
||||
|
||||
// Never mangle arguments
|
||||
options.except.push('arguments');
|
||||
options.reserved.push('arguments');
|
||||
|
||||
// We only need to mangle declaration nodes. Special logic wired
|
||||
// into the code generator will display the mangled name if it's
|
||||
@@ -422,7 +421,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||
|
||||
if (options.cache) {
|
||||
this.globals.each(function(symbol){
|
||||
if (options.except.indexOf(symbol.name) < 0) {
|
||||
if (options.reserved.indexOf(symbol.name) < 0) {
|
||||
to_mangle.push(symbol);
|
||||
}
|
||||
});
|
||||
@@ -439,7 +438,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||
if (node instanceof AST_Scope) {
|
||||
var p = tw.parent(), a = [];
|
||||
node.variables.each(function(symbol){
|
||||
if (options.except.indexOf(symbol.name) < 0) {
|
||||
if (options.reserved.indexOf(symbol.name) < 0) {
|
||||
a.push(symbol);
|
||||
}
|
||||
});
|
||||
@@ -452,7 +451,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||
node.mangled_name = name;
|
||||
return true;
|
||||
}
|
||||
if (options.screw_ie8 && node instanceof AST_SymbolCatch) {
|
||||
if (!options.ie8 && node instanceof AST_SymbolCatch) {
|
||||
to_mangle.push(node.definition());
|
||||
return;
|
||||
}
|
||||
@@ -573,89 +572,3 @@ var base54 = (function() {
|
||||
};
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
"LICENSE"
|
||||
],
|
||||
"dependencies": {
|
||||
"source-map": "~0.5.1",
|
||||
"yargs": "~3.10.0"
|
||||
"commander": "~2.9.0",
|
||||
"source-map": "~0.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"acorn": "~0.6.0",
|
||||
|
||||
@@ -7,7 +7,7 @@ var createHash = require("crypto").createHash;
|
||||
var fork = require("child_process").fork;
|
||||
var args = process.argv.slice(2);
|
||||
if (!args.length) {
|
||||
args.push("-mc", "warnings=false");
|
||||
args.push("-mc");
|
||||
}
|
||||
args.push("--stats");
|
||||
var urls = [
|
||||
@@ -29,11 +29,11 @@ function done() {
|
||||
var info = results[url];
|
||||
console.log();
|
||||
console.log(url);
|
||||
console.log(info.log);
|
||||
var elapsed = 0;
|
||||
info.log.replace(/: ([0-9]+\.[0-9]{3})s/g, function(match, time) {
|
||||
elapsed += parseFloat(time);
|
||||
});
|
||||
console.log(info.log.replace(/Elapsed: ([0-9]+)\s*/g, function(match, time) {
|
||||
elapsed += 1e-3 * parseInt(time);
|
||||
return "";
|
||||
}));
|
||||
console.log("Run-time:", elapsed.toFixed(3), "s");
|
||||
console.log("Original:", info.input, "bytes");
|
||||
console.log("Uglified:", info.output, "bytes");
|
||||
|
||||
@@ -2,7 +2,7 @@ ascii_only_true: {
|
||||
options = {}
|
||||
beautify = {
|
||||
ascii_only : true,
|
||||
screw_ie8 : true,
|
||||
ie8 : false,
|
||||
beautify : false,
|
||||
}
|
||||
input: {
|
||||
@@ -20,7 +20,7 @@ ascii_only_false: {
|
||||
options = {}
|
||||
beautify = {
|
||||
ascii_only : false,
|
||||
screw_ie8 : true,
|
||||
ie8 : false,
|
||||
beautify : false,
|
||||
}
|
||||
input: {
|
||||
@@ -33,4 +33,3 @@ 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\'}'
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
issue_1321_no_debug: {
|
||||
mangle_props = {
|
||||
ignore_quoted: true
|
||||
keep_quoted: true
|
||||
}
|
||||
input: {
|
||||
var x = {};
|
||||
@@ -19,7 +19,7 @@ issue_1321_no_debug: {
|
||||
|
||||
issue_1321_debug: {
|
||||
mangle_props = {
|
||||
ignore_quoted: true,
|
||||
keep_quoted: true,
|
||||
debug: ""
|
||||
}
|
||||
input: {
|
||||
@@ -39,7 +39,7 @@ issue_1321_debug: {
|
||||
|
||||
issue_1321_with_quoted: {
|
||||
mangle_props = {
|
||||
ignore_quoted: false
|
||||
keep_quoted: false
|
||||
}
|
||||
input: {
|
||||
var x = {};
|
||||
|
||||
@@ -23,7 +23,7 @@ typeof_eq_undefined: {
|
||||
typeof_eq_undefined_ie8: {
|
||||
options = {
|
||||
comparisons: true,
|
||||
screw_ie8: false
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
var a = typeof b != "undefined";
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
screw_ie8: {
|
||||
options = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
try { throw "foo"; } catch (x) { console.log(x); }
|
||||
@@ -16,10 +16,10 @@ screw_ie8: {
|
||||
|
||||
support_ie8: {
|
||||
options = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
try { throw "foo"; } catch (x) { console.log(x); }
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
mangle_catch: {
|
||||
options = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: false,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: false,
|
||||
}
|
||||
input: {
|
||||
@@ -22,11 +22,11 @@ mangle_catch: {
|
||||
|
||||
mangle_catch_ie8: {
|
||||
options = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: false,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: false,
|
||||
}
|
||||
input: {
|
||||
@@ -44,11 +44,11 @@ mangle_catch_ie8: {
|
||||
|
||||
mangle_catch_var: {
|
||||
options = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: false,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: false,
|
||||
}
|
||||
input: {
|
||||
@@ -66,11 +66,11 @@ mangle_catch_var: {
|
||||
|
||||
mangle_catch_var_ie8: {
|
||||
options = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: false,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: false,
|
||||
}
|
||||
input: {
|
||||
@@ -88,11 +88,11 @@ mangle_catch_var_ie8: {
|
||||
|
||||
mangle_catch_toplevel: {
|
||||
options = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: true,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
@@ -110,11 +110,11 @@ mangle_catch_toplevel: {
|
||||
|
||||
mangle_catch_ie8_toplevel: {
|
||||
options = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: true,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
@@ -132,11 +132,11 @@ mangle_catch_ie8_toplevel: {
|
||||
|
||||
mangle_catch_var_toplevel: {
|
||||
options = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: true,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
@@ -154,11 +154,11 @@ mangle_catch_var_toplevel: {
|
||||
|
||||
mangle_catch_var_ie8_toplevel: {
|
||||
options = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: true,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
@@ -176,11 +176,11 @@ mangle_catch_var_ie8_toplevel: {
|
||||
|
||||
mangle_catch_redef_1: {
|
||||
options = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: false,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: false,
|
||||
}
|
||||
input: {
|
||||
@@ -198,11 +198,11 @@ mangle_catch_redef_1: {
|
||||
|
||||
mangle_catch_redef_1_ie8: {
|
||||
options = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: false,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: false,
|
||||
}
|
||||
input: {
|
||||
@@ -220,11 +220,11 @@ mangle_catch_redef_1_ie8: {
|
||||
|
||||
mangle_catch_redef_1_toplevel: {
|
||||
options = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: true,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
@@ -242,11 +242,11 @@ mangle_catch_redef_1_toplevel: {
|
||||
|
||||
mangle_catch_redef_1_ie8_toplevel: {
|
||||
options = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: true,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
@@ -264,11 +264,11 @@ mangle_catch_redef_1_ie8_toplevel: {
|
||||
|
||||
mangle_catch_redef_2: {
|
||||
options = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: false,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: false,
|
||||
}
|
||||
input: {
|
||||
@@ -285,11 +285,11 @@ mangle_catch_redef_2: {
|
||||
|
||||
mangle_catch_redef_2_ie8: {
|
||||
options = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: false,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: false,
|
||||
}
|
||||
input: {
|
||||
@@ -306,11 +306,11 @@ mangle_catch_redef_2_ie8: {
|
||||
|
||||
mangle_catch_redef_2_toplevel: {
|
||||
options = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: true,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
@@ -327,11 +327,11 @@ mangle_catch_redef_2_toplevel: {
|
||||
|
||||
mangle_catch_redef_2_ie8_toplevel: {
|
||||
options = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: true,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
function_iife_catch: {
|
||||
mangle = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
function f(n) {
|
||||
@@ -21,7 +21,7 @@ function_iife_catch: {
|
||||
|
||||
function_iife_catch_ie8: {
|
||||
mangle = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
function f(n) {
|
||||
@@ -42,7 +42,7 @@ function_iife_catch_ie8: {
|
||||
|
||||
function_catch_catch: {
|
||||
mangle = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
var o = 0;
|
||||
@@ -70,7 +70,7 @@ function_catch_catch: {
|
||||
|
||||
function_catch_catch_ie8: {
|
||||
mangle = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
var o = 0;
|
||||
|
||||
@@ -245,7 +245,7 @@ issue_1532: {
|
||||
issue_186: {
|
||||
beautify = {
|
||||
beautify: false,
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
var x = 3;
|
||||
@@ -264,7 +264,7 @@ issue_186: {
|
||||
issue_186_ie8: {
|
||||
beautify = {
|
||||
beautify: false,
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
var x = 3;
|
||||
@@ -283,7 +283,7 @@ issue_186_ie8: {
|
||||
issue_186_beautify: {
|
||||
beautify = {
|
||||
beautify: true,
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
var x = 3;
|
||||
@@ -310,7 +310,7 @@ issue_186_beautify: {
|
||||
issue_186_beautify_ie8: {
|
||||
beautify = {
|
||||
beautify: true,
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
var x = 3;
|
||||
@@ -340,7 +340,7 @@ issue_186_bracketize: {
|
||||
beautify = {
|
||||
beautify: false,
|
||||
bracketize: true,
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
var x = 3;
|
||||
@@ -360,7 +360,7 @@ issue_186_bracketize_ie8: {
|
||||
beautify = {
|
||||
beautify: false,
|
||||
bracketize: true,
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
var x = 3;
|
||||
@@ -380,7 +380,7 @@ issue_186_beautify_bracketize: {
|
||||
beautify = {
|
||||
beautify: true,
|
||||
bracketize: true,
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
var x = 3;
|
||||
@@ -412,7 +412,7 @@ issue_186_beautify_bracketize_ie8: {
|
||||
beautify = {
|
||||
beautify: true,
|
||||
bracketize: true,
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
var x = 3;
|
||||
|
||||
@@ -13,7 +13,7 @@ keep_properties: {
|
||||
dot_properties: {
|
||||
options = {
|
||||
properties: true,
|
||||
screw_ie8: false
|
||||
ie8: true,
|
||||
};
|
||||
input: {
|
||||
a["foo"] = "bar";
|
||||
@@ -36,7 +36,7 @@ dot_properties: {
|
||||
dot_properties_es5: {
|
||||
options = {
|
||||
properties: true,
|
||||
screw_ie8: true
|
||||
ie8: false,
|
||||
};
|
||||
input: {
|
||||
a["foo"] = "bar";
|
||||
@@ -125,7 +125,7 @@ evaluate_string_length: {
|
||||
|
||||
mangle_properties: {
|
||||
mangle_props = {
|
||||
ignore_quoted: false
|
||||
keep_quoted: false
|
||||
};
|
||||
input: {
|
||||
a["foo"] = "bar";
|
||||
@@ -148,7 +148,7 @@ mangle_unquoted_properties: {
|
||||
properties: false
|
||||
}
|
||||
mangle_props = {
|
||||
ignore_quoted: true
|
||||
keep_quoted: true
|
||||
}
|
||||
beautify = {
|
||||
beautify: false,
|
||||
@@ -233,12 +233,12 @@ mangle_debug_suffix: {
|
||||
}
|
||||
}
|
||||
|
||||
mangle_debug_suffix_ignore_quoted: {
|
||||
mangle_debug_suffix_keep_quoted: {
|
||||
options = {
|
||||
properties: false
|
||||
}
|
||||
mangle_props = {
|
||||
ignore_quoted: true,
|
||||
keep_quoted: true,
|
||||
debug: "XYZ",
|
||||
reserved: []
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
do_screw: {
|
||||
options = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
}
|
||||
beautify = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
ascii_only: true,
|
||||
}
|
||||
input: {
|
||||
@@ -14,10 +14,10 @@ do_screw: {
|
||||
|
||||
dont_screw: {
|
||||
options = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
}
|
||||
beautify = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
ascii_only: true,
|
||||
}
|
||||
input: {
|
||||
@@ -28,7 +28,7 @@ dont_screw: {
|
||||
|
||||
do_screw_constants: {
|
||||
options = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
f(undefined, Infinity);
|
||||
@@ -38,7 +38,7 @@ do_screw_constants: {
|
||||
|
||||
dont_screw_constants: {
|
||||
options = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
f(undefined, Infinity);
|
||||
@@ -47,9 +47,15 @@ dont_screw_constants: {
|
||||
}
|
||||
|
||||
do_screw_try_catch: {
|
||||
options = { screw_ie8: true };
|
||||
mangle = { screw_ie8: true };
|
||||
beautify = { screw_ie8: true };
|
||||
options = {
|
||||
ie8: false,
|
||||
}
|
||||
mangle = {
|
||||
ie8: false,
|
||||
}
|
||||
beautify = {
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
good = function(e){
|
||||
return function(error){
|
||||
@@ -75,9 +81,15 @@ do_screw_try_catch: {
|
||||
}
|
||||
|
||||
dont_screw_try_catch: {
|
||||
options = { screw_ie8: false };
|
||||
mangle = { screw_ie8: false };
|
||||
beautify = { screw_ie8: false };
|
||||
options = {
|
||||
ie8: true,
|
||||
}
|
||||
mangle = {
|
||||
ie8: true,
|
||||
}
|
||||
beautify = {
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
bad = function(e){
|
||||
return function(error){
|
||||
@@ -103,9 +115,15 @@ dont_screw_try_catch: {
|
||||
}
|
||||
|
||||
do_screw_try_catch_undefined: {
|
||||
options = { screw_ie8: true };
|
||||
mangle = { screw_ie8: true };
|
||||
beautify = { screw_ie8: true };
|
||||
options = {
|
||||
ie8: false,
|
||||
}
|
||||
mangle = {
|
||||
ie8: false,
|
||||
}
|
||||
beautify = {
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
function a(b){
|
||||
try {
|
||||
@@ -132,9 +150,15 @@ do_screw_try_catch_undefined: {
|
||||
}
|
||||
|
||||
dont_screw_try_catch_undefined: {
|
||||
options = { screw_ie8: false };
|
||||
mangle = { screw_ie8: false };
|
||||
beautify = { screw_ie8: false };
|
||||
options = {
|
||||
ie8: true,
|
||||
}
|
||||
mangle = {
|
||||
ie8: true,
|
||||
}
|
||||
beautify = {
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
function a(b){
|
||||
try {
|
||||
@@ -164,11 +188,11 @@ reduce_vars: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
unused: true,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
@@ -196,10 +220,10 @@ reduce_vars: {
|
||||
|
||||
issue_1586_1: {
|
||||
options = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: false,
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
@@ -215,10 +239,10 @@ issue_1586_1: {
|
||||
|
||||
issue_1586_2: {
|
||||
options = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
}
|
||||
mangle = {
|
||||
screw_ie8: true,
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
|
||||
@@ -12,7 +12,7 @@ if (typeof phantom == "undefined") {
|
||||
});
|
||||
var args = process.argv.slice(2);
|
||||
if (!args.length) {
|
||||
args.push("-mc", "warnings=false");
|
||||
args.push("-mc");
|
||||
}
|
||||
args.push("--stats");
|
||||
var child_process = require("child_process");
|
||||
|
||||
@@ -2,6 +2,10 @@ var assert = require("assert");
|
||||
var exec = require("child_process").exec;
|
||||
var readFileSync = require("fs").readFileSync;
|
||||
|
||||
function read(path) {
|
||||
return readFileSync(path, "utf8");
|
||||
}
|
||||
|
||||
describe("bin/uglifyjs", function () {
|
||||
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
|
||||
it("should produce a functional build when using --self", function (done) {
|
||||
@@ -20,7 +24,7 @@ describe("bin/uglifyjs", function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should be able to filter comments correctly with `--comment all`", function (done) {
|
||||
it("Should be able to filter comments correctly with `--comments all`", function (done) {
|
||||
var command = uglifyjscmd + ' test/input/comments/filter.js --comments all';
|
||||
|
||||
exec(command, function (err, stdout) {
|
||||
@@ -50,8 +54,8 @@ describe("bin/uglifyjs", 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-inline';
|
||||
it("Should append source map to output when using --source-map url=inline", function (done) {
|
||||
var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map url=inline";
|
||||
|
||||
exec(command, function (err, stdout) {
|
||||
if (err) throw err;
|
||||
@@ -61,7 +65,7 @@ describe("bin/uglifyjs", function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("should not append source map to output when not using --source-map-inline", function (done) {
|
||||
it("should not append source map to output when not using --source-map url=inline", function (done) {
|
||||
var command = uglifyjscmd + ' test/input/issue-1323/sample.js';
|
||||
|
||||
exec(command, function (err, stdout) {
|
||||
@@ -137,7 +141,7 @@ describe("bin/uglifyjs", function () {
|
||||
exec(command, function (err, stdout) {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(stdout, readFileSync("test/input/issue-1482/default.js", "utf8"));
|
||||
assert.strictEqual(stdout, read("test/input/issue-1482/default.js"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -147,55 +151,59 @@ describe("bin/uglifyjs", function () {
|
||||
exec(command, function (err, stdout) {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(stdout, readFileSync("test/input/issue-1482/bracketize.js", "utf8"));
|
||||
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 --in-source-map inline --source-map-inline';
|
||||
var command = uglifyjscmd + " test/input/issue-520/input.js -mc toplevel --source-map content=inline,url=inline";
|
||||
|
||||
exec(command, function (err, stdout) {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(stdout, readFileSync("test/input/issue-520/output.js", "utf8"));
|
||||
assert.strictEqual(stdout, read("test/input/issue-520/output.js"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should warn for missing inline source map", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/issue-1323/sample.js --in-source-map inline';
|
||||
var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map content=inline,url=inline";
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
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}();",
|
||||
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=",
|
||||
"",
|
||||
].join("\n"));
|
||||
assert.strictEqual(stderr, "WARN: inline source map not found\n");
|
||||
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 --in-source-map inline --source-map-inline';
|
||||
var command = uglifyjscmd + " test/input/issue-520/input.js test/input/issue-520/output.js --source-map content=inline,url=inline";
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
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();
|
||||
});
|
||||
});
|
||||
it("Should fail with acorn and inline source map", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/issue-520/input.js --in-source-map inline --source-map-inline --acorn';
|
||||
var command = uglifyjscmd + " test/input/issue-520/input.js --source-map content=inline,url=inline -p acorn";
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
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();
|
||||
});
|
||||
});
|
||||
it("Should fail with SpiderMonkey and inline source map", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/issue-520/input.js --in-source-map inline --source-map-inline --spidermonkey';
|
||||
var command = uglifyjscmd + " test/input/issue-520/input.js --source-map content=inline,url=inline -p spidermonkey";
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -208,7 +216,7 @@ describe("bin/uglifyjs", function () {
|
||||
assert.strictEqual(lines[0], "Parse error at test/input/invalid/simple.js:1,12");
|
||||
assert.strictEqual(lines[1], "function f(a{}");
|
||||
assert.strictEqual(lines[2], " ^");
|
||||
assert.strictEqual(lines[3], "SyntaxError: Unexpected token punc «{», expected punc «,»");
|
||||
assert.strictEqual(lines[3], "ERROR: Unexpected token punc «{», expected punc «,»");
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -221,7 +229,7 @@ describe("bin/uglifyjs", function () {
|
||||
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[2], "\t\t \t ^");
|
||||
assert.strictEqual(lines[3], "SyntaxError: Invalid syntax: 0abc");
|
||||
assert.strictEqual(lines[3], "ERROR: Invalid syntax: 0abc");
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -234,7 +242,7 @@ describe("bin/uglifyjs", function () {
|
||||
assert.strictEqual(lines[0], "Parse error at test/input/invalid/eof.js:2,0");
|
||||
assert.strictEqual(lines[1], "foo, bar(");
|
||||
assert.strictEqual(lines[2], " ^");
|
||||
assert.strictEqual(lines[3], "SyntaxError: Unexpected token: eof (undefined)");
|
||||
assert.strictEqual(lines[3], "ERROR: Unexpected token: eof (undefined)");
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -247,17 +255,7 @@ describe("bin/uglifyjs", function () {
|
||||
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[2], " ^");
|
||||
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");
|
||||
assert.strictEqual(lines[3], "ERROR: Unexpected token: eof (undefined)");
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -271,7 +269,7 @@ describe("bin/uglifyjs", function () {
|
||||
"Parse error at test/input/invalid/assign_1.js:1,18",
|
||||
"console.log(1 || 5--);",
|
||||
" ^",
|
||||
"SyntaxError: Invalid use of -- operator"
|
||||
"ERROR: Invalid use of -- operator"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
@@ -286,7 +284,7 @@ describe("bin/uglifyjs", function () {
|
||||
"Parse error at test/input/invalid/assign_2.js:1,32",
|
||||
"console.log(2 || (Math.random() /= 2));",
|
||||
" ^",
|
||||
"SyntaxError: Invalid assignment"
|
||||
"ERROR: Invalid assignment"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
@@ -301,9 +299,34 @@ describe("bin/uglifyjs", function () {
|
||||
"Parse error at test/input/invalid/assign_3.js:1,18",
|
||||
"console.log(3 || ++this);",
|
||||
" ^",
|
||||
"SyntaxError: Invalid use of ++ operator"
|
||||
"ERROR: Invalid use of ++ operator"
|
||||
].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"));
|
||||
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, '\\"');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -75,7 +75,6 @@ describe("comment filters", function() {
|
||||
|
||||
it("Should handle shebang and preamble correctly", function() {
|
||||
var code = UglifyJS.minify("#!/usr/bin/node\nvar x = 10;", {
|
||||
fromString: true,
|
||||
output: { preamble: "/* Build */" }
|
||||
}).code;
|
||||
assert.strictEqual(code, "#!/usr/bin/node\n/* Build */\nvar x=10;");
|
||||
@@ -83,7 +82,6 @@ describe("comment filters", function() {
|
||||
|
||||
it("Should handle preamble without shebang correctly", function() {
|
||||
var code = UglifyJS.minify("var x = 10;", {
|
||||
fromString: true,
|
||||
output: { preamble: "/* Build */" }
|
||||
}).code;
|
||||
assert.strictEqual(code, "/* Build */\nvar x=10;");
|
||||
|
||||
@@ -20,7 +20,7 @@ describe("Comment", function() {
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(function() {
|
||||
uglify.parse(tests[i], {fromString: true})
|
||||
uglify.parse(tests[i]);
|
||||
}, fail, tests[i]);
|
||||
}
|
||||
});
|
||||
@@ -43,7 +43,7 @@ describe("Comment", function() {
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.throws(function() {
|
||||
uglify.parse(tests[i], {fromString: true})
|
||||
uglify.parse(tests[i]);
|
||||
}, fail, tests[i]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,9 +6,7 @@ describe("comment before constant", function() {
|
||||
|
||||
it("Should test comment before constant is retained and output after mangle.", function() {
|
||||
var result = Uglify.minify(js, {
|
||||
fromString: true,
|
||||
compress: { collapse_vars: false, reduce_vars: false },
|
||||
mangle: {},
|
||||
output: { comments: true },
|
||||
});
|
||||
assert.strictEqual(result.code, 'function f(){/*c1*/var/*c2*/n=/*c3*/!1;return n}');
|
||||
@@ -16,12 +14,9 @@ describe("comment before constant", function() {
|
||||
|
||||
it("Should test code works when comments disabled.", function() {
|
||||
var result = Uglify.minify(js, {
|
||||
fromString: true,
|
||||
compress: { collapse_vars: false, reduce_vars: false },
|
||||
mangle: {},
|
||||
output: { comments: false },
|
||||
});
|
||||
assert.strictEqual(result.code, 'function f(){var n=!1;return n}');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ describe("Directives", function() {
|
||||
assert.strictEqual(
|
||||
uglify.minify(
|
||||
'"use strict";\'use strict\';"use strict";"use strict";;\'use strict\';console.log(\'use strict\');',
|
||||
{fromString: true, output: {beautify: true, quote_style: 3}, compress: false}
|
||||
{output: {beautify: true, quote_style: 3}, compress: false}
|
||||
).code,
|
||||
'"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++) {
|
||||
assert.strictEqual(
|
||||
uglify.minify(tests[i][0], {fromString: true, quote_style: 3, compress: false, mangle: false}).code,
|
||||
uglify.minify(tests[i][0], {compress: false, mangle: false}).code,
|
||||
tests[i][1],
|
||||
tests[i][0]
|
||||
);
|
||||
@@ -234,7 +234,7 @@ describe("Directives", function() {
|
||||
|
||||
it("Should add double semicolon when relying on automatic semicolon insertion", function() {
|
||||
var code = uglify.minify('"use strict";"use\\x20strict";',
|
||||
{fromString: true, output: {semicolons: false}, compress: false}
|
||||
{output: {semicolons: false}, compress: false}
|
||||
).code;
|
||||
assert.strictEqual(code, '"use strict";;"use strict"\n');
|
||||
});
|
||||
@@ -340,7 +340,7 @@ describe("Directives", function() {
|
||||
];
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.strictEqual(
|
||||
uglify.minify(tests[i][0], {fromString: true, output:{quote_style: tests[i][1]}, compress: false}).code,
|
||||
uglify.minify(tests[i][0], {output:{quote_style: tests[i][1]}, compress: false}).code,
|
||||
tests[i][2],
|
||||
tests[i][0] + " using mode " + tests[i][1]
|
||||
);
|
||||
@@ -362,7 +362,7 @@ describe("Directives", function() {
|
||||
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.strictEqual(
|
||||
uglify.minify(tests[i][0], {fromString: true, compress: {collapse_vars: true, side_effects: true}}).code,
|
||||
uglify.minify(tests[i][0], {compress: {collapse_vars: true, side_effects: true}}).code,
|
||||
tests[i][1],
|
||||
tests[i][0]
|
||||
);
|
||||
|
||||
@@ -1,58 +1,80 @@
|
||||
var Uglify = require('../../');
|
||||
var assert = require("assert");
|
||||
var exec = require("child_process").exec;
|
||||
var path = require("path");
|
||||
var readFileSync = require("fs").readFileSync;
|
||||
|
||||
describe("minify() with input file globs", function() {
|
||||
it("minify() with one input file glob string.", function() {
|
||||
var result = Uglify.minify("test/input/issue-1242/foo.*");
|
||||
assert.strictEqual(result.code, 'function foo(o){print("Foo:",2*o)}var print=console.log.bind(console);');
|
||||
describe("bin/uglifyjs with input file globs", function() {
|
||||
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
|
||||
it("bin/uglifyjs with one input file extension glob.", function(done) {
|
||||
var command = uglifyjscmd + ' "test/input/issue-1242/foo.*" -cm';
|
||||
|
||||
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("minify() with an array of one input file glob.", function() {
|
||||
var result = Uglify.minify([
|
||||
"test/input/issue-1242/b*.es5",
|
||||
]);
|
||||
assert.strictEqual(result.code, 'function bar(n){return 3*n}function baz(n){return n/2}');
|
||||
});
|
||||
it("minify() with an array of multiple input file globs.", function() {
|
||||
var result = Uglify.minify([
|
||||
"test/input/issue-1242/???.es5",
|
||||
"test/input/issue-1242/*.js",
|
||||
], {
|
||||
compress: { toplevel: true }
|
||||
it("bin/uglifyjs with one input file name glob.", function(done) {
|
||||
var command = uglifyjscmd + ' "test/input/issue-1242/b*.es5" -cm';
|
||||
|
||||
exec(command, function(err, stdout) {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(stdout, 'function bar(n){return 3*n}function baz(n){return n/2}\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() {
|
||||
var glob = "test/input/issue-1242/blah.*";
|
||||
assert.strictEqual(Uglify.simple_glob(glob).length, 1);
|
||||
assert.strictEqual(Uglify.simple_glob(glob)[0], glob);
|
||||
assert.throws(function() {
|
||||
Uglify.minify(glob);
|
||||
}, "should throw file not found");
|
||||
it("bin/uglifyjs with multiple input file globs.", function(done) {
|
||||
var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel';
|
||||
|
||||
exec(command, function(err, stdout) {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(stdout, 'var print=console.log.bind(console),a=function(n){return 3*n}(3),b=function(n){return n/2}(12);print("qux",a,b),function(n){print("Foo:",2*n)}(11);\n');
|
||||
done();
|
||||
});
|
||||
it('"?" in glob string should not match "/"', function() {
|
||||
var glob = "test/input?issue-1242/foo.*";
|
||||
assert.strictEqual(Uglify.simple_glob(glob).length, 1);
|
||||
assert.strictEqual(Uglify.simple_glob(glob)[0], glob);
|
||||
assert.throws(function() {
|
||||
Uglify.minify(glob);
|
||||
}, "should throw file not found");
|
||||
});
|
||||
it("should handle special characters in glob string", function() {
|
||||
var result = Uglify.minify("test/input/issue-1632/^{*}[???](*)+$.??");
|
||||
assert.strictEqual(result.code, "console.log(x);");
|
||||
it("should throw with non-matching glob string", function(done) {
|
||||
var command = uglifyjscmd + ' "test/input/issue-1242/blah.*"';
|
||||
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.ok(/^ERROR: ENOENT/.test(stderr));
|
||||
done();
|
||||
});
|
||||
it("should handle array of glob strings - matching and otherwise", function() {
|
||||
});
|
||||
it('"?" in glob string should not match "/"', function(done) {
|
||||
var command = uglifyjscmd + ' "test/input?issue-1242/foo.*"';
|
||||
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.ok(/^ERROR: ENOENT/.test(stderr));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("should handle special characters in glob string", function(done) {
|
||||
var command = uglifyjscmd + ' "test/input/issue-1632/^{*}[???](*)+$.??" -cm';
|
||||
|
||||
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) {
|
||||
var dir = "test/input/issue-1242";
|
||||
var matches = Uglify.simple_glob([
|
||||
var command = uglifyjscmd + ' "' + [
|
||||
path.join(dir, "b*.es5"),
|
||||
path.join(dir, "z*.es5"),
|
||||
path.join(dir, "*.js"),
|
||||
]);
|
||||
assert.strictEqual(matches.length, 4);
|
||||
assert.strictEqual(matches[0], path.join(dir, "bar.es5"));
|
||||
assert.strictEqual(matches[1], path.join(dir, "baz.es5"));
|
||||
assert.strictEqual(matches[2], path.join(dir, "z*.es5"));
|
||||
assert.strictEqual(matches[3], path.join(dir, "qux.js"));
|
||||
path.join(dir, "*.js")
|
||||
].join('" "') + '"';
|
||||
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.ok(/^ERROR: ENOENT.*?z\*\.es5/.test(stderr));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,12 +8,7 @@ describe("Huge number of comments.", function() {
|
||||
for (i = 1; i <= 5000; ++i) { js += "// " + i + "\n"; }
|
||||
for (; i <= 10000; ++i) { js += "/* " + i + " */ /**/"; }
|
||||
js += "x; }";
|
||||
var result = Uglify.minify(js, {
|
||||
fromString: true,
|
||||
mangle: false,
|
||||
compress: {}
|
||||
});
|
||||
var result = Uglify.minify(js, { mangle: false });
|
||||
assert.strictEqual(result.code, "function lots_of_comments(x){return 7-x}");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ describe("input sourcemaps", function() {
|
||||
transpilemap = sourceMap || getMap();
|
||||
|
||||
var result = Uglify.minify(transpiled, {
|
||||
fromString: true,
|
||||
inSourceMap: transpilemap,
|
||||
outSourceMap: true
|
||||
sourceMap: {
|
||||
content: transpilemap
|
||||
}
|
||||
});
|
||||
|
||||
map = new SourceMapConsumer(result.map);
|
||||
|
||||
@@ -11,7 +11,7 @@ describe("let", function() {
|
||||
s += "var v" + i + "=0;";
|
||||
}
|
||||
s += '}';
|
||||
var result = Uglify.minify(s, {fromString: true, compress: false});
|
||||
var result = Uglify.minify(s, {compress: false});
|
||||
|
||||
// Verify that select keywords and reserved keywords not produced
|
||||
assert.strictEqual(result.code.indexOf("var let="), -1);
|
||||
|
||||
@@ -3,9 +3,8 @@ var assert = require("assert");
|
||||
|
||||
describe("line-endings", function() {
|
||||
var options = {
|
||||
fromString: true,
|
||||
mangle: false,
|
||||
compress: false,
|
||||
mangle: false,
|
||||
output: {
|
||||
beautify: false,
|
||||
comments: /^!/,
|
||||
|
||||
@@ -6,43 +6,41 @@ describe("Input file as map", function() {
|
||||
var jsMap = {
|
||||
'/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};'
|
||||
};
|
||||
var result = Uglify.minify(jsMap, {fromString: true, outSourceMap: true});
|
||||
var result = Uglify.minify(jsMap, {sourceMap: true});
|
||||
|
||||
var map = JSON.parse(result.map);
|
||||
assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3};');
|
||||
assert.deepEqual(map.sources, ['/scripts/foo.js']);
|
||||
assert.strictEqual(map.file, undefined);
|
||||
|
||||
result = Uglify.minify(jsMap, {fromString: true, outFileName: 'out.js'});
|
||||
assert.strictEqual(result.map, null);
|
||||
result = Uglify.minify(jsMap);
|
||||
assert.strictEqual(result.map, undefined);
|
||||
|
||||
result = Uglify.minify(jsMap, {fromString: true, outFileName: 'out.js', outSourceMap: true});
|
||||
result = Uglify.minify(jsMap, {sourceMap: {filename: 'out.js'}});
|
||||
map = JSON.parse(result.map);
|
||||
assert.strictEqual(map.file, 'out.js');
|
||||
});
|
||||
|
||||
it("Should accept array of objects and strings", function() {
|
||||
it("Should accept array of strings", function() {
|
||||
var jsSeq = [
|
||||
{'/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};'},
|
||||
'var foo = {"x": 1, y: 2, \'z\': 3};',
|
||||
'var bar = 15;'
|
||||
];
|
||||
var result = Uglify.minify(jsSeq, {fromString: true, outSourceMap: true});
|
||||
var result = Uglify.minify(jsSeq, {sourceMap: true});
|
||||
|
||||
var map = JSON.parse(result.map);
|
||||
assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3},bar=15;');
|
||||
assert.strictEqual(map.sources[0], '/scripts/foo.js');
|
||||
assert.deepEqual(map.sources, ['0', '1']);
|
||||
});
|
||||
|
||||
it("Should correctly include source", function() {
|
||||
var jsSeq = [
|
||||
{'/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};'},
|
||||
'var bar = 15;'
|
||||
];
|
||||
var result = Uglify.minify(jsSeq, {fromString: true, outSourceMap: true, sourceMapIncludeSources: true});
|
||||
var jsMap = {
|
||||
'/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};'
|
||||
};
|
||||
var result = Uglify.minify(jsMap, {sourceMap: {includeSources: true}});
|
||||
|
||||
var map = JSON.parse(result.map);
|
||||
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};', 'var bar = 15;']);
|
||||
assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3};');
|
||||
assert.deepEqual(map.sourcesContent, ['var foo = {"x": 1, y: 2, \'z\': 3};']);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -2,10 +2,14 @@ var Uglify = require('../../');
|
||||
var assert = require("assert");
|
||||
var readFileSync = require("fs").readFileSync;
|
||||
|
||||
function read(path) {
|
||||
return readFileSync(path, "utf8");
|
||||
}
|
||||
|
||||
describe("minify", 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 result = Uglify.minify(js, {fromString: true});
|
||||
var result = Uglify.minify(js);
|
||||
assert.strictEqual(result.code, 'function foo(n){return n?3:7}');
|
||||
});
|
||||
|
||||
@@ -13,7 +17,7 @@ describe("minify", function() {
|
||||
it("Should preserve quotes in object literals", function() {
|
||||
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
|
||||
var result = Uglify.minify(js, {
|
||||
fromString: true, output: {
|
||||
output: {
|
||||
keep_quoted_props: true
|
||||
}});
|
||||
assert.strictEqual(result.code, 'var foo={"x":1,y:2,"z":3};');
|
||||
@@ -22,7 +26,7 @@ describe("minify", function() {
|
||||
it("Should preserve quote styles when quote_style is 3", function() {
|
||||
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
|
||||
var result = Uglify.minify(js, {
|
||||
fromString: true, output: {
|
||||
output: {
|
||||
keep_quoted_props: true,
|
||||
quote_style: 3
|
||||
}});
|
||||
@@ -32,7 +36,7 @@ describe("minify", function() {
|
||||
it("Should not preserve quotes in object literals when disabled", function() {
|
||||
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
|
||||
var result = Uglify.minify(js, {
|
||||
fromString: true, output: {
|
||||
output: {
|
||||
keep_quoted_props: false,
|
||||
quote_style: 3
|
||||
}});
|
||||
@@ -44,12 +48,13 @@ describe("minify", function() {
|
||||
it("Shouldn't mangle quoted properties", function() {
|
||||
var js = 'a["foo"] = "bar"; a.color = "red"; x = {"bar": 10};';
|
||||
var result = Uglify.minify(js, {
|
||||
fromString: true,
|
||||
compress: {
|
||||
properties: false
|
||||
},
|
||||
mangleProperties: {
|
||||
ignore_quoted: true
|
||||
mangle: {
|
||||
properties: {
|
||||
keep_quoted: true
|
||||
}
|
||||
},
|
||||
output: {
|
||||
keep_quoted_props: true,
|
||||
@@ -63,10 +68,12 @@ describe("minify", function() {
|
||||
|
||||
describe("inSourceMap", function() {
|
||||
it("Should read the given string filename correctly when sourceMapIncludeSources is enabled (#1236)", function() {
|
||||
var result = Uglify.minify('./test/input/issue-1236/simple.js', {
|
||||
outSourceMap: "simple.min.js.map",
|
||||
inSourceMap: "./test/input/issue-1236/simple.js.map",
|
||||
sourceMapIncludeSources: true
|
||||
var result = Uglify.minify(read("./test/input/issue-1236/simple.js"), {
|
||||
sourceMap: {
|
||||
content: read("./test/input/issue-1236/simple.js.map"),
|
||||
filename: "simple.min.js",
|
||||
includeSources: true
|
||||
}
|
||||
});
|
||||
|
||||
var map = JSON.parse(result.map);
|
||||
@@ -77,10 +84,12 @@ describe("minify", function() {
|
||||
'let foo = x => "foo " + x;\nconsole.log(foo("bar"));');
|
||||
});
|
||||
it("Should process inline source map", function() {
|
||||
var code = Uglify.minify("./test/input/issue-520/input.js", {
|
||||
var code = Uglify.minify(read("./test/input/issue-520/input.js"), {
|
||||
compress: { toplevel: true },
|
||||
inSourceMap: "inline",
|
||||
sourceMapInline: true
|
||||
sourceMap: {
|
||||
content: "inline",
|
||||
url: "inline"
|
||||
}
|
||||
}).code + "\n";
|
||||
assert.strictEqual(code, readFileSync("test/input/issue-520/output.js", "utf8"));
|
||||
});
|
||||
@@ -91,9 +100,11 @@ describe("minify", function() {
|
||||
warnings.push(txt);
|
||||
};
|
||||
try {
|
||||
var result = Uglify.minify("./test/input/issue-1323/sample.js", {
|
||||
inSourceMap: "inline",
|
||||
var result = Uglify.minify(read("./test/input/issue-1323/sample.js"), {
|
||||
mangle: false,
|
||||
sourceMap: {
|
||||
content: "inline"
|
||||
}
|
||||
});
|
||||
assert.strictEqual(result.code, "var bar=function(){function foo(bar){return bar}return foo}();");
|
||||
assert.strictEqual(warnings.length, 1);
|
||||
@@ -105,20 +116,13 @@ describe("minify", function() {
|
||||
it("Should fail with multiple input and inline source map", function() {
|
||||
assert.throws(function() {
|
||||
Uglify.minify([
|
||||
"./test/input/issue-520/input.js",
|
||||
"./test/input/issue-520/output.js"
|
||||
read("./test/input/issue-520/input.js"),
|
||||
read("./test/input/issue-520/output.js")
|
||||
], {
|
||||
inSourceMap: "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
|
||||
sourceMap: {
|
||||
content: "inline",
|
||||
url: "inline"
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -127,17 +131,16 @@ describe("minify", function() {
|
||||
describe("sourceMapInline", function() {
|
||||
it("should append source map to output js when sourceMapInline is enabled", function() {
|
||||
var result = Uglify.minify('var a = function(foo) { return foo; };', {
|
||||
fromString: true,
|
||||
sourceMapInline: true
|
||||
sourceMap: {
|
||||
url: "inline"
|
||||
}
|
||||
});
|
||||
var code = result.code;
|
||||
assert.strictEqual(code, "var a=function(n){return n};\n" +
|
||||
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIj8iXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsR0FBSUEsR0FBSSxTQUFTQyxHQUFPLE1BQU9BIn0=");
|
||||
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsR0FBSUEsR0FBSSxTQUFTQyxHQUFPLE1BQU9BIn0=");
|
||||
});
|
||||
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; };', {
|
||||
fromString: true
|
||||
});
|
||||
var result = Uglify.minify('var a = function(foo) { return foo; };');
|
||||
var code = result.code;
|
||||
assert.strictEqual(code, "var a=function(n){return n};");
|
||||
});
|
||||
@@ -146,7 +149,6 @@ describe("minify", function() {
|
||||
describe("#__PURE__", function() {
|
||||
it("should drop #__PURE__ hint after use", function() {
|
||||
var result = Uglify.minify('//@__PURE__ comment1 #__PURE__ comment2\n foo(), bar();', {
|
||||
fromString: true,
|
||||
output: {
|
||||
comments: "all",
|
||||
beautify: false,
|
||||
@@ -157,7 +159,6 @@ describe("minify", function() {
|
||||
});
|
||||
it("should not drop #__PURE__ hint if function is retained", function() {
|
||||
var result = Uglify.minify("var a = /*#__PURE__*/(function(){ foo(); })();", {
|
||||
fromString: true,
|
||||
output: {
|
||||
comments: "all",
|
||||
beautify: false,
|
||||
@@ -171,11 +172,11 @@ describe("minify", function() {
|
||||
describe("JS_Parse_Error", function() {
|
||||
it("should throw syntax error", function() {
|
||||
assert.throws(function() {
|
||||
Uglify.minify("function f(a{}", { fromString: true });
|
||||
Uglify.minify("function f(a{}");
|
||||
}, function(err) {
|
||||
assert.ok(err instanceof Error);
|
||||
assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token punc «{», expected punc «,»");
|
||||
assert.strictEqual(err.filename, 0);
|
||||
assert.strictEqual(err.filename, "0");
|
||||
assert.strictEqual(err.line, 1);
|
||||
assert.strictEqual(err.col, 12);
|
||||
return true;
|
||||
@@ -191,5 +192,4 @@ describe("minify", function() {
|
||||
assert.strictEqual(ast.print_to_string(), "function f(a){for(var i=0;i<a;i++)console.log(i)}");
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
@@ -34,7 +34,6 @@ describe("New", function() {
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.strictEqual(
|
||||
uglify.minify(tests[i], {
|
||||
fromString: true,
|
||||
output: {beautify: true},
|
||||
compress: false,
|
||||
mangle: false
|
||||
@@ -76,7 +75,6 @@ describe("New", function() {
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
assert.strictEqual(
|
||||
uglify.minify(tests[i], {
|
||||
fromString: true,
|
||||
output: {beautify: false},
|
||||
compress: false,
|
||||
mangle: false
|
||||
|
||||
@@ -42,8 +42,8 @@ describe("test/jetstream.js", function() {
|
||||
run("npm", ["install", "phantomjs-prebuilt@2.1.14"], done);
|
||||
});
|
||||
[
|
||||
"-mc warnings=false",
|
||||
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto,warnings=false",
|
||||
"-mc",
|
||||
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
|
||||
].forEach(function(options) {
|
||||
it("Should pass with options " + options, function(done) {
|
||||
var args = options.split(/ /);
|
||||
|
||||
@@ -13,9 +13,7 @@ describe("screw-ie8", function () {
|
||||
}\
|
||||
console.log('undefined is ' + undefined);\
|
||||
return b === undefined;\
|
||||
};", {
|
||||
fromString: true
|
||||
}
|
||||
};"
|
||||
).code,
|
||||
'function a(o){try{throw"Stuff"}catch(o){console.log("caught: "+o)}return console.log("undefined is "+void 0),void 0===o}'
|
||||
);
|
||||
|
||||
@@ -3,14 +3,14 @@ var exec = require("child_process").exec;
|
||||
var uglify = require("../../");
|
||||
|
||||
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);
|
||||
|
||||
var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs';
|
||||
var command = uglifyjs + " --self -cm --wrap SpiderUglify --dump-spidermonkey-ast | " +
|
||||
uglifyjs + " --spidermonkey -cm";
|
||||
var command = uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey | " +
|
||||
uglifyjs + " -p spidermonkey -cm";
|
||||
|
||||
exec(command, function (err, stdout) {
|
||||
exec(command, function(err, stdout) {
|
||||
if (err) throw err;
|
||||
|
||||
eval(stdout);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// derived from https://github.com/qfox/uglyfuzzer by Peter van der Zee
|
||||
"use strict";
|
||||
|
||||
// check both cli and file modes of nodejs (!). See #1695 for details. and the various settings of uglify.
|
||||
// check both CLI and file modes of nodejs (!). See #1695 for details. and the various settings of uglify.
|
||||
// bin/uglifyjs s.js -c && bin/uglifyjs s.js -c passes=3 && bin/uglifyjs s.js -c passes=3 -m
|
||||
// cat s.js | node && node s.js && bin/uglifyjs s.js -c | node && bin/uglifyjs s.js -c passes=3 | node && bin/uglifyjs s.js -c passes=3 -m | node
|
||||
|
||||
@@ -768,7 +768,6 @@ function createVarName(maybe, dontStore) {
|
||||
function try_beautify(code, result) {
|
||||
try {
|
||||
var beautified = UglifyJS.minify(code, {
|
||||
fromString: true,
|
||||
compress: false,
|
||||
mangle: false,
|
||||
output: {
|
||||
@@ -802,8 +801,8 @@ var default_options = {
|
||||
mangle: {
|
||||
"cache": null,
|
||||
"eval": false,
|
||||
"ie8": false,
|
||||
"keep_fnames": false,
|
||||
"screw_ie8": true,
|
||||
"toplevel": false,
|
||||
},
|
||||
output: infer_options(UglifyJS.OutputStream),
|
||||
@@ -869,10 +868,7 @@ function log(options) {
|
||||
}
|
||||
}
|
||||
|
||||
var minify_options = require("./ufuzz.json").map(function(options) {
|
||||
options.fromString = true;
|
||||
return JSON.stringify(options);
|
||||
});
|
||||
var minify_options = require("./ufuzz.json").map(JSON.stringify);
|
||||
var original_code, original_result;
|
||||
var uglify_code, uglify_result, ok;
|
||||
for (var round = 1; round <= num_iterations; round++) {
|
||||
|
||||
@@ -11,20 +11,12 @@
|
||||
"compress": false
|
||||
},
|
||||
{
|
||||
"compress": {
|
||||
"warnings": false
|
||||
},
|
||||
"mangle": false
|
||||
},
|
||||
{},
|
||||
{
|
||||
"compress": {
|
||||
"warnings": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"compress": {
|
||||
"toplevel": true,
|
||||
"warnings": false
|
||||
"toplevel": true
|
||||
},
|
||||
"mangle": {
|
||||
"toplevel": true
|
||||
@@ -33,8 +25,7 @@
|
||||
{
|
||||
"compress": {
|
||||
"keep_fargs": false,
|
||||
"passes": 3,
|
||||
"warnings": false
|
||||
"passes": 3
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"props": [
|
||||
[
|
||||
"$&",
|
||||
"$'",
|
||||
"$*",
|
||||
@@ -5599,5 +5598,4 @@
|
||||
"zoom",
|
||||
"zoomAndPan",
|
||||
"zoomRectScreen"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
exports["Compressor"] = Compressor;
|
||||
exports["DefaultsError"] = DefaultsError;
|
||||
exports["Dictionary"] = Dictionary;
|
||||
exports["JS_Parse_Error"] = JS_Parse_Error;
|
||||
exports["MAP"] = MAP;
|
||||
exports["OutputStream"] = OutputStream;
|
||||
exports["SourceMap"] = SourceMap;
|
||||
exports["TreeTransformer"] = TreeTransformer;
|
||||
exports["TreeWalker"] = TreeWalker;
|
||||
exports["base54"] = base54;
|
||||
exports["defaults"] = defaults;
|
||||
exports["mangle_properties"] = mangle_properties;
|
||||
exports["merge"] = merge;
|
||||
exports["minify"] = minify;
|
||||
exports["parse"] = parse;
|
||||
exports["push_uniq"] = push_uniq;
|
||||
exports["string_template"] = string_template;
|
||||
exports["tokenizer"] = tokenizer;
|
||||
exports["is_identifier"] = is_identifier;
|
||||
exports["SymbolDef"] = SymbolDef;
|
||||
|
||||
261
tools/node.js
261
tools/node.js
@@ -1,10 +1,3 @@
|
||||
// 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 UglifyJS = exports;
|
||||
@@ -19,6 +12,7 @@ var FILES = UglifyJS.FILES = [
|
||||
"../lib/sourcemap.js",
|
||||
"../lib/mozilla-ast.js",
|
||||
"../lib/propmangle.js",
|
||||
"../lib/minify.js",
|
||||
"./exports.js",
|
||||
].map(function(file){
|
||||
return require.resolve(file);
|
||||
@@ -31,168 +25,6 @@ new Function("MOZ_SourceMap", "exports", FILES.map(function(file){
|
||||
UglifyJS
|
||||
);
|
||||
|
||||
UglifyJS.AST_Node.warn_function = function(txt) {
|
||||
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) {
|
||||
@@ -227,94 +59,3 @@ UglifyJS.describe_ast = function() {
|
||||
doitem(UglifyJS.AST_Node);
|
||||
return out + "";
|
||||
};
|
||||
|
||||
function readReservedFile(filename, reserved) {
|
||||
if (!reserved) {
|
||||
reserved = { vars: [], props: [] };
|
||||
}
|
||||
var data = fs.readFileSync(filename, "utf8");
|
||||
data = JSON.parse(data);
|
||||
if (data.vars) {
|
||||
data.vars.forEach(function(name){
|
||||
UglifyJS.push_uniq(reserved.vars, name);
|
||||
});
|
||||
}
|
||||
if (data.props) {
|
||||
data.props.forEach(function(name){
|
||||
UglifyJS.push_uniq(reserved.props, name);
|
||||
});
|
||||
}
|
||||
return reserved;
|
||||
}
|
||||
|
||||
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 ];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user