Compare commits

..

1 Commits

Author SHA1 Message Date
Mihai Bazon
c7d3c07f5a v2.4.17 2015-03-11 00:03:12 +02:00
20 changed files with 179 additions and 6676 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,2 @@
/node_modules/
/npm-debug.log
tmp/ tmp/
node_modules/

View File

@@ -1,10 +1,6 @@
language: node_js language: node_js
before_install: "npm install -g npm" before_install: "npm install -g npm"
node_js: node_js:
- "iojs" - "0.8"
- "0.12"
- "0.11"
- "0.10" - "0.10"
matrix: - "0.11"
fast_finish: true
sudo: false

228
README.md
View File

@@ -52,88 +52,70 @@ a double dash to prevent input files being used as option arguments:
The available options are: The available options are:
``` ```
--source-map Specify an output file where to generate source --source-map Specify an output file where to generate source map.
map. [string]
--source-map-root The path to the original source to be included --source-map-root The path to the original source to be included in the
in the source map. source map. [string]
--source-map-url The path to the source map to be added in //# --source-map-url The path to the source map to be added in //#
sourceMappingURL. Defaults to the value passed sourceMappingURL. Defaults to the value passed with
with --source-map. --source-map. [string]
--source-map-include-sources Pass this flag if you want to include the --source-map-include-sources
content of source files in the source map as Pass this flag if you want to include the content of
sourcesContent property. source files in the source map as sourcesContent
--in-source-map Input source map, useful if you're compressing property. [boolean]
JS that was generated from some other original --in-source-map Input source map, useful if you're compressing JS that was
code. generated from some other original code.
--screw-ie8 Pass this flag if you don't care about full --screw-ie8 Pass this flag if you don't care about full compliance
compliance with Internet Explorer 6-8 quirks with Internet Explorer 6-8 quirks (by default UglifyJS
(by default UglifyJS will try to be IE-proof). will try to be IE-proof). [boolean]
--expr Parse a single expression, rather than a --expr Parse a single expression, rather than a program (for
program (for parsing JSON) parsing JSON) [boolean]
-p, --prefix Skip prefix for original filenames that appear -p, --prefix Skip prefix for original filenames that appear in source
in source maps. For example -p 3 will drop 3 maps. For example -p 3 will drop 3 directories from file
directories from file names and ensure they are names and ensure they are relative paths. You can also
relative paths. You can also specify -p specify -p relative, which will make UglifyJS figure out
relative, which will make UglifyJS figure out itself the relative paths between original sources, the
itself the relative paths between original source map and the output file. [string]
sources, the source map and the output file. -o, --output Output file (default STDOUT).
-o, --output Output file (default STDOUT). -b, --beautify Beautify output/specify output options. [string]
-b, --beautify Beautify output/specify output options. -m, --mangle Mangle names/pass mangler options. [string]
-m, --mangle Mangle names/pass mangler options. -r, --reserved Reserved names to exclude from mangling.
-r, --reserved Reserved names to exclude from mangling. -c, --compress Enable compressor/pass compressor options. Pass options
-c, --compress Enable compressor/pass compressor options. Pass like -c hoist_vars=false,if_return=false. Use -c with no
options like -c argument to use the default compression options. [string]
hoist_vars=false,if_return=false. Use -c with -d, --define Global definitions [string]
no argument to use the default compression -e, --enclose Embed everything in a big function, with a configurable
options. parameter/argument list. [string]
-d, --define Global definitions --comments Preserve copyright comments in the output. By default this
-e, --enclose Embed everything in a big function, with a works like Google Closure, keeping JSDoc-style comments
configurable parameter/argument list. that contain "@license" or "@preserve". You can optionally
--comments Preserve copyright comments in the output. By pass one of the following arguments to this flag:
default this works like Google Closure, keeping - "all" to keep all comments
JSDoc-style comments that contain "@license" or - a valid JS regexp (needs to start with a slash) to keep
"@preserve". You can optionally pass one of the only comments that match.
following arguments to this flag: Note that currently not *all* comments can be kept when
- "all" to keep all comments compression is on, because of dead code removal or
- a valid JS regexp (needs to start with a cascading statements into sequences. [string]
slash) to keep only comments that match. --preamble Preamble to prepend to the output. You can use this to
Note that currently not *all* comments can be insert a comment, for example for licensing information.
kept when compression is on, because of dead This will not be parsed, but the source map will adjust
code removal or cascading statements into for its presence.
sequences. --stats Display operations run time on STDERR. [boolean]
--preamble Preamble to prepend to the output. You can use --acorn Use Acorn for parsing. [boolean]
this to insert a comment, for example for --spidermonkey Assume input files are SpiderMonkey AST format (as JSON).
licensing information. This will not be [boolean]
parsed, but the source map will adjust for its --self Build itself (UglifyJS2) as a library (implies
presence. --wrap=UglifyJS --export-all) [boolean]
--stats Display operations run time on STDERR. --wrap Embed everything in a big function, making the “exports”
--acorn Use Acorn for parsing. and “global” variables available. You need to pass an
--spidermonkey Assume input files are SpiderMonkey AST format argument to this option to specify the name that your
(as JSON). module will take when included in, say, a browser.
--self Build itself (UglifyJS2) as a library (implies [string]
--wrap=UglifyJS --export-all) --export-all Only used when --wrap, this tells UglifyJS to add code to
--wrap Embed everything in a big function, making the automatically export all globals. [boolean]
“exports” and “global” variables available. You --lint Display some scope warnings [boolean]
need to pass an argument to this option to -v, --verbose Verbose [boolean]
specify the name that your module will take -V, --version Print version number and exit. [boolean]
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.
--keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name.
--reserved-file File containing reserved names
--reserve-domprops Make (most?) DOM properties reserved for
--mangle-props
--mangle-props Mangle property names
--mangle-regex Only mangle property names matching the regex
--name-cache File to hold mangled names mappings
``` ```
Specify `--output` (`-o`) to declare the output file. Otherwise the output Specify `--output` (`-o`) to declare the output file. Otherwise the output
@@ -207,73 +189,6 @@ comma-separated list of names. For example:
to prevent the `require`, `exports` and `$` names from being changed. to prevent the `require`, `exports` and `$` names from being changed.
### Mangling property names (`--mangle-props`)
**Note:** this will probably break your code. Mangling property names is a
separate step, different from variable name mangling. Pass
`--mangle-props`. It will mangle all properties that are seen in some
object literal, or that are assigned to. For example:
```js
var x = {
foo: 1
};
x.bar = 2;
x["baz"] = 3;
x[condition ? "moo" : "boo"] = 4;
console.log(x.something());
```
In the above code, `foo`, `bar`, `baz`, `moo` and `boo` will be replaced
with single characters, while `something()` will be left as is.
In order for this to be of any use, we should avoid mangling standard JS
names. For instance, if your code would contain `x.length = 10`, then
`length` becomes a candidate for mangling and it will be mangled throughout
the code, regardless if it's being used as part of your own objects or
accessing an array's length. To avoid that, you can use `--reserved-file`
to pass a filename that should contain the names to be excluded from
mangling. This file can be used both for excluding variable names and
property names. It could look like this, for example:
```js
{
"vars": [ "define", "require", ... ],
"props": [ "length", "prototype", ... ]
}
```
`--reserved-file` can be an array of file names (either a single
comma-separated argument, or you can pass multiple `--reserved-file`
arguments) — in this case it will exclude names from all those files.
A default exclusion file is provided in `tools/domprops.json` which should
cover most standard JS and DOM properties defined in various browsers. Pass
`--reserve-domprops` to read that in.
You can also use a regular expression to define which property names should be
mangled. For example, `--mangle-regex="/^_/"` will only mangle property names
that start with an underscore.
When you compress multiple files using this option, in order for them to
work together in the end we need to ensure somehow that one property gets
mangled to the same name in all of them. For this, pass `--name-cache
filename.json` and UglifyJS will maintain these mappings in a file which can
then be reused. It should be initially empty. Example:
```
rm -f /tmp/cache.json # start fresh
uglifyjs file1.js file2.js --mangle-props --name-cache /tmp/cache.json -o part1.js
uglifyjs file3.js file4.js --mangle-props --name-cache /tmp/cache.json -o part2.js
```
Now, `part1.js` and `part2.js` will be consistent with each other in terms
of mangled property names.
Using the name cache is not necessary if you compress all your files in a
single call to UglifyJS.
## Compressor options ## Compressor options
You need to pass `--compress` (`-c`) to enable the compressor. Optionally You need to pass `--compress` (`-c`) to enable the compressor. Optionally
@@ -357,15 +272,14 @@ contrived cases, but should be fine for most code. You might want to try it
on your own code, it should reduce the minified size. Here's what happens on your own code, it should reduce the minified size. Here's what happens
when this flag is on: when this flag is on:
- `new Array(1, 2, 3)` or `Array(1, 2, 3)` → `[ 1, 2, 3 ]` - `new Array(1, 2, 3)` or `Array(1, 2, 3)` → `[1, 2, 3 ]`
- `new Object()` → `{}` - `new Object()` → `{}`
- `String(exp)` or `exp.toString()` → `"" + exp` - `String(exp)` or `exp.toString()` → `"" + exp`
- `new Object/RegExp/Function/Error/Array (...)` → we discard the `new` - `new Object/RegExp/Function/Error/Array (...)` → we discard the `new`
- `typeof foo == "undefined"` → `foo === void 0` - `typeof foo == "undefined"` → `foo === void 0`
- `void 0` → `undefined` (if there is a variable named "undefined" in - `void 0` → `undefined` (if there is a variable named "undefined" in
scope; we do it because the variable name will be mangled, typically scope; we do it because the variable name will be mangled, typically
reduced to a single character) reduced to a single character).
- discards unused function arguments (affects `function.length`)
### Conditional compilation ### Conditional compilation
@@ -596,16 +510,6 @@ var result = UglifyJS.minify("compiled.js", {
// same as before, it returns `code` and `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 The `inSourceMap` is only used if you also request `outSourceMap` (it makes
no sense otherwise). no sense otherwise).

