Babel is a JavaScript compiler.
You can play it interactively at the playground.
Play with the AST nodes at AST Explorer or Babel AST Explorer.
Have fun playing with Babel.
npm install
# hand-made plugins
make example1-hand
# hand-made plugins
make example2-hand
Sharable set of Babel plugins and/or config options
.
Check out the @babel/preset-react, this package exports an object with plugins
.
// my-quick-preset
import { declare } from '@babel/helper-plugin-utils';
export default declare((api, opts) => {
// return a babel config object
return {
plugins: [
// ... plugin configure object
],
};
});
In usage,
{
"presets": [
"@babel/preset-env",
["my-quick-preset", { "quick": true, "time": 12 }]
]
}
The execution order is my-quick-preset
, then @babel/preset-env
.
{ "quick": true, "time": 12 }
will be the option value in the function as the second parameter opts
.
# install dependencies
npm install
# check out the output
make example1
Babel's code transformations are enabled by applying plugins (or presets) to the configuration file.
- Plugins run before presets!
- Plugins ordering is first to last.
# install dependencies
npm install
# check out the output
make example2
Babel is a compiler, or a transpiler.
Babel need an AST tree to work. Each AST nodes has the basic interface.
interface Node {
type: string;
}
Babale has three primary stages: parse, transform, generate.
Traverse the AST tree first, then we can transform the AST. When traversing, we are actually visiting them.
import * as t from '@babel/types';
const innerVisitor = {
Identifier(path) {
console.log(this.someState); // should be 'anything'
// ...
},
BinaryExpression(path) {
// ...
},
};
const BaseVisitor = {
enter(path) {
const { node } = path;
if (node !== null && node.type === 'Identifier' && node.name === 'n') {
node.name = 'nn';
}
// equivalent predicate
if (t.isIdentifier(node, { name: 'n' })) {
node.name = 'nn';
}
},
BinaryExpression(path) {
if (t.isIdentifier(path.get('left'))) {
// ...
}
if (path.get('left').isIdentifier({ name: 'n' })) {
// ...
}
path.replaceWith(
/* a AST node to replace */
t.binaryExpression(/* */)
);
path.replaceWithSourceString(`function a() {/* */}`);
},
ClassMethod(path) {
path.get('body').unshiftContainer('body' /* some AST node */);
path.get('body').pushContainer('body' /* some AST node */);
/* nested travering */
path.traverse(innerVisitor, { someState: 'anything' });
},
FunctionDeclaration: {
enter(path) {
/* */
path.insertBefore(/* some AST node */);
path.insertAfter(/* some AST node */);
path.scope.hasBinding('n');
path.scope.hasOwnBinding('n');
},
},
Identifier(path) {
path.remove();
path.parentPath.replaceWith(/* some AST node */);
path.parentPath.remove();
/* */
},
'ExportNameDecalration|Flow'(path) {
throw path.buildCodeFrameError('Error');
/* */
},
'BlockStatement|ReturnStatement': {
enter(path) {
/* */
},
},
};
In a sense, paths are a reactive representation of a node's position in the tree and all sorts of information about the node.
We can test a plugin with the help of jest
and babel-plugin-tester
.
Here is some example code at test/plugin.test.js
.
@babel/helper-plugin-test-runner
is kind of unavailable outside the@babel/babel
project.
Templates
@babel/template
allows us to write strings of code with placeholders that we can use instead of manually building up a massive AST, also known as quasiquotes.