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.