View File

@@ -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_Seq) {
walk(node.cdr);
return true;
}
if (node instanceof U2.AST_String) {
add(node.value);
return true;
}
if (node instanceof U2.AST_Conditional) {
walk(node.consequent);
walk(node.alternative);
return true;
}
throw out;
}));
})(node);
} catch(ex) {
if (ex !== out) throw ex;
}
}
function add(name) {
output.props[name] = true;
}
}

View File

@@ -67,11 +67,6 @@ You need to pass an argument to this option to specify the name that your module
.describe("bare-returns", "Allow return outside of functions. Useful when minifying CommonJS modules.") .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("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("quotes", "Quote style (0 - auto, 1 - single, 2 - double, 3 - original)")
.describe("reserved-file", "File containing reserved names")
.describe("reserve-domprops", "Make (most?) DOM properties reserved for --mangle-props")
.describe("mangle-props", "Mangle property names")
.describe("mangle-regex", "Only mangle property names matching the regex")
.describe("name-cache", "File to hold mangled names mappings")
.alias("p", "prefix") .alias("p", "prefix")
.alias("o", "output") .alias("o", "output")
@@ -89,21 +84,13 @@ You need to pass an argument to this option to specify the name that your module
.string("source-map-root") .string("source-map-root")
.string("source-map-url") .string("source-map-url")
.string("b") .string("b")
.string("beautify")
.string("m") .string("m")
.string("mangle")
.string("c") .string("c")
.string("compress")
.string("d") .string("d")
.string("define")
.string("e") .string("e")
.string("enclose")
.string("comments") .string("comments")
.string("wrap") .string("wrap")
.string("p") .string("p")
.string("prefix")
.string("name-cache")
.array("reserved-file")
.boolean("expr") .boolean("expr")
.boolean("source-map-include-sources") .boolean("source-map-include-sources")
@@ -111,18 +98,14 @@ You need to pass an argument to this option to specify the name that your module
.boolean("export-all") .boolean("export-all")
.boolean("self") .boolean("self")
.boolean("v") .boolean("v")
.boolean("verbose")
.boolean("stats") .boolean("stats")
.boolean("acorn") .boolean("acorn")
.boolean("spidermonkey") .boolean("spidermonkey")
.boolean("lint") .boolean("lint")
.boolean("V") .boolean("V")
.boolean("version")
.boolean("noerr") .boolean("noerr")
.boolean("bare-returns") .boolean("bare-returns")
.boolean("keep-fnames") .boolean("keep-fnames")
.boolean("mangle-props")
.boolean("reserve-domprops")
.wrap(80) .wrap(80)
@@ -133,24 +116,24 @@ normalize(ARGS);
if (ARGS.noerr) { if (ARGS.noerr) {
UglifyJS.DefaultsError.croak = function(msg, defs) { UglifyJS.DefaultsError.croak = function(msg, defs) {
print_error("WARN: " + msg); sys.error("WARN: " + msg);
}; };
} }
if (ARGS.version || ARGS.V) { if (ARGS.version || ARGS.V) {
var json = require("../package.json"); var json = require("../package.json");
print(json.name + ' ' + json.version); sys.puts(json.name + ' ' + json.version);
process.exit(0); process.exit(0);
} }
if (ARGS.ast_help) { if (ARGS.ast_help) {
var desc = UglifyJS.describe_ast(); var desc = UglifyJS.describe_ast();
print(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2)); sys.puts(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
process.exit(0); process.exit(0);
} }
if (ARGS.h || ARGS.help) { if (ARGS.h || ARGS.help) {
print(yargs.help()); sys.puts(yargs.help());
process.exit(0); process.exit(0);
} }
@@ -161,15 +144,6 @@ if (ARGS.acorn) {
var COMPRESS = getOptions("c", true); var COMPRESS = getOptions("c", true);
var MANGLE = getOptions("m", true); var MANGLE = getOptions("m", true);
var BEAUTIFY = getOptions("b", true); var BEAUTIFY = getOptions("b", true);
var RESERVED = null;
if (ARGS.reserved_file) ARGS.reserved_file.forEach(function(filename){
RESERVED = UglifyJS.readReservedFile(filename, RESERVED);
});
if (ARGS.reserve_domprops) {
RESERVED = UglifyJS.readDefaultReservedFile(RESERVED);
}
if (ARGS.d) { if (ARGS.d) {
if (COMPRESS) COMPRESS.global_defs = getOptions("d"); if (COMPRESS) COMPRESS.global_defs = getOptions("d");
@@ -179,28 +153,6 @@ if (ARGS.r) {
if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/); 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) { if (ARGS.quotes === true) {
ARGS.quotes = 3; ARGS.quotes = 3;
} }
@@ -225,12 +177,13 @@ if (ARGS.keep_fnames) {
if (BEAUTIFY) if (BEAUTIFY)
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY); UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
if (ARGS.comments != null) { if (ARGS.comments) {
if (/^\/.*\/[a-zA-Z]*$/.test(ARGS.comments)) { if (/^\/.*\/[a-zA-Z]*$/.test(ARGS.comments)) {
var regex_pos = ARGS.comments.lastIndexOf("/");
try { try {
OUTPUT_OPTIONS.comments = extractRegex(ARGS.comments); OUTPUT_OPTIONS.comments = new RegExp(ARGS.comments.substr(1, regex_pos - 1), ARGS.comments.substr(regex_pos + 1));
} catch (e) { } catch (e) {
print_error("ERROR: Invalid --comments: " + e.message); sys.error("ERROR: Invalid --comments: " + e.message);
} }
} else if (ARGS.comments == "all") { } else if (ARGS.comments == "all") {
OUTPUT_OPTIONS.comments = true; OUTPUT_OPTIONS.comments = true;
@@ -250,7 +203,7 @@ var files = ARGS._.slice();
if (ARGS.self) { if (ARGS.self) {
if (files.length > 0) { if (files.length > 0) {
print_error("WARN: Ignoring input files since --self was passed"); sys.error("WARN: Ignoring input files since --self was passed");
} }
files = UglifyJS.FILES; files = UglifyJS.FILES;
if (!ARGS.wrap) ARGS.wrap = "UglifyJS"; if (!ARGS.wrap) ARGS.wrap = "UglifyJS";
@@ -262,7 +215,7 @@ var ORIG_MAP = ARGS.in_source_map;
if (ORIG_MAP) { if (ORIG_MAP) {
ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP)); ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP));
if (files.length == 0) { if (files.length == 0) {
print_error("INFO: Using file from the input source map: " + ORIG_MAP.file); sys.error("INFO: Using file from the input source map: " + ORIG_MAP.file);
files = [ ORIG_MAP.file ]; files = [ ORIG_MAP.file ];
} }
if (ARGS.source_map_root == null) { if (ARGS.source_map_root == null) {
@@ -275,12 +228,12 @@ if (files.length == 0) {
} }
if (files.indexOf("-") >= 0 && ARGS.source_map) { if (files.indexOf("-") >= 0 && ARGS.source_map) {
print_error("ERROR: Source map doesn't work with input from STDIN"); sys.error("ERROR: Source map doesn't work with input from STDIN");
process.exit(1); process.exit(1);
} }
if (files.filter(function(el){ return el == "-" }).length > 1) { if (files.filter(function(el){ return el == "-" }).length > 1) {
print_error("ERROR: Can read a single file from STDIN (two or more dashes specified)"); sys.error("ERROR: Can read a single file from STDIN (two or more dashes specified)");
process.exit(1); process.exit(1);
} }
@@ -303,9 +256,9 @@ try {
var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS); var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
} catch(ex) { } catch(ex) {
if (ex instanceof UglifyJS.DefaultsError) { if (ex instanceof UglifyJS.DefaultsError) {
print_error(ex.msg); sys.error(ex.msg);
print_error("Supported options:"); sys.error("Supported options:");
print_error(sys.inspect(ex.defs)); sys.error(sys.inspect(ex.defs));
process.exit(1); process.exit(1);
} }
} }
@@ -313,7 +266,7 @@ try {
async.eachLimit(files, 1, function (file, cb) { async.eachLimit(files, 1, function (file, cb) {
read_whole_file(file, function (err, code) { read_whole_file(file, function (err, code) {
if (err) { if (err) {
print_error("ERROR: can't read file: " + file); sys.error("ERROR: can't read file: " + file);
process.exit(1); process.exit(1);
} }
if (ARGS.p != null) { if (ARGS.p != null) {
@@ -350,9 +303,9 @@ async.eachLimit(files, 1, function (file, cb) {
}); });
} catch(ex) { } catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) { if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Parse error at " + file + ":" + ex.line + "," + ex.col); sys.error("Parse error at " + file + ":" + ex.line + "," + ex.col);
print_error(ex.message); sys.error(ex.message);
print_error(ex.stack); sys.error(ex.stack);
process.exit(1); process.exit(1);
} }
throw ex; throw ex;
@@ -366,11 +319,11 @@ async.eachLimit(files, 1, function (file, cb) {
TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL); TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
}); });
if (ARGS.wrap != null) { if (ARGS.wrap) {
TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all); TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all);
} }
if (ARGS.enclose != null) { if (ARGS.enclose) {
var arg_parameter_list = ARGS.enclose; var arg_parameter_list = ARGS.enclose;
if (arg_parameter_list === true) { if (arg_parameter_list === true) {
arg_parameter_list = []; arg_parameter_list = [];
@@ -381,33 +334,11 @@ async.eachLimit(files, 1, function (file, cb) {
TOPLEVEL = TOPLEVEL.wrap_enclose(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
});
writeNameCache("props", cache);
})();
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint; var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint;
var TL_CACHE = readNameCache("vars");
if (SCOPE_IS_NEEDED) { if (SCOPE_IS_NEEDED) {
time_it("scope", function(){ time_it("scope", function(){
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE }); TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8 });
if (ARGS.lint) { if (ARGS.lint) {
TOPLEVEL.scope_warnings(); TOPLEVEL.scope_warnings();
} }
@@ -422,20 +353,17 @@ async.eachLimit(files, 1, function (file, cb) {
if (SCOPE_IS_NEEDED) { if (SCOPE_IS_NEEDED) {
time_it("scope", function(){ time_it("scope", function(){
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE }); TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8 });
if (MANGLE && !TL_CACHE) { if (MANGLE) {
TOPLEVEL.compute_char_frequency(MANGLE); TOPLEVEL.compute_char_frequency(MANGLE);
} }
}); });
} }
if (MANGLE) time_it("mangle", function(){ if (MANGLE) time_it("mangle", function(){
MANGLE.cache = TL_CACHE;
TOPLEVEL.mangle_names(MANGLE); TOPLEVEL.mangle_names(MANGLE);
}); });
writeNameCache("vars", TL_CACHE);
if (ARGS.source_map_include_sources) { if (ARGS.source_map_include_sources) {
for (var file in SOURCES_CONTENT) { for (var file in SOURCES_CONTENT) {
if (SOURCES_CONTENT.hasOwnProperty(file)) { if (SOURCES_CONTENT.hasOwnProperty(file)) {
@@ -463,15 +391,15 @@ async.eachLimit(files, 1, function (file, cb) {
if (OUTPUT_FILE) { if (OUTPUT_FILE) {
fs.writeFileSync(OUTPUT_FILE, output, "utf8"); fs.writeFileSync(OUTPUT_FILE, output, "utf8");
} else { } else {
print(output); sys.print(output);
} }
if (ARGS.stats) { if (ARGS.stats) {
print_error(UglifyJS.string_template("Timing information (compressed {count} files):", { sys.error(UglifyJS.string_template("Timing information (compressed {count} files):", {
count: files.length count: files.length
})); }));
for (var i in STATS) if (STATS.hasOwnProperty(i)) { for (var i in STATS) if (STATS.hasOwnProperty(i)) {
print_error(UglifyJS.string_template("- {name}: {time}s", { sys.error(UglifyJS.string_template("- {name}: {time}s", {
name: i, name: i,
time: (STATS[i] / 1000).toFixed(3) time: (STATS[i] / 1000).toFixed(3)
})); }));
@@ -490,15 +418,15 @@ function normalize(o) {
function getOptions(x, constants) { function getOptions(x, constants) {
x = ARGS[x]; x = ARGS[x];
if (x == null) return null; if (!x) return null;
var ret = {}; var ret = {};
if (x !== "") { if (x !== true) {
var ast; var ast;
try { try {
ast = UglifyJS.parse(x, { expression: true }); ast = UglifyJS.parse(x, { expression: true });
} catch(ex) { } catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) { if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Error parsing arguments in: " + x); sys.error("Error parsing arguments in: " + x);
process.exit(1); process.exit(1);
} }
} }
@@ -517,8 +445,8 @@ function getOptions(x, constants) {
ret[name] = true; ret[name] = true;
return true; // no descend return true; // no descend
} }
print_error(node.TYPE) sys.error(node.TYPE)
print_error("Error parsing arguments in: " + x); sys.error("Error parsing arguments in: " + x);
process.exit(1); process.exit(1);
})); }));
} }
@@ -550,11 +478,3 @@ function time_it(name, cont) {
} }
return ret; return ret;
} }
function print_error(msg) {
console.error("%s", msg);
}
function print(txt) {
console.log("%s", txt);
}

View File

@@ -489,7 +489,7 @@ merge(Compressor.prototype, {
seq = []; seq = [];
}; };
statements.forEach(function(stat){ statements.forEach(function(stat){
if (stat instanceof AST_SimpleStatement && seq.length < 2000) seq.push(stat.body); if (stat instanceof AST_SimpleStatement) seq.push(stat.body);
else push_seq(), ret.push(stat); else push_seq(), ret.push(stat);
}); });
push_seq(); push_seq();
@@ -1086,7 +1086,7 @@ merge(Compressor.prototype, {
var tt = new TreeTransformer( var tt = new TreeTransformer(
function before(node, descend, in_list) { function before(node, descend, in_list) {
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) { if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
if (compressor.option("unsafe") && !compressor.option("keep_fargs")) { if (!compressor.option("keep_fargs")) {
for (var a = node.argnames, i = a.length; --i >= 0;) { for (var a = node.argnames, i = a.length; --i >= 0;) {
var sym = a[i]; var sym = a[i];
if (sym.unreferenced()) { if (sym.unreferenced()) {
@@ -1168,12 +1168,12 @@ merge(Compressor.prototype, {
return make_node(AST_EmptyStatement, node); return make_node(AST_EmptyStatement, node);
} }
if (def.length == 0) { if (def.length == 0) {
return in_list ? MAP.splice(side_effects.body) : side_effects; return side_effects;
} }
node.definitions = def; node.definitions = def;
if (side_effects) { if (side_effects) {
side_effects.body.unshift(node); side_effects.body.unshift(node);
return in_list ? MAP.splice(side_effects.body) : side_effects; node = side_effects;
} }
return node; return node;
} }
@@ -2104,34 +2104,6 @@ merge(Compressor.prototype, {
} }
break; break;
} }
if (compressor.option("conditionals")) {
if (self.operator == "&&") {
var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor);
if (ll.length > 1) {
if (ll[1]) {
compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
return rr[0];
} else {
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
return ll[0];
}
}
}
else if (self.operator == "||") {
var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor);
if (ll.length > 1) {
if (ll[1]) {
compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
return ll[0];
} else {
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
return rr[0];
}
}
}
}
if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) { if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) {
case "&&": case "&&":
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
@@ -2183,7 +2155,7 @@ merge(Compressor.prototype, {
} }
break; break;
} }
if (compressor.option("comparisons") && self.is_boolean()) { if (compressor.option("comparisons")) {
if (!(compressor.parent() instanceof AST_Binary) if (!(compressor.parent() instanceof AST_Binary)
|| compressor.parent() instanceof AST_Assign) { || compressor.parent() instanceof AST_Assign) {
var negated = make_node(AST_UnaryPrefix, self, { var negated = make_node(AST_UnaryPrefix, self, {
@@ -2258,11 +2230,10 @@ merge(Compressor.prototype, {
} }
} }
} }
// x && (y && z) ==> x && y && z // x * (y * z) ==> x * y * z
// x || (y || z) ==> x || y || z
if (self.right instanceof AST_Binary if (self.right instanceof AST_Binary
&& self.right.operator == self.operator && self.right.operator == self.operator
&& (self.operator == "&&" || self.operator == "||")) && (self.operator == "*" || self.operator == "&&" || self.operator == "||"))
{ {
self.left = make_node(AST_Binary, self.left, { self.left = make_node(AST_Binary, self.left, {
operator : self.operator, operator : self.operator,
@@ -2296,8 +2267,16 @@ merge(Compressor.prototype, {
OPT(AST_Infinity, function (self, compressor) { OPT(AST_Infinity, function (self, compressor) {
return make_node(AST_Binary, self, { return make_node(AST_Binary, self, {
operator : '/', operator : '/',
left : make_node(AST_Number, self, {value: 1}), left : make_node(AST_Number, null, {value: 1}),
right : make_node(AST_Number, self, {value: 0}) right : make_node(AST_Number, null, {value: 0})
});
});
OPT(AST_NaN, function (self, compressor) {
return make_node(AST_Binary, self, {
operator : '/',
left : make_node(AST_Number, null, {value: 0}),
right : make_node(AST_Number, null, {value: 0})
}); });
}); });
@@ -2364,7 +2343,6 @@ merge(Compressor.prototype, {
&& alternative instanceof AST_Assign && alternative instanceof AST_Assign
&& consequent.operator == alternative.operator && consequent.operator == alternative.operator
&& consequent.left.equivalent_to(alternative.left) && consequent.left.equivalent_to(alternative.left)
&& !consequent.left.has_side_effects(compressor)
) { ) {
/* /*
* Stuff like this: * Stuff like this:
@@ -2385,7 +2363,6 @@ merge(Compressor.prototype, {
if (consequent instanceof AST_Call if (consequent instanceof AST_Call
&& alternative.TYPE === consequent.TYPE && alternative.TYPE === consequent.TYPE
&& consequent.args.length == alternative.args.length && consequent.args.length == alternative.args.length
&& !consequent.expression.has_side_effects(compressor)
&& consequent.expression.equivalent_to(alternative.expression)) { && consequent.expression.equivalent_to(alternative.expression)) {
if (consequent.args.length == 0) { if (consequent.args.length == 0) {
return make_node(AST_Seq, self, { return make_node(AST_Seq, self, {
@@ -2503,7 +2480,7 @@ merge(Compressor.prototype, {
}); });
function literals_in_boolean_context(self, compressor) { function literals_in_boolean_context(self, compressor) {
if (compressor.option("booleans") && compressor.in_boolean_context() && !self.has_side_effects(compressor)) { if (compressor.option("booleans") && compressor.in_boolean_context()) {
return make_node(AST_True, self); return make_node(AST_True, self);
} }
return self; return self;

View File

@@ -176,6 +176,7 @@ function OutputStream(options) {
might_need_space = false; might_need_space = false;
} }
might_need_semicolon = false; might_need_semicolon = false;
maybe_newline();
} }
if (!options.beautify && options.preserve_line && stack[stack.length - 1]) { if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {

View File

@@ -108,7 +108,7 @@ var OPERATORS = makePredicate([
"||" "||"
]); ]);
var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF")); var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000"));
var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:")); var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:"));
@@ -187,9 +187,8 @@ function parse_js_number(num) {
} }
}; };
function JS_Parse_Error(message, filename, line, col, pos) { function JS_Parse_Error(message, line, col, pos) {
this.message = message; this.message = message;
this.filename = filename;
this.line = line; this.line = line;
this.col = col; this.col = col;
this.pos = pos; this.pos = pos;
@@ -201,7 +200,7 @@ JS_Parse_Error.prototype.toString = function() {
}; };
function js_error(message, filename, line, col, pos) { function js_error(message, filename, line, col, pos) {
throw new JS_Parse_Error(message, filename, line, col, pos); throw new JS_Parse_Error(message, line, col, pos);
}; };
function is_token(token, type, val) { function is_token(token, type, val) {
@@ -213,7 +212,7 @@ var EX_EOF = {};
function tokenizer($TEXT, filename, html5_comments) { function tokenizer($TEXT, filename, html5_comments) {
var S = { var S = {
text : $TEXT, text : $TEXT.replace(/\uFEFF/g, ''),
filename : filename, filename : filename,
pos : 0, pos : 0,
tokpos : 0, tokpos : 0,
@@ -298,8 +297,7 @@ function tokenizer($TEXT, filename, html5_comments) {
}; };
function skip_whitespace() { function skip_whitespace() {
var ch; while (WHITESPACE_CHARS(peek()))
while (WHITESPACE_CHARS(ch = peek()) || ch == "\u2028" || ch == "\u2029")
next(); next();
}; };
@@ -354,13 +352,8 @@ function tokenizer($TEXT, filename, html5_comments) {
case 120 : return String.fromCharCode(hex_bytes(2)); // \x case 120 : return String.fromCharCode(hex_bytes(2)); // \x
case 117 : return String.fromCharCode(hex_bytes(4)); // \u case 117 : return String.fromCharCode(hex_bytes(4)); // \u
case 10 : return ""; // newline case 10 : return ""; // newline
case 13 : // \r default : return ch;
if (peek() == "\n") { // DOS newline
next(true, in_string);
return "";
}
} }
return ch;
}; };
function hex_bytes(n) { function hex_bytes(n) {
@@ -377,7 +370,7 @@ function tokenizer($TEXT, filename, html5_comments) {
var read_string = with_eof_error("Unterminated string constant", function(quote_char){ var read_string = with_eof_error("Unterminated string constant", function(quote_char){
var quote = next(), ret = ""; var quote = next(), ret = "";
for (;;) { for (;;) {
var ch = next(true, true); var ch = next(true);
if (ch == "\\") { if (ch == "\\") {
// read OctalEscapeSequence (XXX: deprecated if "strict mode") // read OctalEscapeSequence (XXX: deprecated if "strict mode")
// https://github.com/mishoo/UglifyJS/issues/178 // https://github.com/mishoo/UglifyJS/issues/178

View File

@@ -1,221 +0,0 @@
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
-------------------------------- (C) ---------------------------------
Author: Mihai Bazon
<mihai.bazon@gmail.com>
http://mihai.bazon.net/blog
Distributed under the BSD license:
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
***********************************************************************/
"use strict";
function find_builtins() {
var a = [];
[ Object, Array, Function, Number,
String, Boolean, Error, Math,
Date, RegExp
].forEach(function(ctor){
Object.getOwnPropertyNames(ctor).map(add);
if (ctor.prototype) {
Object.getOwnPropertyNames(ctor.prototype).map(add);
}
});
function add(name) {
push_uniq(a, name);
}
return a;
}
function mangle_properties(ast, options) {
options = defaults(options, {
reserved : null,
cache : null,
only_cache : false,
regex : null
});
var reserved = options.reserved;
if (reserved == null)
reserved = find_builtins();
var cache = options.cache;
if (cache == null) {
cache = {
cname: -1,
props: new Dictionary()
};
}
var regex = options.regex;
var names_to_mangle = [];
// step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){
if (node instanceof AST_ObjectKeyVal) {
add(node.key);
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above
add(node.key.name);
}
else if (node instanceof AST_Dot) {
if (this.parent() instanceof AST_Assign) {
add(node.property);
}
}
else if (node instanceof AST_Sub) {
if (this.parent() instanceof AST_Assign) {
addStrings(node.property);
}
}
}));
// step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(function(node){
if (node instanceof AST_ObjectKeyVal) {
if (should_mangle(node.key)) {
node.key = mangle(node.key);
}
}
else if (node instanceof AST_ObjectProperty) {
// setter or getter
if (should_mangle(node.key.name)) {
node.key.name = mangle(node.key.name);
}
}
else if (node instanceof AST_Dot) {
if (should_mangle(node.property)) {
node.property = mangle(node.property);
}
}
else if (node instanceof AST_Sub) {
node.property = mangleStrings(node.property);
}
// else if (node instanceof AST_String) {
// if (should_mangle(node.value)) {
// AST_Node.warn(
// "Found \"{prop}\" property candidate for mangling in an arbitrary string [{file}:{line},{col}]", {
// file : node.start.file,
// line : node.start.line,
// col : node.start.col,
// prop : node.value
// }
// );
// }
// }
}));
// only function declarations after this line
function can_mangle(name) {
if (reserved.indexOf(name) >= 0) return false;
if (options.only_cache) {
return cache.props.has(name);
}
if (/^[0-9.]+$/.test(name)) return false;
return true;
}
function should_mangle(name) {
if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false;
return cache.props.has(name)
|| names_to_mangle.indexOf(name) >= 0;
}
function add(name) {
if (can_mangle(name))
push_uniq(names_to_mangle, name);
}
function mangle(name) {
var mangled = cache.props.get(name);
if (!mangled) {
do {
mangled = base54(++cache.cname);
} while (!can_mangle(mangled));
cache.props.set(name, mangled);
}
return mangled;
}
function addStrings(node) {
var out = {};
try {
(function walk(node){
node.walk(new TreeWalker(function(node){
if (node instanceof AST_Seq) {
walk(node.cdr);
return true;
}
if (node instanceof AST_String) {
add(node.value);
return true;
}
if (node instanceof AST_Conditional) {
walk(node.consequent);
walk(node.alternative);
return true;
}
throw out;
}));
})(node);
} catch(ex) {
if (ex !== out) throw ex;
}
}
function mangleStrings(node) {
return node.transform(new TreeTransformer(function(node){
if (node instanceof AST_Seq) {
node.cdr = mangleStrings(node.cdr);
}
else if (node instanceof AST_String) {
if (should_mangle(node.value)) {
node.value = mangle(node.value);
}
}
else if (node instanceof AST_Conditional) {
node.consequent = mangleStrings(node.consequent);
node.alternative = mangleStrings(node.alternative);
}
return node;
}));
}
}

View File

@@ -67,26 +67,18 @@ SymbolDef.prototype = {
|| this.orig[0] instanceof AST_SymbolDefun)); || this.orig[0] instanceof AST_SymbolDefun));
}, },
mangle: function(options) { mangle: function(options) {
var cache = options.cache && options.cache.props; if (!this.mangled_name && !this.unmangleable(options)) {
if (this.global && cache && cache.has(this.name)) {
this.mangled_name = cache.get(this.name);
}
else if (!this.mangled_name && !this.unmangleable(options)) {
var s = this.scope; var s = this.scope;
if (!options.screw_ie8 && this.orig[0] instanceof AST_SymbolLambda) if (!options.screw_ie8 && this.orig[0] instanceof AST_SymbolLambda)
s = s.parent_scope; s = s.parent_scope;
this.mangled_name = s.next_mangled(options, this); this.mangled_name = s.next_mangled(options, this);
if (this.global && cache) {
cache.set(this.name, this.mangled_name);
}
} }
} }
}; };
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
options = defaults(options, { options = defaults(options, {
screw_ie8: false, screw_ie8: false
cache: null
}); });
// pass 1: setup scope chaining and handle definitions // pass 1: setup scope chaining and handle definitions
@@ -191,10 +183,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
} }
}); });
self.walk(tw); self.walk(tw);
if (options.cache) {
this.cname = options.cache.cname;
}
}); });
AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
@@ -356,15 +344,6 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
// the AST_SymbolDeclaration that it points to). // the AST_SymbolDeclaration that it points to).
var lname = -1; var lname = -1;
var to_mangle = []; var to_mangle = [];
if (options.cache) {
this.globals.each(function(symbol){
if (options.except.indexOf(symbol.name) < 0) {
to_mangle.push(symbol);
}
});
}
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_LabeledStatement) { if (node instanceof AST_LabeledStatement) {
// lname is incremented when we get to the AST_Label // lname is incremented when we get to the AST_Label
@@ -399,10 +378,6 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
}); });
this.walk(tw); this.walk(tw);
to_mangle.forEach(function(def){ def.mangle(options) }); to_mangle.forEach(function(def){ def.mangle(options) });
if (options.cache) {
options.cache.cname = this.cname;
}
}); });
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){

View File

@@ -106,12 +106,10 @@ function defaults(args, defs, croak) {
}; };
function merge(obj, ext) { function merge(obj, ext) {
var count = 0;
for (var i in ext) if (ext.hasOwnProperty(i)) { for (var i in ext) if (ext.hasOwnProperty(i)) {
obj[i] = ext[i]; obj[i] = ext[i];
count++;
} }
return count; return obj;
}; };
function noop() {}; function noop() {};
@@ -300,11 +298,5 @@ Dictionary.prototype = {
for (var i in this._values) for (var i in this._values)
ret.push(f(this._values[i], i.substr(1))); ret.push(f(this._values[i], i.substr(1)));
return ret; return ret;
}, }
toObject: function() { return this._values }
};
Dictionary.fromObject = function(obj) {
var dict = new Dictionary();
dict._size = merge(dict._values, obj);
return dict;
}; };

