Compare commits

...

39 Commits

Author SHA1 Message Date
Mihai Bazon
ba9936a572 v2.4.24 2015-07-22 16:58:09 +03:00
Mihai Bazon
905b601178 Don't attempt to negate non-boolean AST_Binary
Fix #751
2015-07-22 16:55:55 +03:00
Mihai Bazon
63fb2d5a44 Merge pull request #735 from kzc/master
optimizations for && and || where left side is constant expression
2015-07-20 09:58:01 +03:00
Mihai Bazon
85a5fc0aeb Don't drop parens in a * (b * c). Close #744 2015-06-30 10:10:29 +03:00
kzc
9d398d999c spacing 2015-06-14 17:45:19 -04:00
kzc
f47b2b52a5 operator && and || optimization: add "else" before "if" as intended 2015-06-14 17:44:26 -04:00
kzc
fedb6191a1 optimizations for && and || where left side is constant expression 2015-06-11 23:22:38 -04:00
Mihai Bazon
5bf617ebde Merge pull request #733 from jcxplorer/add-mangle-regex-option
Add --mangle-regex option
2015-06-09 16:33:21 +03:00
Joao Carlos
0b82e1cd5b Change --mangle-regex to accept a full regex 2015-06-09 15:14:41 +03:00
Joao Carlos
9aef34a816 Show descriptive error when --mangle-regex is invalid 2015-06-09 14:31:49 +03:00
Joao Carlos
0ac6918a41 Add --mangle-regex option 2015-06-09 14:16:50 +03:00
Mihai Bazon
c6fa291571 v2.4.23 2015-05-20 17:48:30 +03:00
Mihai Bazon
bce4307e9e Treat \uFEFF as whitespace.
Fix #714
2015-05-20 16:17:46 +03:00
Mihai Bazon
96ad94ab41 v2.4.22 2015-05-18 13:58:25 +03:00
Mihai Bazon
a5b60217ce Fix compressing conditionals
Only transform foo() ? EXP(x) : EXP(y) into EXP(foo() ? x : y) if EXP has no
side effects.

Fix #710
2015-05-18 13:56:04 +03:00
Mihai Bazon
44fd6694eb fix again reserved props 2015-05-13 22:03:00 +03:00
Mihai Bazon
e48db3a8b6 Make reserved names take priority over the name cache 2015-05-07 15:01:16 +03:00
Mihai Bazon
e637bdaf4e Only drop the BOM when it's the first character.
Close #704
2015-05-05 10:11:38 +03:00
Mihai Bazon
d558abbdb7 v2.4.21 2015-05-04 19:14:42 +03:00
Mihai Bazon
4aed0830e5 Fix blank lines in the output.
The issue was more obvious when max_line_len has a small value, rather than
the default 32K characters.  A blank line showed up after most statements.
2015-05-04 17:55:42 +03:00
Mihai Bazon
d2dda34b2a Remove deprecated calls to utils.print/utils.error
Close #542, #641, #647
2015-05-04 15:07:16 +03:00
Mihai Bazon
c3a10c135e Avoid spurious brackets when dropping unused vars
Fix #702
2015-05-04 14:49:17 +03:00
Mihai Bazon
92e4340732 Fix parsing strings with literal DOS newlines
(should not set newline_before)

Fix #693
2015-04-23 12:08:19 +03:00
Mihai Bazon
7b22f2031f If name_cache is specified, do rename cached properties
(even if --mangle-props is not there)
2015-04-22 17:34:49 +03:00
Mihai Bazon
3b14582d6b Fix tests 2015-04-17 11:28:59 +03:00
Mihai Bazon
274e1b3dc7 Drop NaN -> 0/0 transformation.
Fix #687
2015-04-17 11:26:36 +03:00
Fábio Santos
de58b0289d Added expect_exact for testing the OutputStream
This works almost exactly like `expect`, except that you pass a literal string
of which the result is compared with the generated output.
2015-04-14 20:26:47 +02:00
XhmikosR
efea52a4f4 Normalize package.json.
* Specify the files to install in package.json
* Add missing properties
* Follow `npm init`'s scheme
2015-04-14 20:17:03 +02:00
Jordan Harband
763bd36b60 Test on latest node and io.js
Per 0262b4244c - if you're going to stop testing on 0.8, you should be testing on 0.12.

Also allow failures on unstable nodes and "older than two latest" `io.js` versions, and enable "sudo: false" which makes tests run faster.
2015-04-14 20:06:09 +02:00
Mihai Bazon
0552dbd93c v2.4.20 2015-04-13 18:59:21 +03:00
Mihai Bazon
18c63ff3d8 Fix compression of conditionals
Don't move the condition on the right side of an assignment when
the left side may have side effects.

