In-Depth Analysis of Babel

April 16, 2023

Babel Diagram

Why Babel is Needed

In development, we rarely interact with Babel directly, but Babel is indispensable for frontend development:

  • We want to use ES6+ syntax, TypeScript, or develop React projects, all of which rely on Babel.
  • Learning Babel helps us understand the transition from writing code to production deployment.

Babel is a toolchain primarily used to convert ECMAScript 2015+ code into backward-compatible versions for older browsers. Its functionalities include syntax transformation, source code transformation, and Polyfill.

Using Babel from the Command Line

Babel can be used independently without configuring build tools like webpack.

Install required packages:

  • @babel/core: Core Babel code, must be installed.
  • @babel/cli: Allows using Babel from the command line.
npm install @babel/cli @babel/core

Transform source code:

npx babel src --out-dir dist
  • src: source directory
  • --out-dir: output folder

Using Plugins

To transform arrow functions:

npm install @babel/plugin-transform-arrow-functions -D
npx babel src --out-dir dist --plugins=@babel/plugin-transform-arrow-functions

Babel Presets

If there are many transformations, configuring one by one is cumbersome. We can use presets:

  • Install preset:

    npm install @babel/preset-env -D
  • Execute:

    npx babel src --out-dir dist --presets=@babel/preset-env

Babel Internal Principles

How does Babel convert ES6/TypeScript/React code to ES5?

Babel can be seen as a compiler that transforms source code into a form the browser can understand. Workflow:

  • Parsing
  • Transformation
  • Code Generation

Babel Compiler Execution Principle

Compiler Diagram

Tokens array:

Tokens

Generate AST (Abstract Syntax Tree) from tokens:

AST

Each object represents a node. Traverse nodes, apply Babel plugins, generate new AST, then output code.

babel-loader

In practice, we often use Babel with build tools, e.g., webpack:

{
  test: /\.js$/,
  use: {
    loader: "babel-loader",
    options: {
      plugins: [
        "@babel/plugin-transform-arrow-functions",
        "@babel/plugin-transform-block-scoping"
      ],
      presets: ["@babel/preset-env"]
    }
  }
}

Setting Target Browsers with browserslist

Babel needs to know which browsers to target.

  • Use browserslist or target option
use: {
  loader: "babel-loader",
  options: {
    presets: [
      ["@babel/preset-env", { targets: ["chrome 88"] }]
    ],
    plugins: [
      "@babel/plugin-transform-arrow-functions",
      "@babel/plugin-transform-block-scoping"
    ]
  }
}

Stage-X Presets

  • TC39: Technical Committee 39, part of ECMA standardizing JavaScript evolution.

  • Features are added in stages:

    • Stage 0: Strawman
    • Stage 1: Proposal
    • Stage 2: Draft
    • Stage 3: Candidate
    • Stage 4: Finished

Babel Stage-X Configuration

Before Babel7 (e.g., Babel6), we used babel-preset-stage-x, now deprecated in favor of preset-env:

module.exports = {
  preset: ["stage-0"]
}

Babel Configuration Files

Two types of config files:

  • babel.config.json (or .js, .cjs, .mjs)
  • .babelrc.json (or .babelrc, .js, .cjs, .mjs)

.babelrc.json was popular early, but babel.config.json is recommended for monorepos.

module.exports = {
  presets: [["@babel/preset-env"]],
}

Understanding Polyfills

  • Syntax features like Promise, Generator, Symbol may not be supported in some browsers.
  • Use polyfill to patch.

Using Polyfills

Install:

npm install core-js regenerator-runtime --save

Exclude node_modules in loader:

{
  test: '/\.m?js$/',
  exclude: /node-modules/,
  use: 'babel-loader',
}

Configure preset-env:

  • useBuiltIns: how polyfills are used
  • corejs: version (usually 3.x)
  • proposals: include stage proposals if true
module.exports = {
  presets: [
    ["@babel/preset-env", {
      useBuiltIns: "usage",
      corejs: 3
    }],
    ["@babel/preset-react"]
  ]
}

Plugin-transform-runtime

  • Avoid global pollution when creating a library.

  • Install:

    npm install @babel/plugin-transform-runtime -D
  • Configure:

    plugins: [["@babel/plugin-transform-runtime", { corejs: 3 }]]

JSX Support

  • React uses jsx. Transform with preset:

    npm install @babel/preset-react -D

TypeScript Compilation

Using ts-loader

npm install ts-loader -D
{
  test: /\.ts$/,
  exclude: /node_modules/,
  use: "babel-loader"
}

Using babel-loader

  • Supports TypeScript via @babel/preset-typescript.
module.exports = {
  presets: [
    ["@babel/preset-env", { useBuiltIns: "usage", corejs: 3 }],
    ["@babel/preset-react"],
    ["@babel/preset-typescript"]
  ]
}

ts-loader vs babel-loader

  • ts-loader: only compiles TS -> JS, cannot add polyfills.
  • babel-loader: compiles TS -> JS, can add polyfills, does not check types.

Best Practice

Use Babel for code transformation and tsc for type checking.

Babel & TypeScript

Babel & TypeScript Workflow