Compare commits

...

18 Commits

Author SHA1 Message Date
Alex Lam S.L
3bc7cc82bb v3.5.12 2019-05-12 10:40:13 +08:00
Alex Lam S.L
45fbdbc2dc improve tests (#3408) 2019-05-12 09:44:02 +08:00
Alex Lam S.L
54cb678055 fix corner case in assignments (#3407)
fixes #3406
2019-05-12 03:52:46 +08:00
Alex Lam S.L
e88c439eac improve tests (#3405) 2019-05-11 22:06:14 +08:00
Alex Lam S.L
9fc8cd4076 fix corner case in functions (#3403)
fixes #3402
2019-05-11 18:55:45 +08:00
Alex Lam S.L
5476cb8f05 fix corner case in inline (#3401)
fixes #3400
2019-05-10 01:22:44 +08:00
Alex Lam S.L
6a30e1d6be improve tests (#3399) 2019-05-09 07:18:22 +08:00
Alex Lam S.L
e4881245d9 v3.5.11 2019-05-07 01:45:40 +08:00
Alex Lam S.L
354fec8a9c make enclose & wrap work with sourceMap (#3396)
fixes #3313
2019-05-04 20:25:52 +08:00
Alex Lam S.L
11cdab745d fix corner cases in sourceMap (#3397)
fixes #3255
fixes #3294
2019-05-04 20:08:57 +08:00
Alex Lam S.L
a89d424a0b render comments in custom ASTs gracefully (#3393)
fixes #3246
2019-05-02 13:50:51 +08:00
Alex Lam S.L
429d2b56b7 v3.5.10 2019-05-02 00:01:40 +08:00
Alex Lam S.L
2ea96549c5 unify logging functionality (#3392)
fixes #3253
fixes #3254
2019-04-30 06:32:24 +08:00
Alex Lam S.L
fba008e298 remove blanket safeguard from source-map (#3391)
Things has stabilised since 80a18fe2fa, so it makes sense to remove the unconditional masking for ease of debugging.
2019-04-30 02:40:36 +08:00
Alex Lam S.L
c37a8e927e fix corner case in properties (#3390)
fixes #3389
2019-04-29 17:23:00 +08:00
Alex Lam S.L
413bbe0480 fix corner case in evaluate (#3388)
fixes #3387
2019-04-29 08:55:46 +08:00
Alex Lam S.L
34075fc4c4 v3.5.9 2019-04-27 17:00:58 +08:00
Alex Lam S.L
e5436ca566 enhance side_effects (#3384) 2019-04-25 15:15:50 +08:00
37 changed files with 1020 additions and 572 deletions

View File

@@ -1,35 +1,46 @@
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- libstdc++-4.9-dev
cache: cache:
directories: tmp directories: tmp
language: generic language: generic
matrix: matrix:
fast_finish: true fast_finish: true
sudo: false
env: env:
global: - NODE=0.10 TYPE=compress
- UGLIFYJS_TEST_ALL=1 - NODE=0.10 TYPE=mocha
matrix: - NODE=0.10 TYPE=release/benchmark
- NODEJS_VER=node/0.10 - NODE=0.10 TYPE=release/jetstream
- NODEJS_VER=node/0.12 - NODE=0.12 TYPE=compress
- NODEJS_VER=node/4 - NODE=0.12 TYPE=mocha
- NODEJS_VER=node/6 - NODE=0.12 TYPE=release/benchmark
- NODEJS_VER=node/8 - NODE=0.12 TYPE=release/jetstream
- NODEJS_VER=node/10 - NODE=4 TYPE=compress
- NODEJS_VER=node/latest - NODE=4 TYPE=mocha
- NODE=4 TYPE=release/benchmark
- NODE=4 TYPE=release/jetstream
- NODE=6 TYPE=compress
- NODE=6 TYPE=mocha
- NODE=6 TYPE=release/benchmark
- NODE=6 TYPE=release/jetstream
- NODE=8 TYPE=compress
- NODE=8 TYPE=mocha
- NODE=8 TYPE=release/benchmark
- NODE=8 TYPE=release/jetstream
- NODE=10 TYPE=compress
- NODE=10 TYPE=mocha
- NODE=10 TYPE=release/benchmark
- NODE=10 TYPE=release/jetstream
- NODE=latest TYPE=compress
- NODE=latest TYPE=mocha
- NODE=latest TYPE=release/benchmark
- NODE=latest TYPE=release/jetstream
before_install: before_install:
- git clone --branch v1.4.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs - git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
- . ~/.nvs/nvs.sh - . ~/.nvs/nvs.sh
- nvs --version - nvs --version
install: install:
- nvs add $NODEJS_VER - nvs add node/$NODE
- nvs use $NODEJS_VER - nvs use node/$NODE
- node --version - node --version
- npm --version --no-update-notifier - npm --version --no-update-notifier
- npm install --no-optional --no-save --no-update-notifier - npm install --no-audit --no-optional --no-save --no-update-notifier
script: script:
- npm test --no-update-notifier - node test/$TYPE

View File

@@ -767,9 +767,6 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `unused` (default: `true`) -- drop unreferenced functions and variables (simple - `unused` (default: `true`) -- drop unreferenced functions and variables (simple
direct variable assignments do not count as references unless set to `"keep_assign"`) direct variable assignments do not count as references unless set to `"keep_assign"`)
- `warnings` (default: `false`) -- display warnings when dropping unreachable
code or unused declarations etc.
## Mangle options ## Mangle options
- `eval` (default `false`) -- Pass `true` to mangle names visible in scopes - `eval` (default `false`) -- Pass `true` to mangle names visible in scopes

View File

@@ -1,26 +1,74 @@
environment:
UGLIFYJS_TEST_ALL: 1
matrix:
- NODEJS_VER: node/0.10
- NODEJS_VER: node/0.12
- NODEJS_VER: node/4
- NODEJS_VER: node/6
- NODEJS_VER: node/8
- NODEJS_VER: node/10
- NODEJS_VER: node/latest
install:
- git clone --branch v1.4.2 --depth 1 https://github.com/jasongin/nvs.git %LOCALAPPDATA%\nvs
- set PATH=%LOCALAPPDATA%\nvs;%PATH%
- nvs --version
- nvs add %NODEJS_VER%
- nvs use %NODEJS_VER%
- node --version
- npm --version --no-update-notifier
- npm install --no-optional --no-save --no-update-notifier
build: off build: off
cache: cache:
- tmp - tmp
matrix: matrix:
fast_finish: true fast_finish: true
environment:
matrix:
- NODE: 0.10
TYPE: compress
- NODE: 0.10
TYPE: mocha
- NODE: 0.10
TYPE: release/benchmark
- NODE: 0.10
TYPE: release/jetstream
- NODE: 0.12
TYPE: compress
- NODE: 0.12
TYPE: mocha
- NODE: 0.12
TYPE: release/benchmark
- NODE: 0.12
TYPE: release/jetstream
- NODE: 4
TYPE: compress
- NODE: 4
TYPE: mocha
- NODE: 4
TYPE: release/benchmark
- NODE: 4
TYPE: release/jetstream
- NODE: 6
TYPE: compress
- NODE: 6
TYPE: mocha
- NODE: 6
TYPE: release/benchmark
- NODE: 6
TYPE: release/jetstream
- NODE: 8
TYPE: compress
- NODE: 8
TYPE: mocha
- NODE: 8
TYPE: release/benchmark
- NODE: 8
TYPE: release/jetstream
- NODE: 10
TYPE: compress
- NODE: 10
TYPE: mocha
- NODE: 10
TYPE: release/benchmark
- NODE: 10
TYPE: release/jetstream
- NODE: latest
TYPE: compress
- NODE: latest
TYPE: mocha
- NODE: latest
TYPE: release/benchmark
- NODE: latest
TYPE: release/jetstream
install:
- git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git %LOCALAPPDATA%\nvs
- set PATH=%LOCALAPPDATA%\nvs;%PATH%
- nvs --version
- nvs add node/%NODE%
- nvs use node/%NODE%
- node --version
- npm --version --no-update-notifier
- npm install --no-audit --no-optional --no-save --no-update-notifier
test_script: test_script:
- npm test --no-update-notifier - node test/%TYPE%

View File

@@ -63,7 +63,7 @@ if (program.configFile) {
} }
} }
if (!program.output && program.sourceMap && program.sourceMap.url != "inline") { if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
fatal("ERROR: cannot write source map to STDOUT"); fatal("cannot write source map to STDOUT");
} }
[ [
"compress", "compress",
@@ -78,6 +78,15 @@ if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
options[name] = program[name]; options[name] = program[name];
} }
}); });
if (program.verbose) {
options.warnings = "verbose";
} else if (program.warn) {
options.warnings = true;
}
if (options.warnings) {
UglifyJS.AST_Node.log_function(print_error, options.warnings == "verbose");
delete options.warnings;
}
if (program.beautify) { if (program.beautify) {
options.output = typeof program.beautify == "object" ? program.beautify : {}; options.output = typeof program.beautify == "object" ? program.beautify : {};
if (!("beautify" in options.output)) { if (!("beautify" in options.output)) {
@@ -124,7 +133,7 @@ if (program.parse) {
if (!program.parse.acorn && !program.parse.spidermonkey) { if (!program.parse.acorn && !program.parse.spidermonkey) {
options.parse = program.parse; options.parse = program.parse;
} else if (program.sourceMap && program.sourceMap.content == "inline") { } else if (program.sourceMap && program.sourceMap.content == "inline") {
fatal("ERROR: inline source map only works with built-in parser"); fatal("inline source map only works with built-in parser");
} }
} }
if (~program.rawArgs.indexOf("--rename")) { if (~program.rawArgs.indexOf("--rename")) {
@@ -144,15 +153,8 @@ if (typeof program.sourceMap == "object" && "base" in program.sourceMap) {
}; };
}(); }();
} }
if (program.verbose) {
options.warnings = "verbose";
} else if (program.warn) {
options.warnings = true;
}
if (program.self) { if (program.self) {
if (program.args.length) { if (program.args.length) UglifyJS.AST_Node.warn("Ignoring input files since --self was passed");
print_error("WARN: Ignoring input files since --self was passed");
}
if (!options.wrap) options.wrap = "UglifyJS"; if (!options.wrap) options.wrap = "UglifyJS";
simple_glob(UglifyJS.FILES).forEach(function(name) { simple_glob(UglifyJS.FILES).forEach(function(name) {
files[convert_path(name)] = read_file(name); files[convert_path(name)] = read_file(name);
@@ -180,12 +182,9 @@ function convert_ast(fn) {
} }
function run() { function run() {
UglifyJS.AST_Node.warn_function = function(msg) {
print_error("WARN: " + msg);
};
var content = program.sourceMap && program.sourceMap.content; var content = program.sourceMap && program.sourceMap.content;
if (content && content != "inline") { if (content && content != "inline") {
print_error("INFO: Using input source map: " + content); UglifyJS.AST_Node.info("Using input source map: " + content);
options.sourceMap.content = read_file(content, content); options.sourceMap.content = read_file(content, content);
} }
if (program.timings) options.timings = true; if (program.timings) options.timings = true;
@@ -216,8 +215,10 @@ function run() {
var ex = result.error; var ex = result.error;
if (ex.name == "SyntaxError") { if (ex.name == "SyntaxError") {
print_error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col); print_error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
var file = files[ex.filename];
if (file) {
var col = ex.col; var col = ex.col;
var lines = files[ex.filename].split(/\r?\n/); var lines = file.split(/\r?\n/);
var line = lines[ex.line - 1]; var line = lines[ex.line - 1];
if (!line && !col) { if (!line && !col) {
line = lines[ex.line - 2]; line = lines[ex.line - 2];
@@ -233,7 +234,7 @@ function run() {
print_error(line.slice(0, col).replace(/\S/g, " ") + "^"); print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
} }
} }
if (ex.defs) { } else if (ex.defs) {
print_error("Supported options:"); print_error("Supported options:");
print_error(format_object(ex.defs)); print_error(format_object(ex.defs));
} }
@@ -293,7 +294,11 @@ function run() {
} }
function fatal(message) { function fatal(message) {
if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:") if (message instanceof Error) {
message = message.stack.replace(/^\S*?Error:/, "ERROR:")
} else {
message = "ERROR: " + message;
}
print_error(message); print_error(message);
process.exit(1); process.exit(1);
} }
@@ -370,9 +375,9 @@ function parse_js(flag) {
}); });
} }
})); }));
} catch(ex) { } catch (ex) {
if (flag) { if (flag) {
fatal("Error parsing arguments for '" + flag + "': " + value); fatal("cannot parse arguments for '" + flag + "': " + value);
} else { } else {
options[value] = null; options[value] = null;
} }

View File

@@ -118,9 +118,25 @@ var AST_Node = DEFNODE("Node", "start end", {
} }
}, null); }, null);
AST_Node.warn = function(txt, props) { (AST_Node.log_function = function(fn, verbose) {
if (AST_Node.warn_function) AST_Node.warn_function(string_template(txt, props)); var printed = Object.create(null);
}; if (fn) {
AST_Node.info = verbose ? function(text, props) {
log("INFO: " + string_template(text, props));
} : noop;
AST_Node.warn = function(text, props) {
log("WARN: " + string_template(text, props));
};
} else {
AST_Node.info = AST_Node.warn = noop;
}
function log(msg) {
if (printed[msg]) return;
printed[msg] = true;
fn(msg);
}
})();
/* -----[ statements ]----- */ /* -----[ statements ]----- */
@@ -321,18 +337,25 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
$propdoc: { $propdoc: {
globals: "[Object/S] a map of name -> SymbolDef for all undeclared names", globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
}, },
wrap_commonjs: function(name) { wrap: function(name) {
var body = this.body; var body = this.body;
var wrapped_tl = "(function(exports){'$ORIG';})(typeof " + name + "=='undefined'?(" + name + "={}):" + name + ");"; return parse([
wrapped_tl = parse(wrapped_tl); "(function(exports){'$ORIG';})(typeof ",
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function(node) { name,
"=='undefined'?(",
name,
"={}):",
name,
");"
].join(""), {
filename: "wrap=" + JSON.stringify(name)
}).transform(new TreeTransformer(function(node) {
if (node instanceof AST_Directive && node.value == "$ORIG") { if (node instanceof AST_Directive && node.value == "$ORIG") {
return MAP.splice(body); return MAP.splice(body);
} }
})); }));
return wrapped_tl;
}, },
wrap_enclose: function(args_values) { enclose: function(args_values) {
if (typeof args_values != "string") args_values = ""; if (typeof args_values != "string") args_values = "";
var index = args_values.indexOf(":"); var index = args_values.indexOf(":");
if (index < 0) index = args_values.length; if (index < 0) index = args_values.length;
@@ -343,7 +366,9 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
'){"$ORIG"})(', '){"$ORIG"})(',
args_values.slice(index + 1), args_values.slice(index + 1),
")" ")"
].join("")).transform(new TreeTransformer(function(node) { ].join(""), {
filename: "enclose=" + JSON.stringify(args_values)
}).transform(new TreeTransformer(function(node) {
if (node instanceof AST_Directive && node.value == "$ORIG") { if (node instanceof AST_Directive && node.value == "$ORIG") {
return MAP.splice(body); return MAP.splice(body);
} }

View File

@@ -94,7 +94,6 @@ function Compressor(options, false_by_default) {
unsafe_regexp : false, unsafe_regexp : false,
unsafe_undefined: false, unsafe_undefined: false,
unused : !false_by_default, unused : !false_by_default,
warnings : false,
}, true); }, true);
var global_defs = this.options["global_defs"]; var global_defs = this.options["global_defs"];
if (typeof global_defs == "object") for (var key in global_defs) { if (typeof global_defs == "object") for (var key in global_defs) {
@@ -144,7 +143,6 @@ function Compressor(options, false_by_default) {
}; };
var sequences = this.options["sequences"]; var sequences = this.options["sequences"];
this.sequences_limit = sequences == 1 ? 800 : sequences | 0; this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
this.warnings_produced = {};
} }
Compressor.prototype = new TreeTransformer; Compressor.prototype = new TreeTransformer;
@@ -175,7 +173,7 @@ merge(Compressor.prototype, {
node.walk(new TreeWalker(function() { node.walk(new TreeWalker(function() {
count++; count++;
})); }));
this.info("pass " + pass + ": last_count: " + min_count + ", count: " + count); AST_Node.info("pass " + pass + ": last_count: " + min_count + ", count: " + count);
if (count < min_count) { if (count < min_count) {
min_count = count; min_count = count;
stopping = false; stopping = false;
@@ -191,24 +189,6 @@ merge(Compressor.prototype, {
} }
return node; return node;
}, },
info: function() {
if (this.options.warnings == "verbose") {
AST_Node.warn.apply(AST_Node, arguments);
}
},
warn: function(text, props) {
if (this.options.warnings) {
// only emit unique warnings
var message = string_template(text, props);
if (!(message in this.warnings_produced)) {
this.warnings_produced[message] = true;
AST_Node.warn.apply(AST_Node, arguments);
}
}
},
clear_warnings: function() {
this.warnings_produced = {};
},
before: function(node, descend, in_list) { before: function(node, descend, in_list) {
if (node._squeezed) return node; if (node._squeezed) return node;
var is_scope = node instanceof AST_Scope; var is_scope = node instanceof AST_Scope;
@@ -1138,7 +1118,7 @@ merge(Compressor.prototype, {
if (value_def && candidate instanceof AST_VarDef) return node; if (value_def && candidate instanceof AST_VarDef) return node;
} }
CHANGED = abort = true; CHANGED = abort = true;
compressor.info("Collapsing {name} [{file}:{line},{col}]", { AST_Node.info("Collapsing {name} [{file}:{line},{col}]", {
name: node.print_to_string(), name: node.print_to_string(),
file: node.start.file, file: node.start.file,
line: node.start.line, line: node.start.line,
@@ -1963,7 +1943,7 @@ merge(Compressor.prototype, {
statements.length = n; statements.length = n;
CHANGED = n != len; CHANGED = n != len;
if (has_quit) has_quit.forEach(function(stat) { if (has_quit) has_quit.forEach(function(stat) {
extract_declarations_from_unreachable_code(compressor, stat, statements); extract_declarations_from_unreachable_code(stat, statements);
}); });
} }
@@ -2216,13 +2196,13 @@ merge(Compressor.prototype, {
} }
} }
function extract_declarations_from_unreachable_code(compressor, stat, target) { function extract_declarations_from_unreachable_code(stat, target) {
if (!(stat instanceof AST_Defun)) { if (!(stat instanceof AST_Defun)) {
compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start); AST_Node.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
} }
stat.walk(new TreeWalker(function(node) { stat.walk(new TreeWalker(function(node) {
if (node instanceof AST_Definitions) { if (node instanceof AST_Definitions) {
compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start); AST_Node.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
node.remove_initializers(); node.remove_initializers();
target.push(node); target.push(node);
return true; return true;
@@ -2553,9 +2533,6 @@ merge(Compressor.prototype, {
return this.tail_node().is_string(compressor); return this.tail_node().is_string(compressor);
}); });
def(AST_String, return_true); def(AST_String, return_true);
def(AST_Sub, function(compressor) {
return this.expression.is_string(compressor) && this.property instanceof AST_Number;
});
def(AST_SymbolRef, function(compressor) { def(AST_SymbolRef, function(compressor) {
var fixed = this.fixed_value(); var fixed = this.fixed_value();
if (!fixed) return false; if (!fixed) return false;
@@ -2602,8 +2579,8 @@ merge(Compressor.prototype, {
return make_node_from_constant(value, orig); return make_node_from_constant(value, orig);
} }
function warn(compressor, node) { function warn(node) {
compressor.warn("global_defs " + node.print_to_string() + " redefined [{file}:{line},{col}]", node.start); AST_Node.warn("global_defs " + node.print_to_string() + " redefined [{file}:{line},{col}]", node.start);
} }
AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) { AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
@@ -2619,7 +2596,7 @@ merge(Compressor.prototype, {
child = parent; child = parent;
} }
if (is_lhs(child, parent)) { if (is_lhs(child, parent)) {
warn(compressor, node); warn(node);
return; return;
} }
return def; return def;
@@ -2631,7 +2608,7 @@ merge(Compressor.prototype, {
}); });
def(AST_SymbolDeclaration, function(compressor) { def(AST_SymbolDeclaration, function(compressor) {
if (!this.global()) return; if (!this.global()) return;
if (HOP(compressor.option("global_defs"), this.name)) warn(compressor, this); if (HOP(compressor.option("global_defs"), this.name)) warn(this);
}); });
def(AST_SymbolRef, function(compressor, suffix) { def(AST_SymbolRef, function(compressor, suffix) {
if (!this.global()) return; if (!this.global()) return;
@@ -2994,7 +2971,8 @@ merge(Compressor.prototype, {
val = global_objs[exp.name]; val = global_objs[exp.name];
} else { } else {
val = exp._eval(compressor, cached, depth + 1); val = exp._eval(compressor, cached, depth + 1);
if (!val || val === exp || !HOP(val, key)) return this; if (!val || val === exp) return this;
if (typeof val == "object" && !HOP(val, key)) return this;
if (typeof val == "function") switch (key) { if (typeof val == "function") switch (key) {
case "name": case "name":
return val.node.name ? val.node.name.name : ""; return val.node.name ? val.node.name.name : "";
@@ -3039,7 +3017,7 @@ merge(Compressor.prototype, {
try { try {
return val[key].apply(val, args); return val[key].apply(val, args);
} catch (ex) { } catch (ex) {
compressor.warn("Error evaluating {code} [{file}:{line},{col}]", { AST_Node.warn("Error evaluating {code} [{file}:{line},{col}]", {
code: this.print_to_string(), code: this.print_to_string(),
file: this.start.file, file: this.start.file,
line: this.start.line, line: this.start.line,
@@ -3650,7 +3628,7 @@ merge(Compressor.prototype, {
sym.__unused = true; sym.__unused = true;
if (trim) { if (trim) {
a.pop(); a.pop();
compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", template(sym)); AST_Node[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", template(sym));
} }
} else { } else {
trim = false; trim = false;
@@ -3660,7 +3638,7 @@ merge(Compressor.prototype, {
if (drop_funcs && node instanceof AST_Defun && node !== self) { if (drop_funcs && node instanceof AST_Defun && node !== self) {
var def = node.name.definition(); var def = node.name.definition();
if (!(def.id in in_use_ids)) { if (!(def.id in in_use_ids)) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name)); AST_Node[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
def.eliminated++; def.eliminated++;
return make_node(AST_EmptyStatement, node); return make_node(AST_EmptyStatement, node);
} }
@@ -3681,7 +3659,7 @@ merge(Compressor.prototype, {
} }
var var_defs = var_defs_by_id.get(sym.id); var var_defs = var_defs_by_id.get(sym.id);
if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) { if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name)); AST_Node.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
if (def.value) { if (def.value) {
var ref = make_node(AST_SymbolRef, def.name, def.name); var ref = make_node(AST_SymbolRef, def.name, def.name);
sym.references.push(ref); sym.references.push(ref);
@@ -3704,9 +3682,10 @@ merge(Compressor.prototype, {
} else if (compressor.option("functions") } else if (compressor.option("functions")
&& def.value === def.name.fixed_value() && def.value === def.name.fixed_value()
&& def.value instanceof AST_Function && def.value instanceof AST_Function
&& !(def.value.name && def.value.name.definition().assignments)
&& can_rename(def.value, def.name.name) && can_rename(def.value, def.name.name)
&& (!compressor.has_directive("use strict") || parent instanceof AST_Scope)) { && (!compressor.has_directive("use strict") || parent instanceof AST_Scope)) {
compressor.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name)); AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
var defun = make_node(AST_Defun, def, def.value); var defun = make_node(AST_Defun, def, def.value);
defun.name = make_node(AST_SymbolDefun, def.name, def.name); defun.name = make_node(AST_SymbolDefun, def.name, def.name);
var name_def = def.name.scope.resolve().def_function(defun.name); var name_def = def.name.scope.resolve().def_function(defun.name);
@@ -3743,10 +3722,10 @@ merge(Compressor.prototype, {
} else { } else {
var value = def.value && def.value.drop_side_effect_free(compressor); var value = def.value && def.value.drop_side_effect_free(compressor);
if (value) { if (value) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name)); AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value); side_effects.push(value);
} else { } else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name)); AST_Node[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
} }
sym.eliminated++; sym.eliminated++;
} }
@@ -4172,10 +4151,8 @@ merge(Compressor.prototype, {
var right = this.right.drop_side_effect_free(compressor, first_in_statement); var right = this.right.drop_side_effect_free(compressor, first_in_statement);
if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement); if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement);
if (lazy_op[this.operator]) { if (lazy_op[this.operator]) {
var node; var node = this;
if (right === this.right) { if (right !== node.right) {
node = this;
} else {
node = this.clone(); node = this.clone();
node.right = right.drop_side_effect_free(compressor); node.right = right.drop_side_effect_free(compressor);
} }
@@ -4213,9 +4190,7 @@ merge(Compressor.prototype, {
} }
return this; return this;
} }
if (this.pure) { if (this.pure) AST_Node.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
}
var args = trim(this.args, compressor, first_in_statement); var args = trim(this.args, compressor, first_in_statement);
return args && make_sequence(this, args); return args && make_sequence(this, args);
}); });
@@ -4298,7 +4273,7 @@ merge(Compressor.prototype, {
var body = self.body; var body = self.body;
var node = body.drop_side_effect_free(compressor, true); var node = body.drop_side_effect_free(compressor, true);
if (!node) { if (!node) {
compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start); AST_Node.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
return make_node(AST_EmptyStatement, self); return make_node(AST_EmptyStatement, self);
} }
if (node !== body) { if (node !== body) {
@@ -4377,7 +4352,7 @@ merge(Compressor.prototype, {
body: self.condition body: self.condition
})); }));
} }
extract_declarations_from_unreachable_code(compressor, self.body, body); extract_declarations_from_unreachable_code(self.body, body);
return make_node(AST_BlockStatement, self, { return make_node(AST_BlockStatement, self, {
body: body body: body
}); });
@@ -4450,7 +4425,7 @@ merge(Compressor.prototype, {
if (!cond) { if (!cond) {
if (compressor.option("dead_code")) { if (compressor.option("dead_code")) {
var body = []; var body = [];
extract_declarations_from_unreachable_code(compressor, self.body, body); extract_declarations_from_unreachable_code(self.body, body);
if (self.init instanceof AST_Statement) { if (self.init instanceof AST_Statement) {
body.push(self.init); body.push(self.init);
} else if (self.init) { } else if (self.init) {
@@ -4497,20 +4472,18 @@ merge(Compressor.prototype, {
cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor); cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
} }
if (!cond) { if (!cond) {
compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start); AST_Node.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
var body = []; var body = [];
extract_declarations_from_unreachable_code(compressor, self.body, body); extract_declarations_from_unreachable_code(self.body, body);
body.push(make_node(AST_SimpleStatement, self.condition, { body.push(make_node(AST_SimpleStatement, self.condition, {
body: self.condition body: self.condition
})); }));
if (self.alternative) body.push(self.alternative); if (self.alternative) body.push(self.alternative);
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
} else if (!(cond instanceof AST_Node)) { } else if (!(cond instanceof AST_Node)) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start); AST_Node.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
var body = []; var body = [];
if (self.alternative) { if (self.alternative) extract_declarations_from_unreachable_code(self.alternative, body);
extract_declarations_from_unreachable_code(compressor, self.alternative, body);
}
body.push(make_node(AST_SimpleStatement, self.condition, { body.push(make_node(AST_SimpleStatement, self.condition, {
body: self.condition body: self.condition
})); }));
@@ -4559,14 +4532,14 @@ merge(Compressor.prototype, {
operator : "||", operator : "||",
left : negated, left : negated,
right : self.body.body right : self.body.body
}) }).transform(compressor)
}).optimize(compressor); }).optimize(compressor);
return make_node(AST_SimpleStatement, self, { return make_node(AST_SimpleStatement, self, {
body: make_node(AST_Binary, self, { body: make_node(AST_Binary, self, {
operator : "&&", operator : "&&",
left : self.condition, left : self.condition,
right : self.body.body right : self.body.body
}) }).transform(compressor)
}).optimize(compressor); }).optimize(compressor);
} }
if (self.body instanceof AST_EmptyStatement if (self.body instanceof AST_EmptyStatement
@@ -4576,7 +4549,7 @@ merge(Compressor.prototype, {
operator : "||", operator : "||",
left : self.condition, left : self.condition,
right : self.alternative.body right : self.alternative.body
}) }).transform(compressor)
}).optimize(compressor); }).optimize(compressor);
} }
if (self.body instanceof AST_Exit if (self.body instanceof AST_Exit
@@ -4725,7 +4698,7 @@ merge(Compressor.prototype, {
if (prev && !aborts(prev)) { if (prev && !aborts(prev)) {
prev.body = prev.body.concat(branch.body); prev.body = prev.body.concat(branch.body);
} else { } else {
extract_declarations_from_unreachable_code(compressor, branch, decl); extract_declarations_from_unreachable_code(branch, decl);
} }
} }
}); });
@@ -4736,7 +4709,7 @@ merge(Compressor.prototype, {
if (compressor.option("dead_code") && all(self.body, is_empty)) { if (compressor.option("dead_code") && all(self.body, is_empty)) {
var body = []; var body = [];
if (self.bcatch) { if (self.bcatch) {
extract_declarations_from_unreachable_code(compressor, self.bcatch, body); extract_declarations_from_unreachable_code(self.bcatch, body);
body.forEach(function(stat) { body.forEach(function(stat) {
if (!(stat instanceof AST_Definitions)) return; if (!(stat instanceof AST_Definitions)) return;
stat.definitions.forEach(function(var_def) { stat.definitions.forEach(function(var_def) {
@@ -4848,7 +4821,7 @@ merge(Compressor.prototype, {
elements: elements elements: elements
}); });
} catch (ex) { } catch (ex) {
compressor.warn("Invalid array length: {length} [{file}:{line},{col}]", { AST_Node.warn("Invalid array length: {length} [{file}:{line},{col}]", {
length: length, length: length,
file: self.start.file, file: self.start.file,
line: self.start.line, line: self.start.line,
@@ -4908,7 +4881,7 @@ merge(Compressor.prototype, {
value: RegExp.apply(RegExp, params), value: RegExp.apply(RegExp, params),
})); }));
} catch (ex) { } catch (ex) {
compressor.warn("Error converting {expr} [{file}:{line},{col}]", { AST_Node.warn("Error converting {expr} [{file}:{line},{col}]", {
expr: self.print_to_string(), expr: self.print_to_string(),
file: self.start.file, file: self.start.file,
line: self.start.line, line: self.start.line,
@@ -5091,8 +5064,8 @@ merge(Compressor.prototype, {
return self; return self;
} catch (ex) { } catch (ex) {
if (ex instanceof JS_Parse_Error) { if (ex instanceof JS_Parse_Error) {
compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start); AST_Node.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
compressor.warn(ex.toString()); AST_Node.warn(ex.toString());
} else { } else {
throw ex; throw ex;
} }
@@ -5422,7 +5395,8 @@ merge(Compressor.prototype, {
OPT(AST_UnaryPrefix, function(self, compressor) { OPT(AST_UnaryPrefix, function(self, compressor) {
var e = self.expression; var e = self.expression;
if (self.operator == "delete" if (compressor.option("evaluate")
&& self.operator == "delete"
&& !(e instanceof AST_SymbolRef && !(e instanceof AST_SymbolRef
|| e instanceof AST_PropAccess || e instanceof AST_PropAccess
|| is_identifier_atom(e))) { || is_identifier_atom(e))) {
@@ -5462,7 +5436,7 @@ merge(Compressor.prototype, {
case "typeof": case "typeof":
// typeof always returns a non-empty string, thus it's // typeof always returns a non-empty string, thus it's
// always true in booleans // always true in booleans
compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start); AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
return (e instanceof AST_SymbolRef ? make_node(AST_True, self) : make_sequence(self, [ return (e instanceof AST_SymbolRef ? make_node(AST_True, self) : make_sequence(self, [
e, e,
make_node(AST_True, self) make_node(AST_True, self)
@@ -5571,7 +5545,7 @@ merge(Compressor.prototype, {
case "===": case "===":
case "!==": case "!==":
if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) { if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) {
compressor.warn("Expression always defined [{file}:{line},{col}]", self.start); AST_Node.warn("Expression always defined [{file}:{line},{col}]", self.start);
return make_sequence(self, [ return make_sequence(self, [
self.right, self.right,
make_node(self.operator == "===" ? AST_False : AST_True, self) make_node(self.operator == "===" ? AST_False : AST_True, self)
@@ -5650,14 +5624,14 @@ merge(Compressor.prototype, {
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if (ll && typeof ll == "string") { if (ll && typeof ll == "string") {
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start); AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [ return make_sequence(self, [
self.right, self.right,
make_node(AST_True, self) make_node(AST_True, self)
]).optimize(compressor); ]).optimize(compressor);
} }
if (rr && typeof rr == "string") { if (rr && typeof rr == "string") {
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start); AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [ return make_sequence(self, [
self.left, self.left,
make_node(AST_True, self) make_node(AST_True, self)
@@ -5717,16 +5691,16 @@ merge(Compressor.prototype, {
case "&&": case "&&":
var ll = fuzzy_eval(self.left); var ll = fuzzy_eval(self.left);
if (!ll) { if (!ll) {
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start); AST_Node.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor); return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor);
} else if (!(ll instanceof AST_Node)) { } else if (!(ll instanceof AST_Node)) {
compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start); AST_Node.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.left, self.right ]).optimize(compressor); return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
} }
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if (!rr) { if (!rr) {
if (compressor.option("booleans") && compressor.in_boolean_context()) { if (compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start); AST_Node.warn("Boolean && always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [ return make_sequence(self, [
self.left, self.left,
make_node(AST_False, self) make_node(AST_False, self)
@@ -5736,7 +5710,7 @@ merge(Compressor.prototype, {
var parent = compressor.parent(); var parent = compressor.parent();
if (parent.operator == "&&" && parent.left === compressor.self() if (parent.operator == "&&" && parent.left === compressor.self()
|| compressor.option("booleans") && compressor.in_boolean_context()) { || compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start); AST_Node.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor); return self.left.optimize(compressor);
} }
} }
@@ -5753,10 +5727,10 @@ merge(Compressor.prototype, {
case "||": case "||":
var ll = fuzzy_eval(self.left); var ll = fuzzy_eval(self.left);
if (!ll) { if (!ll) {
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start); AST_Node.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.left, self.right ]).optimize(compressor); return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
} else if (!(ll instanceof AST_Node)) { } else if (!(ll instanceof AST_Node)) {
compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start); AST_Node.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor); return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor);
} }
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
@@ -5764,12 +5738,12 @@ merge(Compressor.prototype, {
var parent = compressor.parent(); var parent = compressor.parent();
if (parent.operator == "||" && parent.left === compressor.self() if (parent.operator == "||" && parent.left === compressor.self()
|| compressor.option("booleans") && compressor.in_boolean_context()) { || compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start); AST_Node.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor); return self.left.optimize(compressor);
} }
} else if (!(rr instanceof AST_Node)) { } else if (!(rr instanceof AST_Node)) {
if (compressor.option("booleans") && compressor.in_boolean_context()) { if (compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start); AST_Node.warn("Boolean || always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [ return make_sequence(self, [
self.left, self.left,
make_node(AST_True, self) make_node(AST_True, self)
@@ -6071,7 +6045,7 @@ merge(Compressor.prototype, {
} }
} }
var parent = compressor.parent(); var parent = compressor.parent();
if (compressor.option("reduce_vars") && is_lhs(self, parent) !== self) { if (compressor.option("reduce_vars") && is_lhs(compressor.self(), parent) !== compressor.self()) {
var def = self.definition(); var def = self.definition();
var fixed = self.fixed_value(); var fixed = self.fixed_value();
var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor)); var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
@@ -6130,7 +6104,6 @@ merge(Compressor.prototype, {
})); }));
} else { } else {
value = fixed.optimize(compressor); value = fixed.optimize(compressor);
if (value === fixed) value = fixed.clone(true);
} }
def.replaced++; def.replaced++;
return value; return value;
@@ -6282,6 +6255,7 @@ merge(Compressor.prototype, {
if (compressor.option("dead_code") if (compressor.option("dead_code")
&& self.left instanceof AST_SymbolRef && self.left instanceof AST_SymbolRef
&& (def = self.left.definition()).scope === compressor.find_parent(AST_Lambda)) { && (def = self.left.definition()).scope === compressor.find_parent(AST_Lambda)) {
if (self.left.is_immutable()) return strip_assignment();
var level = 0, node, parent = self; var level = 0, node, parent = self;
do { do {
node = parent; node = parent;
@@ -6289,16 +6263,12 @@ merge(Compressor.prototype, {
if (parent instanceof AST_Exit) { if (parent instanceof AST_Exit) {
if (in_try(level, parent)) break; if (in_try(level, parent)) break;
if (is_reachable(def.scope, [ def ])) break; if (is_reachable(def.scope, [ def ])) break;
if (self.operator == "=") return self.right.optimize(compressor);
def.fixed = false; def.fixed = false;
return make_node(AST_Binary, self, { return strip_assignment();
operator: self.operator.slice(0, -1),
left: self.left,
right: self.right
}).optimize(compressor);
} }
} while (parent instanceof AST_Binary && parent.right === node } while (parent instanceof AST_Binary && parent.right === node
|| parent instanceof AST_Sequence && parent.tail_node() === node); || parent instanceof AST_Sequence && parent.tail_node() === node
|| parent instanceof AST_UnaryPrefix);
} }
self = self.lift_sequences(compressor); self = self.lift_sequences(compressor);
if (!compressor.option("assignments")) return self; if (!compressor.option("assignments")) return self;
@@ -6346,6 +6316,14 @@ merge(Compressor.prototype, {
} }
} }
} }
function strip_assignment() {
return (self.operator != "=" ? make_node(AST_Binary, self, {
operator: self.operator.slice(0, -1),
left: self.left,
right: self.right
}) : maintain_this_binding(compressor, compressor.parent(), self, self.right)).optimize(compressor);
}
}); });
OPT(AST_Conditional, function(self, compressor) { OPT(AST_Conditional, function(self, compressor) {
@@ -6359,10 +6337,10 @@ merge(Compressor.prototype, {
} }
var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor); var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
if (!cond) { if (!cond) {
compressor.warn("Condition always false [{file}:{line},{col}]", self.start); AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor); return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
} else if (!(cond instanceof AST_Node)) { } else if (!(cond instanceof AST_Node)) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.start); AST_Node.warn("Condition always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor); return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
} }
var negated = cond.negate(compressor, first_in_statement(compressor)); var negated = cond.negate(compressor, first_in_statement(compressor));
@@ -6601,7 +6579,7 @@ merge(Compressor.prototype, {
}); });
var p = compressor.parent(); var p = compressor.parent();
if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) { if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", { AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
operator : p.operator, operator : p.operator,
value : self.value, value : self.value,
file : p.start.file, file : p.start.file,
@@ -6693,7 +6671,7 @@ merge(Compressor.prototype, {
return sym; return sym;
} }
} }
if (is_lhs(self, compressor.parent())) return self; if (is_lhs(compressor.self(), compressor.parent())) return self;
if (key !== prop) { if (key !== prop) {
var sub = self.flatten_object(property, compressor); var sub = self.flatten_object(property, compressor);
if (sub) { if (sub) {
@@ -6784,14 +6762,14 @@ merge(Compressor.prototype, {
OPT(AST_Dot, function(self, compressor) { OPT(AST_Dot, function(self, compressor) {
if (self.property == "arguments" || self.property == "caller") { if (self.property == "arguments" || self.property == "caller") {
compressor.warn("Function.prototype.{prop} not supported [{file}:{line},{col}]", { AST_Node.warn("Function.prototype.{prop} not supported [{file}:{line},{col}]", {
prop: self.property, prop: self.property,
file: self.start.file, file: self.start.file,
line: self.start.line, line: self.start.line,
col: self.start.col col: self.start.col
}); });
} }
if (is_lhs(self, compressor.parent())) return self; if (is_lhs(compressor.self(), compressor.parent())) return self;
if (compressor.option("unsafe_proto") if (compressor.option("unsafe_proto")
&& self.expression instanceof AST_Dot && self.expression instanceof AST_Dot
&& self.expression.property == "prototype") { && self.expression.property == "prototype") {

View File

@@ -8,7 +8,7 @@ var to_base64 = typeof btoa == "undefined" ? function(str) {
} : btoa; } : btoa;
function read_source_map(name, code) { function read_source_map(name, code) {
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code); var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(\S+)\s*$/.exec(code);
if (!match) { if (!match) {
AST_Node.warn("inline source map not found: " + name); AST_Node.warn("inline source map not found: " + name);
return null; return null;
@@ -51,7 +51,6 @@ function to_json(cache) {
} }
function minify(files, options) { function minify(files, options) {
var warn_function = AST_Node.warn_function;
try { try {
options = defaults(options, { options = defaults(options, {
compress: {}, compress: {},
@@ -78,7 +77,6 @@ function minify(files, options) {
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]); set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]); set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]); set_shorthand("toplevel", options, [ "compress", "mangle" ]);
set_shorthand("warnings", options, [ "compress" ]);
var quoted_props; var quoted_props;
if (options.mangle) { if (options.mangle) {
options.mangle = defaults(options.mangle, { options.mangle = defaults(options.mangle, {
@@ -116,11 +114,9 @@ function minify(files, options) {
}, true); }, true);
} }
var warnings = []; var warnings = [];
if (options.warnings && !AST_Node.warn_function) { if (options.warnings) AST_Node.log_function(function(warning) {
AST_Node.warn_function = function(warning) {
warnings.push(warning); warnings.push(warning);
}; }, options.warnings == "verbose");
}
if (timings) timings.parse = Date.now(); if (timings) timings.parse = Date.now();
var source_maps, toplevel; var source_maps, toplevel;
if (files instanceof AST_Toplevel) { if (files instanceof AST_Toplevel) {
@@ -155,12 +151,13 @@ function minify(files, options) {
if (quoted_props) { if (quoted_props) {
reserve_quoted_keys(toplevel, quoted_props); reserve_quoted_keys(toplevel, quoted_props);
} }
if (options.wrap) { [ "enclose", "wrap" ].forEach(function(action) {
toplevel = toplevel.wrap_commonjs(options.wrap); var option = options[action];
} if (!option) return;
if (options.enclose) { var orig = toplevel.print_to_string().slice(0, -1);
toplevel = toplevel.wrap_enclose(options.enclose); toplevel = toplevel[action](option);
} files[toplevel.start.file] = toplevel.print_to_string().replace(orig, "");
});
if (timings) timings.rename = Date.now(); if (timings) timings.rename = Date.now();
if (options.rename) { if (options.rename) {
toplevel.figure_out_scope(options.mangle); toplevel.figure_out_scope(options.mangle);
@@ -208,10 +205,14 @@ function minify(files, options) {
result.code = stream.get(); result.code = stream.get();
if (options.sourceMap) { if (options.sourceMap) {
result.map = options.output.source_map.toString(); result.map = options.output.source_map.toString();
if (options.sourceMap.url == "inline") { var url = options.sourceMap.url;
if (url) {
result.code = result.code.replace(/\n\/\/# sourceMappingURL=\S+\s*$/, "");
if (url == "inline") {
result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(result.map); result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(result.map);
} else if (options.sourceMap.url) { } else {
result.code += "\n//# sourceMappingURL=" + options.sourceMap.url; result.code += "\n//# sourceMappingURL=" + url;
}
} }
} }
} }
@@ -240,7 +241,5 @@ function minify(files, options) {
return result; return result;
} catch (ex) { } catch (ex) {
return { error: ex }; return { error: ex };
} finally {
AST_Node.warn_function = warn_function;
} }
} }

View File

@@ -217,23 +217,12 @@ function OutputStream(options) {
var flush_mappings = mappings ? function() { var flush_mappings = mappings ? function() {
mappings.forEach(function(mapping) { mappings.forEach(function(mapping) {
try {
options.source_map.add( options.source_map.add(
mapping.token.file, mapping.token.file,
mapping.line, mapping.col, mapping.line, mapping.col,
mapping.token.line, mapping.token.col, mapping.token.line, mapping.token.col,
!mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name !mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name
); );
} catch(ex) {
AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
file: mapping.token.file,
line: mapping.token.line,
col: mapping.token.col,
cline: mapping.line,
ccol: mapping.col,
name: mapping.name || ""
})
}
}); });
mappings = []; mappings = [];
} : noop; } : noop;
@@ -462,16 +451,11 @@ function OutputStream(options) {
function prepend_comments(node) { function prepend_comments(node) {
var self = this; var self = this;
var start = node.start; var scan = node instanceof AST_Exit && node.value;
if (!start) return; var comments = dump(node);
if (start.comments_before && start.comments_before._dumped === self) return; if (!comments) return;
var comments = start.comments_before;
if (!comments) {
comments = start.comments_before = [];
}
comments._dumped = self;
if (node instanceof AST_Exit && node.value) { if (scan) {
var tw = new TreeWalker(function(node) { var tw = new TreeWalker(function(node) {
var parent = tw.parent(); var parent = tw.parent();
if (parent instanceof AST_Exit if (parent instanceof AST_Exit
@@ -482,11 +466,8 @@ function OutputStream(options) {
|| parent instanceof AST_Sequence && parent.expressions[0] === node || parent instanceof AST_Sequence && parent.expressions[0] === node
|| parent instanceof AST_Sub && parent.expression === node || parent instanceof AST_Sub && parent.expression === node
|| parent instanceof AST_UnaryPostfix) { || parent instanceof AST_UnaryPostfix) {
var text = node.start.comments_before; var before = dump(node);
if (text && text._dumped !== self) { if (before) comments = comments.concat(before);
text._dumped = self;
comments = comments.concat(text);
}
} else { } else {
return true; return true;
} }
@@ -529,13 +510,29 @@ function OutputStream(options) {
} }
}); });
if (!last_nlb) { if (!last_nlb) {
if (start.nlb) { if (node.start.nlb) {
print("\n"); print("\n");
indent(); indent();
} else { } else {
space(); space();
} }
} }
function dump(node) {
var token = node.start;
if (!token) {
if (!scan) return;
node.start = token = new AST_Token();
}
var comments = token.comments_before;
if (!comments) {
if (!scan) return;
token.comments_before = comments = [];
}
if (comments._dumped === self) return;
comments._dumped = self;
return comments;
}
} }
function append_comments(node, tail) { function append_comments(node, tail) {

View File

@@ -504,7 +504,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
var regexp = new RegExp(source, mods); var regexp = new RegExp(source, mods);
regexp.raw_source = source; regexp.raw_source = source;
return token("regexp", regexp); return token("regexp", regexp);
} catch(e) { } catch (e) {
parse_error(e.message); parse_error(e.message);
} }
}); });
@@ -556,7 +556,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return function(x) { return function(x) {
try { try {
return cont(x); return cont(x);
} catch(ex) { } catch (ex) {
if (ex === EX_EOF) parse_error(eof_error); if (ex === EX_EOF) parse_error(eof_error);
else throw ex; else throw ex;
} }

View File

@@ -70,7 +70,7 @@ function configure_error_stack(fn) {
err.name = this.name; err.name = this.name;
try { try {
throw err; throw err;
} catch(e) { } catch (e) {
return e.stack; return e.stack;
} }
} }

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit", "description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "3.5.8", "version": "3.5.12",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -31,7 +31,7 @@
"semver": "~6.0.0" "semver": "~6.0.0"
}, },
"scripts": { "scripts": {
"test": "node test/run-tests.js" "test": "node test/compress.js && node test/mocha.js"
}, },
"keywords": [ "keywords": [
"cli", "cli",

175
test/run-tests.js → test/compress.js Executable file → Normal file
View File

@@ -1,25 +1,32 @@
#! /usr/bin/env node #! /usr/bin/env node
var U = require("./node");
var path = require("path");
var fs = require("fs");
var assert = require("assert"); var assert = require("assert");
var fs = require("fs");
var path = require("path");
var sandbox = require("./sandbox"); var sandbox = require("./sandbox");
var semver = require("semver"); var semver = require("semver");
var U = require("./node");
var tests_dir = path.dirname(module.filename);
var failures = 0; var failures = 0;
var failed_files = {}; var failed_files = Object.create(null);
var minify_options = require("./ufuzz.json").map(JSON.stringify); var minify_options = require("./ufuzz.json").map(JSON.stringify);
var dir = path.resolve(path.dirname(module.filename), "compress");
run_compress_tests(); fs.readdirSync(dir).filter(function(name) {
return /\.js$/i.test(name);
}).forEach(function(file) {
log("--- {file}", { file: file });
var tests = parse_test(path.resolve(dir, file));
for (var i in tests) if (!test_case(tests[i])) {
failures++;
failed_files[file] = 1;
}
});
if (failures) { if (failures) {
console.error("\n!!! Failed " + failures + " test cases."); console.error();
console.error("!!! Failed " + failures + " test case(s).");
console.error("!!! " + Object.keys(failed_files).join(", ")); console.error("!!! " + Object.keys(failed_files).join(", "));
process.exit(1); process.exit(1);
} }
console.log();
require("./mocha.js");
/* -----[ utils ]----- */ /* -----[ utils ]----- */
@@ -46,11 +53,11 @@ function parse_test(file) {
filename: file filename: file
}); });
} catch (e) { } catch (e) {
console.log("Caught error while parsing tests in " + file + "\n"); console.error("Caught error while parsing tests in " + file);
console.log(e); console.error(e);
throw e; process.exit(1);
} }
var tests = {}; var tests = Object.create(null);
var tw = new U.TreeWalker(function(node, descend) { var tw = new U.TreeWalker(function(node, descend) {
if (node instanceof U.AST_LabeledStatement if (node instanceof U.AST_LabeledStatement
&& tw.parent() instanceof U.AST_Toplevel) { && tw.parent() instanceof U.AST_Toplevel) {
@@ -167,7 +174,17 @@ function reminify(orig_options, input_code, input_formatted, expect_stdout) {
var options_formatted = JSON.stringify(options, null, 4); var options_formatted = JSON.stringify(options, null, 4);
var result = U.minify(input_code, options); var result = U.minify(input_code, options);
if (result.error) { if (result.error) {
log("!!! failed input reminify\n---INPUT---\n{input}\n---OPTIONS---\n{options}\n--ERROR---\n{error}\n\n", { log([
"!!! failed input reminify",
"---INPUT---",
"{input}",
"---OPTIONS---",
"{options}",
"--ERROR---",
"{error}",
"",
"",
].join("\n"), {
input: input_formatted, input: input_formatted,
options: options_formatted, options: options_formatted,
error: result.error, error: result.error,
@@ -179,7 +196,21 @@ function reminify(orig_options, input_code, input_formatted, expect_stdout) {
stdout = expect_stdout; stdout = expect_stdout;
} }
if (!sandbox.same_stdout(expect_stdout, stdout)) { if (!sandbox.same_stdout(expect_stdout, stdout)) {
log("!!! failed running reminified input\n---INPUT---\n{input}\n---OPTIONS---\n{options}\n---OUTPUT---\n{output}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", { log([
"!!! failed running reminified input",
"---INPUT---",
"{input}",
"---OPTIONS---",
"{options}",
"---OUTPUT---",
"{output}",
"---EXPECTED {expected_type}---",
"{expected}",
"---ACTUAL {actual_type}---",
"{actual}",
"",
"",
].join("\n"), {
input: input_formatted, input: input_formatted,
options: options_formatted, options: options_formatted,
output: result.code, output: result.code,
@@ -200,13 +231,7 @@ function run_code(code) {
return typeof result == "string" ? result.replace(/\u001b\[\d+m/g, "") : result; return typeof result == "string" ? result.replace(/\u001b\[\d+m/g, "") : result;
} }
function run_compress_tests() { function test_case(test) {
var dir = path.resolve(tests_dir, "compress");
fs.readdirSync(dir).filter(function(name) {
return /\.js$/i.test(name);
}).forEach(function(file) {
log("--- {file}", { file: file });
function test_case(test) {
log(" Running test [{name}]", { name: test.name }); log(" Running test [{name}]", { name: test.name });
var output_options = test.beautify || {}; var output_options = test.beautify || {};
var expect; var expect;
@@ -219,28 +244,36 @@ function run_compress_tests() {
var input_code = make_code(input); var input_code = make_code(input);
var input_formatted = make_code(test.input, { var input_formatted = make_code(test.input, {
beautify: true, beautify: true,
comments: "all",
keep_quoted_props: true,
quote_style: 3, quote_style: 3,
keep_quoted_props: true
}); });
try { try {
U.parse(input_code); U.parse(input_code);
} catch (ex) { } catch (ex) {
log("!!! Cannot parse input\n---INPUT---\n{input}\n--PARSE ERROR--\n{error}\n\n", { log([
"!!! Cannot parse input",
"---INPUT---",
"{input}",
"--PARSE ERROR--",
"{error}",
"",
"",
].join("\n"), {
input: input_formatted, input: input_formatted,
error: ex, error: ex,
}); });
return false; return false;
} }
var options = U.defaults(test.options, {
warnings: false
});
var warnings_emitted = []; var warnings_emitted = [];
var original_warn_function = U.AST_Node.warn_function;
if (test.expect_warnings) { if (test.expect_warnings) {
U.AST_Node.warn_function = function(text) { var expected_warnings = make_code(test.expect_warnings, {
warnings_emitted.push("WARN: " + text); beautify: false,
}; quote_style: 2, // force double quote to match JSON
if (!options.warnings) options.warnings = true; });
U.AST_Node.log_function(function(text) {
warnings_emitted.push(text);
}, /"INFO: /.test(expected_warnings));
} }
if (test.mangle && test.mangle.properties && test.mangle.properties.keep_quoted) { if (test.mangle && test.mangle.properties && test.mangle.properties.keep_quoted) {
var quoted_props = test.mangle.properties.reserved; var quoted_props = test.mangle.properties.reserved;
@@ -252,7 +285,7 @@ function run_compress_tests() {
input.figure_out_scope(test.mangle); input.figure_out_scope(test.mangle);
input.expand_names(test.mangle); input.expand_names(test.mangle);
} }
var cmp = new U.Compressor(options, true); var cmp = new U.Compressor(test.options, true);
var output = cmp.compress(input); var output = cmp.compress(input);
output.figure_out_scope(test.mangle); output.figure_out_scope(test.mangle);
if (test.mangle) { if (test.mangle) {
@@ -264,7 +297,17 @@ function run_compress_tests() {
} }
output = make_code(output, output_options); output = make_code(output, output_options);
if (expect != output) { if (expect != output) {
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", { log([
"!!! failed",
"---INPUT---",
"{input}",
"---OUTPUT---",
"{output}",
"---EXPECTED---",
"{expected}",
"",
"",
].join("\n"), {
input: input_formatted, input: input_formatted,
output: output, output: output,
expected: expect expected: expect
@@ -275,7 +318,17 @@ function run_compress_tests() {
try { try {
U.parse(output); U.parse(output);
} catch (ex) { } catch (ex) {
log("!!! Test matched expected result but cannot parse output\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n--REPARSE ERROR--\n{error}\n\n", { log([
"!!! Test matched expected result but cannot parse output",
"---INPUT---",
"{input}",
"---OUTPUT---",
"{output}",
"--REPARSE ERROR--",
"{error}",
"",
"",
].join("\n"), {
input: input_formatted, input: input_formatted,
output: output, output: output,
error: ex, error: ex,
@@ -283,17 +336,22 @@ function run_compress_tests() {
return false; return false;
} }
if (test.expect_warnings) { if (test.expect_warnings) {
U.AST_Node.warn_function = original_warn_function;
var expected_warnings = make_code(test.expect_warnings, {
beautify: false,
quote_style: 2, // force double quote to match JSON
});
warnings_emitted = warnings_emitted.map(function(input) { warnings_emitted = warnings_emitted.map(function(input) {
return input.split(process.cwd() + path.sep).join("").split(path.sep).join("/"); return input.split(process.cwd() + path.sep).join("").split(path.sep).join("/");
}); });
var actual_warnings = JSON.stringify(warnings_emitted); var actual_warnings = JSON.stringify(warnings_emitted);
if (expected_warnings != actual_warnings) { if (expected_warnings != actual_warnings) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED WARNINGS---\n{expected_warnings}\n---ACTUAL WARNINGS---\n{actual_warnings}\n\n", { log([
"!!! failed",
"---INPUT---",
"{input}",
"---EXPECTED WARNINGS---",
"{expected_warnings}",
"---ACTUAL WARNINGS---",
"{actual_warnings}",
"",
"",
].join("\n"), {
input: input_formatted, input: input_formatted,
expected_warnings: expected_warnings, expected_warnings: expected_warnings,
actual_warnings: actual_warnings, actual_warnings: actual_warnings,
@@ -308,7 +366,17 @@ function run_compress_tests() {
test.expect_stdout = stdout; test.expect_stdout = stdout;
} }
if (!sandbox.same_stdout(test.expect_stdout, stdout)) { if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
log("!!! Invalid input or expected stdout\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", { log([
"!!! Invalid input or expected stdout",
"---INPUT---",
"{input}",
"---EXPECTED {expected_type}---",
"{expected}",
"---ACTUAL {actual_type}---",
"{actual}",
"",
"",
].join("\n"), {
input: input_formatted, input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR", expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout, expected: test.expect_stdout,
@@ -319,7 +387,17 @@ function run_compress_tests() {
} }
stdout = run_code(output); stdout = run_code(output);
if (!sandbox.same_stdout(test.expect_stdout, stdout)) { if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", { log([
"!!! failed",
"---INPUT---",
"{input}",
"---EXPECTED {expected_type}---",
"{expected}",
"---ACTUAL {actual_type}---",
"{actual}",
"",
"",
].join("\n"), {
input: input_formatted, input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR", expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout, expected: test.expect_stdout,
@@ -333,15 +411,6 @@ function run_compress_tests() {
} }
} }
return true; return true;
}
var tests = parse_test(path.resolve(dir, file));
for (var i in tests) if (tests.hasOwnProperty(i)) {
if (!test_case(tests[i])) {
failures++;
failed_files[file] = 1;
}
}
});
} }
function tmpl() { function tmpl() {

View File

@@ -3497,10 +3497,10 @@ issue_2437_1: {
return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}), return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
result; result;
} }
var req, detectFunc = function(){}; var req = new XMLHttpRequest(), detectFunc = function(){};
(req = new XMLHttpRequest()).onreadystatechange = detectFunc; return req.onreadystatechange = detectFunc,
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc; result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc,
return req.onreadystatechange = null, result; req.onreadystatechange = null, result;
}()); }());
} }
} }
@@ -3545,8 +3545,8 @@ issue_2437_2: {
if (xhrDesc) if (xhrDesc)
return (req = new XMLHttpRequest()).onreadystatechange, return (req = new XMLHttpRequest()).onreadystatechange,
Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}); Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {});
var req; var req = new XMLHttpRequest();
(req = new XMLHttpRequest).onreadystatechange = function(){}, req.onreadystatechange = function(){},
req[SYMBOL_FAKE_ONREADYSTATECHANGE_1], req[SYMBOL_FAKE_ONREADYSTATECHANGE_1],
req.onreadystatechange = null; req.onreadystatechange = null;
}(); }();
@@ -4194,7 +4194,7 @@ may_throw_2: {
var a = x(); var a = x();
++b; ++b;
return b(a); return b(a);
} catch(e) {} } catch (e) {}
console.log(b); console.log(b);
} }
f(0); f(0);
@@ -4204,7 +4204,7 @@ may_throw_2: {
try { try {
var a = x(); var a = x();
return (++b)(a); return (++b)(a);
} catch(e) {} } catch (e) {}
console.log(b); console.log(b);
} }
f(0); f(0);

View File

@@ -1435,3 +1435,39 @@ iife_condition: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
angularjs_chain: {
options = {
conditionals: true,
passes: 2,
side_effects: true,
}
input: {
function nonComputedMember(left, right, context, create) {
var lhs = left();
if (create && create !== 1) {
if (lhs && lhs[right] == null) {
lhs[right] = {};
}
}
var value = lhs != null ? lhs[right] : undefined;
if (context) {
return { context: lhs, name: right, value: value };
} else {
return value;
}
}
}
expect: {
function nonComputedMember(left, right, context, create) {
var lhs = left();
create && 1 !== create && lhs && null == lhs[right] && (lhs[right] = {});
var value = null != lhs ? lhs[right] : void 0;
return context ? {
context: lhs,
name: right,
value: value
} : value;
}
}
}

View File

@@ -960,3 +960,56 @@ unsafe_string_replace: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3402: {
options = {
dead_code: true,
evaluate: true,
functions: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
var f = function f() {
f = 42;
console.log(typeof f);
};
"function" == typeof f && f();
"function" == typeof f && f();
console.log(typeof f);
}
expect: {
function f() {
console.log(typeof f);
}
f();
f();
console.log(typeof f);
}
expect_stdout: [
"function",
"function",
"function",
]
}
issue_3406: {
options = {
dead_code: true,
}
input: {
console.log(function f(a) {
return delete (f = a);
}());
}
expect: {
console.log(function f(a) {
return delete (0, a);
}());
}
expect_stdout: "true"
}

View File

@@ -144,7 +144,7 @@ unused_var_in_catch: {
function foo() { function foo() {
try { try {
foo(); foo();
} catch(ex) { } catch (ex) {
var x = 10; var x = 10;
} }
} }
@@ -153,7 +153,7 @@ unused_var_in_catch: {
function foo() { function foo() {
try { try {
foo(); foo();
} catch(ex) {} } catch (ex) {}
} }
} }
} }
@@ -166,7 +166,7 @@ used_var_in_catch: {
function foo() { function foo() {
try { try {
foo(); foo();
} catch(ex) { } catch (ex) {
var x = 10; var x = 10;
} }
return x; return x;
@@ -176,7 +176,7 @@ used_var_in_catch: {
function foo() { function foo() {
try { try {
foo(); foo();
} catch(ex) { } catch (ex) {
var x = 10; var x = 10;
} }
return x; return x;
@@ -797,6 +797,7 @@ assign_chain: {
issue_1583: { issue_1583: {
options = { options = {
keep_fargs: true, keep_fargs: true,
passes: 2,
reduce_funcs: true, reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
@@ -1004,7 +1005,7 @@ issue_1715_4: {
delete_assign_1: { delete_assign_1: {
options = { options = {
booleans: true, booleans: true,
side_effects: true, evaluate: true,
toplevel: true, toplevel: true,
unused: true, unused: true,
} }
@@ -1023,7 +1024,7 @@ delete_assign_1: {
console.log((1 / 0, !0)); console.log((1 / 0, !0));
console.log((1 / 0, !0)); console.log((1 / 0, !0));
console.log((NaN, !0)); console.log((NaN, !0));
console.log((0 / 0, !0)); console.log((NaN, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -1031,8 +1032,8 @@ delete_assign_1: {
delete_assign_2: { delete_assign_2: {
options = { options = {
booleans: true, booleans: true,
evaluate: true,
keep_infinity: true, keep_infinity: true,
side_effects: true,
toplevel: true, toplevel: true,
unused: true, unused: true,
} }
@@ -1051,7 +1052,7 @@ delete_assign_2: {
console.log((Infinity, !0)); console.log((Infinity, !0));
console.log((1 / 0, !0)); console.log((1 / 0, !0));
console.log((NaN, !0)); console.log((NaN, !0));
console.log((0 / 0, !0)); console.log((NaN, !0));
} }
expect_stdout: true expect_stdout: true
} }
@@ -1144,6 +1145,7 @@ var_catch_toplevel: {
options = { options = {
conditionals: true, conditionals: true,
negate_iife: true, negate_iife: true,
passes: 2,
reduce_funcs: true, reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
@@ -1156,7 +1158,7 @@ var_catch_toplevel: {
try { try {
a++; a++;
x(); x();
} catch(a) { } catch (a) {
if (a) var a; if (a) var a;
var a = 10; var a = 10;
} }
@@ -1167,7 +1169,7 @@ var_catch_toplevel: {
!function() { !function() {
try { try {
x(); x();
} catch(a) { } catch (a) {
var a; var a;
} }
}(); }();

View File

@@ -246,7 +246,7 @@ unsafe_constant: {
} }
expect: { expect: {
console.log( console.log(
true.a, void 0,
false.a, false.a,
null.a, null.a,
(void 0).a (void 0).a
@@ -278,7 +278,7 @@ unsafe_object: {
o + 1, o + 1,
2, 2,
o.b + 1, o.b + 1,
1..b + 1 NaN
); );
} }
expect_stdout: true expect_stdout: true
@@ -365,7 +365,7 @@ unsafe_object_repeated: {
o + 1, o + 1,
2, 2,
o.b + 1, o.b + 1,
1..b + 1 NaN
); );
} }
expect_stdout: true expect_stdout: true
@@ -444,8 +444,8 @@ unsafe_integer_key: {
2, 2,
2, 2,
({0:1})[1] + 1, ({0:1})[1] + 1,
1[1] + 1, NaN,
1["1"] + 1 NaN
); );
} }
expect_stdout: true expect_stdout: true
@@ -500,8 +500,8 @@ unsafe_float_key: {
2, 2,
2, 2,
({2.72:1})[3.14] + 1, ({2.72:1})[3.14] + 1,
1[3.14] + 1, NaN,
1["3.14"] + 1 NaN
); );
} }
expect_stdout: true expect_stdout: true
@@ -635,12 +635,12 @@ unsafe_string_bad_index: {
} }
expect: { expect: {
console.log( console.log(
"1234".a + 1, NaN,
"1234"["a"] + 1, NaN,
"1234"[3.14] + 1 NaN
); );
} }
expect_stdout: true expect_stdout: "NaN NaN NaN"
} }
prototype_function: { prototype_function: {
@@ -1730,3 +1730,30 @@ unsafe_string_replace: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3387_1: {
options = {
evaluate: true,
}
input: {
console.log(1 + (2 + "3"[4]));
}
expect: {
console.log(1 + (2 + "3"[4]));
}
expect_stdout: "NaN"
}
issue_3387_2: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(1 + (2 + "3"[4]));
}
expect: {
console.log(NaN);
}
expect_stdout: "NaN"
}

View File

@@ -2860,10 +2860,10 @@ issue_2437: {
result; result;
} }
function detectFunc() {} function detectFunc() {}
var req; var req = new XMLHttpRequest();
(req = new XMLHttpRequest()).onreadystatechange = detectFunc; return req.onreadystatechange = detectFunc,
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc; result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc,
return req.onreadystatechange = null, result; req.onreadystatechange = null, result;
}()); }());
} }
} }
@@ -3065,3 +3065,86 @@ class_iife: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3400: {
options = {
collapse_vars: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
(function(f) {
console.log(f()()[0].p);
})(function() {
function g() {
function h(u) {
var o = {
p: u
};
return console.log(o[g]), o;
}
function e() {
return [ 42 ].map(function(v) {
return h(v);
});
}
return e();
}
return g;
});
}
expect: {
void console.log(function g() {
function e() {
return [42].map(function(v) {
return o = {
p: v
}, console.log(o[g]) , o;
var o;
});
}
return e();
}()[0].p);
}
expect_stdout: [
"undefined",
"42",
]
}
issue_3402: {
options = {
evaluate: true,
functions: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
var f = function f() {
f = 42;
console.log(typeof f);
};
"function" == typeof f && f();
"function" == typeof f && f();
console.log(typeof f);
}
expect: {
var f = function f() {
f = 42;
console.log(typeof f);
};
f();
f();
console.log(typeof f);
}
expect_stdout: [
"function",
"function",
"function",
]
}

View File

@@ -59,9 +59,9 @@ do_screw_try_catch: {
input: { input: {
good = function(e){ good = function(e){
return function(error){ return function(error){
try{ try {
e() e()
} catch(e) { } catch (e) {
error(e) error(e)
} }
} }
@@ -70,9 +70,9 @@ do_screw_try_catch: {
expect: { expect: {
good = function(n){ good = function(n){
return function(t){ return function(t){
try{ try {
n() n()
} catch(n) { } catch (n) {
t(n) t(n)
} }
} }
@@ -93,9 +93,9 @@ dont_screw_try_catch: {
input: { input: {
bad = function(e){ bad = function(e){
return function(error){ return function(error){
try{ try {
e() e()
} catch(e) { } catch (e) {
error(e) error(e)
} }
} }
@@ -104,9 +104,9 @@ dont_screw_try_catch: {
expect: { expect: {
bad = function(t){ bad = function(t){
return function(n){ return function(n){
try{ try {
t() t()
} catch(t) { } catch (t) {
n(t) n(t)
} }
} }
@@ -137,7 +137,7 @@ do_screw_try_catch_undefined: {
} }
expect: { expect: {
function a(o){ function a(o){
try{ try {
throw "Stuff" throw "Stuff"
} catch (o) { } catch (o) {
console.log("caught: "+o) console.log("caught: "+o)
@@ -172,7 +172,7 @@ dont_screw_try_catch_undefined: {
} }
expect: { expect: {
function a(n){ function a(n){
try{ try {
throw "Stuff" throw "Stuff"
} catch (undefined) { } catch (undefined) {
console.log("caught: " + undefined) console.log("caught: " + undefined)

View File

@@ -59,7 +59,6 @@ non_hoisted_function_after_return_2a: {
passes: 2, passes: 2,
side_effects: true, side_effects: true,
unused: true, unused: true,
warnings: "verbose",
} }
input: { input: {
function foo(x) { function foo(x) {
@@ -91,13 +90,13 @@ non_hoisted_function_after_return_2a: {
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:7,16]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:7,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:4,20]", "WARN: Dropping unused variable a [test/compress/issue-1034.js:4,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:11,21]", "WARN: Dropping unused function nope [test/compress/issue-1034.js:11,21]",
"WARN: pass 0: last_count: Infinity, count: 37", "INFO: pass 0: last_count: Infinity, count: 37",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:9,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:9,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:9,12]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:9,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:12,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:12,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:7,20]", "INFO: Dropping unused variable b [test/compress/issue-1034.js:7,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:9,16]", "INFO: Dropping unused variable c [test/compress/issue-1034.js:9,16]",
"WARN: pass 1: last_count: 37, count: 18", "INFO: pass 1: last_count: 37, count: 18",
] ]
} }
@@ -213,7 +212,6 @@ non_hoisted_function_after_return_2a_strict: {
passes: 2, passes: 2,
side_effects: true, side_effects: true,
unused: true, unused: true,
warnings: "verbose",
} }
input: { input: {
"use strict"; "use strict";
@@ -250,13 +248,13 @@ non_hoisted_function_after_return_2a_strict: {
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:8,16]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:8,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:5,20]", "WARN: Dropping unused variable a [test/compress/issue-1034.js:5,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:12,21]", "WARN: Dropping unused function nope [test/compress/issue-1034.js:12,21]",
"WARN: pass 0: last_count: Infinity, count: 48", "INFO: pass 0: last_count: Infinity, count: 48",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:10,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:10,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:10,12]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:10,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:13,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:13,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:8,20]", "INFO: Dropping unused variable b [test/compress/issue-1034.js:8,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:10,16]", "INFO: Dropping unused variable c [test/compress/issue-1034.js:10,16]",
"WARN: pass 1: last_count: 48, count: 29", "INFO: pass 1: last_count: 48, count: 29",
] ]
} }

View File

@@ -1862,3 +1862,29 @@ join_expr: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3389: {
options = {
evaluate: true,
properties: true,
reduce_vars: true,
unsafe: true,
}
input: {
(function() {
var a = "PASS";
if (delete b)
b = a[null] = 42;
console.log(a);
})();
}
expect: {
(function() {
var a = "PASS";
if (delete b)
b = a.null = 42;
console.log(a);
})();
}
expect_stdout: "PASS"
}

View File

@@ -192,38 +192,35 @@ unsafe_evaluate: {
unused: true, unused: true,
} }
input: { input: {
function f0(){ function f0() {
var a = { var a = { b: 1 };
b:1
};
console.log(a.b + 3); console.log(a.b + 3);
} }
function f1() {
function f1(){
var a = { var a = {
b:{ b: { c: 1 },
c:1 d: 2
},
d:2
}; };
console.log(a.b + 3, a.d + 4, a.b.c + 5, a.d.c + 6); console.log(a.b + 3, a.d + 4, a.b.c + 5, a.d.c + 6);
} }
f0();
f1();
} }
expect: { expect: {
function f0(){ function f0() {
console.log(4); console.log(4);
} }
function f1() {
function f1(){
var a = { var a = {
b:{ b: { c: 1 },
c:1 d: 2
},
d:2
}; };
console.log(a.b + 3, 6, 6, 2..c + 6); console.log(a.b + 3, 6, 6, NaN);
} }
f0();
f1();
} }
expect_stdout: true
} }
unsafe_evaluate_side_effect_free_1: { unsafe_evaluate_side_effect_free_1: {

View File

@@ -490,6 +490,7 @@ issue_1758: {
delete_seq_1: { delete_seq_1: {
options = { options = {
booleans: true, booleans: true,
evaluate: true,
side_effects: true, side_effects: true,
} }
input: { input: {
@@ -514,6 +515,7 @@ delete_seq_1: {
delete_seq_2: { delete_seq_2: {
options = { options = {
booleans: true, booleans: true,
evaluate: true,
side_effects: true, side_effects: true,
} }
input: { input: {
@@ -538,6 +540,7 @@ delete_seq_2: {
delete_seq_3: { delete_seq_3: {
options = { options = {
booleans: true, booleans: true,
evaluate: true,
keep_infinity: true, keep_infinity: true,
side_effects: true, side_effects: true,
} }
@@ -563,6 +566,7 @@ delete_seq_3: {
delete_seq_4: { delete_seq_4: {
options = { options = {
booleans: true, booleans: true,
evaluate: true,
sequences: true, sequences: true,
side_effects: true, side_effects: true,
} }
@@ -590,6 +594,7 @@ delete_seq_4: {
delete_seq_5: { delete_seq_5: {
options = { options = {
booleans: true, booleans: true,
evaluate: true,
keep_infinity: true, keep_infinity: true,
sequences: true, sequences: true,
side_effects: true, side_effects: true,
@@ -618,6 +623,7 @@ delete_seq_5: {
delete_seq_6: { delete_seq_6: {
options = { options = {
booleans: true, booleans: true,
evaluate: true,
side_effects: true, side_effects: true,
} }
input: { input: {
@@ -964,3 +970,39 @@ missing_link: {
console.log(a); console.log(a);
} }
} }
angularjs_chain: {
options = {
conditionals: true,
sequences: true,
side_effects: true,
}
input: {
function nonComputedMember(left, right, context, create) {
var lhs = left();
if (create && create !== 1) {
if (lhs && lhs[right] == null) {
lhs[right] = {};
}
}
var value = lhs != null ? lhs[right] : undefined;
if (context) {
return { context: lhs, name: right, value: value };
} else {
return value;
}
}
}
expect: {
function nonComputedMember(left, right, context, create) {
var lhs = left();
create && 1 !== create && lhs && null == lhs[right] && (lhs[right] = {});
var value = null != lhs ? lhs[right] : void 0;
return context ? {
context: lhs,
name: right,
value: value
} : value;
}
}
}

View File

@@ -0,0 +1,6 @@
var Foo = function Foo(){console.log(1+2);}; new Foo();
//# sourceMappingURL=data:application/json;charset=utf-8;base64,DeadBeef
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjpudWxsLCJzb3VyY2VzIjpbInN0ZGluIl0sInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLElBQU0sR0FBRyxHQUFDLEFBQUUsWUFBVyxFQUFFLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFBLEFBQUUsQ0FBQyxJQUFJLEdBQUcsRUFBRSxDQUFDOyJ9

View File

@@ -0,0 +1,2 @@
new function(){console.log(3)};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxJQUFyQyxXQUFnQkEsUUFBUUMsSUFBSSIsInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl19

View File

@@ -62,8 +62,17 @@ if (typeof phantom == "undefined") {
if (debug) { if (debug) {
console.log("http://localhost:" + port + "/"); console.log("http://localhost:" + port + "/");
} else { } else {
child_process.exec("npm install phantomjs-prebuilt@2.1.14 --no-save", function(error) { child_process.spawn(process.platform == "win32" ? "npm.cmd" : "npm", [
if (error) throw error; "install",
"phantomjs-prebuilt@2.1.14",
"--no-audit",
"--no-optional",
"--no-save",
"--no-update-notifier",
], {
stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) {
if (code) throw new Error("npm install failed!");
var program = require("phantomjs-prebuilt").exec(process.argv[1], port); var program = require("phantomjs-prebuilt").exec(process.argv[1], port);
program.stdout.pipe(process.stdout); program.stdout.pipe(process.stdout);
program.stderr.pipe(process.stderr); program.stderr.pipe(process.stderr);

View File

@@ -47,7 +47,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should give sensible error against invalid input source map", function(done) { it("Should give sensible error against invalid input source map", function(done) {
var command = uglifyjscmd + " test/mocha.js --source-map content=blah,url=inline"; var command = uglifyjscmd + " test/mocha.js --source-map content=blah,url=inline --verbose";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.deepEqual(stderr.split(/\n/).slice(0, 2), [ assert.deepEqual(stderr.split(/\n/).slice(0, 2), [
@@ -83,6 +83,7 @@ describe("bin/uglifyjs", function() {
"test/input/issue-2082/sample.js", "test/input/issue-2082/sample.js",
"--source-map", "content=test/input/issue-2082/sample.js.map", "--source-map", "content=test/input/issue-2082/sample.js.map",
"--source-map", "url=inline", "--source-map", "url=inline",
"--verbose",
].join(" "); ].join(" ");
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
if (err) throw err; if (err) throw err;
@@ -113,13 +114,12 @@ describe("bin/uglifyjs", function() {
var child = exec(command, function(err, stdout) { var child = exec(command, function(err, stdout) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-3040/expect.js"));
assert.strictEqual(stdout, read("test/input/pr-3040/expect.js"));
done(); done();
}); });
setTimeout(function() { setTimeout(function() {
fs.writeFileSync(mapFile, read("test/input/pr-3040/input.js.map")); fs.writeFileSync(mapFile, read("test/input/issue-3040/input.js.map"));
child.stdin.end(read("test/input/pr-3040/input.js")); child.stdin.end(read("test/input/issue-3040/input.js"));
}, 1000); }, 1000);
}); });
it("Should work with --keep-fnames (mangle only)", function(done) { it("Should work with --keep-fnames (mangle only)", function(done) {
@@ -202,7 +202,7 @@ describe("bin/uglifyjs", function() {
}); });
}); });
it("Should warn for missing inline source map", function(done) { it("Should warn for missing inline source map", function(done) {
var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map content=inline,url=inline"; var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map content=inline,url=inline --warn";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, [ assert.strictEqual(stdout, [
@@ -221,6 +221,7 @@ describe("bin/uglifyjs", function() {
"test/input/issue-520/input.js", "test/input/issue-520/input.js",
"test/input/issue-1323/sample.js", "test/input/issue-1323/sample.js",
"--source-map", "content=inline,url=inline", "--source-map", "content=inline,url=inline",
"--warn",
].join(" "); ].join(" ");
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
if (err) throw err; if (err) throw err;
@@ -466,7 +467,7 @@ describe("bin/uglifyjs", function() {
done(); done();
}); });
}); });
it("Should throw syntax error (catch(eval))", function(done) { it("Should throw syntax error (catch (eval))", function(done) {
var command = uglifyjscmd + ' test/input/invalid/try.js'; var command = uglifyjscmd + ' test/input/invalid/try.js';
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
@@ -647,7 +648,7 @@ describe("bin/uglifyjs", function() {
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
assert.strictEqual(stdout, ""); assert.strictEqual(stdout, "");
assert.strictEqual(stderr, "Error parsing arguments for 'define': a-b\n"); assert.strictEqual(stderr, "ERROR: cannot parse arguments for 'define': a-b\n");
done(); done();
}); });
}); });
@@ -711,7 +712,7 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + " test/input/enclose/input.js --enclose window,undefined:window --wrap exports"; var command = uglifyjscmd + " test/input/enclose/input.js --enclose window,undefined:window --wrap exports";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, '(function(window,undefined){(function(exports){function enclose(){console.log("test enclose")}enclose()})(typeof exports=="undefined"?exports={}:exports)})(window);\n'); assert.strictEqual(stdout, '(function(exports){(function(window,undefined){function enclose(){console.log("test enclose")}enclose()})(window)})(typeof exports=="undefined"?exports={}:exports);\n');
done(); done();
}); });
}); });

View File

@@ -260,6 +260,60 @@ describe("comments", function() {
].join("\n")); ].join("\n"));
}); });
it("Should handle programmatic AST insertions gracefully", function() {
var ast = UglifyJS.parse([
"function f() {",
" //foo",
" bar;",
" return;",
"}",
].join("\n"));
ast.body[0].body[0] = new UglifyJS.AST_Throw({value: ast.body[0].body[0].body});
ast.body[0].body[1].value = new UglifyJS.AST_Number({value: 42});
assert.strictEqual(ast.print_to_string({comments: "all"}), [
"function f(){",
"//foo",
"throw bar;return 42}",
].join("\n"));
});
it("Should not duplicate sourceMappingURL", function() {
var code = [
"//# sourceMappingURL=input.map",
"print(42);",
"//# sourceMappingURL=input.map",
"",
"//# sourceMappingURL=input.map",
"//# sourceMappingURL=input.map",
"",
].join("\n");
var result = UglifyJS.minify(code, {
output: { comments: true },
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"//# sourceMappingURL=input.map",
"print(42);",
"//# sourceMappingURL=input.map",
"//# sourceMappingURL=input.map",
"//# sourceMappingURL=input.map",
].join("\n"));
result = UglifyJS.minify(code, {
output: { comments: true },
sourceMap: { url: "output.map" },
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"//# sourceMappingURL=input.map",
"print(42);",
"//# sourceMappingURL=input.map",
"//# sourceMappingURL=input.map",
"//# sourceMappingURL=output.map",
].join("\n"));
});
describe("comment before constant", function() { describe("comment before constant", function() {
var js = 'function f() { /*c1*/ var /*c2*/ foo = /*c3*/ false; return foo; }'; var js = 'function f() { /*c1*/ var /*c2*/ foo = /*c3*/ false; return foo; }';

View File

@@ -365,7 +365,7 @@ describe("minify", function() {
wrap: 'exports', wrap: 'exports',
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code, '(function(window,undefined){(function(exports){function enclose(){console.log("test enclose")}enclose()})(typeof exports=="undefined"?exports={}:exports)})(window);'); assert.strictEqual(result.code, '(function(exports){(function(window,undefined){function enclose(){console.log("test enclose")}enclose()})(window)})(typeof exports=="undefined"?exports={}:exports);');
}); });
}); });
}); });

View File

@@ -1,50 +0,0 @@
var assert = require("assert");
var semver = require("semver");
var spawn = require("child_process").spawn;
if (!process.env.UGLIFYJS_TEST_ALL) return;
function run(command, args, done) {
spawn(command, args, {
stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) {
assert.strictEqual(code, 0);
done();
});
}
describe("test/benchmark.js", function() {
this.timeout(10 * 60 * 1000);
[
"-b",
"-b braces",
"-m",
"-mc passes=3",
"-mc passes=3,toplevel",
"-mc passes=3,unsafe",
"-mc keep_fargs=false,passes=3",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
].forEach(function(options) {
it("Should pass with options " + options, function(done) {
var args = options.split(/ /);
args.unshift("test/benchmark.js");
run(process.argv[0], args, done);
});
});
});
if (semver.satisfies(process.version, "0.12")) return;
describe("test/jetstream.js", function() {
this.timeout(20 * 60 * 1000);
[
"-mc",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
].forEach(function(options) {
it("Should pass with options " + options, function(done) {
var args = options.split(/ /);
args.unshift("test/jetstream.js");
args.push("-b", "beautify=false,webkit");
run(process.argv[0], args, done);
});
});
});

View File

@@ -118,32 +118,17 @@ describe("sourcemaps", function() {
assert.strictEqual(result.code + "\n", readFileSync("test/input/issue-520/output.js", "utf8")); assert.strictEqual(result.code + "\n", readFileSync("test/input/issue-520/output.js", "utf8"));
}); });
it("Should warn for missing inline source map", function() { it("Should warn for missing inline source map", function() {
var warn_function = UglifyJS.AST_Node.warn_function;
var warnings = [];
UglifyJS.AST_Node.warn_function = function(txt) {
warnings.push(txt);
};
try {
var result = UglifyJS.minify(read("./test/input/issue-1323/sample.js"), { var result = UglifyJS.minify(read("./test/input/issue-1323/sample.js"), {
mangle: false, mangle: false,
sourceMap: { sourceMap: {
content: "inline" content: "inline"
} },
warnings: true,
}); });
assert.strictEqual(result.code, "var bar=function(bar){return bar};"); assert.strictEqual(result.code, "var bar=function(bar){return bar};");
assert.strictEqual(warnings.length, 1); assert.deepEqual(result.warnings, [ "WARN: inline source map not found: 0" ]);
assert.strictEqual(warnings[0], "inline source map not found: 0");
} finally {
UglifyJS.AST_Node.warn_function = warn_function;
}
}); });
it("Should handle multiple input and inline source map", function() { it("Should handle multiple input and inline source map", function() {
var warn_function = UglifyJS.AST_Node.warn_function;
var warnings = [];
UglifyJS.AST_Node.warn_function = function(txt) {
warnings.push(txt);
};
try {
var result = UglifyJS.minify([ var result = UglifyJS.minify([
read("./test/input/issue-520/input.js"), read("./test/input/issue-520/input.js"),
read("./test/input/issue-1323/sample.js"), read("./test/input/issue-1323/sample.js"),
@@ -151,18 +136,15 @@ describe("sourcemaps", function() {
sourceMap: { sourceMap: {
content: "inline", content: "inline",
url: "inline", url: "inline",
} },
warnings: true,
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code, [ assert.strictEqual(result.code, [
"var Foo=function(){console.log(3)};new Foo;var bar=function(o){return o};", "var Foo=function(){console.log(3)};new Foo;var bar=function(o){return o};",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwiMSJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFdBQWdCQyxRQUFRQyxJQUFJLElBQVMsSUFBSUYsSUNBbkQsSUFBSUcsSUFDQSxTQUFjQSxHQUNWLE9BQU9BIn0=", "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwiMSJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFdBQWdCQyxRQUFRQyxJQUFJLElBQVMsSUFBSUYsSUNBbkQsSUFBSUcsSUFDQSxTQUFjQSxHQUNWLE9BQU9BIn0=",
].join("\n")); ].join("\n"));
assert.strictEqual(warnings.length, 1); assert.deepEqual(result.warnings, [ "WARN: inline source map not found: 1" ]);
assert.strictEqual(warnings[0], "inline source map not found: 1");
} finally {
UglifyJS.AST_Node.warn_function = warn_function;
}
}); });
it("Should drop source contents for includeSources=false", function() { it("Should drop source contents for includeSources=false", function() {
var result = UglifyJS.minify(read("./test/input/issue-520/input.js"), { var result = UglifyJS.minify(read("./test/input/issue-520/input.js"), {
@@ -187,6 +169,18 @@ describe("sourcemaps", function() {
map = JSON.parse(result.map); map = JSON.parse(result.map);
assert.ok(!("sourcesContent" in map)); assert.ok(!("sourcesContent" in map));
}); });
it("Should parse the correct sourceMappingURL", function() {
var result = UglifyJS.minify(read("./test/input/issue-3294/input.js"), {
compress: { toplevel: true },
sourceMap: {
content: "inline",
includeSources: true,
url: "inline"
}
});
if (result.error) throw result.error;
assert.strictEqual(result.code + "\n", readFileSync("test/input/issue-3294/output.js", "utf8"));
});
}); });
describe("sourceMapInline", function() { describe("sourceMapInline", function() {

14
test/release/benchmark.js Normal file
View File

@@ -0,0 +1,14 @@
require("./run")([
"-b",
"-b braces",
"-m",
"-mc passes=3",
"-mc passes=3,toplevel",
"-mc passes=3,unsafe",
"-mc keep_fargs=false,passes=3",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
].map(function(options) {
var args = options.split(/ /);
args.unshift("test/benchmark.js");
return args;
}));

View File

@@ -0,0 +1,9 @@
require("./run")([
"-mc",
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
].map(function(options) {
var args = options.split(/ /);
args.unshift("test/jetstream.js");
args.push("-b", "beautify=false,webkit");
return args;
}));

16
test/release/run.js Normal file
View File

@@ -0,0 +1,16 @@
var child_process = require("child_process");
module.exports = function(tasks) {
(function next() {
if (!tasks.length) return;
var args = tasks.shift();
console.log();
console.log("\u001B[36m$> " + args.join(" ") + "\u001B[39m");
child_process.spawn(process.argv[0], args, {
stdio: [ "ignore", 1, 2 ]
}).on("exit", function(code) {
if (code) process.exit(code);
next();
});
})();
};