Babel a JavaScript compiler

Babel is one of those tools that made JavaScript development in the 2010s approachable and contributed so much to the state of the language today. It is a plugin-based transpiler that compiles your code that has the latest and greatest (and in some cases more) JS features to code that all browsers and NodeJS versions can understand.

The library is put together from a core module that does the heavy lifting, a CLI which allows build tools to be efficient, a bunch of plugins that can be organised into presets, and a configuration file that can be JSON or simply a JS module that exports a function that returns the config object.

As a front-end web developer, I came across babel many times and I always treated it like a black box that had to be configured in webpack for your code to work. This was the first time when I looked into the tool in isolation, and I have to tell you that it's not as frightening as it seems. Check out the babel section in my build tools sandbox repo for all the technical details.

Core module

First, let's see how does the core module work if it's required in a node script and is used without the CLI.

const fs = require('fs')
const babel = require('@babel/core')

const sourceCode = fs.readFileSync('source-code.js')
const generated = babel.transformSync(sourceCode)
fs.writeFileSync('generated-code.js', generated.code)

The transformSync function takes in the source code as a string alongside an optional options? argument and looks for a configuration file named babel.config.json or .babelrc.json to get the compilation rules that are set by the developer.

An example of a config file:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1"
        }
      }
    ]
  ]
}

Here we used probably the most popular preset that there is in the ecosystem and specified some browser targets as well. Browsers are very capable these days, but if any ES6+ feature is not available and our codebase uses it that'll be transformed into an equivalent but a lower version of ECMAScript.

Once babel transform function did its job, we'll have access to the new code as string or the absctract syntax tree itself. In our example, we just want to write the code as a string into another file.

CLI

Using the core module on its own is probably not what you want in a real-world example and that's why the @babel/cli exists. It has the ability to take in --presets and --plugins as arguments in case the babel.config.json is not defined.

Example for compiling the src directory with preset-env:

$ babel src --out-dir lib --presets=@babel/preset-env

Another example where your code has arrow functions and classes that you want to transpile:

$ babel src --out-dir lib --plugins=@babel/plugin-transform-arrow-functions,@babel/plugin-transform-classes

You can combine any number of presets with any number of plugins, there is a specific execution order which you can read more about at Preset Ordering and Plugin Ordering.

Plugins and Presets

Plugins are visitors that loop through your code and change it following a specific rule. For example the @babel/plugin-transform-arrow-functions plugin will transpile all your fat arrow functions into the traditional syntax using the function keyword.

Source code:

const result = sampleData.map((num) => num * 2)

Compiled code

const result = sampleData.map(function (num) {
  return num * 2
})

Presets are essentially just a list of plugins that solve a particular problem. A few common presets are @babel/preset-env for ES6+ features, @babel/preset-react, @babel/preset-typescript, babel-jest, next/babel for NextJS. The maintainers of the library encourage teams, and companies to put together their own preset that best serves their needs, you can read more about the know-how here.

So, I took the liberty to create a preset for myself just for the sake of experimenting and learning. The process was relatively simple and well documented so I had no trouble making my preset, then using it. Called the preset babel-preset-szabi-space (check out the npm package) which at this point has 6 plugins configured. Find out which plugins by looking at the source code.

Babel & React

Let's see how does Babel treat React.

For simplicity's sake, and because Babel is not the right tool for module bundling, I started by importing React and ReactDOM from CDNs. I wrote a Hello World application in React, JSX, and then compiled it using the @babel/plugin-transform-react-jsx plugin via the @babel/cli and finally imported the script onto the HTML page. The result is probably the simplest React application that exists.

<!DOCTYPE html>
<html>
  <head>
    <title>Simple React</title>
    <script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
  </head>

  <body>
    <div id="root"></div>
  </body>

  <script>
    const root = ReactDOM.createRoot(document.getElementById('root'))
    root.render(React.createElement('h1', null, 'Hello, world!'))
  </script>
</html>

The second application was a Todo List from my sandbox-react repo which was a bit more complicated than just a Hello World app so it made sense to bring in some presets. The code contains some ES6+ features, React and JSX therefore the best combo would be a @babel/preset-env with @babel/preset-react.

Take a look at the source code and the compiled code both in the sandbox-build-tools GitHub repository.

The compiled final result turned out very messy and hard to understand for us humans (for the browsers and different NodeJS versions are easier), but luckily we can write code using the latest language features thanks to tools like Babel and the community around it that put together all these wonderful plugins forming a very productive ecosystem.

Conclusion

So, is Babel good? I think the quick answer is yes, it has been and still continues to be one of the main engines of the modern web. But let's not forget why it was created, it was created with the premise of using next-generation JavaScript, today. Web browsers used to be slow in adopting the latest features of JavaScript as the language was evolving. Nowadays though, the language has matured, and the browsers have also gotten better so we might not need all this transpilation anymore.

On the other hand, the tool was written in JavaScript, and looking at other options, for example esbuild which was implemented in GoLang or swc in Rust doing the job much faster than babel. So, I think we are about the enter another phase of web development build tools and while babel will stay as a well-respected library, other tools might take its place that are better equipped in delivering the future of the web.