add new arrows compress option (#2154)
Convert ES5 style anonymous function expressions to arrow functions if permissible by language semantics. Note: `arrows` requires that the `ecma` compress option is set to `6` or greater. fixes #2150
This commit is contained in:
@@ -590,6 +590,10 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
|
||||
- `evaluate` -- attempt to evaluate constant expressions
|
||||
|
||||
- `arrows` (default `true`) -- convert ES5 style anonymous function expressions
|
||||
to arrow functions if permissible by language semantics.
|
||||
Note: `arrows` requires that the `ecma` compress option is set to `6` or greater.
|
||||
|
||||
- `booleans` -- various optimizations for boolean context, for example `!!a
|
||||
? b : c → a ? b : c`
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ function Compressor(options, false_by_default) {
|
||||
return new Compressor(options, false_by_default);
|
||||
TreeTransformer.call(this, this.before, this.after);
|
||||
this.options = defaults(options, {
|
||||
arrows : !false_by_default,
|
||||
booleans : !false_by_default,
|
||||
cascade : !false_by_default,
|
||||
collapse_vars : !false_by_default,
|
||||
@@ -2047,7 +2048,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_Block, function(self, compressor){
|
||||
if (!(self.body instanceof AST_Node)) tighten_body(self.body, compressor);
|
||||
tighten_body(self.body, compressor);
|
||||
return self;
|
||||
});
|
||||
|
||||
@@ -3273,11 +3274,12 @@ merge(Compressor.prototype, {
|
||||
var fun;
|
||||
ast.walk(new TreeWalker(function(node) {
|
||||
if (fun) return true;
|
||||
if (node instanceof AST_Lambda) {
|
||||
if (node instanceof AST_Function) {
|
||||
fun = node;
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
if (!fun) return self;
|
||||
var args = fun.argnames.map(function(arg, i) {
|
||||
return make_node(AST_String, self.args[i], {
|
||||
value: arg.print_to_string()
|
||||
@@ -4499,13 +4501,37 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_Arrow, function(self, compressor){
|
||||
if (self.body.length === 1 && self.body[0] instanceof AST_Return) {
|
||||
if (!(self.body instanceof AST_Node)) tighten_body(self.body, compressor);
|
||||
if (compressor.option("arrows")
|
||||
&& self.body.length == 1
|
||||
&& self.body[0] instanceof AST_Return) {
|
||||
var value = self.body[0].value;
|
||||
self.body = value ? value : [];
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_Function, function(self, compressor){
|
||||
tighten_body(self.body, compressor);
|
||||
if (compressor.option("arrows")
|
||||
&& compressor.option("ecma") >= 6
|
||||
&& !self.name
|
||||
&& !self.is_generator
|
||||
&& !self.uses_arguments
|
||||
&& !self.uses_eval) {
|
||||
var has_special_symbol = false;
|
||||
self.walk(new TreeWalker(function(node) {
|
||||
if (has_special_symbol) return true;
|
||||
if (node instanceof AST_Symbol && !node.definition()) {
|
||||
has_special_symbol = true;
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
if (!has_special_symbol) return make_node(AST_Arrow, self, self).optimize(compressor);
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_Class, function(self, compressor){
|
||||
// HACK to avoid compress failure.
|
||||
// AST_Class is not really an AST_Scope/AST_Block as it lacks a body.
|
||||
|
||||
@@ -210,3 +210,73 @@ no_leading_parentheses: {
|
||||
}
|
||||
expect_exact: "(x,y)=>x(y);async(x,y)=>await x(y);"
|
||||
}
|
||||
|
||||
async_identifiers: {
|
||||
options = {
|
||||
arrows: true,
|
||||
ecma: 6,
|
||||
}
|
||||
input: {
|
||||
var async = function(x){ console.log("async", x); };
|
||||
var await = function(x){ console.log("await", x); };
|
||||
async(1);
|
||||
await(2);
|
||||
}
|
||||
expect: {
|
||||
var async = x => { console.log("async", x); };
|
||||
var await = x => { console.log("await", x); };
|
||||
async(1);
|
||||
await(2);
|
||||
}
|
||||
expect_stdout: [
|
||||
"async 1",
|
||||
"await 2",
|
||||
]
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
async_function_expression: {
|
||||
options = {
|
||||
arrows: true,
|
||||
ecma: 6,
|
||||
evaluate: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
var named = async function foo() {
|
||||
await bar(1 + 0) + (2 + 0);
|
||||
}
|
||||
var anon = async function() {
|
||||
await (1 + 0) + bar(2 + 0);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var named = async function foo() {
|
||||
await bar(1);
|
||||
};
|
||||
var anon = async () => {
|
||||
await 1, bar(2);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
issue_27: {
|
||||
options = {
|
||||
arrows: true,
|
||||
collapse_vars: true,
|
||||
ecma: 6,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function(jQuery) {
|
||||
var $;
|
||||
$ = jQuery;
|
||||
$("body").addClass("foo");
|
||||
})(jQuery);
|
||||
}
|
||||
expect: {
|
||||
(jQuery => {
|
||||
jQuery("body").addClass("foo");
|
||||
})(jQuery);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,14 +167,14 @@ async_inline: {
|
||||
|
||||
async_identifiers: {
|
||||
input: {
|
||||
let async = function(x){ console.log("async", x); };
|
||||
let await = function(x){ console.log("await", x); };
|
||||
var async = function(x){ console.log("async", x); };
|
||||
var await = function(x){ console.log("await", x); };
|
||||
async(1);
|
||||
await(2);
|
||||
}
|
||||
expect: {
|
||||
let async = function(x){ console.log("async", x); };
|
||||
let await = function(x){ console.log("await", x); };
|
||||
var async = function(x){ console.log("async", x); };
|
||||
var await = function(x){ console.log("await", x); };
|
||||
async(1);
|
||||
await(2);
|
||||
}
|
||||
@@ -182,7 +182,6 @@ async_identifiers: {
|
||||
"async 1",
|
||||
"await 2",
|
||||
]
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
async_shorthand_property: {
|
||||
|
||||
@@ -32,3 +32,25 @@ compress_new_function_with_destruct: {
|
||||
Function("[[a]]", "[{bb:b}]", 'return a');
|
||||
}
|
||||
}
|
||||
|
||||
compress_new_function_with_destruct_arrows: {
|
||||
options = {
|
||||
arrows: true,
|
||||
unsafe: true,
|
||||
unsafe_Func: true,
|
||||
ecma: 6
|
||||
}
|
||||
beautify = {
|
||||
ecma: 6
|
||||
}
|
||||
input: {
|
||||
new Function("aa, [bb]", 'return aa;');
|
||||
new Function("aa, {bb}", 'return aa;');
|
||||
new Function("[[aa]], [{bb}]", 'return aa;');
|
||||
}
|
||||
expect: {
|
||||
Function("aa, [bb]", 'return aa;');
|
||||
Function("aa, {bb}", 'return aa;');
|
||||
Function("[[aa]], [{bb}]", 'return aa;');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
arrow_functions: {
|
||||
options = {
|
||||
arrows: true,
|
||||
}
|
||||
input: {
|
||||
(a) => b; // 1 args
|
||||
(a, b) => c; // n args
|
||||
@@ -13,6 +16,9 @@ arrow_functions: {
|
||||
}
|
||||
|
||||
arrow_return: {
|
||||
options = {
|
||||
arrows: true,
|
||||
}
|
||||
input: {
|
||||
() => {};
|
||||
() => { return; };
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var assert = require("assert");
|
||||
var exec = require("child_process").exec;
|
||||
var readFileSync = require("fs").readFileSync;
|
||||
var semver = require("semver");
|
||||
|
||||
function read(path) {
|
||||
return readFileSync(path, "utf8");
|
||||
@@ -9,9 +10,11 @@ function read(path) {
|
||||
describe("bin/uglifyjs", function () {
|
||||
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
|
||||
it("should produce a functional build when using --self", function (done) {
|
||||
this.timeout(30000);
|
||||
this.timeout(60000);
|
||||
|
||||
var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS';
|
||||
var command = uglifyjscmd + ' --self -mc ecma=';
|
||||
command += semver.satisfies(process.version, ">=4") ? "6" : "5";
|
||||
command += ',passes=3,keep_fargs=false,unsafe --wrap WrappedUglifyJS';
|
||||
|
||||
exec(command, function (err, stdout) {
|
||||
if (err) throw err;
|
||||
|
||||
Reference in New Issue
Block a user