Introduction to Babel

Babel is a JavaScript compiler that converts the human readable code to machine friendly format named Abstract Syntax Tree (AST) to visit, analyze and possibly modify the AST then write AST over files as runtime code.

As JavaScript was initially born for browsers and different browsers possibly understand different JavaScript syntax, the desired runtime code should be compatible across different JavaScript interpreters (e.g. IE9 vs Google Chrome, Node.js 6 vs Node.js 8). caniuse.com is a tool born for developers to search availability of syntax and built-in functions (Object prototype). Browserstack is another tool can be used for browser compatibility testing.

Babel is famous for transpiling ES6 JavaScript code to ES5 JavaScript code so developers can write code in ES6 which is more intuitive meanwhile clients consume transpiled ES5 code which actually implements the same logic with older syntax but better compatibility (funny briefing on the history of JavaScript versions). You can also use Babel to analyze your source code and acquire information. For example, babel-plugin-react-intl is a tool for creating multi-language React Web application. It extracts all textual content written in JSX React Component <FormattedMessage/> into a JSON file which helps you internationalize your React application easily.

We don’t have to touch the source code of a babel-plugin during most of the time of development. However, to be able to make contributions to Babel community or debug a weird behavior at web application or Node.js project, we have to understand Babel. It’s not difficult! Let’s understand this tool used by thousands of JavaScript developers every day with ♥️

Babel is Not Scary. Three Things!

Babel is not scary. I personally had the fear for Babel months ago, because I thought I didn’t take any compiler course at university so it would be super difficult for me to understand Babel. To resolve a bug I encountered during development, I was forced to check out some YouTube videos and tutorials about the fundamental of JavaScript compiler. Let’s explore it together today with practical cases and code. I hope you will gain some insights about the magic happening behind babel-plugins. John hopes this post could shorten the time you spend on understanding the fundamental stuff.

1. Parse

Babel parses source code to AST which is an abstraction of your code for the machine. Don’t worry! To read an AST is NOT scary! I agree that to understand how to turn code to an AST is probably scary ^_^

So what does it look like when parsing JavaScript? Using below ES6 class as an example:

import React from 'react'; 

class HelloWorld extends React.Component{
	constructor() {
		super();
		this.HELLO_TEXT = 'Hello World!';
	}
	
	sayHello() {
		console.log(this.HELLO_TEXT)
	}
	
  	render() {
		return (
		  <h1> 
		    {this.HELLO_TEXT}
		  </h1>
		)
	}
}

Before reading subsequent AST examples, please note that parsing source code to AST is “contextless”. For example, converting line #1 import React from 'react' to AST doesn’t require access to the actual react package. The parser only abstracts your source code but doesn’t do runtime evaluation.

The assignment of HELLO_TEXT as class property looks like below in AST

The class method definition sayHello() looks like below in AST

Nowadays, there are different versions of JavaScript ASTs transformed by different parsers. babylon (by Babel) and espree (by eslint) are two popular JavaScript parsers with sophisticated community support. If you understand one of them, the other will be smoothly understood because they are pretty similar.

2. Transform

Babel traverses AST to explore, analyse or modify AST programmatically

The visitor pattern is very commonly used to traverse an AST. There are many AST node types you can visit in Babel. You don’t have to memorize all of them, this is the description of all node types - babel-types.

Each node path has many built-in functions for us to use. Again, we don’t have to memorize them. This the API docs - babel-core API

Example of (a) visiting MemberExpression nodes and converting console.log() to console.debug() (b) visiting JSXIdentifier nodes and converting <h1> to <Title>:

visitor: {
      MemberExpression(path) {
        if (path.node.object.name === "console" && path.node.property.name === "log") {
          path.node.property.name = "debug";
        }
      },

      JSXIdentifier(path) {
        if (path.node.name === "h1") {
          path.node.name = "Title";
        }
      }
    }

Result (this is also an exmaple of #3 Generate):

import React from 'react'; 

class HelloWorld extends React.Component {
	constructor() {
      	super();
		this.HELLO_TEXT = 'Hello World!';
	}
	
	sayHello() {
		console.debug(this.HELLO_TEXT) // 'log()' is replaced by 'debug()'
	}
  
	render() {
		// <h1> is replaced by <Title>
		return (
		  <Title> 
		    {this.HELLO_TEXT}
		  </Title>
		);
	}
}

3. Generate

Finally, Babel generates actual runtime code based on AST

For example, if we transform an AST of above example code written in ES6 to an AST of code written in ES5, the output will be:

// You're awesome if patient for reading below ES5 code. Feel free to skip it.
var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var HelloWorld = function (_React$Component) {
  _inherits(HelloWorld, _React$Component);

  function HelloWorld() {
      _classCallCheck(this, HelloWorld);

      var _this = _possibleConstructorReturn(this, (HelloWorld.__proto__ || Object.getPrototypeOf(HelloWorld)).call(this));

      _this.HELLO_TEXT = 'Hello World!';
      return _this;
  }

  _createClass(HelloWorld, [{
      key: 'sayHello',
      value: function sayHello() {
        console.debug(this.HELLO_TEXT);
      }
  }, {
      key: 'render',
      value: function render() {
        return _react2.default.createElement(
          Title,
          null,
          this.HELLO_TEXT
        );
      }
  }]);

  return HelloWorld;
}(_react2.default.Component);

Development/Debug Tool

AST Explorer is an integrated online tool that is very friendly for beginners as it requires almost 0 setups.

You can play around different AST parsers and write your own transform logic. The AST and generated source code are shown immediately after updating source code.

How is Babel applied in real projects?

It depends on your use case. It can be applied in the bundler of your build pipeline of a JavaScript project. It can also be applied to a Node.js script for code analysis purpose.

Sharing some my own understandings:

Some concerns we may have

The answer is YES. However, you don’t have to worry about debuggability. babel-register module automatically converts back transpiled code in stack trace to original source code with the help of source-map-support.

preset is a collection of many plugins. It resolves the issue where people have to manually set up multiple plugins to achieve one goal. For example, to compile ES6 code to ES5 code, you will need all plugins shown below, yet preset babel-preset-es2015 contains all of them in a single module:

check-es2015-constants
transform-es2015-arrow-functions
transform-es2015-block-scoped-functions
transform-es2015-block-scoping
transform-es2015-classes
transform-es2015-computed-properties
transform-es2015-destructuring
transform-es2015-duplicate-keys
transform-es2015-for-of
transform-es2015-function-name
transform-es2015-literals
transform-es2015-modules-commonjs
transform-es2015-object-super
transform-es2015-parameters
transform-es2015-shorthand-properties
transform-es2015-spread
transform-es2015-sticky-regex
transform-es2015-template-literals
transform-es2015-typeof-symbol
transform-es2015-unicode-regex
transform-regenerator

- The End -

Note: All opinions in this post are personal. All images, attachments and code in this post are original from @John.