bar in function foo(bar) { })return when possible (e.g. return [1,2,3] → return[1,2,3])var result = expression(); foo.bar = result; → foo.bar = expression()var b = () => {
var x = 14;
var y = 7 - x / 2;
return y * (28 / x + 2);
};
var a = () => {
var a = 14;
return (7 - a / 2) * (28 / a + 2);
};
Removing unused arguments is an unsafe optimisation as it will effect runtime behaviour as length will be changed.
Yep.
This is one of those almost-safe optimizations which will "work" most of the time but could silently bite you. We'll need to have it as part of the advanced/unsafe category.
This is also somewhat similar to Number(...) → +... transformation that we already do — it's correct unless native method was tampered with (which we can't check).
In practice, I'm seeing tons of stuff like __d("...",[],function(b,c,d,e,f,g){c.__markCompiled&&c.__markCompiled() /* stuff that doesn't use e,f,g */ },null); in our code and it's most certainly safe to strip those args, saving a lot of unnecessary boilerplate.
Maybe have it as an option. FWIW GCC does this by default.
On Wed, Jun 1, 2016 at 5:39 PM Juriy Zaytsev [email protected]
wrote:
Yep.
This is one of those almost-safe optimizations which will "work" most of
the time but could silently bite you. We'll need to have it as part of the
advanced/unsafe category.This is also somewhat similar to Number(...) → +... transformation that
we already do — it's correct unless native method was tampered with (which
we can't check).In practice, I'm seeing tons of stuff like __d("...",[],function(b,c,d,e,f,g){c.__markCompiled&&c.__markCompiled()
/* stuff that doesn't use e,f,g */ },null); in our code and it's most
certainly safe to strip those args, saving a lot of unnecessary boilerplate.—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/amasad/babel-minify/issues/25#issuecomment-223166015,
or mute the thread
https://github.com/notifications/unsubscribe/AAj2_tmK1DmSSLH89FgpwgO-Wc8flg1xks5qHiZdgaJpZM4Ir6IY
.
[x] Use arrow functions (in ES6 browsers)
[x] Minify Array() and Object() type constructors: #45 🎉
[x] Minify RegExp type constructor
[x] Use assignment operators where possible
[x] Merge object additions near object creation (a={};a.a=1 => a={a:1})
[x] Don't assign or return undefined (var a = undefined and return undefined)
[ ] Math.abs can be generated
Unsafe with -0
Math.abs(x)
(ideal) 0>x?-x:x
[ ] Resolve simple functions
Examples...
var q = 'hello'.charAt(2);
babel var q='hello'.charAt(2);
uglify var q="hello".charAt(2);
closure var q="l";
(function () {
function add(a, b) { return a + b; }
function by3(num) { return num * 3; }
window.q = add(4, by3(2)) + by3(add(4, 5));
} ());
babel (function(){function c(d,a){return d+a}function a(b){return b*3}window.q=c(4,a(2))+a(c(4,5))})();
uglify !function(){function n(n,t){return n+t}function t(n){return 3*n}window.q=n(4,t(2))+t(n(4,5))}();
closure (function(){window.q=37})();
// Edit 3, added July 7th
function foo (num) {
function cube(num) {
return num * 2;
}
function quarter(num) {
return num / 4;
}
return cube(quarter(num));
}
uglify function foo(n){function r(n){return 2*n}function t(n){return n/4}return r(t(n))}
babel function foo(a){return function(a){return 2*a}(function(a){return a/4}(a))}
closure function foo(a){return a/4*2};
boopathi/babel-minify has some
[ ] Remove unnecessary IIFE
(function () {
window.foo = 2;
} ());
(all) (function(){window.foo=2})();
(ideal) window.foo=2;
[ ] Remove shadowed+unused variables/functions (unless they access properties)
var a = 1;a = 2;
(all) var a=1;a=2;
(ideal) var a=2;
function foo() {}
function foo(bar) {
return bar;
}
(all) function foo(){}function foo(a){return a}
(ideal) function foo(c){return c};
[ ] Minify Function type constructor
Unsafe because new Function's functions are created in the global scope; They'd have to be created there.
new Function('a', 'b', 'return a + b');
(all) new Function('a','b','return a + b');
(safe)? function anonymous(a,b) {return a+b}
(ideal) function(a,b) {return a+b}
[ ] constant-folding isn't applied after minify-type-constructors
console.log(1+String("1"));
uglify console.log(1+String("1"));
babel console.log(1+"1");
closure console.log("11");
[ ] dead-code-elimination isn't applied after minify-type-constructors
1+"1"; // this is removed
1+String("1") // this isn't
uglify 1+String("1");
closure "11";"11";
babel 1+"1";
[ ] Math.pow -> Exponentiation Operator in ES2016 browsers
var cube = Math.pow(2, 3);
num = Math.pow(num, 3);
(ideal) var cube=2**3;num**=3;
[ ] Dead code elimination: pure functions
(function () {} ());
babel (function(){})();
closure (function(){})();
uglify // 0 bytes, totally eliminated
(function () {
function twice(num) {
return num * 2;
}
var a=twice(1);
} ());
babel (function(){(function(a){return 2*a})(1)})();
uglify !function(){function n(n){return 2*n}n(1)}();
closure (function(){})();
(ideal) // 0 bytes, totally eliminated
[ ] Remove window where possible (unsafe if output is pasted inside another function)
window.jQuery = window.$ = function() {};
(ideal) jQuery=$=function(){}
[ ] Merge multiple assignments
(function () {
function on() {}
function off() {}
module.exports = on;
module.exports.on = on;
module.exports.off = off;
} ());
uglify !function(){function o(){}function n(){}module.exports=o,module.exports.on=o,module.exports.off=n}();
babel (function(){function a(){}module.exports=a,module.exports.on=a,module.exports.off=function(){}})();
closure (function(){function a(){}module.exports=a;module.exports.on=a;module.exports.off=function(){}})();
(ideal) (function(){function a(){}module.exports.on=module.exports=a;module.exports.off=function(){}})(); // this transform
(next) (function(){module.exports.on=module.exports=function(){},module.exports.off=function(){}})(); // would allow for this
(next) module.exports.on=module.exports=function(){},module.exports.off=function(){}; // and then this
[ ] Remove boolean casting in conditions
if (!!foo) {
log(1);
}
babel !foo||log(1);
uglify foo&&log(1);
closure foo&&log(1);
[ ] Math.min and Math.max with two parameters
http://codegolf.stackexchange.com/a/60293/21413
Unsafe with -0
Math.min(a,b)
Math.max(a,b)
(ideal)
a<b?a:b
a>b?a:b
[ ] Various possible indexOf optimizations
[1].indexOf(1)!==-1; // contains
(ideal)
[1].indexOf(1)>=0;
~[1].indexOf(1); // only works when a truthy value is enough, e.g. if(...)
[1].indexOf(1)===-1; // doesn't contain
(ideal)
[1].indexOf(1)<0;
[ ] Only when accessing properties, compare to undefined instead of using typeof
Removed because accessing a.bar could have side effects
function isBarUndefined(foo) {
return typeof foo.bar === 'undefined'
}
(all) function isBarUndefined(a){return'undefined'==typeof a.bar}
(ideal) function isBarUndefined(a){return void 0===a.bar}
[ ] Generate the string 'undefined' (I think constant-folding conflicts with this by transforming ''+void 0 back into 'undefined')
typeof foo === 'undefined'
(ideal)
typeof foo==''+void 0
[ ] undefined-to-void -> shorten-undefined http://codegolf.stackexchange.com/a/42155/21413
undefined;
(all) void 0;
(ideal) 1..a;
undefined;undefined;
(all) void 0;void 0
(ideal) var u;u;u // assign undefined variable, use twice
Wow, closure is really nailing it, huh...
I wanted to expand on that first "Resolve identity/simple functions" for CC since it does it quite nicely:
(click here to expand <details>)
(function () {
function repeatTwice(text) {
return [text, text].join('');
}
foo = repeatTwice('hello')
} ());
closure output: (function(){foo="hellohello"})();
(function () {
function identity(text) {
return text;
}
foo = identity('hello')
bar = identity('hello')
baz = identity('hello')
} ());
closure output: (function(){baz=bar=foo="hello"})();
And look at this crazy thing:
(function () {
function multiple(text) {
return [text, text, text, text, text, text];
}
foo = multiple('w')
} ());
closure output: (function(){foo="wwwwww".split("")})();
Which at times fails too:
(function () {
var text = 'hello, it’s me';
foo = [text, text, text, text, text, text]
} ());
closure (function(){foo="hello, it\u2019s me;hello, it\u2019s me;hello, it\u2019s me;hello, it\u2019s me;hello, it\u2019s me;hello, it\u2019s me".split(";")})();
babel (function(){var a='hello, it’s me';foo=[a,a,a,a,a,a]})();
uglify !function(){var o="hello, it’s me";foo=[o,o,o,o,o,o]}();
This is being solved
(function () {
function twice(text) {
return [text, text];
}
foo = twice('w')
foo = twice('w')
foo = twice('w')
} ());
closure output: (function(){foo=["w","w"];foo=["w","w"];foo=["w","w"]})();
But a slight change in the length of the text breaks it, even though the output would still be smaller, especially factoring gzip in
(function () {
function twice(text) {
return [text, text];
}
foo = twice('ww') // <---
foo = twice('ww') // <---
foo = twice('ww') // <---
} ());
expected output: (function(){foo=["ww","ww"];foo=["ww","ww"];foo=["ww","ww"]})();
closure output: (function(){function a(a){return[a,a]}foo=a("ww");foo=a("ww");foo=a("ww")})();
This was a wrong repeat implementation, but look at that output
(function () {
function repeat(text, repeat) {
for (var i = 0; i < repeat; i++) {
text += text;
}
return repeat; // mistake here, it should return text
}
foo = repeat('hello', 1)
} ());
closure output: (function(){for(var a=0;1>a;a++);foo=1})();
Notes: foo is resolved. The loop is truncated, but it's still there.
Making two calls no longer resolves foo
...
foo = repeat('hello', 1)
foo = repeat('hello', 1)
...
closure output: (function(){function a(a,b){for(var c=0;c<b;c++);return b}foo=a("hello",1);foo=a("hello",1)})();
re: Use arrow functions (in ES6 browsers)
How should babel-minify handle minifying using modern language features that are normally compiled down to ES5 by Babel?
We can't turn function functions into arrow functions. It's an unsafe optimisation. Arrow functions have no prototype and cannot be used as constructors so they aren't the same.
James: I don't think that's necessary because you can always add the
plugins you need before the minify step
On Wed, Jul 6, 2016, 2:26 PM Sebastian McKenzie [email protected]
wrote:
We can't turn function functions into arrow functions. It's an unsafe
optimisation. Arrow functions have no prototype and cannot be used as
constructors.—
You are receiving this because you commented.Reply to this email directly, view it on GitHub
https://github.com/amasad/babel-minify/issues/25#issuecomment-230911983,
or mute the thread
https://github.com/notifications/unsubscribe/AAj2_nWIPRCQPIfQt-5mt-TEYeHAXyBOks5qTB1ngaJpZM4Ir6IY
.
Yes, but there are places where ES5 code could be optimized more by using ES6+ features. I'm sure there are cases where we'd find arrow functions acceptable to use. Should optimizations like that be inferred? configured?
@thejameskyle Should optimizations like that be inferred?
_Ideally_ the user would either be able to enable/disable each transform or use browserlist to figure out which ES6 transforms are safe. But maybe a target:"es6" could work.
@kittens then maybe the function-to-arrow transform could only apply to functions that are verified to never user the prototype.
It may be early for this, but should babel-minify have a repo dedicated to transforms/plugin suggestions? One-suggestion-per-issue would make it easier to discuss, track and "rate" each transform (difficulty vs. how common it is vs. savings)
Like: https://github.com/postcss/postcss-plugin-suggestion-box and https://github.com/sindresorhus/module-requests
Perhaps babel itself could benefit from one
A few more optimizations for conditionals -
``` js
if (x) { doSomething(a) } else { doSomething(b) }
// to:
doSomething(x ? a : b);
```
``` js
a < b ? false : true
a() ? false : true
b() ? true : false
a ? b : true
a ? b : false
// out:
x === y
!(a < b)
!a()
!!b()
!a || b
```
For the 1st case, I guess we would need to "fold" all the same-named methods as long as they're the only ones present in blocks and are in the same order?
if (x) { foo(a); bar(y); } else { foo(b); bar(z); }
// becomes
foo(x ? a : b);
bar(x ? y : z);
but these won't work:
if (x) { bar(y); foo(a); } else { foo(b); bar(z); }
if (x) { foo(a); } else { foo(b); bar(z); }
May be undefined -> ''.$ instead undefined -> void 0? ([].$, ({}).$, eval.$, Date.$, Map.$, etc., or simple var u; as global symbol.)
@napa3um Already suggested near the end of https://github.com/babel/babili/issues/25#issuecomment-230671685
@hzoo Would it make sense to open a repo for the suggestions as suggested earlier? Or maybe just one-enhancement-per-issue so they can be easily discussed and tracked.
Came across this one, too: https://github.com/nolanlawson/optimize-js
Is there already a babel plugin for this?
We should also turn new Array to Array() no matter which arguments since they're identical and there's no need for new /cc @shinew
So strange, I could swear it wasn't happening for me in repl.
1000 → 1e3 and so onI'd like to see unnecessary, single-use variables get inlined and also functions like this:
(function ([, a, b]) {
return (function (c, d) {
return c + d;
})(a, b);
})(thing);
(function ([, a, b]) {
var e = (function (c, d) {
return c + d;
})(a, b);
return e;
})(thing);
(function ([, a, b]) {
(function (c, d) {
log(c + d);
})(a, b);
})(thing);
var a = b.something();
run(a.other());
// become
(function ([, c, d]) {
return c + d;
})(thing);
(function ([, c, d]) {
return c + d;
})(thing);
(function ([, c, d]) {
log(c + d);
})(thing);
run(b.something().other())
That'd be great! Anything that isn't used more than once should be inlined. I often declare variables for readability but I could just as well write one-liners instead.
Various possible indexOf optimizations
These are shorter:
[1].indexOf(1)!==-1; // contains
(ideal)
[1].includes(1);
```js
[1].indexOf(1)===-1; // doesn't contain
(ideal)
![1].includes(1)
> Remove shadowed+unused variables/functions (unless they access properties)
For property access:
```js
var a = foo.bar;a = foo;
(all) var a=foo.bar;a=foo;
(ideal) foo.bar;var a=foo;
Also, a couple other thoughts:
_classCallCheck helper variable that makes sure new is used when constructing ES6 classes, Babili could create a _babiliUndefined variable that could get mangled down to a single character, which is significantly shorter than void 0. By taking advantage of the same mechanism that Babel uses to generate a non-conflicting variable name, we could avoid problems when people name a variable _babiliUndefined. Example:if (foo === undefined) {
return undefined
}
// becomes
var _babiliUndefined
if (foo === _babiliUndefined) {
return _babiliUndefined
}
// after mangling
var a
if (b === a) {
return a
}
Some of the things presented in https://codegolf.stackexchange.com/q/2682/44689:
[ ] Replace parseInt/parseFloat with +:
parseInt(foo(),16);
parseInt(foo(),10);
parseInt(foo());
parseInt(foo(),8);
parseInt(foo(),2);
// ideal:
+("0x"+foo()|0);
+foo()|0;
+foo()|0;
+("0o"+foo()|0);
+("0b"+foo()|0);
[ ] Math.{floor,ceil,round}:
Math.floor(foo);
Math.round(bar());
Math.ceil(baz.quux);
// ideal
0|foo;
0|bar()+.5;
0|baz.quux+1-1e-9;
[ ] boolean contexts: != → ^:
x != 3 ? a : b;
foo.bar() != -99 ? a : b;
if (theAnswer != 92) {
foo();
}
// ideal
x^3?a:b
foo.bar()^-99?a:b
if (theAnswer^92) {
foo();
}
[ ] boolean contexts: <thing>.length → 0 in <thing>
arr.length ? 'foo' : 'bar';
"foo".length ? 'foo' : 'bar';
// ideal
0 in arr ? 'foo' : 'bar';
0 in "foo" ? 'foo' : 'bar';
Coming from #591, where @loganfsmyth suggested (after some discussion) that babel-minify place a _void0 or similar variable at the top, then replace all uses of undefined with it, allowing for mangling:
It should be possible to either put the var _void0 variable at the top of sourceType: module scripts or ones that are node modules. In other cases, they can be put at the top of function scopes, or the entire code could be wrapped in a block:
{
let _void0
// code here
}
This would increase code size if void 0 only appeared once, but would save 5 characters if used twice and 11 if used thrice.
Replace () => {} with global noop?
Most helpful comment
We can't turn function functions into arrow functions. It's an unsafe optimisation. Arrow functions have no
prototypeand cannot be used as constructors so they aren't the same.