Fix #677
2015-04-13 17:29:48 +03:00
Mihai Bazon
e04ef56243 Use the before visitor in mangle props
(works around a bug in our tree walker which, while cloning nodes, breaks
references between labeled statements and break/continue labels)
2015-04-10 11:33:29 +03:00
Mihai Bazon
5d60484553 More fixes for the breaking changes in yargs
Close #670
2015-04-05 13:20:22 +03:00
Mihai Bazon
3c846e6f7b Merge pull request #669 from galvanix/documentation-inSourceMap
Document passing source maps directly to minify() using inSourceMap
2015-04-04 15:29:03 +03:00
David Caldwell
2850dc69fd Document passing source maps directly to minify() using inSourceMap 2015-04-03 17:27:28 -07:00
Mihai Bazon
94205c3a37 v2.4.19 2015-03-29 14:02:37 +03:00
Mihai Bazon
2ada34b229 Merge pull request #660 from ntkme/fix-long-options
Fix long options
2015-03-29 14:01:21 +03:00
なつき
db396da734 Fix long options 2015-03-29 04:00:42 -07:00
Mihai Bazon
0262b4244c Disable testing with Node 0.8 2015-03-29 13:19:07 +03:00
15 changed files with 419 additions and 98 deletions

3
.gitignore vendored
View File

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

View File

@@ -1,2 +0,0 @@
test
.travis.yml

View File

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

View File

@@ -132,6 +132,7 @@ The available options are:
--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
```
@@ -251,6 +252,10 @@ 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
@@ -591,6 +596,16 @@ var result = UglifyJS.minify("compiled.js", {
// same as before, it returns `code` and `map`
```
If your input source map is not in a file, you can pass it in as an object
using the `inSourceMap` argument:
```javascript
var result = UglifyJS.minify("compiled.js", {
inSourceMap: JSON.parse(my_source_map_string),
outSourceMap: "minified.js.map"
});
```
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
no sense otherwise).

View File