View File

@@ -1,51 +1,37 @@
{ {
"name": "uglify-js", "name": "uglify-js",
"description": "JavaScript parser, mangler/compressor and beautifier toolkit", "description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"homepage": "http://lisperator.net/uglifyjs", "homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "main": "tools/node.js",
"license": "BSD", "version": "2.4.17",
"version": "2.4.24", "engines": { "node" : ">=0.4.0" },
"engines": { "maintainers": [{
"node": ">=0.4.0" "name": "Mihai Bazon",
}, "email": "mihai.bazon@gmail.com",
"maintainers": [ "web": "http://lisperator.net/"
"Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)" }],
], "repository": {
"repository": { "type": "git",
"type": "git", "url": "https://github.com/mishoo/UglifyJS2.git"
"url": "https://github.com/mishoo/UglifyJS2.git" },
}, "dependencies": {
"bugs": { "async" : "~0.2.6",
"url": "https://github.com/mishoo/UglifyJS2/issues" "source-map" : "0.1.34",
}, "yargs": "~1.3.3",
"main": "tools/node.js", "uglify-to-browserify": "~1.0.0"
"bin": { },
"uglifyjs": "bin/uglifyjs" "devDependencies": {
}, "acorn": "~0.6.0",
"files": [ "escodegen": "~1.3.3",
"bin", "esfuzz": "~0.3.1",
"lib", "estraverse": "~1.5.1"
"tools", },
"LICENSE" "browserify": {
], "transform": [ "uglify-to-browserify" ]
"dependencies": { },
"async": "~0.2.6", "bin": {
"source-map": "0.1.34", "uglifyjs" : "bin/uglifyjs"
"uglify-to-browserify": "~1.0.0", },
"yargs": "~3.5.4" "license": "BSD",
}, "scripts": {"test": "node test/run-tests.js"}
"devDependencies": {
"acorn": "~0.6.0",
"escodegen": "~1.3.3",
"esfuzz": "~0.3.1",
"estraverse": "~1.5.1"
},
"browserify": {
"transform": [
"uglify-to-browserify"
]
},
"scripts": {
"test": "node test/run-tests.js"
}
} }

