From 79131cd64749ee834118f32c2237693072950924 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sat, 27 May 2017 22:18:28 +0800 Subject: [PATCH 1/6] extend `node_version` range on applicable tests (#2015) --- test/compress/dead-code.js | 2 +- test/compress/functions.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js index 20e154b2163..0fcbbe43985 100644 --- a/test/compress/dead-code.js +++ b/test/compress/dead-code.js @@ -99,7 +99,7 @@ dead_code_2_should_warn_strict: { f(); } expect_stdout: true - node_version: "=4" + node_version: ">=4" } dead_code_constant_boolean_should_warn_more: { diff --git a/test/compress/functions.js b/test/compress/functions.js index ce8bab801ae..5ebd9d19d23 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -243,5 +243,5 @@ hoist_funs_strict: { "5 'function' 'function'", "6 'undefined' 'function'", ] - node_version: "=4" + node_version: ">=4" } From fec14379f6bae209484d75ba1db5c6b5ee37ae5a Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 28 May 2017 04:09:40 +0800 Subject: [PATCH 2/6] improve CLI usability (#2016) Report supported options upon invalid option syntax. fixes #1883 --- bin/uglifyjs | 2 +- test/mocha/cli.js | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/bin/uglifyjs b/bin/uglifyjs index 8ea7d16b5b3..158dc5711ac 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -351,7 +351,7 @@ function parse_js(flag, constants) { } })); } catch(ex) { - fatal("Error parsing arguments for '" + flag + "': " + value); + options[value] = null; } return options; } diff --git a/test/mocha/cli.js b/test/mocha/cli.js index 565b6069520..4234f2fb29a 100644 --- a/test/mocha/cli.js +++ b/test/mocha/cli.js @@ -537,4 +537,13 @@ describe("bin/uglifyjs", function () { done(); }); }); + it("Should print supported options on invalid option syntax", function(done) { + var command = uglifyjscmd + " test/input/comments/filter.js -b ascii-only"; + exec(command, function (err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.ok(/^Supported options:\n\{[^}]+}\nERROR: `ascii-only` is not a supported option/.test(stderr), stderr); + done(); + }); + }); }); From c6c9f4f5a837bea16609b1d430e6c20687f3742b Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 28 May 2017 18:21:44 +0800 Subject: [PATCH 3/6] implement `--help options` (#2017) --- bin/uglifyjs | 12 ++++++++++++ lib/parse.js | 2 +- test/ufuzz.js | 29 ++++++----------------------- tools/node.js | 17 +++++++++++++++++ 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/bin/uglifyjs b/bin/uglifyjs index 158dc5711ac..b9406e1cfa2 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -25,6 +25,18 @@ program.version(info.name + ' ' + info.version); program.parseArgv = program.parse; program.parse = undefined; if (process.argv.indexOf("ast") >= 0) program.helpInformation = UglifyJS.describe_ast; +else if (process.argv.indexOf("options") >= 0) program.helpInformation = function() { + var text = []; + var options = UglifyJS.default_options(); + for (var option in options) { + text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:"); + Object.keys(options[option]).forEach(function(name) { + text.push(" " + name); + }); + text.push(""); + } + return text.join("\n"); +}; program.option("-p, --parse ", "Specify parser options.", parse_js("parse", true)); program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true)); program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true)); diff --git a/lib/parse.js b/lib/parse.js index c960b493e03..a02fce8f978 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -700,7 +700,7 @@ function parse($TEXT, options) { shebang : true, strict : false, toplevel : null, - }); + }, true); var S = { input : (typeof $TEXT == "string" diff --git a/test/ufuzz.js b/test/ufuzz.js index ff403c60cac..e3361e382e2 100644 --- a/test/ufuzz.js +++ b/test/ufuzz.js @@ -12,7 +12,7 @@ stream._handle.setBlocking(true); }); -var UglifyJS = require("./node"); +var UglifyJS = require(".."); var randomBytes = require("crypto").randomBytes; var sandbox = require("./sandbox"); @@ -962,32 +962,15 @@ function try_beautify(code, result) { console.log(code); } -function infer_options(ctor) { - try { - ctor({ 0: 0 }); - } catch (e) { - return e.defs; - } -} - -var default_options = { - compress: infer_options(UglifyJS.Compressor), - mangle: { - "cache": null, - "eval": false, - "ie8": false, - "keep_fnames": false, - "toplevel": false, - }, - output: infer_options(UglifyJS.OutputStream), -}; +var default_options = UglifyJS.default_options(); function log_suspects(minify_options, component) { var options = component in minify_options ? minify_options[component] : true; if (!options) return; - options = UglifyJS.defaults(options, default_options[component]); - var suspects = Object.keys(default_options[component]).filter(function(name) { - if (options[name]) { + if (typeof options != "object") options = {}; + var defs = default_options[component]; + var suspects = Object.keys(defs).filter(function(name) { + if ((name in options ? options : defs)[name]) { var m = JSON.parse(JSON.stringify(minify_options)); var o = JSON.parse(JSON.stringify(options)); o[name] = false; diff --git a/tools/node.js b/tools/node.js index 07c62d1e583..dc2701060ec 100644 --- a/tools/node.js +++ b/tools/node.js @@ -63,3 +63,20 @@ function describe_ast() { doitem(AST_Node); return out + "\n"; } + +function infer_options(options) { + var result = UglifyJS.minify("", options); + return result.error && result.error.defs; +} + +UglifyJS.default_options = function() { + var defs = {}; + Object.keys(infer_options({ 0: 0 })).forEach(function(component) { + var options = {}; + options[component] = { 0: 0 }; + if (options = infer_options(options)) { + defs[component] = options; + } + }); + return defs; +}; From e62b879b488f40acd4e7e95d70c53a91c45810ed Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 28 May 2017 22:57:20 +0800 Subject: [PATCH 4/6] display default values in `--help options` (#2018) --- README.md | 5 +++-- bin/uglifyjs | 11 ++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 985fe507489..d7e65b27940 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ a double dash to prevent input files being used as option arguments: ``` -h, --help Print usage information. + `--help options` for details on available options. -V, --version Print version number. -p, --parse Specify parser options: `acorn` Use Acorn for parsing. @@ -713,14 +714,14 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code; ### Mangle properties options -- `reserved` (default: `[]`) -- Do not mangle property names listed in the +- `reserved` (default: `[]`) -- Do not mangle property names listed in the `reserved` array. - `regex` (default: `null`) -— Pass a RegExp literal to only mangle property names matching the regular expression. - `keep_quoted` (default: `false`) -— Only mangle unquoted property names. - `debug` (default: `false`) -— Mangle names with the original name still present. Pass an empty string `""` to enable, or a non-empty string to set the debug suffix. -- `builtins` (default: `false`) -- Use `true` to allow the mangling of builtin +- `builtins` (default: `false`) -- Use `true` to allow the mangling of builtin DOM properties. Not recommended to override this setting. ## Output options diff --git a/bin/uglifyjs b/bin/uglifyjs index b9406e1cfa2..a2039f7e14f 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -21,7 +21,7 @@ var options = { compress: false, mangle: false }; -program.version(info.name + ' ' + info.version); +program.version(info.name + " " + info.version); program.parseArgv = program.parse; program.parse = undefined; if (process.argv.indexOf("ast") >= 0) program.helpInformation = UglifyJS.describe_ast; @@ -30,8 +30,13 @@ else if (process.argv.indexOf("options") >= 0) program.helpInformation = functio var options = UglifyJS.default_options(); for (var option in options) { text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:"); - Object.keys(options[option]).forEach(function(name) { - text.push(" " + name); + var defs = options[option]; + var padding = ""; + Object.keys(defs).map(function(name) { + if (padding.length < name.length) padding = Array(name.length + 1).join(" "); + return [ name, JSON.stringify(defs[name]) ]; + }).forEach(function(tokens) { + text.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]); }); text.push(""); } From 1aa38051fbf0bde7071041f6ac37e666aecb4c0a Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Mon, 29 May 2017 10:51:41 +0800 Subject: [PATCH 5/6] better fix for #512 & #2010 (#2019) - remove duplicated functionalities - fix similar issue with `else` --- lib/compress.js | 120 ++++++++++++++++++------------------ test/compress/if_return.js | 2 + test/compress/issue-1052.js | 24 +++++++- 3 files changed, 86 insertions(+), 60 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 6359696b292..374d14d4b69 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -924,6 +924,45 @@ merge(Compressor.prototype, { // step. nevertheless, it's good to check. continue loop; case stat instanceof AST_If: + var ab = aborts(stat.body); + if (can_merge_flow(ab)) { + if (ab.label) { + remove(ab.label.thedef.references, ab); + } + CHANGED = true; + var funs = extract_functions_from_statement_array(ret); + var body = as_statement_array_with_return(stat.body, ab); + stat = stat.clone(); + stat.condition = stat.condition.negate(compressor); + stat.body = make_node(AST_BlockStatement, stat, { + body: as_statement_array(stat.alternative).concat(ret) + }); + stat.alternative = make_node(AST_BlockStatement, stat, { + body: body + }); + ret = [ stat.transform(compressor) ].concat(funs); + continue loop; + } + + var ab = aborts(stat.alternative); + if (can_merge_flow(ab)) { + if (ab.label) { + remove(ab.label.thedef.references, ab); + } + CHANGED = true; + var funs = extract_functions_from_statement_array(ret); + stat = stat.clone(); + stat.body = make_node(AST_BlockStatement, stat.body, { + body: as_statement_array(stat.body).concat(ret) + }); + var body = as_statement_array_with_return(stat.alternative, ab); + stat.alternative = make_node(AST_BlockStatement, stat.alternative, { + body: body + }); + ret = [ stat.transform(compressor) ].concat(funs); + continue loop; + } + if (stat.body instanceof AST_Return) { var value = stat.body.value; //--- @@ -960,23 +999,6 @@ merge(Compressor.prototype, { continue loop; } //--- - // if (foo()) return [ void bar() ]; [ else x...; ] y... ==> if (!foo()) { x...; y... } else bar(); - if (in_lambda && (!value || value instanceof AST_UnaryPrefix && value.operator == "void")) { - CHANGED = true; - stat = stat.clone(); - stat.condition = stat.condition.negate(compressor); - var funs = extract_functions_from_statement_array(ret); - var body = as_statement_array(stat.alternative).concat(ret); - stat.body = make_node(AST_BlockStatement, stat, { - body: body - }); - stat.alternative = value ? make_node(AST_SimpleStatement, value, { - body: value.expression - }) : null; - ret = [ stat.transform(compressor) ].concat(funs); - continue loop; - } - //--- // if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e; // // if sequences is not enabled, this can lead to an endless loop (issue #866). @@ -995,48 +1017,6 @@ merge(Compressor.prototype, { } } - var ab = aborts(stat.body); - var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null; - if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda) - || (ab instanceof AST_Continue && self === loop_body(lct)) - || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) { - if (ab.label) { - remove(ab.label.thedef.references, ab); - } - CHANGED = true; - var body = as_statement_array(stat.body).slice(0, -1); - stat = stat.clone(); - stat.condition = stat.condition.negate(compressor); - stat.body = make_node(AST_BlockStatement, stat, { - body: as_statement_array(stat.alternative).concat(ret) - }); - stat.alternative = make_node(AST_BlockStatement, stat, { - body: body - }); - ret = [ stat.transform(compressor) ]; - continue loop; - } - - var ab = aborts(stat.alternative); - var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null; - if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda) - || (ab instanceof AST_Continue && self === loop_body(lct)) - || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) { - if (ab.label) { - remove(ab.label.thedef.references, ab); - } - CHANGED = true; - stat = stat.clone(); - stat.body = make_node(AST_BlockStatement, stat.body, { - body: as_statement_array(stat.body).concat(ret) - }); - stat.alternative = make_node(AST_BlockStatement, stat.alternative, { - body: as_statement_array(stat.alternative).slice(0, -1) - }); - ret = [ stat.transform(compressor) ]; - continue loop; - } - ret.unshift(stat); break; default: @@ -1056,6 +1036,28 @@ merge(Compressor.prototype, { } return false; } + + function is_return_void(value) { + return !value || value instanceof AST_UnaryPrefix && value.operator == "void"; + } + + function can_merge_flow(ab) { + if (!ab) return false; + var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null; + return ab instanceof AST_Return && in_lambda && is_return_void(ab.value) + || ab instanceof AST_Continue && self === loop_body(lct) + || ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct; + } + + function as_statement_array_with_return(node, ab) { + var body = as_statement_array(node).slice(0, -1); + if (ab.value) { + body.push(make_node(AST_SimpleStatement, ab.value, { + body: ab.value.expression + })); + } + return body; + } }; function eliminate_dead_code(statements, compressor) { diff --git a/test/compress/if_return.js b/test/compress/if_return.js index c09d67b6ea4..72b69e709f9 100644 --- a/test/compress/if_return.js +++ b/test/compress/if_return.js @@ -307,6 +307,8 @@ issue_512: { options = { conditionals: true, if_return: true, + sequences: true, + side_effects: true, } input: { function a() { diff --git a/test/compress/issue-1052.js b/test/compress/issue-1052.js index e3dc73228d3..30a563fe115 100644 --- a/test/compress/issue-1052.js +++ b/test/compress/issue-1052.js @@ -136,7 +136,29 @@ defun_hoist_funs: { function f() {} function g() {} function h() {} - !window; + if (window); + } + } +} + +defun_else_if_return: { + options = { + hoist_funs: false, + if_return: true, + } + input: { + function e() { + function f() {} + if (window) function g() {} + else return; + function h() {} + } + } + expect: { + function e() { + function f() {} + if (window) function g() {} + function h() {} } } } From 4e0a22e5c88ac841a3bb67081454f2a83dbfe1a7 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Mon, 29 May 2017 10:52:13 +0800 Subject: [PATCH 6/6] v3.0.13 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index adccb1211da..815bdae3f6d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "homepage": "http://lisperator.net/uglifyjs", "author": "Mihai Bazon (http://lisperator.net/)", "license": "BSD-2-Clause", - "version": "3.0.12", + "version": "3.0.13", "engines": { "node": ">=0.8.0" },