Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
daaefc17b9 | ||
|
|
1d407e761e | ||
|
|
2b44f4ae30 | ||
|
|
e51c3541da | ||
|
|
3bf194684b | ||
|
|
aae7d49d0c | ||
|
|
0459af2ecc | ||
|
|
04f2344efc | ||
|
|
bad9d5cf88 | ||
|
|
a0f5f862df | ||
|
|
41996be86f | ||
|
|
5fd8244a2e | ||
|
|
c14e280585 | ||
|
|
bc3fa78e8c | ||
|
|
8c7c107765 | ||
|
|
3dd328dce3 | ||
|
|
014f428153 | ||
|
|
a3b2eb75bd | ||
|
|
da295de82b | ||
|
|
4f8ca4626e | ||
|
|
e54748365c | ||
|
|
2d99d06601 | ||
|
|
98cf95e5b5 |
10
.github/ISSUE_TEMPLATE.md
vendored
10
.github/ISSUE_TEMPLATE.md
vendored
@@ -6,15 +6,15 @@
|
||||
|
||||
<!-- Note: for ES6 see: https://github.com/mishoo/UglifyJS2/tree/harmony#harmony -->
|
||||
|
||||
**`uglify-js` version (`uglifyjs -V`)**
|
||||
**Uglify version (`uglifyjs -V`)**
|
||||
|
||||
**JavaScript input - ideally as small as possible.**
|
||||
**JavaScript input** <!-- ideally as small as possible -->
|
||||
|
||||
**The `uglifyjs` CLI command executed or `minify()` options used.**
|
||||
|
||||
**JavaScript output produced and/or the error or warning.**
|
||||
**JavaScript output or error produced.**
|
||||
|
||||
<!--
|
||||
Note: the release version of uglify-js only supports ES5. Those wishing
|
||||
to minify ES6 should use the experimental harmony branch.
|
||||
Note: `uglify-js` only supports ES5.
|
||||
Those wishing to minify ES6 should use `uglify-es`.
|
||||
-->
|
||||
|
||||
142
README.md
142
README.md
@@ -2,14 +2,14 @@ UglifyJS 3
|
||||
==========
|
||||
[](https://travis-ci.org/mishoo/UglifyJS2)
|
||||
|
||||
UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit.
|
||||
UglifyJS is a JavaScript parser, minifier, compressor and beautifier toolkit.
|
||||
|
||||
#### Note:
|
||||
- **`uglify-js@3.x` has a new API and CLI and is not backwards compatible with [`uglify-js@2.x`](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
|
||||
- **Documentation for UglifyJS `2.x` releases can be found [here](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
|
||||
- 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.
|
||||
- Node 7 has a known performance regression and runs `uglify-js` twice as slow.
|
||||
- `uglify-js` only supports ECMAScript 5 (ES5).
|
||||
- Those wishing to minify
|
||||
ES2015+ (ES6+) should use the `npm` package [**uglify-es**](https://github.com/mishoo/UglifyJS2/tree/harmony).
|
||||
|
||||
Install
|
||||
-------
|
||||
@@ -25,12 +25,6 @@ From NPM for programmatic use:
|
||||
|
||||
npm install uglify-js
|
||||
|
||||
From Git:
|
||||
|
||||
git clone git://github.com/mishoo/UglifyJS2.git
|
||||
cd UglifyJS2
|
||||
npm link .
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
@@ -42,8 +36,7 @@ in sequence and apply any compression options. The files are parsed in the
|
||||
same global scope, that is, a reference from a file to some
|
||||
variable/function declared in another file will be matched properly.
|
||||
|
||||
If you want to read from STDIN instead, pass a single dash instead of input
|
||||
files.
|
||||
If no input file is specified, UglifyJS will read from STDIN.
|
||||
|
||||
If you wish to pass your options before the input files, separate the two with
|
||||
a double dash to prevent input files being used as option arguments:
|
||||
@@ -96,8 +89,9 @@ The available options are:
|
||||
`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.
|
||||
-o, --output <file> Output file path (default STDOUT). Specify `ast` or
|
||||
`spidermonkey` to write UglifyJS or SpiderMonkey AST
|
||||
as JSON to STDOUT respectively.
|
||||
--comments [filter] Preserve copyright comments in the output. By
|
||||
default this works like Google Closure, keeping
|
||||
JSDoc-style comments that contain "@license" or
|
||||
@@ -155,16 +149,21 @@ debugging your compressed JavaScript. To get a source map, pass
|
||||
`--source-map --output output.js` (source map will be written out to
|
||||
`output.js.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.
|
||||
Additional options:
|
||||
|
||||
- `--source-map filename=<NAME>` to specify the name of the source map.
|
||||
|
||||
- `--source-map root=<URL>` to pass the URL where the original files can be found.
|
||||
Otherwise UglifyJS assumes HTTP `X-SourceMap` is being used and will omit the
|
||||
`//# sourceMappingURL=` directive.
|
||||
|
||||
- `--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 \
|
||||
uglifyjs js/file1.js js/file2.js \
|
||||
-o foo.min.js -c -m \
|
||||
--source-map base="/home/doe/work/foo/src",root="http://foo.com/src"
|
||||
--source-map root="http://foo.com/src",url=foo.min.js.map
|
||||
|
||||
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
|
||||
@@ -183,11 +182,9 @@ CoffeeScript → compiled JS, UglifyJS can generate a map from CoffeeScript →
|
||||
compressed JS by mapping every token in the compiled JS to its original
|
||||
location.
|
||||
|
||||
To use this feature you need to pass `--in-source-map
|
||||
/path/to/input/source.map` or `--in-source-map inline` if the source map is
|
||||
included inline with the sources. Normally the input source map should also
|
||||
point to the file containing the generated JS, so if that's correct you can
|
||||
omit input files from the command line.
|
||||
To use this feature pass `--source-map content="/path/to/input/source.map"`
|
||||
or `--source-map content=inline` if the source map is included inline with
|
||||
the sources.
|
||||
|
||||
## Mangler options
|
||||
|
||||
@@ -470,7 +467,7 @@ You can also use conditional compilation via the programmatic API. With the diff
|
||||
property name is `global_defs` and is a compressor property:
|
||||
|
||||
```js
|
||||
uglifyJS.minify([ "input.js"], {
|
||||
uglifyJS.minify(fs.readFileSync("input.js", "utf8"), {
|
||||
compress: {
|
||||
dead_code: true,
|
||||
global_defs: {
|
||||
@@ -490,21 +487,21 @@ can pass additional arguments that control the code output:
|
||||
Passing `-b` will set this to true, but you might need to pass `-b` even
|
||||
when you want to generate minified code, in order to specify additional
|
||||
arguments, so you can use `-b beautify=false` to override it.
|
||||
- `indent-level` (default 4)
|
||||
- `indent-start` (default 0) -- prefix all lines by that many spaces
|
||||
- `quote-keys` (default `false`) -- pass `true` to quote all keys in literal
|
||||
- `indent_level` (default 4)
|
||||
- `indent_start` (default 0) -- prefix all lines by that many spaces
|
||||
- `quote_keys` (default `false`) -- pass `true` to quote all keys in literal
|
||||
objects
|
||||
- `space-colon` (default `true`) -- insert a space after the colon signs
|
||||
- `ascii-only` (default `false`) -- escape Unicode characters in strings and
|
||||
- `space_colon` (default `true`) -- insert a space after the colon signs
|
||||
- `ascii_only` (default `false`) -- escape Unicode characters in strings and
|
||||
regexps (affects directives with non-ascii characters becoming invalid)
|
||||
- `inline-script` (default `false`) -- escape the slash in occurrences of
|
||||
- `inline_script` (default `false`) -- escape the slash in occurrences of
|
||||
`</script` in strings
|
||||
- `width` (default 80) -- only takes effect when beautification is on, this
|
||||
specifies an (orientative) line width that the beautifier will try to
|
||||
obey. It refers to the width of the line text (excluding indentation).
|
||||
It doesn't work very well currently, but it does make the code generated
|
||||
by UglifyJS more readable.
|
||||
- `max-line-len` (default 32000) -- maximum line length (for uglified code)
|
||||
- `max_line_len` (default 32000) -- maximum line length (for uglified code)
|
||||
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
|
||||
`do`, `while` or `with` statements, even if their body is a single
|
||||
statement.
|
||||
@@ -596,7 +593,8 @@ performs all the steps in a configurable manner.
|
||||
Example:
|
||||
```javascript
|
||||
var result = UglifyJS.minify("var b = function() {};");
|
||||
console.log(result.code); // minified output
|
||||
console.log(result.code); // minified output
|
||||
console.log(result.error); // runtime error
|
||||
```
|
||||
|
||||
You can also compress multiple files:
|
||||
@@ -675,61 +673,47 @@ Other options:
|
||||
|
||||
##### mangle
|
||||
|
||||
- `reserved` - 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).
|
||||
- `toplevel` — mangle names declared in the toplevel scope (disabled by
|
||||
default).
|
||||
|
||||
- `eval` — mangle names visible in scopes where eval or with are used
|
||||
(disabled by default).
|
||||
- `eval` — mangle names visible in scopes where eval or with are used
|
||||
(disabled by default).
|
||||
|
||||
- `keep_fnames` -- default `false`. Pass `true` to not mangle
|
||||
function names. Useful for code relying on `Function.prototype.name`.
|
||||
See also: the `keep_fnames` [compress option](#compressor-options).
|
||||
- `keep_fnames` -- default `false`. Pass `true` to not mangle
|
||||
function names. Useful for code relying on `Function.prototype.name`.
|
||||
See also: the `keep_fnames` [compress option](#compressor-options).
|
||||
|
||||
Examples:
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
//tst.js
|
||||
var globalVar;
|
||||
function funcName(firstLongName, anotherLongName)
|
||||
{
|
||||
```javascript
|
||||
// test.js
|
||||
var globalVar;
|
||||
function funcName(firstLongName, anotherLongName)
|
||||
{
|
||||
var myVariable = firstLongName + anotherLongName;
|
||||
}
|
||||
}
|
||||
```
|
||||
```javascript
|
||||
var code = fs.readFileSync("test.js", "utf8");
|
||||
|
||||
UglifyJS.minify("tst.js").code;
|
||||
// 'function funcName(a,n){}var globalVar;'
|
||||
UglifyJS.minify(code).code;
|
||||
// 'function funcName(a,n){}var globalVar;'
|
||||
|
||||
UglifyJS.minify("tst.js", { mangle: { reserved: ['firstLongName'] } }).code;
|
||||
// 'function funcName(firstLongName,a){}var globalVar;'
|
||||
UglifyJS.minify(code, { mangle: { reserved: ['firstLongName'] } }).code;
|
||||
// 'function funcName(firstLongName,a){}var globalVar;'
|
||||
|
||||
UglifyJS.minify("tst.js", { mangle: { toplevel: true } }).code;
|
||||
// 'function n(n,a){}var a;'
|
||||
```
|
||||
UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
|
||||
// 'function n(n,a){}var a;'
|
||||
```
|
||||
|
||||
##### mangle.properties options
|
||||
|
||||
- `regex` — Pass a RegExp to only mangle certain names
|
||||
- `keep_quoted` — Only mangle unquoted property names
|
||||
- `debug` — Mangle names with the original name still present. Defaults to `false`.
|
||||
Pass an empty string to enable, or a non-empty string to set the suffix.
|
||||
- `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
|
||||
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k
|
||||
|
||||
#### Harmony
|
||||
|
||||
If you wish to use the experimental [harmony](https://github.com/mishoo/UglifyJS2/commits/harmony)
|
||||
branch to minify ES2015+ (ES6+) code please use the following in your `package.json` file:
|
||||
|
||||
```
|
||||
"uglify-js": "git+https://github.com/mishoo/UglifyJS2.git#harmony"
|
||||
```
|
||||
|
||||
or to directly install the experimental harmony version of uglify:
|
||||
|
||||
```
|
||||
npm install --save-dev uglify-js@github:mishoo/UglifyJS2#harmony
|
||||
```
|
||||
|
||||
See [#448](https://github.com/mishoo/UglifyJS2/issues/448) for additional details.
|
||||
[acorn]: https://github.com/ternjs/acorn
|
||||
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k
|
||||
|
||||
74
bin/uglifyjs
74
bin/uglifyjs
@@ -15,6 +15,7 @@ var path = require("path");
|
||||
var program = require("commander");
|
||||
var UglifyJS = require("../tools/node");
|
||||
|
||||
var skip_keys = [ "cname", "enclosed", "parent_scope", "scope", "thedef", "uses_eval", "uses_with" ];
|
||||
var files = {};
|
||||
var options = {
|
||||
compress: false,
|
||||
@@ -89,7 +90,7 @@ if (program.mangleProps) {
|
||||
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);
|
||||
UglifyJS._push_uniq(program.mangleProps.reserved, name);
|
||||
});
|
||||
}
|
||||
if (typeof options.mangle != "object") options.mangle = {};
|
||||
@@ -107,6 +108,12 @@ if (program.nameCache) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (program.output == "ast") {
|
||||
options.output = {
|
||||
ast: true,
|
||||
code: false
|
||||
};
|
||||
}
|
||||
if (program.parse) {
|
||||
if (program.parse.acorn || program.parse.spidermonkey) {
|
||||
if (program.sourceMap) fatal("ERROR: inline source map only works with built-in parser");
|
||||
@@ -165,7 +172,7 @@ function run() {
|
||||
UglifyJS.AST_Node.warn_function = function(msg) {
|
||||
console.error("WARN:", msg);
|
||||
};
|
||||
if (program.stats) program.stats = Date.now();
|
||||
if (program.stats) program.stats = Date.now();
|
||||
try {
|
||||
if (program.parse) {
|
||||
if (program.parse.acorn) {
|
||||
@@ -185,9 +192,13 @@ function run() {
|
||||
});
|
||||
}
|
||||
}
|
||||
var result = UglifyJS.minify(files, options);
|
||||
} catch (ex) {
|
||||
if (ex instanceof UglifyJS.JS_Parse_Error) {
|
||||
fatal(ex);
|
||||
}
|
||||
var result = UglifyJS.minify(files, options);
|
||||
if (result.error) {
|
||||
var ex = result.error;
|
||||
if (ex.name == "SyntaxError") {
|
||||
console.error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
|
||||
var col = ex.col;
|
||||
var lines = files[ex.filename].split(/\r?\n/);
|
||||
@@ -209,10 +220,32 @@ function run() {
|
||||
console.error("Supported options:");
|
||||
console.error(ex.defs);
|
||||
}
|
||||
fatal("ERROR: " + ex.message);
|
||||
}
|
||||
if (program.output == "spidermonkey") {
|
||||
console.log(JSON.stringify(UglifyJS.parse(result.code).to_mozilla_ast(), null, 2));
|
||||
fatal(ex);
|
||||
} else if (program.output == "ast") {
|
||||
console.log(JSON.stringify(result.ast, function(key, value) {
|
||||
if (skip_key(key)) return;
|
||||
if (value instanceof UglifyJS.AST_Token) return;
|
||||
if (value instanceof UglifyJS.Dictionary) return;
|
||||
if (value instanceof UglifyJS.AST_Node) {
|
||||
var result = {
|
||||
_class: "AST_" + value.TYPE
|
||||
};
|
||||
value.CTOR.PROPS.forEach(function(prop) {
|
||||
result[prop] = value[prop];
|
||||
});
|
||||
return result;
|
||||
}
|
||||
return value;
|
||||
}, 2));
|
||||
} else if (program.output == "spidermonkey") {
|
||||
console.log(JSON.stringify(UglifyJS.minify(result.code, {
|
||||
compress: false,
|
||||
mangle: false,
|
||||
output: {
|
||||
ast: true,
|
||||
code: false
|
||||
}
|
||||
}).ast.to_mozilla_ast(), null, 2));
|
||||
} else if (program.output) {
|
||||
fs.writeFileSync(program.output, result.code);
|
||||
if (result.map) {
|
||||
@@ -230,8 +263,9 @@ function run() {
|
||||
}
|
||||
|
||||
function fatal(message) {
|
||||
console.error(message);
|
||||
process.exit(1);
|
||||
if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:")
|
||||
console.error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// A file glob function that only supports "*" and "?" wildcards in the basename.
|
||||
@@ -270,7 +304,7 @@ function read_file(path, default_value) {
|
||||
return fs.readFileSync(path, "utf8");
|
||||
} catch (ex) {
|
||||
if (ex.code == "ENOENT" && default_value != null) return default_value;
|
||||
fatal("ERROR: " + ex.message);
|
||||
fatal(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,9 +312,17 @@ function parse_js(flag, constants) {
|
||||
return function(value, options) {
|
||||
options = options || {};
|
||||
try {
|
||||
UglifyJS.parse(value, {
|
||||
expression: true
|
||||
}).walk(new UglifyJS.TreeWalker(function(node) {
|
||||
UglifyJS.minify(value, {
|
||||
parse: {
|
||||
expression: true
|
||||
},
|
||||
compress: false,
|
||||
mangle: false,
|
||||
output: {
|
||||
ast: true,
|
||||
code: false
|
||||
}
|
||||
}).ast.walk(new UglifyJS.TreeWalker(function(node) {
|
||||
if (node instanceof UglifyJS.AST_Assign) {
|
||||
var name = node.left.print_to_string();
|
||||
var value = node.right;
|
||||
@@ -337,3 +379,7 @@ function to_cache(key) {
|
||||
}
|
||||
return cache[key];
|
||||
}
|
||||
|
||||
function skip_key(key) {
|
||||
return skip_keys.indexOf(key) >= 0;
|
||||
}
|
||||
|
||||
@@ -619,7 +619,7 @@ var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
|
||||
$documentation: "Unary postfix expression, i.e. `i++`"
|
||||
}, AST_Unary);
|
||||
|
||||
var AST_Binary = DEFNODE("Binary", "left operator right", {
|
||||
var AST_Binary = DEFNODE("Binary", "operator left right", {
|
||||
$documentation: "Binary expression, i.e. `a + b`",
|
||||
$propdoc: {
|
||||
left: "[AST_Node] left-hand side expression",
|
||||
|
||||
@@ -525,6 +525,14 @@ merge(Compressor.prototype, {
|
||||
return lhs instanceof AST_SymbolRef && lhs.definition().orig[0] instanceof AST_SymbolLambda;
|
||||
}
|
||||
|
||||
function is_reference_const(ref) {
|
||||
if (!(ref instanceof AST_SymbolRef)) return false;
|
||||
var orig = ref.definition().orig;
|
||||
for (var i = orig.length; --i >= 0;) {
|
||||
if (orig[i] instanceof AST_SymbolConst) return true;
|
||||
}
|
||||
}
|
||||
|
||||
function find_variable(compressor, name) {
|
||||
var scope, i = 0;
|
||||
while (scope = compressor.parent(i++)) {
|
||||
@@ -790,7 +798,8 @@ merge(Compressor.prototype, {
|
||||
return make_node(AST_SymbolRef, expr.name, expr.name);
|
||||
}
|
||||
} else {
|
||||
return expr[expr instanceof AST_Assign ? "left" : "expression"];
|
||||
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
|
||||
return !is_reference_const(lhs) && lhs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1973,6 +1982,7 @@ merge(Compressor.prototype, {
|
||||
&& node instanceof AST_Assign
|
||||
&& node.operator == "="
|
||||
&& node.left instanceof AST_SymbolRef
|
||||
&& !is_reference_const(node.left)
|
||||
&& scope === self) {
|
||||
node.right.walk(tw);
|
||||
return true;
|
||||
@@ -3178,7 +3188,7 @@ merge(Compressor.prototype, {
|
||||
&& (left.operator == "++" || left.operator == "--")) {
|
||||
left = left.expression;
|
||||
} else left = null;
|
||||
if (!left || is_lhs_read_only(left)) {
|
||||
if (!left || is_lhs_read_only(left) || is_reference_const(left)) {
|
||||
expressions[++i] = cdr;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -108,38 +108,46 @@ function minify(files, options) {
|
||||
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 result = {};
|
||||
if (options.output.ast) {
|
||||
result.ast = toplevel;
|
||||
}
|
||||
if (!HOP(options.output, "code") || options.output.code) {
|
||||
if (options.sourceMap) {
|
||||
if (typeof options.sourceMap.content == "string") {
|
||||
options.sourceMap.content = JSON.parse(options.sourceMap.content);
|
||||
}
|
||||
options.output.source_map = SourceMap({
|
||||
file: options.sourceMap.filename,
|
||||
orig: options.sourceMap.content,
|
||||
root: options.sourceMap.root
|
||||
});
|
||||
if (options.sourceMap.includeSources) {
|
||||
for (var name in files) {
|
||||
options.output.source_map.get().setSourceContent(name, files[name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
delete options.output.ast;
|
||||
delete options.output.code;
|
||||
var stream = OutputStream(options.output);
|
||||
toplevel.print(stream);
|
||||
result.code = stream.get();
|
||||
if (options.sourceMap) {
|
||||
result.map = options.output.source_map.toString();
|
||||
if (options.sourceMap.url == "inline") {
|
||||
result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(result.map);
|
||||
} else if (options.sourceMap.url) {
|
||||
result.code += "\n//# sourceMappingURL=" + options.sourceMap.url;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (warnings.length) {
|
||||
result.warnings = warnings;
|
||||
}
|
||||
return result;
|
||||
} catch (ex) {
|
||||
return { error: ex };
|
||||
} finally {
|
||||
AST_Node.warn_function = warn_function;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"homepage": "http://lisperator.net/uglifyjs",
|
||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||
"license": "BSD-2-Clause",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.4",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
|
||||
@@ -2186,3 +2186,49 @@ compound_assignment: {
|
||||
}
|
||||
expect_stdout: "4"
|
||||
}
|
||||
|
||||
reassign_const_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
const a = 1;
|
||||
a = 2;
|
||||
return a;
|
||||
}
|
||||
console.log(f());
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
const a = 1;
|
||||
a = 2;
|
||||
return a;
|
||||
}
|
||||
console.log(f());
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
reassign_const_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
const a = 1;
|
||||
++a;
|
||||
return a;
|
||||
}
|
||||
console.log(f());
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
const a = 1;
|
||||
++a;
|
||||
return a;
|
||||
}
|
||||
console.log(f());
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
@@ -1147,3 +1147,28 @@ var_catch_toplevel: {
|
||||
}();
|
||||
}
|
||||
}
|
||||
|
||||
reassign_const: {
|
||||
options = {
|
||||
cascade: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
const a = 1;
|
||||
a = 2;
|
||||
return a;
|
||||
}
|
||||
console.log(f());
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
const a = 1;
|
||||
return a = 2, a;
|
||||
}
|
||||
console.log(f());
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
@@ -2440,8 +2440,8 @@ issue_1865: {
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
function f(a) {
|
||||
a.b = false;
|
||||
function f(some) {
|
||||
some.thing = false;
|
||||
}
|
||||
console.log(function() {
|
||||
var some = { thing: true };
|
||||
@@ -2450,8 +2450,8 @@ issue_1865: {
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
function f(a) {
|
||||
a.b = false;
|
||||
function f(some) {
|
||||
some.thing = false;
|
||||
}
|
||||
console.log(function() {
|
||||
var some = { thing: true };
|
||||
|
||||
@@ -710,3 +710,27 @@ issue_27: {
|
||||
})(jQuery);
|
||||
}
|
||||
}
|
||||
|
||||
reassign_const: {
|
||||
options = {
|
||||
cascade: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
const a = 1;
|
||||
a++;
|
||||
return a;
|
||||
}
|
||||
console.log(f());
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
const a = 1;
|
||||
return a++, a;
|
||||
}
|
||||
console.log(f());
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
13
test/exports.js
Normal file
13
test/exports.js
Normal file
@@ -0,0 +1,13 @@
|
||||
exports["Compressor"] = Compressor;
|
||||
exports["JS_Parse_Error"] = JS_Parse_Error;
|
||||
exports["OutputStream"] = OutputStream;
|
||||
exports["SourceMap"] = SourceMap;
|
||||
exports["TreeWalker"] = TreeWalker;
|
||||
exports["base54"] = base54;
|
||||
exports["defaults"] = defaults;
|
||||
exports["mangle_properties"] = mangle_properties;
|
||||
exports["minify"] = minify;
|
||||
exports["parse"] = parse;
|
||||
exports["string_template"] = string_template;
|
||||
exports["tokenizer"] = tokenizer;
|
||||
exports["is_identifier"] = is_identifier;
|
||||
@@ -1,4 +1,4 @@
|
||||
var UglifyJS = require('../../');
|
||||
var UglifyJS = require("../node");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("Accessor tokens", function() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var UglifyJS = require('../../');
|
||||
var UglifyJS = require("../node");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("arguments", function() {
|
||||
|
||||
@@ -19,7 +19,7 @@ describe("bin/uglifyjs", function () {
|
||||
eval(stdout);
|
||||
|
||||
assert.strictEqual(typeof WrappedUglifyJS, 'object');
|
||||
assert.strictEqual(true, WrappedUglifyJS.parse('foo;') instanceof WrappedUglifyJS.AST_Node);
|
||||
assert.strictEqual(WrappedUglifyJS.minify("foo([true,,2+3]);").code, "foo([!0,,5]);");
|
||||
|
||||
done();
|
||||
});
|
||||
@@ -185,7 +185,7 @@ describe("bin/uglifyjs", function () {
|
||||
|
||||
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.split(/\n/)[0], "ERROR: inline source map only works with singular input");
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -509,4 +509,15 @@ describe("bin/uglifyjs", function () {
|
||||
return JSON.stringify(map).replace(/"/g, '\\"');
|
||||
}
|
||||
});
|
||||
it("Should dump AST as JSON", function(done) {
|
||||
var command = uglifyjscmd + " test/input/global_defs/simple.js -mco ast";
|
||||
exec(command, function (err, stdout) {
|
||||
if (err) throw err;
|
||||
|
||||
var ast = JSON.parse(stdout);
|
||||
assert.strictEqual(ast._class, "AST_Toplevel");
|
||||
assert.ok(Array.isArray(ast.body));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var UglifyJS = require('../../');
|
||||
var UglifyJS = require("../node");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("comment filters", function() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var assert = require("assert");
|
||||
var uglify = require("../../");
|
||||
var uglify = require("../node");
|
||||
|
||||
describe("Comment", function() {
|
||||
it("Should recognize eol of single line comments", function() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var assert = require("assert");
|
||||
var uglify = require("../../");
|
||||
var uglify = require("../node");
|
||||
|
||||
describe("Directives", function() {
|
||||
it ("Should allow tokenizer to store directives state", function() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var UglifyJS = require('../../');
|
||||
var UglifyJS = require("../node");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("Getters and setters", function() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var Uglify = require('../../');
|
||||
var Uglify = require("../node");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("line-endings", function() {
|
||||
|
||||
@@ -114,17 +114,18 @@ describe("minify", function() {
|
||||
}
|
||||
});
|
||||
it("Should fail with multiple input and inline source map", function() {
|
||||
assert.throws(function() {
|
||||
Uglify.minify([
|
||||
read("./test/input/issue-520/input.js"),
|
||||
read("./test/input/issue-520/output.js")
|
||||
], {
|
||||
sourceMap: {
|
||||
content: "inline",
|
||||
url: "inline"
|
||||
}
|
||||
});
|
||||
var result = Uglify.minify([
|
||||
read("./test/input/issue-520/input.js"),
|
||||
read("./test/input/issue-520/output.js")
|
||||
], {
|
||||
sourceMap: {
|
||||
content: "inline",
|
||||
url: "inline"
|
||||
}
|
||||
});
|
||||
var err = result.error;
|
||||
assert.ok(err instanceof Error);
|
||||
assert.strictEqual(err.stack.split(/\n/)[0], "Error: inline source map only works with singular input");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -170,26 +171,14 @@ describe("minify", function() {
|
||||
});
|
||||
|
||||
describe("JS_Parse_Error", function() {
|
||||
it("should throw syntax error", function() {
|
||||
assert.throws(function() {
|
||||
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.line, 1);
|
||||
assert.strictEqual(err.col, 12);
|
||||
return true;
|
||||
});
|
||||
it("should return syntax error", function() {
|
||||
var result = Uglify.minify("function f(a{}");
|
||||
var err = result.error;
|
||||
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.line, 1);
|
||||
assert.strictEqual(err.col, 12);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Compressor", function() {
|
||||
it("should be backward compatible with ast.transform(compressor)", function() {
|
||||
var ast = Uglify.parse("function f(a){for(var i=0;i<a;i++)console.log(i)}");
|
||||
ast.figure_out_scope();
|
||||
ast = ast.transform(Uglify.Compressor());
|
||||
assert.strictEqual(ast.print_to_string(), "function f(a){for(var i=0;i<a;i++)console.log(i)}");
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var assert = require("assert");
|
||||
var uglify = require("../../");
|
||||
var uglify = require("../node");
|
||||
|
||||
describe("Number literals", function () {
|
||||
it("Should not allow legacy octal literals in strict mode", function() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var UglifyJS = require("../../");
|
||||
var UglifyJS = require("../node");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("operator", function() {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
var assert = require("assert");
|
||||
var exec = require("child_process").exec;
|
||||
var uglify = require("../../");
|
||||
var uglify = require("../node");
|
||||
|
||||
describe("spidermonkey export/import sanity test", function() {
|
||||
it("should produce a functional build when using --self with spidermonkey", function(done) {
|
||||
this.timeout(20000);
|
||||
this.timeout(30000);
|
||||
|
||||
var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs';
|
||||
var command = uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey | " +
|
||||
@@ -15,18 +15,7 @@ describe("spidermonkey export/import sanity test", function() {
|
||||
|
||||
eval(stdout);
|
||||
assert.strictEqual(typeof SpiderUglify, "object");
|
||||
|
||||
var ast = SpiderUglify.parse("foo([true,,2+3]);");
|
||||
assert.strictEqual(true, ast instanceof SpiderUglify.AST_Node);
|
||||
|
||||
ast.figure_out_scope();
|
||||
ast = SpiderUglify.Compressor({}).compress(ast);
|
||||
assert.strictEqual(true, ast instanceof SpiderUglify.AST_Node);
|
||||
|
||||
var stream = SpiderUglify.OutputStream({});
|
||||
ast.print(stream);
|
||||
var code = stream.toString();
|
||||
assert.strictEqual(code, "foo([!0,,5]);");
|
||||
assert.strictEqual(SpiderUglify.minify("foo([true,,2+3]);").code, "foo([!0,,5]);");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var UglifyJS = require('../../');
|
||||
var UglifyJS = require("../node");
|
||||
var assert = require("assert");
|
||||
|
||||
describe("String literals", function() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var assert = require("assert");
|
||||
var uglify = require("../../");
|
||||
var uglify = require("../node");
|
||||
|
||||
describe("With", function() {
|
||||
it("Should throw syntaxError when using with statement in strict mode", function() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Testing UglifyJS <-> SpiderMonkey AST conversion
|
||||
// through generative testing.
|
||||
|
||||
var UglifyJS = require(".."),
|
||||
var UglifyJS = require("./node"),
|
||||
escodegen = require("escodegen"),
|
||||
esfuzz = require("esfuzz"),
|
||||
estraverse = require("estraverse"),
|
||||
|
||||
6
test/node.js
Normal file
6
test/node.js
Normal file
@@ -0,0 +1,6 @@
|
||||
var fs = require("fs");
|
||||
|
||||
new Function("MOZ_SourceMap", "exports", require("../tools/node").FILES.map(function(file) {
|
||||
if (/exports\.js$/.test(file)) file = require.resolve("./exports");
|
||||
return fs.readFileSync(file, "utf8");
|
||||
}).join("\n\n"))(require("source-map"), exports);
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /usr/bin/env node
|
||||
|
||||
var U = require("../tools/node");
|
||||
var U = require("./node");
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
var assert = require("assert");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var UglifyJS = require("..");
|
||||
var UglifyJS = require("./node");
|
||||
var ok = require("assert");
|
||||
|
||||
module.exports = function () {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
stream._handle.setBlocking(true);
|
||||
});
|
||||
|
||||
var UglifyJS = require("..");
|
||||
var UglifyJS = require("./node");
|
||||
var randomBytes = require("crypto").randomBytes;
|
||||
var sandbox = require("./sandbox");
|
||||
|
||||
@@ -856,23 +856,21 @@ function createVarName(maybe, dontStore) {
|
||||
}
|
||||
|
||||
function try_beautify(code, result) {
|
||||
try {
|
||||
var beautified = UglifyJS.minify(code, {
|
||||
compress: false,
|
||||
mangle: false,
|
||||
output: {
|
||||
beautify: true,
|
||||
bracketize: true,
|
||||
},
|
||||
}).code;
|
||||
if (sandbox.same_stdout(sandbox.run_code(beautified), result)) {
|
||||
console.log("// (beautified)");
|
||||
console.log(beautified);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
var beautified = UglifyJS.minify(code, {
|
||||
compress: false,
|
||||
mangle: false,
|
||||
output: {
|
||||
beautify: true,
|
||||
bracketize: true,
|
||||
},
|
||||
});
|
||||
if (beautified.error) {
|
||||
console.log("// !!! beautify failed !!!");
|
||||
console.log(e.stack);
|
||||
console.log(beautified.error.stack);
|
||||
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) {
|
||||
console.log("// (beautified)");
|
||||
console.log(beautified.code);
|
||||
return;
|
||||
}
|
||||
console.log("//");
|
||||
console.log(code);
|
||||
@@ -908,12 +906,13 @@ function log_suspects(minify_options, component) {
|
||||
var o = JSON.parse(JSON.stringify(options));
|
||||
o[name] = false;
|
||||
m[component] = o;
|
||||
try {
|
||||
var r = sandbox.run_code(UglifyJS.minify(original_code, m).code);
|
||||
return sandbox.same_stdout(original_result, r);
|
||||
} catch (e) {
|
||||
var result = UglifyJS.minify(original_code, m);
|
||||
if (result.error) {
|
||||
console.log("Error testing options." + component + "." + name);
|
||||
console.log(e);
|
||||
console.log(result.error);
|
||||
} else {
|
||||
var r = sandbox.run_code(result.code);
|
||||
return sandbox.same_stdout(original_result, r);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -981,18 +980,16 @@ for (var round = 1; round <= num_iterations; round++) {
|
||||
original_code = createTopLevelCode();
|
||||
original_result = sandbox.run_code(original_code);
|
||||
(typeof original_result != "string" ? fallback_options : minify_options).forEach(function(options) {
|
||||
try {
|
||||
uglify_code = UglifyJS.minify(original_code, JSON.parse(options)).code;
|
||||
} catch (e) {
|
||||
uglify_code = e;
|
||||
}
|
||||
|
||||
ok = typeof uglify_code == "string";
|
||||
if (ok) {
|
||||
uglify_code = UglifyJS.minify(original_code, JSON.parse(options));
|
||||
if (!uglify_code.error) {
|
||||
uglify_code = uglify_code.code;
|
||||
uglify_result = sandbox.run_code(uglify_code);
|
||||
ok = sandbox.same_stdout(original_result, uglify_result);
|
||||
} else if (typeof original_result != "string") {
|
||||
ok = uglify_code.name == original_result.name;
|
||||
} else {
|
||||
uglify_code = uglify_code.error;
|
||||
if (typeof original_result != "string") {
|
||||
ok = uglify_code.name == original_result.name;
|
||||
}
|
||||
}
|
||||
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
|
||||
else if (verbose_error && typeof original_result != "string") {
|
||||
|
||||
@@ -1,15 +1,4 @@
|
||||
exports["Compressor"] = Compressor;
|
||||
exports["Dictionary"] = Dictionary;
|
||||
exports["JS_Parse_Error"] = JS_Parse_Error;
|
||||
exports["OutputStream"] = OutputStream;
|
||||
exports["SourceMap"] = SourceMap;
|
||||
exports["TreeWalker"] = TreeWalker;
|
||||
exports["base54"] = base54;
|
||||
exports["defaults"] = defaults;
|
||||
exports["mangle_properties"] = mangle_properties;
|
||||
exports["minify"] = minify;
|
||||
exports["parse"] = parse;
|
||||
exports["push_uniq"] = push_uniq;
|
||||
exports["string_template"] = string_template;
|
||||
exports["tokenizer"] = tokenizer;
|
||||
exports["is_identifier"] = is_identifier;
|
||||
exports["_push_uniq"] = push_uniq;
|
||||
|
||||
@@ -18,15 +18,19 @@ var FILES = UglifyJS.FILES = [
|
||||
return require.resolve(file);
|
||||
});
|
||||
|
||||
new Function("MOZ_SourceMap", "exports", FILES.map(function(file){
|
||||
return fs.readFileSync(file, "utf8");
|
||||
}).join("\n\n"))(
|
||||
new Function("MOZ_SourceMap", "exports", function() {
|
||||
var code = FILES.map(function(file) {
|
||||
return fs.readFileSync(file, "utf8");
|
||||
});
|
||||
code.push("exports.describe_ast = " + describe_ast.toString());
|
||||
return code.join("\n\n");
|
||||
}())(
|
||||
require("source-map"),
|
||||
UglifyJS
|
||||
);
|
||||
|
||||
UglifyJS.describe_ast = function() {
|
||||
var out = UglifyJS.OutputStream({ beautify: true });
|
||||
function describe_ast() {
|
||||
var out = OutputStream({ beautify: true });
|
||||
function doitem(ctor) {
|
||||
out.print("AST_" + ctor.TYPE);
|
||||
var props = ctor.SELF_PROPS.filter(function(prop){
|
||||
@@ -56,6 +60,6 @@ UglifyJS.describe_ast = function() {
|
||||
});
|
||||
}
|
||||
};
|
||||
doitem(UglifyJS.AST_Node);
|
||||
doitem(AST_Node);
|
||||
return out + "";
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user