View File

@@ -86,9 +86,7 @@ ifs_4: {
x(foo)[10].bar.baz = something_else(); x(foo)[10].bar.baz = something_else();
} }
expect: { expect: {
foo && bar x(foo)[10].bar.baz = (foo && bar) ? something() : something_else();
? x(foo)[10].bar.baz = something()
: x(foo)[10].bar.baz = something_else();
} }
} }
@@ -135,7 +133,6 @@ ifs_6: {
comparisons: true comparisons: true
}; };
input: { input: {
var x;
if (!foo && !bar && !baz && !boo) { if (!foo && !bar && !baz && !boo) {
x = 10; x = 10;
} else { } else {
@@ -143,7 +140,6 @@ ifs_6: {
} }
} }
expect: { expect: {
var x;
x = foo || bar || baz || boo ? 20 : 10; x = foo || bar || baz || boo ? 20 : 10;
} }
} }
@@ -153,7 +149,6 @@ cond_1: {
conditionals: true conditionals: true
}; };
input: { input: {
var do_something; // if undeclared it's assumed to have side-effects
if (some_condition()) { if (some_condition()) {
do_something(x); do_something(x);
} else { } else {
@@ -161,7 +156,6 @@ cond_1: {
} }
} }
expect: { expect: {
var do_something;
do_something(some_condition() ? x : y); do_something(some_condition() ? x : y);
} }
} }
@@ -171,7 +165,6 @@ cond_2: {
conditionals: true conditionals: true
}; };
input: { input: {
var x, FooBar;
if (some_condition()) { if (some_condition()) {
x = new FooBar(1); x = new FooBar(1);
} else { } else {
@@ -179,7 +172,6 @@ cond_2: {
} }
} }
expect: { expect: {
var x, FooBar;
x = new FooBar(some_condition() ? 1 : 2); x = new FooBar(some_condition() ? 1 : 2);
} }
} }
@@ -189,7 +181,6 @@ cond_3: {
conditionals: true conditionals: true
}; };
input: { input: {
var FooBar;
if (some_condition()) { if (some_condition()) {
new FooBar(1); new FooBar(1);
} else { } else {
@@ -197,7 +188,6 @@ cond_3: {
} }
} }
expect: { expect: {
var FooBar;
some_condition() ? new FooBar(1) : FooBar(2); some_condition() ? new FooBar(1) : FooBar(2);
} }
} }
@@ -207,7 +197,6 @@ cond_4: {
conditionals: true conditionals: true
}; };
input: { input: {
var do_something;
if (some_condition()) { if (some_condition()) {
do_something(); do_something();
} else { } else {
@@ -215,7 +204,6 @@ cond_4: {
} }
} }
expect: { expect: {
var do_something;
some_condition(), do_something(); some_condition(), do_something();
} }
} }
@@ -315,7 +303,6 @@ cond_7_1: {
evaluate : true evaluate : true
}; };
input: { input: {
var x;
// access to global should be assumed to have side effects // access to global should be assumed to have side effects
if (y) { if (y) {
x = 1+1; x = 1+1;
@@ -324,7 +311,6 @@ cond_7_1: {
} }
} }
expect: { expect: {
var x;
x = (y, 2); x = (y, 2);
} }
} }
@@ -335,7 +321,6 @@ cond_8: {
evaluate : true evaluate : true
}; };
input: { input: {
var a;
// compress these // compress these
a = condition ? true : false; a = condition ? true : false;
@@ -370,7 +355,6 @@ cond_8: {
} }
expect: { expect: {
var a;
a = !!condition; a = !!condition;
a = !condition; a = !condition;
a = !!condition(); a = !!condition();
@@ -383,164 +367,4 @@ cond_8: {
a = condition ? 0 : true; a = condition ? 0 : true;
a = condition ? 1 : 0; a = condition ? 1 : 0;
} }
} }
conditional_and: {
options = {
conditionals: true,
evaluate : true
};
input: {
var a;
// compress these
a = true && condition;
a = 1 && console.log("a");
a = 2 * 3 && 2 * condition;
a = 5 == 5 && condition + 3;
a = "string" && 4 - condition;
a = 5 + "" && condition / 5;
a = -4.5 && 6 << condition;
a = 6 && 7;
a = false && condition;
a = NaN && console.log("b");
a = 0 && console.log("c");
a = undefined && 2 * condition;
a = null && condition + 3;
a = 2 * 3 - 6 && 4 - condition;
a = 10 == 7 && condition / 5;
a = !"string" && 6 % condition;
a = 0 && 7;
// don't compress these
a = condition && true;
a = console.log("a") && 2;
a = 4 - condition && "string";
a = 6 << condition && -4.5;
a = condition && false;
a = console.log("b") && NaN;
a = console.log("c") && 0;
a = 2 * condition && undefined;
a = condition + 3 && null;
}
expect: {
var a;
a = condition;
a = console.log("a");
a = 2 * condition;
a = condition + 3;
a = 4 - condition;
a = condition / 5;
a = 6 << condition;
a = 7;
a = false;
a = NaN;
a = 0;
a = void 0;
a = null;
a = 0;
a = false;
a = false;
a = 0;
a = condition && true;
a = console.log("a") && 2;
a = 4 - condition && "string";
a = 6 << condition && -4.5;
a = condition && false;
a = console.log("b") && NaN;
a = console.log("c") && 0;
a = 2 * condition && void 0;
a = condition + 3 && null;
}
}
conditional_or: {
options = {
conditionals: true,
evaluate : true
};
input: {
var a;
// compress these
a = true || condition;
a = 1 || console.log("a");
a = 2 * 3 || 2 * condition;
a = 5 == 5 || condition + 3;
a = "string" || 4 - condition;
a = 5 + "" || condition / 5;
a = -4.5 || 6 << condition;
a = 6 || 7;
a = false || condition;
a = 0 || console.log("b");
a = NaN || console.log("c");
a = undefined || 2 * condition;
a = null || condition + 3;
a = 2 * 3 - 6 || 4 - condition;
a = 10 == 7 || condition / 5;
a = !"string" || 6 % condition;
a = null || 7;
a = console.log(undefined && condition || null);
a = console.log(undefined || condition && null);
// don't compress these
a = condition || true;
a = console.log("a") || 2;
a = 4 - condition || "string";
a = 6 << condition || -4.5;
a = condition || false;
a = console.log("b") || NaN;
a = console.log("c") || 0;
a = 2 * condition || undefined;
a = condition + 3 || null;
}
expect: {
var a;
a = true;
a = 1;
a = 6;
a = true;
a = "string";
a = "5";
a = -4.5;
a = 6;
a = condition;
a = console.log("b");
a = console.log("c");
a = 2 * condition;
a = condition + 3;
a = 4 - condition;
a = condition / 5;
a = 6 % condition;
a = 7;
a = console.log(null);
a = console.log(condition && null);
a = condition || true;
a = console.log("a") || 2;
a = 4 - condition || "string";
a = 6 << condition || -4.5;
a = condition || false;
a = console.log("b") || NaN;
a = console.log("c") || 0;
a = 2 * condition || void 0;
a = condition + 3 || null;
}
}