@@ -70,6 +70,7 @@ You need to pass an argument to this option to specify the name that your module
.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")
@@ -88,13 +89,19 @@ You need to pass an argument to this option to specify the name that your module
.string("source-map-root")
.string("source-map-url")
.string("b")
.string("beautify")
.string("m")
.string("mangle")
.string("c")
.string("compress")
.string("d")
.string("define")
.string("e")
.string("enclose")
.string("comments")
.string("wrap")
.string("p")
.string("prefix")
.string("name-cache")
.array("reserved-file")
@@ -104,11 +111,13 @@ You need to pass an argument to this option to specify the name that your module
.boolean("export-all")
.boolean("self")
.boolean("v")
.boolean("verbose")
.boolean("stats")
.boolean("acorn")
.boolean("spidermonkey")
.boolean("lint")
.boolean("V")
.boolean("version")
.boolean("noerr")
.boolean("bare-returns")
.boolean("keep-fnames")
@@ -124,24 +133,24 @@ normalize(ARGS);
if (ARGS.noerr) {
UglifyJS.DefaultsError.croak = function(msg, defs) {
sys.error("WARN: " + msg);
print_error("WARN: " + msg);
};
}
if (ARGS.version || ARGS.V) {
var json = require("../package.json");
sys.puts(json.name + ' ' + json.version);
print(json.name + ' ' + json.version);
process.exit(0);
}
if (ARGS.ast_help) {
var desc = UglifyJS.describe_ast();
sys.puts(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
print(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
process.exit(0);
}
if (ARGS.h || ARGS.help) {
sys.puts(yargs.help());
print(yargs.help());
process.exit(0);
}
@@ -183,6 +192,15 @@ function writeNameCache(key, cache) {
return UglifyJS.writeNameCache(ARGS.name_cache, key, cache);
}
function extractRegex(str) {
if (/^\/.*\/[a-zA-Z]*$/.test(str)) {
var regex_pos = str.lastIndexOf("/");
return new RegExp(str.substr(1, regex_pos - 1), str.substr(regex_pos + 1));
} else {
throw new Error("Invalid regular expression: " + str);
}
}
if (ARGS.quotes === true) {
ARGS.quotes = 3;
}
@@ -207,13 +225,12 @@ if (ARGS.keep_fnames) {
if (BEAUTIFY)
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
if (ARGS.comments) {
if (ARGS.comments != null) {
if (/^\/.*\/[a-zA-Z]*$/.test(ARGS.comments)) {
var regex_pos = ARGS.comments.lastIndexOf("/");
try {
OUTPUT_OPTIONS.comments = new RegExp(ARGS.comments.substr(1, regex_pos - 1), ARGS.comments.substr(regex_pos + 1));
OUTPUT_OPTIONS.comments = extractRegex(ARGS.comments);
} catch (e) {
sys.error("ERROR: Invalid --comments: " + e.message);
print_error("ERROR: Invalid --comments: " + e.message);
}
} else if (ARGS.comments == "all") {
OUTPUT_OPTIONS.comments = true;
@@ -233,7 +250,7 @@ var files = ARGS._.slice();
if (ARGS.self) {
if (files.length > 0) {
sys.error("WARN: Ignoring input files since --self was passed");
print_error("WARN: Ignoring input files since --self was passed");
}
files = UglifyJS.FILES;
if (!ARGS.wrap) ARGS.wrap = "UglifyJS";
@@ -245,7 +262,7 @@ var ORIG_MAP = ARGS.in_source_map;
if (ORIG_MAP) {
ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP));
if (files.length == 0) {
sys.error("INFO: Using file from the input source map: " + ORIG_MAP.file);
print_error("INFO: Using file from the input source map: " + ORIG_MAP.file);
files = [ ORIG_MAP.file ];
}
if (ARGS.source_map_root == null) {
@@ -258,12 +275,12 @@ if (files.length == 0) {
}
if (files.indexOf("-") >= 0 && ARGS.source_map) {
sys.error("ERROR: Source map doesn't work with input from STDIN");
print_error("ERROR: Source map doesn't work with input from STDIN");
process.exit(1);
}
if (files.filter(function(el){ return el == "-" }).length > 1) {
sys.error("ERROR: Can read a single file from STDIN (two or more dashes specified)");
print_error("ERROR: Can read a single file from STDIN (two or more dashes specified)");
process.exit(1);
}
@@ -286,9 +303,9 @@ try {
var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
} catch(ex) {
if (ex instanceof UglifyJS.DefaultsError) {
sys.error(ex.msg);
sys.error("Supported options:");
sys.error(sys.inspect(ex.defs));
print_error(ex.msg);
print_error("Supported options:");
print_error(sys.inspect(ex.defs));
process.exit(1);
}
}
@@ -296,7 +313,7 @@ try {
async.eachLimit(files, 1, function (file, cb) {
read_whole_file(file, function (err, code) {
if (err) {
sys.error("ERROR: can't read file: " + file);
print_error("ERROR: can't read file: " + file);
process.exit(1);
}
if (ARGS.p != null) {
@@ -333,9 +350,9 @@ async.eachLimit(files, 1, function (file, cb) {
});
} catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) {
sys.error("Parse error at " + file + ":" + ex.line + "," + ex.col);
sys.error(ex.message);
sys.error(ex.stack);
print_error("Parse error at " + file + ":" + ex.line + "," + ex.col);
print_error(ex.message);
print_error(ex.stack);
process.exit(1);
}
throw ex;
@@ -349,11 +366,11 @@ async.eachLimit(files, 1, function (file, cb) {
TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
});
if (ARGS.wrap) {
if (ARGS.wrap != null) {
TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all);
}
if (ARGS.enclose) {
if (ARGS.enclose != null) {
var arg_parameter_list = ARGS.enclose;
if (arg_parameter_list === true) {
arg_parameter_list = [];
@@ -364,12 +381,23 @@ async.eachLimit(files, 1, function (file, cb) {
TOPLEVEL = TOPLEVEL.wrap_enclose(arg_parameter_list);
}
if (ARGS.mangle_props) (function(){
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
reserved : reserved,
cache : cache,
only_cache : !ARGS.mangle_props,
regex : regex
});
writeNameCache("props", cache);
})();
@@ -435,15 +463,15 @@ async.eachLimit(files, 1, function (file, cb) {
if (OUTPUT_FILE) {
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
} else {
sys.print(output);
print(output);
}
if (ARGS.stats) {
sys.error(UglifyJS.string_template("Timing information (compressed {count} files):", {
print_error(UglifyJS.string_template("Timing information (compressed {count} files):", {
count: files.length
}));
for (var i in STATS) if (STATS.hasOwnProperty(i)) {
sys.error(UglifyJS.string_template("- {name}: {time}s", {
print_error(UglifyJS.string_template("- {name}: {time}s", {
name: i,
time: (STATS[i] / 1000).toFixed(3)
}));
@@ -470,7 +498,7 @@ function getOptions(x, constants) {
ast = UglifyJS.parse(x, { expression: true });
} catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) {
sys.error("Error parsing arguments in: " + x);
print_error("Error parsing arguments in: " + x);
process.exit(1);
}
}
@@ -489,8 +517,8 @@ function getOptions(x, constants) {
ret[name] = true;
return true; // no descend
}
sys.error(node.TYPE)
sys.error("Error parsing arguments in: " + x);
print_error(node.TYPE)
print_error("Error parsing arguments in: " + x);
process.exit(1);
}));
}
@@ -522,3 +550,11 @@ function time_it(name, cont) {
}
return ret;
}
function print_error(msg) {
console.error("%s", msg);
}
function print(txt) {
console.log("%s", txt);
}

View File

@@ -1168,12 +1168,12 @@ merge(Compressor.prototype, {
return make_node(AST_EmptyStatement, node);
}
if (def.length == 0) {
return side_effects;
return in_list ? MAP.splice(side_effects.body) : side_effects;
}
node.definitions = def;
if (side_effects) {
side_effects.body.unshift(node);
node = side_effects;
return in_list ? MAP.splice(side_effects.body) : side_effects;
}
return node;
}
@@ -2104,6 +2104,34 @@ merge(Compressor.prototype, {
}
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) {
case "&&":
var ll = self.left.evaluate(compressor);
@@ -2155,7 +2183,7 @@ merge(Compressor.prototype, {
}
break;
}
if (compressor.option("comparisons")) {
if (compressor.option("comparisons") && self.is_boolean()) {
if (!(compressor.parent() instanceof AST_Binary)
|| compressor.parent() instanceof AST_Assign) {
var negated = make_node(AST_UnaryPrefix, self, {
@@ -2230,10 +2258,11 @@ merge(Compressor.prototype, {
}
}
}
// x * (y * z) ==> x * y * z
// x && (y && z) ==> x && y && z
// x || (y || z) ==> x || y || z
if (self.right instanceof AST_Binary
&& self.right.operator == self.operator
&& (self.operator == "*" || self.operator == "&&" || self.operator == "||"))
&& (self.operator == "&&" || self.operator == "||"))
{
self.left = make_node(AST_Binary, self.left, {
operator : self.operator,
@@ -2272,14 +2301,6 @@ merge(Compressor.prototype, {
});
});
OPT(AST_NaN, function (self, compressor) {
return make_node(AST_Binary, self, {
operator : '/',
left : make_node(AST_Number, self, {value: 0}),
right : make_node(AST_Number, self, {value: 0})
});
});
OPT(AST_Undefined, function(self, compressor){
if (compressor.option("unsafe")) {
var scope = compressor.find_parent(AST_Scope);
@@ -2343,6 +2364,7 @@ merge(Compressor.prototype, {
&& alternative instanceof AST_Assign
&& consequent.operator == alternative.operator
&& consequent.left.equivalent_to(alternative.left)
&& !consequent.left.has_side_effects(compressor)
) {
/*
* Stuff like this:
@@ -2363,6 +2385,7 @@ merge(Compressor.prototype, {
if (consequent instanceof AST_Call
&& alternative.TYPE === consequent.TYPE
&& consequent.args.length == alternative.args.length
&& !consequent.expression.has_side_effects(compressor)
&& consequent.expression.equivalent_to(alternative.expression)) {
if (consequent.args.length == 0) {
return make_node(AST_Seq, self, {

View File

@@ -176,7 +176,6 @@ function OutputStream(options) {
might_need_space = false;
}
might_need_semicolon = false;
maybe_newline();
}
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"));
var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF"));
var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:"));
@@ -213,7 +213,7 @@ var EX_EOF = {};
function tokenizer($TEXT, filename, html5_comments) {
var S = {
text : $TEXT.replace(/\uFEFF/g, ''),
text : $TEXT,
filename : filename,
pos : 0,
tokpos : 0,
@@ -354,8 +354,13 @@ function tokenizer($TEXT, filename, html5_comments) {
case 120 : return String.fromCharCode(hex_bytes(2)); // \x
case 117 : return String.fromCharCode(hex_bytes(4)); // \u
case 10 : return ""; // newline
default : return ch;
case 13 : // \r
if (peek() == "\n") { // DOS newline
next(true, in_string);
return "";
}
}
return ch;
};
function hex_bytes(n) {
@@ -372,7 +377,7 @@ function tokenizer($TEXT, filename, html5_comments) {
var read_string = with_eof_error("Unterminated string constant", function(quote_char){
var quote = next(), ret = "";
for (;;) {
var ch = next(true);
var ch = next(true, true);
if (ch == "\\") {
// read OctalEscapeSequence (XXX: deprecated if "strict mode")
// https://github.com/mishoo/UglifyJS/issues/178

View File

@@ -63,7 +63,9 @@ function find_builtins() {
function mangle_properties(ast, options) {
options = defaults(options, {
reserved : null,
cache : null
cache : null,
only_cache : false,
regex : null
});
var reserved = options.reserved;
@@ -78,6 +80,8 @@ function mangle_properties(ast, options) {
};
}
var regex = options.regex;
var names_to_mangle = [];
// step 1: find candidates to mangle
@@ -102,7 +106,7 @@ function mangle_properties(ast, options) {
}));
// step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(null, function(node){
return ast.transform(new TreeTransformer(function(node){
if (node instanceof AST_ObjectKeyVal) {
if (should_mangle(node.key)) {
node.key = mangle(node.key);
@@ -140,11 +144,16 @@ function mangle_properties(ast, options) {
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;
}

View File

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

View File

@@ -86,7 +86,9 @@ ifs_4: {
x(foo)[10].bar.baz = something_else();
}
expect: {
x(foo)[10].bar.baz = (foo && bar) ? something() : something_else();
foo && bar
? x(foo)[10].bar.baz = something()
: x(foo)[10].bar.baz = something_else();
}
}
@@ -133,6 +135,7 @@ ifs_6: {
comparisons: true
};
input: {
var x;
if (!foo && !bar && !baz && !boo) {
x = 10;
} else {
@@ -140,6 +143,7 @@ ifs_6: {
}
}
expect: {
var x;
x = foo || bar || baz || boo ? 20 : 10;
}
}
@@ -149,6 +153,7 @@ cond_1: {
conditionals: true
};
input: {
var do_something; // if undeclared it's assumed to have side-effects
if (some_condition()) {
do_something(x);
} else {
@@ -156,6 +161,7 @@ cond_1: {
}
}
expect: {
var do_something;
do_something(some_condition() ? x : y);
}
}
@@ -165,6 +171,7 @@ cond_2: {
conditionals: true
};
input: {
var x, FooBar;
if (some_condition()) {
x = new FooBar(1);
} else {
@@ -172,6 +179,7 @@ cond_2: {
}
}
expect: {
var x, FooBar;
x = new FooBar(some_condition() ? 1 : 2);
}
}
@@ -181,6 +189,7 @@ cond_3: {
conditionals: true
};
input: {
var FooBar;
if (some_condition()) {
new FooBar(1);
} else {
@@ -188,6 +197,7 @@ cond_3: {
}
}
expect: {
var FooBar;
some_condition() ? new FooBar(1) : FooBar(2);
}
}
@@ -197,6 +207,7 @@ cond_4: {
conditionals: true
};
input: {
var do_something;
if (some_condition()) {
do_something();
} else {
@@ -204,6 +215,7 @@ cond_4: {
}
}
expect: {
var do_something;
some_condition(), do_something();
}
}
@@ -303,6 +315,7 @@ cond_7_1: {
evaluate : true
};
input: {
var x;
// access to global should be assumed to have side effects
if (y) {
x = 1+1;
@@ -311,6 +324,7 @@ cond_7_1: {
}
}
expect: {
var x;
x = (y, 2);
}
}
@@ -321,6 +335,7 @@ cond_8: {
evaluate : true
};
input: {
var a;
// compress these
a = condition ? true : false;
@@ -355,6 +370,7 @@ cond_8: {
}
expect: {
var a;
a = !!condition;
a = !condition;
a = !!condition();
@@ -368,3 +384,163 @@ cond_8: {
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

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

View File

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

View File

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

View File

@@ -1,10 +1,8 @@
var path = require("path");
var fs = require("fs");
var vm = require("vm");
var sys = require("util");
var UglifyJS = vm.createContext({
sys : sys,
console : console,
process : process,
Buffer : Buffer,
@@ -19,7 +17,7 @@ function load_global(file) {
} catch(ex) {
// XXX: in case of a syntax error, the message is kinda
// useless. (no location information).
sys.debug("ERROR in file: " + file + " / " + ex);
console.log("ERROR in file: " + file + " / " + ex);
process.exit(1);
}
};
@@ -42,7 +40,7 @@ var FILES = exports.FILES = [
FILES.forEach(load_global);
UglifyJS.AST_Node.warn_function = function(txt) {
sys.error("WARN: " + txt);
console.error("WARN: %s", txt);
};
// XXX: perhaps we shouldn't export everything but heck, I'm lazy.