Transformation Operations" class="reference-link">Transformation Operations
If you need to access the path
of that property instead, use the get
method of a path, passing in the string to the property.
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
You can’t current use get
on a Container (the body
array of a BlockStatement
), but you chain the dot syntax instead.
export default function f() {
return bar;
}
For the example above, if you wanted to get the path corresponding to the return
, you could chain the various properties, using a number as the index when traversing the array.
ExportDefaultDeclaration(path) {
path.get("declaration.body.body.0");
}
Check if a node is a certain type" class="reference-link">Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
You can also do a shallow check for properties on that node:
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
This is functionally equivalent to:
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
Check if a path is a certain type" class="reference-link">Check if a path is a certain type
A path has the same methods for checking the type of a node:
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
is equivalent to doing:
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
Check if an identifier is referenced" class="reference-link">Check if an identifier is referenced
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
Alternatively:
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
Find a specific parent path" class="reference-link">Find a specific parent path
Call the provided callback
with the NodePath
s of all the parents.
When the callback
returns a truthy value, we return that NodePath
.
If the current path should be included as well:
path.find((path) => path.isObjectExpression());
Find the closest parent function or program:
path.getFunctionParent();
Walk up the tree until we hit a parent node path in a list
Get Sibling Paths" class="reference-link">Get Sibling Paths
If a path is in a list like in the body of a Function
/Program
, it will have “siblings”.
- Check if a path is part of a list with
path.inList
- You can get the surrounding siblings with
path.getSibling(index)
, - The current path’s index in the container with
path.key
, - The path’s container (an array of all sibling nodes) with
path.container
- Get the name of the key of the list container with
path.listKey
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
path.getPrevSibling() // path(undefined) *
path.getNextSibling() // pathB
path.getAllPrevSiblings() // []
path.getAllNextSiblings() // [pathB, pathC]
}
}
};
}
path(undefined)
is aNodePath
where thepath.node === undefined
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
path.skip()
skips traversing the children of the current path.
path.stop()
stops traversal entirely.
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
Replacing a node" class="reference-link">Replacing a node
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
function square(n) {
- return n * n;
+ return n ** 2;
}
Replacing a node with multiple nodes" class="reference-link">Replacing a node with multiple nodes
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
Replacing a node with a source string" class="reference-link">Replacing a node with a source string
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
Inserting a sibling node" class="reference-link">Inserting a sibling node
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
+ "Because I'm easy come, easy go.";
return n * n;
}
+ "A little high, little low.";
Inserting into a container" class="reference-link">Inserting into a container
If you want to insert into an AST node that is an array like body
.
Similar to insertBefore
/insertAfter
, except that you have to specify the listKey
, which is usually body
.
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
FunctionDeclaration(path) {
path.remove();
}
- function square(n) {
- return n * n;
- }
Replacing a parent" class="reference-link">Replacing a parent
Just call replaceWith
with the parentPath: path.parentPath
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
Removing a parent" class="reference-link">Removing a parent
BinaryExpression(path) {
path.parentPath.remove();
}
function square(n) {
- return n * n;
}
Checking if a local variable is bound" class="reference-link">Checking if a local variable is bound
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its own binding:
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
Generating a UID" class="reference-link">Generating a UID
This will generate an identifier that doesn’t collide with any locally defined variables.
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
Pushing a variable declaration to a parent scope" class="reference-link">Pushing a variable declaration to a parent scope
Sometimes you may want to push a VariableDeclaration
so you can assign to it.
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
Alternatively, you can rename a binding to a generated unique identifier:
- function square(n) {
- return n * n;
+ function square(_n) {
}