Though Babel is a transpiler to convert ESNext code to ES5, it can be used to optimize the code as well.
Let's say we want to convert following ES6 code using Babel.
1let a = 10; 2let b = 42; 3 4if (a < b) { 5 console.log("a is less than b"); 6} else { 7 console.log("b is less than a"); 8}
It will get translated to:
1"use strict"; 2 3var a = 10; 4var b = 42; 5 6if (a < b) { 7 console.log("a is less than b"); 8} else { 9 console.log("b is less than a"); 10}
All good so far. Let's try Babel's constant folding plugin (Link is not available). I am compiling the ES6 code from command line this time.
1 2babel --plugins constant-folding index.js --out-file bundle.js -w 3
This gives us following output:
1"use strict"; 2 3var a = 10; 4var b = 42; 5 6if (true) { 7 console.log("a is less than b"); 8} else { 9 console.log("b is less than a"); 10}
If condition changed from (a < b) to (true). The constant-folding plugin has smartly evaluated the conditional expression a < b and replaced the code with the result of that expression.
This plugin can also optimize other expressions as shown below.
1// Unary operators 2console.log(!true); 3 4// Binary expression 5console.log(20 + 22); 6 7// Function calls 8console.log(Math.min(91, 2 + 20));
Which gets optimized to:
1// Unary operators 2console.log(false); 3 4// Binary expression 5console.log(42); 6 7// Function calls 8console.log(22);
How does this actually work?
Though we are using a "constant folding plugin" to achieve this optimization, the real work happens in Babel itself.
For every expression in the code, the constant folding plugin calls evaluate function from Babel source code. This function checks whether it can confidently find end value for a given expression.
The evaluate function returns confidence level and end value for any given expression. Based on this "confidence level", constant folding plugin replaces the expression with their end values altering our original code as follows.
How evaluate handles different cases
For code Math.min(10, 20), evaluate will return
{ confident: true, value: 10 }
For code a < b, evaluate will return
{ confident: true, value: true }.
But for user defined function like foo('bar') or browser defined console.log('hello'), evaluate will return
{ confident: false, value: undefined }.
In the above case "confident" value will be "false" even if function returns a constant value. For example for code foo(100), evaluate will return
{ confident: false, value: undefined }.
In the above case function foo will always return 100. Still Babel has low confidence level. Why? That's because Babel sees that it is a function and it bails out. It does not even look inside to try to figure things out.
Here is evaluate code in Babel. You should check it out.
How much optimization is possible?
How much help we will get from Babel for optimizing our code? Will it optimize everything?
The answer is unfortunately no.
As of now, Babel optimizes logical, binary, conditional expressions. It can also evaluate function calls on literals like "babel".length confidently if the literal is string or number.
For function calls, it supports only certain callees like String, Number and Math. So call to a user defined function, even if it's returning a fixed value, will not be optimized.
Experimental feature
This feature looks great. But it's available as experimental feature. If you use the plugin you will get following warning. unless you enable experimental flag.
1$ babel --plugins constant-folding index.js --out-file bundle.js -w 2 3[BABEL] index.js: THE TRANSFORMER constant-folding HAS BEEN MARKED AS EXPERIMENTAL AND IS WIP. USE AT YOUR OWN RISK. THIS WILL HIGHLY LIKELY BREAK YOUR CODE SO USE WITH **EXTREME** CAUTION. ENABLE THE `experimental` OPTION TO IGNORE THIS WARNING.
In order to get rid of warning you need to pass --experimental flag like this.
1$ babel --plugins constant-folding index.js --out-file bundle.js -w 2--experimental
Eliminating dead code
In above code example, we know that the result of if (a < b) is true based on values of a and b. Since the result is not going to change no matter what there is no need to have the if and else clauses.
That's dead code.
Can Babel help us eliminate dead code?
Yes with the help of minification.deadCodeElimination option.
1 2babel --optional minification.deadCodeElimination index.js --out-file bundle.js -w 3
Which converts earlier code to:
1"use strict"; 2 3console.log("a is less than b");
I will talk about how Babel can eliminate dead code in a later post.