fe / babel / js / 5 mins

Babel in less than 5 mins

Babel is a JavaScript compiler mainly known for React and the ability to use js syntax not yet shipped to the browser.

In this introduction we’ll try to cover some of the core ideas such as @babel/core, @babel/cli, plugins, presets and the config file.

The main idea

In the js world, we like to use new and exotic syntaxes even if the browser doesn’t provide support and never will.

Before ie11 deprecation, a classic example was using the arrow function syntax.

For a task like this you would need a tool to convert const hello = () => 'hi'; into function hello() { return 'hi'; }.

Another common example would be transforming React jsx code into js consumable by the browser. E.g., <MyComponent /> transformed into React.createElement(MyComponent, null);.

For trivial pieces of code, you may even think to write a node script by yourself, but it wouldn’t be practical and sustainable in the long run.

Here comes Babel at rescue.

Babel defines itself as a JavaScript compiler.

To be specific, Babel is a transpiler, which it means it converts a js version of the code to another js version of the code instead of binary code.

To do that, Babel will parse the code and generate a json representation called AST, then transform the AST as necessary, and finally generate the output code.

About @babel/core @babel/cli

Babel is composed of a collection of packages. The core package, as the name suggests, is @babel/core.

@babel/core provides the fundamentals blocks to parse, transform and generate code.

In theory, you could import @babel/core in a script directly and do something like this:

// output-transform.js
var babel = require("@babel/core");

let options = {
    presets: ["@babel/preset-react"]
};

let output = babel.transformFileSync('someComponent.jsx', options);
 
console.log(output.code);

And then in cli you would node output-transform.js to print the code generated.

As this is a common operation, some scaffolding has been done already so you don’t have to write all the time your own script. @babel/cli wraps @babel/core and allows to use Babel directly in cli.

Using Babel in the command line is one of the most common ways of using Babel when not used in combination with a bundler.

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

Then in package.json within the scripts section you can have something like:

"js:build" : "babel src/script.js --out-file dist/script-compiled.js"

so you can run npm run js:build and generate dist/script-compiled.js.

About plugins and presets

Plugins extend Babel functionalities, allowing new syntaxes (Syntax Plugins) or code transformations (Transform Plugins).

Presets instead are collections of plugins already made to facilitate the most common cases — e.g.: preset-env, preset-react.

Plugins run before Presets. The Plugin order is first to last. The Preset order is last to first instead.

How to use a plugin or a preset? Install the plugin or preset via npm and then add it to the Babel config file (e.g. .babelrc.json or babel.config.json).

E.g.: @babel/plugin-proposal-pipeline-operator

// install the package via npm
npm install --save-dev @babel/plugin-proposal-pipeline-operator


In babel.config.json:

{
    "plugins": [
        [ 
          "@babel/plugin-proposal-pipeline-operator",
          { "proposal": "hack", "topicToken": "^^" }
        ]
    ]
}

Config Files

Babel accepts configuration from babel.config.* or .babelrc.* or package.json.

babel.config.*

The babel.config.* file which can have .json, .js, .cjs, .mjs extensions is a Project-wide configuration.

Project-wide configuration means that you put babel.config.json in the root folder of your project and Babel will use it while transpiling each file in the project.

// Assuming

.
├── babel.config.json
├── package.json
└── src
    ├── script-1.js
    └── sub
        └── script-2.js


// when you run babel "npx babel src --out-dir lib"
// script-1.js and script-2.js will be transpiled using the 
// configs in babel.config.json

.
├── babel.config.json
├── package-lock.json
├── package.json
├── lib
│    ├── script-1.js
│    └── sub
│        └── script-2.js
└── src
    ├── script-1.js
    └── sub
        └── script-2.js

But what if you want to transpile script-2.js with a different set of plugins or presets?

Then you would use a .babelrc.* config file.

.babelrc.*

.babelrc.* file which can have .json, .js, .cjs, .mjs extensions is a File-relative configuration.

File-relative configuration means that you put .babelrc.json in a folder and babel will use it for each file in that folder.

.babelrc.json will override whatever configuration in babel.config.json.

Assuming the following:

.
├── babel.config.json
├── package-lock.json
├── package.json
└── src
    ├── script-1.js
    └── sub
        ├── .babelrc.json
        └── script-2.js

script-1.js will be transpiled using the plugins and presets specified in babel.config.json, while script-2.js will be transpiled using the plugins and presets specified in .babelrc.json

Config in package.json

The same result would have been achievable by moving the content of babel.config.json or .babelrc.json in package.json within a babel section.

// moving configs in package.json
{
    "babel": {
        "presets": [
            [
                "@babel/preset-env",
                {
                    "useBuiltIns": "entry",
                    "corejs": "3.22"
                }
            ]
        ]
    }
}

Config Format

Your babel.config.json and .babelrc.json will have the following structure.

{
    "plugins": [...],
    "presets": [...]
}

You may have only presets or only plugins or both. Both are an array of strings or tuples.

In the case of a string, it’s the name of the presets or plugins.

// E.g.: @babel/preset-react
npm install --save-dev @babel/preset-react

{
    "presets": ["@babel/preset-react"]
}

In the case of a tuple — which is an array with a string and an object — we got the plugin or preset name as a string and the plugin or preset options as an object.

// E.g.: @babel/preset-env
npm install --save-dev @babel/preset-env

// babel.config.json
{
    "presets": [
        [
            "@babel/preset-env",
            {
                "useBuiltIns": "entry",
                "corejs": "3.22"
            }
        ]
    ]
}

// Tuple structure explanation:
["@babel/preset-env", { "useBuiltIns": "entry", "corejs": "3.22"}]
        ^^^                        ^^^
[    preset name    ,            preset options                  ]
        ^^^                        ^^^
[      string       ,             object                         ]

preset name   : "@babel/preset-env"
preset option : { "useBuiltIns": "entry", "corejs": "3.22"}

Summary

In this introduction, we overviewed the basic concepts of Babel. In particular, the relation between @babel/core and @babel/cli, the steps Babel takes to transpile the code which is: parse (generate the AST), transform and generate the code and how presets and plugins enable new syntaxes.

Finally, we saw Babel config files babel.config.json and .babelrc.json and the format they may have.

With these concepts in mind, you should be able to select and install a plugin or a preset and use Babel in cli.