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.

let a = 10;
let b = 42;

if (a < b) {
  console.log("a is less than b");
} else {
  console.log("b is less than a");
}

It will get translated to:

"use strict";

var a = 10;
var b = 42;

if (a < b) {
  console.log("a is less than b");
} else {
  console.log("b is less than a");
}

All good so far. Let’s try Babel’s constant folding plugin. I am compiling the ES6 code from command line this time.

babel --plugins constant-folding index.js --out-file bundle.js -w

This gives us following output:

"use strict";

var a = 10;
var b = 42;

if (true) {
  console.log("a is less than b");
} else {
  console.log("b is less than a");
}

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.

// Unary operators
console.log(!true);

// Binary expression
console.log(20 + 22);

// Function calls
console.log(Math.min(91, 2 + 20));

Which gets optimized to:

// Unary operators
console.log(false);

// Binary expression
console.log(42);

// Function calls
console.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.

$ babel --plugins constant-folding index.js --out-file bundle.js -w

[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.

$ babel --plugins constant-folding index.js --out-file bundle.js -w
--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.

babel --optional minification.deadCodeElimination index.js --out-file bundle.js -w

Which converts earlier code to:

"use strict";

console.log("a is less than b");

I will talk about how Babel can eliminate dead code in a later post.