aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--README.md5
-rwxr-xr-xbin/uglifyjs25
-rw-r--r--lib/compress.js36
-rw-r--r--lib/output.js10
-rw-r--r--lib/parse.js6
-rw-r--r--lib/propmangle.js32
-rw-r--r--package.json4
-rw-r--r--test/compress/conditionals.js160
-rw-r--r--test/compress/issue-747.js37
-rw-r--r--test/compress/issue-751.js29
-rwxr-xr-xtest/run-tests.js5
-rw-r--r--tools/node.js3
13 files changed, 329 insertions, 25 deletions
diff --git a/.travis.yml b/.travis.yml
index 4e046b8f..c33b24d1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,8 +3,8 @@ before_install: "npm install -g npm"
node_js:
- "iojs"
- "0.12"
- - "0.11"
- "0.10"
+ - "0.8"
matrix:
fast_finish: true
sudo: false
diff --git a/README.md b/README.md
index 42579151..b6abc379 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/bin/uglifyjs b/bin/uglifyjs
index 8851660f..4768f766 100755
--- a/bin/uglifyjs
+++ b/bin/uglifyjs
@@ -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")
@@ -191,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;
}
@@ -217,9 +227,8 @@ if (BEAUTIFY)
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) {
print_error("ERROR: Invalid --comments: " + e.message);
}
@@ -375,10 +384,20 @@ async.eachLimit(files, 1, function (file, cb) {
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
+ only_cache : !ARGS.mangle_props,
+ regex : regex
});
writeNameCache("props", cache);
})();
diff --git a/lib/compress.js b/lib/compress.js
index 530e7c2f..401a1c75 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -896,6 +896,7 @@ merge(Compressor.prototype, {
def(AST_Call, function(compressor){
var pure = compressor.option("pure_funcs");
if (!pure) return true;
+ if (typeof pure == "function") return pure(this);
return pure.indexOf(this.expression.print_to_string()) < 0;
});
@@ -2104,6 +2105,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 +2184,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 +2259,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,
diff --git a/lib/output.js b/lib/output.js
index 1d67b1b9..396c6a29 100644
--- a/lib/output.js
+++ b/lib/output.js
@@ -161,6 +161,8 @@ function OutputStream(options) {
str = String(str);
var ch = str.charAt(0);
if (might_need_semicolon) {
+ might_need_semicolon = false;
+
if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) {
if (options.semicolons || requireSemicolonChars(ch)) {
OUTPUT += ";";
@@ -171,11 +173,17 @@ function OutputStream(options) {
current_pos++;
current_line++;
current_col = 0;
+
+ if (/^\s+$/.test(str)) {
+ // reset the semicolon flag, since we didn't print one
+ // now and might still have to later
+ might_need_semicolon = true;
+ }
}
+
if (!options.beautify)
might_need_space = false;
}
- might_need_semicolon = false;
}
if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
diff --git a/lib/parse.js b/lib/parse.js
index e65c4faa..ab72ad2d 100644
--- a/lib/parse.js
+++ b/lib/parse.js
@@ -480,7 +480,11 @@ function tokenizer($TEXT, filename, html5_comments) {
regexp += ch;
}
var mods = read_name();
- return token("regexp", new RegExp(regexp, mods));
+ try {
+ return token("regexp", new RegExp(regexp, mods));
+ } catch(e) {
+ parse_error(e.message);
+ }
});
function read_operator(prefix) {
diff --git a/lib/propmangle.js b/lib/propmangle.js
index 0890eaa7..840bda91 100644
--- a/lib/propmangle.js
+++ b/lib/propmangle.js
@@ -64,7 +64,8 @@ function mangle_properties(ast, options) {
options = defaults(options, {
reserved : null,
cache : null,
- only_cache : false
+ only_cache : false,
+ regex : null
});
var reserved = options.reserved;
@@ -79,7 +80,10 @@ function mangle_properties(ast, options) {
};
}
+ var regex = options.regex;
+
var names_to_mangle = [];
+ var unmangleable = [];
// step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){
@@ -105,20 +109,14 @@ function mangle_properties(ast, options) {
// 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);
- }
+ 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);
- }
+ node.key.name = mangle(node.key.name);
}
else if (node instanceof AST_Dot) {
- if (should_mangle(node.property)) {
- node.property = mangle(node.property);
- }
+ node.property = mangle(node.property);
}
else if (node instanceof AST_Sub) {
node.property = mangleStrings(node.property);
@@ -140,6 +138,7 @@ function mangle_properties(ast, options) {
// only function declarations after this line
function can_mangle(name) {
+ if (unmangleable.indexOf(name) >= 0) return false;
if (reserved.indexOf(name) >= 0) return false;
if (options.only_cache) {
return cache.props.has(name);
@@ -149,6 +148,7 @@ function mangle_properties(ast, options) {
}
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;
@@ -157,9 +157,17 @@ function mangle_properties(ast, options) {
function add(name) {
if (can_mangle(name))
push_uniq(names_to_mangle, name);
+
+ if (!should_mangle(name)) {
+ push_uniq(unmangleable, name);
+ }
}
function mangle(name) {
+ if (!should_mangle(name)) {
+ return name;
+ }
+
var mangled = cache.props.get(name);
if (!mangled) {
do {
@@ -202,9 +210,7 @@ function mangle_properties(ast, options) {
node.cdr = mangleStrings(node.cdr);
}
else if (node instanceof AST_String) {
- if (should_mangle(node.value)) {
- node.value = mangle(node.value);
- }
+ node.value = mangle(node.value);
}
else if (node instanceof AST_Conditional) {
node.consequent = mangleStrings(node.consequent);
diff --git a/package.json b/package.json
index 27f8c857..0bf1de09 100644
--- a/package.json
+++ b/package.json
@@ -4,9 +4,9 @@
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD",
- "version": "2.4.23",
+ "version": "2.4.24",
"engines": {
- "node": ">=0.4.0"
+ "node": ">=0.8.0"
},
"maintainers": [
"Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)"
diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js
index 1b961812..16ef6d66 100644
--- a/test/compress/conditionals.js
+++ b/test/compress/conditionals.js
@@ -384,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;
+ }
+}
diff --git a/test/compress/issue-747.js b/test/compress/issue-747.js
new file mode 100644
index 00000000..0a4e4502
--- /dev/null
+++ b/test/compress/issue-747.js
@@ -0,0 +1,37 @@
+dont_reuse_prop: {
+ mangle_props = {
+ regex: /asd/
+ };
+
+ input: {
+ var obj = {};
+ obj.a = 123;
+ obj.asd = 256;
+ console.log(obj.a);
+ }
+ expect: {
+ var obj = {};
+ obj.a = 123;
+ obj.b = 256;
+ console.log(obj.a);
+ }
+}
+
+unmangleable_props_should_always_be_reserved: {
+ mangle_props = {
+ regex: /asd/
+ };
+
+ input: {
+ var obj = {};
+ obj.asd = 256;
+ obj.a = 123;
+ console.log(obj.a);
+ }
+ expect: {
+ var obj = {};
+ obj.b = 256;
+ obj.a = 123;
+ console.log(obj.a);
+ }
+} \ No newline at end of file
diff --git a/test/compress/issue-751.js b/test/compress/issue-751.js
new file mode 100644
index 00000000..829b7ca5
--- /dev/null
+++ b/test/compress/issue-751.js
@@ -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();
+ }
+}
diff --git a/test/run-tests.js b/test/run-tests.js
index 215f6af8..fc7476f9 100755
--- a/test/run-tests.js
+++ b/test/run-tests.js
@@ -31,7 +31,7 @@ function tmpl() {
function log() {
var txt = tmpl.apply(this, arguments);
- sys.puts(txt);
+ console.log("%s", txt);
}
function log_directory(dir) {
@@ -92,6 +92,9 @@ function run_compress_tests() {
}
var input = as_toplevel(test.input);
var input_code = make_code(test.input);
+ if (test.mangle_props) {
+ input = U.mangle_properties(input, test.mangle_props);
+ }
var output = input.transform(cmp);
output.figure_out_scope();
output = make_code(output, false);
diff --git a/tools/node.js b/tools/node.js
index cbe49e39..7bba3231 100644
--- a/tools/node.js
+++ b/tools/node.js
@@ -84,6 +84,9 @@ exports.minify = function(files, options) {
});
});
}
+ if (options.wrap) {
+ toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll);
+ }
// 2. compress
if (options.compress) {