Frontend Development 16 min read

Comprehensive Guide to Babel: History, Installation, Core Principles, Plugins, Presets, and Custom Plugin Development

This article provides a detailed overview of Babel, covering its definition, history, installation, configuration, core compilation phases, plugin and preset mechanisms, polyfill handling, and a step‑by‑step example of creating a custom try‑catch plugin for JavaScript code transformation.

ByteFE
ByteFE
ByteFE
Comprehensive Guide to Babel: History, Installation, Core Principles, Plugins, Presets, and Custom Plugin Development

Preface

The name "Babel" alludes to the biblical Tower of Babel, symbolizing the translation of one language into many. The article begins with a brief reference to the story to illustrate the concept of code translation.

What is Babel

Babel is a JavaScript compiler that transforms modern ECMAScript (ES2015+) code into syntax compatible with older browsers or other environments. Its main capabilities include syntax conversion, providing polyfills, source code transformation, and more.

History of Babel

In 2014, high‑school student Sebastian McKenzie released the first version of Babel under the name 6to5 , which primarily converted ES6 to ES5. After ES6 was officially released as ES2015, the project was renamed to Babel on February 15, 2015, reflecting its broader ambition to become a universal translation tool.

Using Babel

To start using Babel, initialize a project and install the core packages:

npm install --save-dev @babel/core @babel/cli @babel/preset-env

The typical project structure includes a package.json script, a babel.config.js file, and a source directory (e.g., src/index.js ). Running npm run babel shows the transformation results, such as converting const to var and arrow functions to regular functions.

Babel Principles

Parse Phase

The parser performs lexical analysis (tokenizing the source code) and syntactic analysis (building an Abstract Syntax Tree, AST). Tokens are objects like {"type":"Keyword","value":"const"} . The parser used by Babel is @babel/parser , which follows the ESTree specification.

// Example source
const a = 1;
// Tokens
[
  {"type":"Keyword","value":"const"},
  {"type":"Identifier","value":"a"},
  {"type":"Punctuator","value":"="},
  {"type":"Numeric","value":"1"},
  {"type":"Punctuator","value":";"}
]

Transform Phase

Babel traverses the AST using a depth‑first algorithm. For each node, a Visitor object can define handlers that modify, add, or remove nodes. A simple visitor example:

const visitor = {
  FunctionDeclaration(path, state) {
    console.log('I am a function declaration');
  }
};

Visitors can define enter and exit hooks to run code before and after traversing a node.

const visitor = {
  FunctionDeclaration: {
    enter(path, state) {
      console.log('enter');
    },
    exit(path, state) {
      console.log('exit');
    }
  }
};

Generate Phase

After transformation, Babel generates code from the new AST. The generator prints each node type appropriately, re‑adding necessary punctuation. For example, a WhileStatement node is printed as:

export function WhileStatement(this: Printer, node: t.WhileStatement) {
  this.word("while");
  this.space();
  this.token("(");
  this.print(node.test, node);
  this.token(")");
  this.printBlock(node);
}

The implementation lives in @babel/generator 's src/generators directory.

Plugins

Plugins are the building blocks that tell Babel how to transform specific syntax. They are referenced in the configuration as @babel/plugin‑xxx . Common plugins include:

@babel/plugin-transform-react-jsx – converts JSX to React.createElement calls.

@babel/plugin-transform-arrow-functions – turns arrow functions into regular functions.

@babel/plugin-transform-destructuring – rewrites destructuring assignments.

Plugins can be written in two forms: a function that returns a visitor object, or an object that directly contains the visitor.

// Function form
export default function(babel, options, dirname) {
  return {
    inherits: parentPlugin,
    manipulateOptions(options, parserOptions) { options.xxx = ''; },
    pre(file) { this.cache = new Map(); },
    visitor: {
      FunctionDeclaration(path, state) {
        this.cache.set(path.node.value, 1);
      }
    },
    post(file) { console.log(this.cache); }
  };
}
// Object form
export default {
  pre(state) { this.cache = new Map(); },
  visitor: {
    FunctionDeclaration(path, state) {
      this.cache.set(path.node.value, 1);
    }
  },
  post(state) { console.log(this.cache); }
};

Presets

Presets bundle multiple plugins into a single package for convenience. Instead of adding each plugin individually, a preset can be referenced in babel.config.js :

{
  "presets": [
    "presetA",
    ["presetA"],
    ["presetA", { "target": { "chrome": "58" } }]
  ]
}

Execution order: plugins run before presets; within each list, execution proceeds from last to first.

Polyfill

Babel distinguishes between syntax transformations (e.g., const , arrow functions) and built‑in features (e.g., String.prototype.startsWith ). Syntax is transformed automatically, while built‑ins require a polyfill. Since @babel/polyfill is deprecated, core‑js is used instead.

npm install --save core-js
// babel.config.js
const presets = [
  [
    '@babel/env',
    {
      debug: true,
      useBuiltIns: 'usage', // usage | entry | false
      corejs: 3
    }
  ]
];

Writing a Custom Babel Plugin

The article walks through creating a simple plugin babel-plugin-try-catch that automatically wraps the body of async functions in a try…catch block.

const template = require('@babel/template');
function babelPlugintryCatch({ types: t }) {
  return {
    visitor: {
      FunctionDeclaration: {
        enter(path) {
          const { params, generator, async, id, body } = path.node;
          if (async) {
            const catchHandler = template.statement('console.log(error)')();
            const tryStatement = t.tryStatement(body, t.catchClause(t.identifier('error'), t.BlockStatement([catchHandler])));
            path.replaceWith(t.functionDeclaration(id, params, t.BlockStatement([tryStatement]), generator, async));
            path.skip();
          }
        }
      }
    }
  };
}
module.exports = babelPlugintryCatch;

After adding the plugin to the Babel configuration and running npm run babel , the async function is transformed to include the generated try…catch wrapper.

Summary

Babel is a JavaScript compiler that parses source code into an AST, applies transformations via plugins (or presets that group plugins), and generates code compatible with target environments. Understanding its three phases—parse, transform, generate—enables developers to write custom plugins, manage polyfills, and streamline modern JavaScript development.

JavaScriptASTCompilerBabelpolyfillpluginsPresets
ByteFE
Written by

ByteFE

Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.