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.

    1. BinaryExpression(path) {
    2. path.get('left');
    3. }
    4. Program(path) {
    5. path.get('body.0');
    6. }

    You can’t current use get on a Container (the body array of a BlockStatement), but you chain the dot syntax instead.

    1. export default function f() {
    2. return bar;
    3. }

    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.

    1. ExportDefaultDeclaration(path) {
    2. path.get("declaration.body.body.0");
    3. }

    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:

    1. BinaryExpression(path) {
    2. if (t.isIdentifier(path.node.left)) {
    3. // ...
    4. }
    5. }

    You can also do a shallow check for properties on that node:

    1. BinaryExpression(path) {
    2. if (t.isIdentifier(path.node.left, { name: "n" })) {
    3. // ...
    4. }
    5. }

    This is functionally equivalent to:

    1. BinaryExpression(path) {
    2. if (
    3. path.node.left != null &&
    4. path.node.left.type === "Identifier" &&
    5. path.node.left.name === "n"
    6. ) {
    7. // ...
    8. }
    9. }

    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:

    1. BinaryExpression(path) {
    2. if (path.get('left').isIdentifier({ name: "n" })) {
    3. // ...
    4. }
    5. }

    is equivalent to doing:

    1. BinaryExpression(path) {
    2. if (t.isIdentifier(path.node.left, { name: "n" })) {
    3. // ...
    4. }
    5. }

    Check if an identifier is referenced" class="reference-link">Check if an identifier is referenced

    1. Identifier(path) {
    2. if (path.isReferencedIdentifier()) {
    3. // ...
    4. }
    5. }

    Alternatively:

    1. Identifier(path) {
    2. if (t.isReferenced(path.node, path.parent)) {
    3. // ...
    4. }
    5. }

    Find a specific parent path" class="reference-link">Find a specific parent path

    Call the provided callback with the NodePaths of all the parents. When the callback returns a truthy value, we return that NodePath.

      If the current path should be included as well:

      1. path.find((path) => path.isObjectExpression());

      Find the closest parent function or program:

      1. 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
      1. var a = 1; // pathA, path.key = 0
      2. var b = 2; // pathB, path.key = 1
      1. export default function({ types: t }) {
      2. return {
      3. visitor: {
      4. VariableDeclaration(path) {
      5. // if the current path is pathA
      6. path.inList // true
      7. path.listKey // "body"
      8. path.key // 0
      9. path.getSibling(0) // pathA
      10. path.getSibling(path.key + 1) // pathB
      11. path.container // [pathA, pathB, pathC]
      12. path.getPrevSibling() // path(undefined) *
      13. path.getNextSibling() // pathB
      14. path.getAllPrevSiblings() // []
      15. path.getAllNextSiblings() // [pathB, pathC]
      16. }
      17. }
      18. };
      19. }
      • path(undefined) is a NodePath where the path.node === undefined

      If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.

      1. BinaryExpression(path) {
      2. if (path.node.operator !== '**') return;
      3. }

      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.

      1. outerPath.traverse({
      2. Function(innerPath) {
      3. innerPath.skip(); // if checking the children is irrelevant
      4. },
      5. ReferencedIdentifier(innerPath, state) {
      6. state.iife = true;
      7. innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
      8. }
      9. });

      Replacing a node" class="reference-link">Replacing a node

      1. BinaryExpression(path) {
      2. path.replaceWith(
      3. t.binaryExpression("**", path.node.left, t.numberLiteral(2))
      4. );
      5. }
      1. function square(n) {
      2. - return n * n;
      3. + return n ** 2;
      4. }

      Replacing a node with multiple nodes" class="reference-link">Replacing a node with multiple nodes

      1. ReturnStatement(path) {
      2. path.replaceWithMultiple([
      3. t.expressionStatement(t.stringLiteral("Is this the real life?")),
      4. t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
      5. t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
      6. ]);
      7. }
      1. function square(n) {
      2. - return n * n;
      3. + "Is this the real life?";
      4. + "Is this just fantasy?";
      5. + "(Enjoy singing the rest of the song in your head)";
      6. }

      Replacing a node with a source string" class="reference-link">Replacing a node with a source string

      1. FunctionDeclaration(path) {
      2. path.replaceWithSourceString(`function add(a, b) {
      3. return a + b;
      4. }`);
      5. }
      1. - function square(n) {
      2. - return n * n;
      3. + function add(a, b) {
      4. + return a + b;

      Inserting a sibling node" class="reference-link">Inserting a sibling node

      1. FunctionDeclaration(path) {
      2. path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
      3. path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
      4. }
      1. + "Because I'm easy come, easy go.";
      2. return n * n;
      3. }
      4. + "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.

      1. ClassMethod(path) {
      2. path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
      3. path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
      4. }
      1. FunctionDeclaration(path) {
      2. path.remove();
      3. }
      1. - function square(n) {
      2. - return n * n;
      3. - }

      Replacing a parent" class="reference-link">Replacing a parent

      Just call replaceWith with the parentPath: path.parentPath

      1. BinaryExpression(path) {
      2. path.parentPath.replaceWith(
      3. t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
      4. );
      5. }
      1. function square(n) {
      2. - return n * n;
      3. + "Anyway the wind blows, doesn't really matter to me, to me.";
      4. }

      Removing a parent" class="reference-link">Removing a parent

      1. BinaryExpression(path) {
      2. path.parentPath.remove();
      3. }
      1. function square(n) {
      2. - return n * n;
      3. }

      Checking if a local variable is bound" class="reference-link">Checking if a local variable is bound

      1. FunctionDeclaration(path) {
      2. if (path.scope.hasBinding("n")) {
      3. // ...
      4. }
      5. }

      This will walk up the scope tree and check for that particular binding.

      You can also check if a scope has its own binding:

      1. FunctionDeclaration(path) {
      2. if (path.scope.hasOwnBinding("n")) {
      3. // ...
      4. }
      5. }

      Generating a UID" class="reference-link">Generating a UID

      This will generate an identifier that doesn’t collide with any locally defined variables.

      1. FunctionDeclaration(path) {
      2. path.scope.generateUidIdentifier("uid");
      3. // Node { type: "Identifier", name: "_uid" }
      4. path.scope.generateUidIdentifier("uid");
      5. // Node { type: "Identifier", name: "_uid2" }
      6. }

      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.

      1. FunctionDeclaration(path) {
      2. const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
      3. path.remove();
      4. path.scope.parent.push({ id, init: path.node });
      5. }
      1. - function square(n) {
      2. + var _square = function square(n) {
      3. return n * n;
      4. - }
      5. + };
      1. FunctionDeclaration(path) {
      2. path.scope.rename("n", "x");
      3. }
      1. - function square(n) {
      2. - return n * n;
      3. + function square(x) {
      4. + return x * x;
      5. }

      Alternatively, you can rename a binding to a generated unique identifier:

      1. - function square(n) {
      2. - return n * n;
      3. + function square(_n) {
      4. }