Babel Templates
@babel/types
is a package that contains a lot of utility functions for AST nodes.
@babel/template
is a tiny but incredibly useful module.
It allows you to write strings of code with placeholders
that you can use instead of manually building up a massive AST.
Templates and placeholders
We can see in the example
src/template/hello-babel-template.mjs
that the template variables IMPORT_NAME
and SOURCE
in the template object buildRequire
are filled
with the ASTs built using babel-types
and stored in the variables identifier
and stringLiteral
.
The resulting AST ast
is used to generate the code using @babel/generator
.
import template from "babel-template";
import _generate from "@babel/generator";
const generate = _generate.default;
import * as _t from "babel-types";
const t = _t.default;
import compast from "compact-js-ast"
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const identifier = t.identifier("myModule"); // See https://babeljs.io/docs/babel-types#identifier
const stringLiteral = t.stringLiteral("my-module");
//console.log(compast(identifier, { parse: false, }));
//console.log(compast(stringLiteral, { parse: false, }));
const ast = buildRequire({
IMPORT_NAME: identifier,
SOURCE: stringLiteral
});
console.log(generate(ast).code); // var myModule = require("my-module");
You can use two different kinds of placeholders:
- syntactic placeholders (e.g.
%%name%%
) or - identifier placeholders (e.g.
NAME
).
@babel/template
supports both those approaches by default, but they can’t be mixed. If you need to be explicit about what syntax you are using, you can use the syntacticPlaceholders
option.
The execution produces:
➜ babel-learning git:(main) node src/template/hello-babel-template.mjs
var myModule = require("my-module");
See also
- The example at /doc/manipulation.md#replacing-a-node-with-multiple-nodes
- The example at /src/manipulating-ast-with-js/babel-template-example.js
Replacing a node with multiple nodes
The following plugin replaces the return
statement with two statements: a let
statement and a return
statement.
module.exports = function (babel) {
return {
name: "ast-transform2", // not required
visitor: {
ReturnStatement(path) {
if (path.node.argument.type == "BinaryExpression" && path.node.argument.left.name == "n") {
path.replaceWithMultiple([
babel.template(`let a = N`)({N: path.node.argument.left }),
babel.template(`return 4*a`)()
])
}
}
}
};
}
When executed with input:
➜ babel-learning git:(main) cat src/manipulation/square2.js
let square = n => { return n * n; }
We get:
➜ babel-learning git:(main) npx babel src/manipulation/square2.js --plugins=./src/manipulation/replacewithmultiple-plugin.cjs
"use strict";
let square = n => {
let a = n;
return 4 * a;
};
Note: When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
See section replacewithmultiple
at file
/src/manipulating-ast-with-js/README.md