Babel Tutorial Part 1 - Intro
Greetings, friends! I'm going to start a small series about Babel, the modern JavaScript transpiler! If you've used JavasScript libraries/frameworks such as React or Vue, then you've likely used Webpack with a Babel loader. This Babel loader lets you transpile your code using Babel! When starting a new React project, you may have used Create React App to get started. Similarly, you may have used the Vue CLI when bootstrapping a new Vue project. Internally, both Create React App and the Vue CLI use Webpack and Babel. Before we start using Babel, we should learn what a transpiler is and understand the motivation behind using one.
What is a Transpiler?
According to Wikipedia, a transpiler is "a type of translator that takes the source code of a program written in a programming language as its input and produces an equivalent source code in the same or a different programming language."
I won't go into all the details on how transpilers work here, but there are already awesome articles like this for explaining how they work in great detail. In summary, transpilers similar to Babel consists of three parts: parsing (which includes lexical analysis and syntactic analysis), transforming, and generating. The parser will create an abstract syntax tree (AST) from an input string. What is the input string? It's the code you write! If you go to the AST Explorer, you can visualize how code is broken down into nodes on an AST. Make sure you choose JavaScript as the language. You may also notice that there are multiple JavaScript parsers to choose from, including the one used by Babel: @babel/parser.
How does Babel Transpile Code?
Babel recommends reading The Super Tiny Compiler to understand at a high-level how Babel's transpiler works. Babel parses the source code into an AST using @babel/parser, formerly known as "Babylon". According to Babel, this parser was heavily inspired by the popular JavaScript parsers, acorn and acorn-jsx. Then, Babel performs operations or transformations on the AST using plugins and presets (predefined groups of plugins). In each plugin, it uses @babel/traverse to traverse through the AST. Then, it uses @babel/generator to convert the transformed AST back into code, which is simply just a string. This string can be written to a file and named something like "output.js". Most of the orchestration between parsing, transforming, and generating code is done through @babel/core, but you're able to use each Babel package individually if needed. Additionally, the @babel/cli package provides a built-in CLI to transpile files from the command line.
I like to think of the whole Babel transpilation process as a simple composition function:
const output = generate(transform(parse(input)))
Why Do We Need a JavaScript Transpiler?
1. Different desired syntax and/or features
JavaScript is a very powerful and flexible language, but it wasn't always that great. The JavaScript community has made different flavors of JavaScript over the years such as CoffeeScript or TypeScript to give developers an optional way of writing their code. CoffeeScript was popular among Ruby developers for its Ruby-like syntax. Recently, TypeScript has become very popular among developers for adding amazing type support to JavaScript's dynamically typed language to help catch a lot of errors during the transpilation stage.
The Babel community has created a lot of plugins for transpiling or manipulating JavaScript code. Some plugins let you use features that may never exist in the JavaScript standard or they perform operations on your code such as minification. A popular Babel preset (group of plugins) known as @babel/preset-react contains a list of plugins for transforming JSX code down to JavaScript. The React Babel plugins takes it a step further by compiling JSX down to React Classic Runtime code.
Input code:
const profile = (
<div>
<img src="avatar.png" className="profile" />
<h3>{[user.firstName, user.lastName].join(' ')}</h3>
</div>
)
Output code:
const profile = React.createElement(
'div',
null,
React.createElement('img', { src: 'avatar.png', className: 'profile' }),
React.createElement('h3', null, [user.firstName, user.lastName].join(' '))
)
If you're familiar with Vue, then you know that Vue also supports JSX. Vue needs to compile the JSX down to render functions. This is achieved by using the Vue Babel plugin, babel-plugin-transform-vue-jsx. Babel plugins sure are amazing!
2. To support older browsers
The ECMAScript 2015 (ES6) standard really helped JavaScript become more versatile, popular, and cleaner. However, there are older browsers such as Internet Explorer 11 (IE 11) that are still supported by many companies because they see in their analytics that a good percentage of users use them. It's up to company management to decide what percentage, say 4%, is good enough to keep supporting Internet Explorer 11. To support IE 11, JavaScript must be transpiled down to ECMAScript 5 (ES5), which came out in 2009! Luckily, the JavaScript community has created a lot of polyfills around features that did not exist in older browsers. JavaScript polyfills are essentially pieces of code that replace modern JavaScript code with older methods or syntax, so that it can be supported in older browsers. Polyfills may sometimes be known as shims, which are simply libraries of code that bring a new API to an older environment. A polyfill is a shim for a browser API. An example of an ES6 to ES5 polyfill would be replacing all "let" and "const" keywords with "var". In reality, though, a bit of code is necessary to simulate "let" and "const" correctly.
Babel uses a package called core-js to polyfill modern JavaScript to older versions, but there are also polyfills for converting proposed or future JavaScript features (sometimes called ESNext) into existing JavaScript. Core-js is extremely popular. According to npm, it has almost 33 MILLION downloads PER WEEK!!! 😮
Babel not only lets you target IE 11, but also lets you specify which browsers or environments (such as Node versions) to target using browserslist. Browserslist lets you provide queries to search for what features are available across various browsers such as Chrome, Firefox, IE, Safari, Opera, and more. For example, if I use the following query, then it will use global statistics to determine which browsers are still supported and have a greater than 0.5% usage across the world.
'> 0.5%, not dead'
Babel also uses regenerator-runtime for polyfilling JavaScript code that uses generators and async/await, so you can transpile code that uses these features down to ES5, supported by IE 11.
Prior to Babel 7.4, the @babel/polyfill package was used to encapsulate both core-js and regenerator-runtime, but now it's recommended to use each of these packages on their own. The @babel/polyfill package has been deprecated.
3. To use new features sooner
Ecma International's TC39 is a group of JavaScript developers, implementers, academics, and other smart people who help maintain and evolve the JavaScript language. This is a very important job as millions of people across the world use JavaScript. Thus, every change to the language must be thought out carefully. The ECMAScript committee goes through a series of proposals and gets feedback from the JavaScript community. For the features that make it to Stage 4 (finished) proposals and eventually get introduced into the JavaScript language, the committee decides what features are ready and should be released into the next iteration of the ECMAScript standard, which has been happening every year since ECMAScript 2015. These new features are additions to the language such as Promises or Optional Chaining. Once JavaScript features are released as part of a standard, browser vendors such as Google Chrome, Apple Safari, and Mozilla Firefox, must have developers introduce these new features to their own browsers. They also should adhere to the specifications created by the ECMAScript committee, but occasionally some features differentiate between the vendors. Some features are ignored entirely. 😢
Babel has a set of plugins and tools for transforming brand new features like the proposed Promise.any method into older JavaScript, so you can use it today across all browsers. As of the time of this writing, some evergreen browsers such as Chrome have support for Promise.any
(starting in Chrome 85). By evergreen browsers, I mean browsers like Chrome, Firefox, Safari, and Edge that will continue getting updates unlike Internet Explorer. For using new features in modern browsers, it's still important to use polyfills Babel provides just as we would if we were trying to support IE. You can use polyfills supported by core-js to use new JavaScript features and transpile them down to features supported in the browsers you want to target during Babel's transpilation process.
Babel is More than a Transpiler!
You may have realized by now that Babel is more than just a transpiler. With all the plugins Babel has, it has become a toolchain of utilities for parsing JavaScript into an AST, analyzing or transforming the AST, and generating code. If you look at all the Babel plugins, you'll quickly see they help Babel really shine. There are two main types of plugins: Transform Plugins and Syntax Plugins. By default, using a transform plugin will enable the corresponding syntax plugin. For example, the @babel/plugin-transform-react-jsx transform plugin is designed to inherit and enable the @babel/plugin-syntax-jsx syntax plugin. Some syntax plugins are boring and don't really do much. If you look at the source code for @babel/plugin-syntax-jsx, you'll see that it's only enabling the jsx functionality of the Babel parser.
When is it useful to use the syntax plugin without the transform plugin? Sometimes, you don't need a transformer plugin and only need to enable the parser to understand a certain syntax such as JSX. You can use the Babel parser for linting or you can manipulate the AST in your own way. If you're using a formatter such as Prettier or a linter such as ESLint to analyze code containing JSX, then it'd be helpful to use Babel's parser. In fact, Prettier and ESLint both let you choose your own parser. There's even a package called babel-eslint for using ESLint together with Babel. This helps the linter understand new JavaScript features such as optional chaining. Sorry to babble on about babel's parser! Hopefully, you see how powerful it is!
Next Steps
This series continues in Part 2! We'll actually use Babel and setup a small React project without using Create React App. I encourage everyone to read the Babel documentation and introduce yourselves to all the official Babel packages. I hope you feel stronger as a developer now that you know more about Babel and transpilers! Happy coding! :)