screw_ie8 : true,
drop_console : false,
angular : false,
+ expression : false,
warnings : true,
global_defs : {},
passes : 1,
merge(Compressor.prototype, {
option: function(key) { return this.options[key] },
compress: function(node) {
+ if (this.option("expression")) {
+ node = node.process_expression(true);
+ }
var passes = +this.options.passes || 1;
for (var pass = 0; pass < passes && pass < 3; ++pass) {
if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this, true);
node = node.transform(this);
}
+ if (this.option("expression")) {
+ node = node.process_expression(false);
+ }
return node;
},
warn: function(text, props) {
return this.print_to_string() == node.print_to_string();
});
+ AST_Node.DEFMETHOD("process_expression", function(insert) {
+ var self = this;
+ var tt = new TreeTransformer(function(node) {
+ if (insert && node instanceof AST_SimpleStatement) {
+ return make_node(AST_Return, node, {
+ value: node.body
+ });
+ }
+ if (!insert && node instanceof AST_Return) {
+ return make_node(AST_SimpleStatement, node, {
+ body: node.value || make_node(AST_Undefined, node)
+ });
+ }
+ if (node instanceof AST_Lambda && node !== self) {
+ return node;
+ }
+ if (node instanceof AST_Block) {
+ var index = node.body.length - 1;
+ if (index >= 0) {
+ node.body[index] = node.body[index].transform(tt);
+ }
+ }
+ if (node instanceof AST_If) {
+ node.body = node.body.transform(tt);
+ if (node.alternative) {
+ node.alternative = node.alternative.transform(tt);
+ }
+ }
+ if (node instanceof AST_With) {
+ node.body = node.body.transform(tt);
+ }
+ return node;
+ });
+ return self.transform(tt);
+ });
+
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
var reduce_vars = rescan && compressor.option("reduce_vars");
var safe_ids = [];
def(AST_Constant, return_null);
def(AST_This, return_null);
def(AST_Call, function(compressor, first_in_statement){
- if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return this;
+ if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) {
+ if (this.expression instanceof AST_Function) {
+ var node = this.clone();
+ node.expression = node.expression.process_expression(false);
+ return node;
+ }
+ return this;
+ }
if (this.pure) {
compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
this.pure.value = this.pure.value.replace(/[@#]__PURE__/g, ' ');
});
OPT(AST_Call, function(self, compressor){
+ var exp = self.expression;
if (compressor.option("unused")
- && self.expression instanceof AST_Function
- && !self.expression.uses_arguments
- && !self.expression.uses_eval
- && self.args.length > self.expression.argnames.length) {
- var end = self.expression.argnames.length;
+ && exp instanceof AST_Function
+ && !exp.uses_arguments
+ && !exp.uses_eval
+ && self.args.length > exp.argnames.length) {
+ var end = exp.argnames.length;
for (var i = end, len = self.args.length; i < len; i++) {
var node = self.args[i].drop_side_effect_free(compressor);
if (node) {
self.args.length = end;
}
if (compressor.option("unsafe")) {
- var exp = self.expression;
if (exp instanceof AST_SymbolRef && exp.undeclared()) {
switch (exp.name) {
case "Array":
return best_of(self, node);
}
}
- if (compressor.option("side_effects")) {
- if (self.expression instanceof AST_Function
- && self.args.length == 0
- && !AST_Block.prototype.has_side_effects.call(self.expression, compressor)) {
- return make_node(AST_Undefined, self).transform(compressor);
+ if (exp instanceof AST_Function) {
+ if (exp.body[0] instanceof AST_Return
+ && exp.body[0].value.is_constant()) {
+ var args = self.args.concat(exp.body[0].value);
+ return AST_Seq.from_array(args).transform(compressor);
+ }
+ if (compressor.option("side_effects")) {
+ if (!AST_Block.prototype.has_side_effects.call(exp, compressor)) {
+ var args = self.args.concat(make_node(AST_Undefined, self));
+ return AST_Seq.from_array(args).transform(compressor);
+ }
}
}
if (compressor.option("drop_console")) {
- if (self.expression instanceof AST_PropAccess) {
- var name = self.expression.expression;
+ if (exp instanceof AST_PropAccess) {
+ var name = exp.expression;
while (name.expression) {
name = name.expression;
}
}
}
}
- if (self.args.length == 0
- && self.expression instanceof AST_Function
- && self.expression.body[0] instanceof AST_Return
- && self.expression.body[0].value.is_constant()) {
- return self.expression.body[0].value;
- }
if (compressor.option("negate_iife")
&& compressor.parent() instanceof AST_SimpleStatement
&& is_iife_call(self)) {
--- /dev/null
+cond_5: {
+ options = {
+ conditionals: true,
+ expression: true,
+ }
+ input: {
+ if (some_condition()) {
+ if (some_other_condition()) {
+ do_something();
+ } else {
+ alternate();
+ }
+ } else {
+ alternate();
+ }
+
+ if (some_condition()) {
+ if (some_other_condition()) {
+ do_something();
+ }
+ }
+ }
+ expect: {
+ some_condition() && some_other_condition() ? do_something() : alternate();
+ if (some_condition() && some_other_condition()) do_something();
+ }
+}
+
+dead_code_const_annotation_regex: {
+ options = {
+ booleans : true,
+ conditionals : true,
+ dead_code : true,
+ evaluate : true,
+ expression : true,
+ loops : true,
+ }
+ input: {
+ var unused;
+ // @constraint this shouldn't be a constant
+ var CONST_FOO_ANN = false;
+ if (CONST_FOO_ANN) {
+ console.log("reachable");
+ }
+ }
+ expect: {
+ var unused;
+ var CONST_FOO_ANN = !1;
+ if (CONST_FOO_ANN) console.log('reachable');
+ }
+}
+
+drop_console_2: {
+ options = {
+ drop_console: true,
+ expression: true,
+ }
+ input: {
+ console.log('foo');
+ console.log.apply(console, arguments);
+ }
+ expect: {
+ // with regular compression these will be stripped out as well
+ void 0;
+ void 0;
+ }
+}
+
+drop_value: {
+ options = {
+ expression: true,
+ side_effects: true,
+ }
+ input: {
+ (1, [2, foo()], 3, {a:1, b:bar()});
+ }
+ expect: {
+ foo(), {a:1, b:bar()};
+ }
+}
+
+wrongly_optimized: {
+ options = {
+ conditionals: true,
+ booleans: true,
+ evaluate: true,
+ expression: true,
+ }
+ input: {
+ function func() {
+ foo();
+ }
+ if (func() || true) {
+ bar();
+ }
+ }
+ expect: {
+ function func() {
+ foo();
+ }
+ // TODO: optimize to `func(), bar()`
+ if (func(), !0) bar();
+ }
+}
+
+negate_iife_1: {
+ options = {
+ expression: true,
+ negate_iife: true,
+ }
+ input: {
+ (function(){ stuff() })();
+ }
+ expect: {
+ (function(){ stuff() })();
+ }
+}
+
+negate_iife_3: {
+ options = {
+ conditionals: true,
+ expression: true,
+ negate_iife: true,
+ }
+ input: {
+ (function(){ return t })() ? console.log(true) : console.log(false);
+ }
+ expect: {
+ (function(){ return t })() ? console.log(true) : console.log(false);
+ }
+}
+
+negate_iife_3_off: {
+ options = {
+ conditionals: true,
+ expression: true,
+ negate_iife: false,
+ }
+ input: {
+ (function(){ return t })() ? console.log(true) : console.log(false);
+ }
+ expect: {
+ (function(){ return t })() ? console.log(true) : console.log(false);
+ }
+}
+
+negate_iife_4: {
+ options = {
+ conditionals: true,
+ expression: true,
+ negate_iife: true,
+ sequences: true,
+ }
+ input: {
+ (function(){ return t })() ? console.log(true) : console.log(false);
+ (function(){
+ console.log("something");
+ })();
+ }
+ expect: {
+ (function(){ return t })() ? console.log(true) : console.log(false), function(){
+ console.log("something");
+ }();
+ }
+}
+
+negate_iife_5: {
+ options = {
+ conditionals: true,
+ expression: true,
+ negate_iife: true,
+ sequences: true,
+ }
+ input: {
+ if ((function(){ return t })()) {
+ foo(true);
+ } else {
+ bar(false);
+ }
+ (function(){
+ console.log("something");
+ })();
+ }
+ expect: {
+ (function(){ return t })() ? foo(true) : bar(false), function(){
+ console.log("something");
+ }();
+ }
+}
+
+negate_iife_5_off: {
+ options = {
+ conditionals: true,
+ expression: true,
+ negate_iife: false,
+ sequences: true,
+ };
+ input: {
+ if ((function(){ return t })()) {
+ foo(true);
+ } else {
+ bar(false);
+ }
+ (function(){
+ console.log("something");
+ })();
+ }
+ expect: {
+ (function(){ return t })() ? foo(true) : bar(false), function(){
+ console.log("something");
+ }();
+ }
+}
+
+issue_1254_negate_iife_true: {
+ options = {
+ expression: true,
+ negate_iife: true,
+ }
+ input: {
+ (function() {
+ return function() {
+ console.log('test')
+ };
+ })()();
+ }
+ expect_exact: '(function(){return function(){console.log("test")}})()();'
+}
+
+issue_1254_negate_iife_nested: {
+ options = {
+ expression: true,
+ negate_iife: true,
+ }
+ input: {
+ (function() {
+ return function() {
+ console.log('test')
+ };
+ })()()()()();
+ }
+ expect_exact: '(function(){return function(){console.log("test")}})()()()()();'
+}
+
+conditional: {
+ options = {
+ expression: true,
+ pure_funcs: [ "pure" ],
+ side_effects: true,
+ }
+ input: {
+ pure(1 | a() ? 2 & b() : 7 ^ c());
+ pure(1 | a() ? 2 & b() : 5);
+ pure(1 | a() ? 4 : 7 ^ c());
+ pure(1 | a() ? 4 : 5);
+ pure(3 ? 2 & b() : 7 ^ c());
+ pure(3 ? 2 & b() : 5);
+ pure(3 ? 4 : 7 ^ c());
+ pure(3 ? 4 : 5);
+ }
+ expect: {
+ 1 | a() ? b() : c();
+ 1 | a() && b();
+ 1 | a() || c();
+ a();
+ 3 ? b() : c();
+ 3 && b();
+ 3 || c();
+ pure(3 ? 4 : 5);
+ }
+}
+
+limit_1: {
+ options = {
+ expression: true,
+ sequences: 3,
+ }
+ input: {
+ a;
+ b;
+ c;
+ d;
+ e;
+ f;
+ g;
+ h;
+ i;
+ j;
+ k;
+ }
+ expect: {
+ // Turned into a single return statement
+ // so it can no longer be split into lines
+ a,b,c,d,e,f,g,h,i,j,k;
+ }
+}
+
+iife: {
+ options = {
+ expression: true,
+ sequences: true,
+ }
+ input: {
+ x = 42;
+ (function a() {})();
+ !function b() {}();
+ ~function c() {}();
+ +function d() {}();
+ -function e() {}();
+ void function f() {}();
+ typeof function g() {}();
+ }
+ expect: {
+ x = 42, function a() {}(), function b() {}(), function c() {}(),
+ function d() {}(), function e() {}(), function f() {}(), typeof function g() {}();
+ }
+}