View File

@@ -1,5 +1,5 @@
unused_funarg_1: { unused_funarg_1: {
options = { unused: true, unsafe: true }; options = { unused: true };
input: { input: {
function f(a, b, c, d, e) { function f(a, b, c, d, e) {
return a + b; return a + b;
@@ -13,7 +13,7 @@ unused_funarg_1: {
} }
unused_funarg_2: { unused_funarg_2: {
options = { unused: true, unsafe: true }; options = { unused: true };
input: { input: {
function f(a, b, c, d, e) { function f(a, b, c, d, e) {
return a + c; return a + c;
@@ -165,7 +165,7 @@ used_var_in_catch: {
} }
keep_fnames: { keep_fnames: {
options = { unused: true, keep_fnames: true, unsafe: true }; options = { unused: true, keep_fnames: true };
input: { input: {
function foo() { function foo() {
return function bar(baz) {}; return function bar(baz) {};
@@ -176,4 +176,4 @@ keep_fnames: {
return function bar() {}; return function bar() {};
} }
} }
} }

View File

@@ -6,7 +6,7 @@ NaN_and_Infinity_must_have_parens: {
} }
expect: { expect: {
(1/0).toString(); (1/0).toString();
NaN.toString(); // transformation to 0/0 dropped (0/0).toString();
} }
} }

View File

@@ -1,29 +0,0 @@
negate_booleans_1: {
options = {
comparisons: true
};
input: {
var a = !a || !b || !c || !d || !e || !f;
}
expect: {
var a = !(a && b && c && d && e && f);
}
}
negate_booleans_2: {
options = {
comparisons: true
};
input: {
var match = !x && // should not touch this one
(!z || c) &&
(!k || d) &&
the_stuff();
}
expect: {
var match = !x &&
(!z || c) &&
(!k || d) &&
the_stuff();
}
}

View File

@@ -84,12 +84,7 @@ function run_compress_tests() {
warnings: false warnings: false
}); });
var cmp = new U.Compressor(options, true); var cmp = new U.Compressor(options, true);
var expect; var expect = make_code(as_toplevel(test.expect), false);
if (test.expect) {
expect = make_code(as_toplevel(test.expect), false);
} else {
expect = test.expect_exact;
}
var input = as_toplevel(test.input); var input = as_toplevel(test.input);
var input_code = make_code(test.input); var input_code = make_code(test.input);
var output = input.transform(cmp); var output = input.transform(cmp);
@@ -155,7 +150,7 @@ function parse_test(file) {
} }
if (node instanceof U.AST_LabeledStatement) { if (node instanceof U.AST_LabeledStatement) {
assert.ok( assert.ok(
node.label.name == "input" || node.label.name == "expect" || node.label.name == "expect_exact", node.label.name == "input" || node.label.name == "expect",
tmpl("Unsupported label {name} [{line},{col}]", { tmpl("Unsupported label {name} [{line},{col}]", {
name: node.label.name, name: node.label.name,
line: node.label.start.line, line: node.label.start.line,
@@ -167,16 +162,7 @@ function parse_test(file) {
if (stat.body.length == 1) stat = stat.body[0]; if (stat.body.length == 1) stat = stat.body[0];
else if (stat.body.length == 0) stat = new U.AST_EmptyStatement(); else if (stat.body.length == 0) stat = new U.AST_EmptyStatement();
} }
if (node.label.name === "expect_exact") { test[node.label.name] = stat;
if (!(stat.TYPE === "SimpleStatement" && stat.body.TYPE === "String")) {
throw new Error(
"The value of the expect_exact clause should be a string, " +
"like `expect_exact: \"some.exact.javascript;\"`");
}
test[node.label.name] = stat.body.start.value
} else {
test[node.label.name] = stat;
}
return true; return true;
} }
}); });

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,10 @@
var path = require("path"); var path = require("path");
var fs = require("fs"); var fs = require("fs");
var vm = require("vm"); var vm = require("vm");
var sys = require("util");
var UglifyJS = vm.createContext({ var UglifyJS = vm.createContext({
sys : sys,
console : console, console : console,
process : process, process : process,
Buffer : Buffer, Buffer : Buffer,
@@ -17,7 +19,7 @@ function load_global(file) {
} catch(ex) { } catch(ex) {
// XXX: in case of a syntax error, the message is kinda // XXX: in case of a syntax error, the message is kinda
// useless. (no location information). // useless. (no location information).
console.log("ERROR in file: " + file + " / " + ex); sys.debug("ERROR in file: " + file + " / " + ex);
process.exit(1); process.exit(1);
} }
}; };
@@ -31,8 +33,7 @@ var FILES = exports.FILES = [
"../lib/output.js", "../lib/output.js",
"../lib/compress.js", "../lib/compress.js",
"../lib/sourcemap.js", "../lib/sourcemap.js",
"../lib/mozilla-ast.js", "../lib/mozilla-ast.js"
"../lib/propmangle.js"
].map(function(file){ ].map(function(file){
return fs.realpathSync(path.join(path.dirname(__filename), file)); return fs.realpathSync(path.join(path.dirname(__filename), file));
}); });
@@ -40,7 +41,7 @@ var FILES = exports.FILES = [
FILES.forEach(load_global); FILES.forEach(load_global);
UglifyJS.AST_Node.warn_function = function(txt) { UglifyJS.AST_Node.warn_function = function(txt) {
console.error("WARN: %s", txt); sys.error("WARN: " + txt);
}; };
// XXX: perhaps we shouldn't export everything but heck, I'm lazy. // XXX: perhaps we shouldn't export everything but heck, I'm lazy.
@@ -191,63 +192,3 @@ exports.describe_ast = function() {
doitem(UglifyJS.AST_Node); doitem(UglifyJS.AST_Node);
return out + ""; return out + "";
}; };
function readReservedFile(filename, reserved) {
if (!reserved) {
reserved = { vars: [], props: [] };
}
var data = fs.readFileSync(filename, "utf8");
data = JSON.parse(data);
if (data.vars) {
data.vars.forEach(function(name){
UglifyJS.push_uniq(reserved.vars, name);
});
}
if (data.props) {
data.props.forEach(function(name){
UglifyJS.push_uniq(reserved.props, name);
});
}
return reserved;
}
exports.readReservedFile = readReservedFile;
exports.readDefaultReservedFile = function(reserved) {
return readReservedFile(path.join(__dirname, "domprops.json"), reserved);
};
exports.readNameCache = function(filename, key) {
var cache = null;
if (filename) {
try {
var cache = fs.readFileSync(filename, "utf8");
cache = JSON.parse(cache)[key];
if (!cache) throw "init";
cache.props = UglifyJS.Dictionary.fromObject(cache.props);
} catch(ex) {
cache = {
cname: -1,
props: new UglifyJS.Dictionary()
};
}
}
return cache;
};
exports.writeNameCache = function(filename, key, cache) {
if (filename) {
var data;
try {
data = fs.readFileSync(filename, "utf8");
data = JSON.parse(data);
} catch(ex) {
data = {};
}
data[key] = {
cname: cache.cname,
props: cache.props.toObject()
};
fs.writeFileSync(filename, JSON.stringify(data, null, 2), "utf8");
}
};

View File

@@ -1,61 +0,0 @@
<html>
<head>
</head>
<body>
<script>(function(){
var props = {};
function addObject(obj) {
if (obj == null) return;
try {
Object.getOwnPropertyNames(obj).forEach(add);
} catch(ex) {}
if (obj.prototype) {
Object.getOwnPropertyNames(obj.prototype).forEach(add);
}
if (typeof obj == "function") {
try {
Object.getOwnPropertyNames(new obj).forEach(add);
} catch(ex) {}
}
}
function add(name) {
props[name] = true;
}
Object.getOwnPropertyNames(window).forEach(function(thing){
addObject(window[thing]);
});
try {
addObject(new Event("click"));
addObject(new Event("contextmenu"));
addObject(new Event("mouseup"));
addObject(new Event("mousedown"));
addObject(new Event("keydown"));
addObject(new Event("keypress"));
addObject(new Event("keyup"));
} catch(ex) {}
var ta = document.createElement("textarea");
ta.style.width = "100%";
ta.style.height = "20em";
ta.style.boxSizing = "border-box";
<!-- ta.value = Object.keys(props).sort(cmp).map(function(name){ -->
<!-- return JSON.stringify(name); -->
<!-- }).join(",\n"); -->
ta.value = JSON.stringify({
vars: [],
props: Object.keys(props).sort(cmp)
}, null, 2);
document.body.appendChild(ta);
function cmp(a, b) {
a = a.toLowerCase();
b = b.toLowerCase();
return a < b ? -1 : a > b ? 1 : 0;
}
})();</script>
</body>
</html>