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:
parent
7b95b63ca1
commit
d1f085bce7
|
|
@ -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
|
- `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
|
- `booleans` -- various optimizations for boolean context, for example `!!a
|
||||||
? b : c → a ? b : c`
|
? b : c → a ? b : c`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ function Compressor(options, false_by_default) {
|
||||||
return new Compressor(options, false_by_default);
|
return new Compressor(options, false_by_default);
|
||||||
TreeTransformer.call(this, this.before, this.after);
|
TreeTransformer.call(this, this.before, this.after);
|
||||||
this.options = defaults(options, {
|
this.options = defaults(options, {
|
||||||
|
arrows : !false_by_default,
|
||||||
booleans : !false_by_default,
|
booleans : !false_by_default,
|
||||||
cascade : !false_by_default,
|
cascade : !false_by_default,
|
||||||
collapse_vars : !false_by_default,
|
collapse_vars : !false_by_default,
|
||||||
|
|
@ -2047,7 +2048,7 @@ merge(Compressor.prototype, {
|
||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_Block, function(self, compressor){
|
OPT(AST_Block, function(self, compressor){
|
||||||
if (!(self.body instanceof AST_Node)) tighten_body(self.body, compressor);
|
tighten_body(self.body, compressor);
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -3273,11 +3274,12 @@ merge(Compressor.prototype, {
|
||||||
var fun;
|
var fun;
|
||||||
ast.walk(new TreeWalker(function(node) {
|
ast.walk(new TreeWalker(function(node) {
|
||||||
if (fun) return true;
|
if (fun) return true;
|
||||||
if (node instanceof AST_Lambda) {
|
if (node instanceof AST_Function) {
|
||||||
fun = node;
|
fun = node;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
if (!fun) return self;
|
||||||
var args = fun.argnames.map(function(arg, i) {
|
var args = fun.argnames.map(function(arg, i) {
|
||||||
return make_node(AST_String, self.args[i], {
|
return make_node(AST_String, self.args[i], {
|
||||||
value: arg.print_to_string()
|
value: arg.print_to_string()
|
||||||
|
|
@ -4499,13 +4501,37 @@ merge(Compressor.prototype, {
|
||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_Arrow, function(self, compressor){
|
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;
|
var value = self.body[0].value;
|
||||||
self.body = value ? value : [];
|
self.body = value ? value : [];
|
||||||
}
|
}
|
||||||
return self;
|
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){
|
OPT(AST_Class, function(self, compressor){
|
||||||
// HACK to avoid compress failure.
|
// HACK to avoid compress failure.
|
||||||
// AST_Class is not really an AST_Scope/AST_Block as it lacks a body.
|
// 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);"
|
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: {
|
async_identifiers: {
|
||||||
input: {
|
input: {
|
||||||
let async = function(x){ console.log("async", x); };
|
var async = function(x){ console.log("async", x); };
|
||||||
let await = function(x){ console.log("await", x); };
|
var await = function(x){ console.log("await", x); };
|
||||||
async(1);
|
async(1);
|
||||||
await(2);
|
await(2);
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
let async = function(x){ console.log("async", x); };
|
var async = function(x){ console.log("async", x); };
|
||||||
let await = function(x){ console.log("await", x); };
|
var await = function(x){ console.log("await", x); };
|
||||||
async(1);
|
async(1);
|
||||||
await(2);
|
await(2);
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +182,6 @@ async_identifiers: {
|
||||||
"async 1",
|
"async 1",
|
||||||
"await 2",
|
"await 2",
|
||||||
]
|
]
|
||||||
node_version: ">=8"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async_shorthand_property: {
|
async_shorthand_property: {
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,25 @@ compress_new_function_with_destruct: {
|
||||||
Function("[[a]]", "[{bb:b}]", 'return a');
|
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: {
|
arrow_functions: {
|
||||||
|
options = {
|
||||||
|
arrows: true,
|
||||||
|
}
|
||||||
input: {
|
input: {
|
||||||
(a) => b; // 1 args
|
(a) => b; // 1 args
|
||||||
(a, b) => c; // n args
|
(a, b) => c; // n args
|
||||||
|
|
@ -13,6 +16,9 @@ arrow_functions: {
|
||||||
}
|
}
|
||||||
|
|
||||||
arrow_return: {
|
arrow_return: {
|
||||||
|
options = {
|
||||||
|
arrows: true,
|
||||||
|
}
|
||||||
input: {
|
input: {
|
||||||
() => {};
|
() => {};
|
||||||
() => { return; };
|
() => { return; };
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
var assert = require("assert");
|
var assert = require("assert");
|
||||||
var exec = require("child_process").exec;
|
var exec = require("child_process").exec;
|
||||||
var readFileSync = require("fs").readFileSync;
|
var readFileSync = require("fs").readFileSync;
|
||||||
|
var semver = require("semver");
|
||||||
|
|
||||||
function read(path) {
|
function read(path) {
|
||||||
return readFileSync(path, "utf8");
|
return readFileSync(path, "utf8");
|
||||||
|
|
@ -9,9 +10,11 @@ function read(path) {
|
||||||
describe("bin/uglifyjs", function () {
|
describe("bin/uglifyjs", function () {
|
||||||
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
|
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
|
||||||
it("should produce a functional build when using --self", function (done) {
|
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) {
|
exec(command, function (err, stdout) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user