painotpi
painotpi

Reputation: 6996

Babel unshiftcontainer not creating an element

Playing around with babel transforms and I've written this snippet to insert a VariableDeclaration after my path

export default function (babel) {
  const { types: t } = babel;

  return {
    name: "ast-transform", // not required
    visitor: {
      VariableDeclaration (path) {
        if (path.node.kind !== 'var') {
          return;
        }

        const _id = t.Identifier('qwe');
        const _init = t.numericLiteral(42);

        console.log(_id, _init);

        const _node = t.variableDeclaration('var', [
          t.variableDeclarator(
            t.identifier("uid"),
            t.numericLiteral(42)
          )
        ]);

        path
          .unshiftContainer(
            'body',
            _node
          )

      }
    }
  };
}

Get the following error,

unknown: Cannot read property 'body' of undefined

Edit:

If I do the following instead of unshiftContainer it works,

const _program = path.findParent(p => p.isProgram());
_program.node.body.unshift(_node);

Why doesn't unshiftContainer work?

Link to gist on ast-explorer

Upvotes: 4

Views: 835

Answers (1)

Maxim Mazurok
Maxim Mazurok

Reputation: 4138

In order to use unshiftContainer("body") you have to have a node with "body". For example, Program or FunctionDeclaration (1, 2, 3)

But you were trying to do that on VariableDeclaration which doesn't have body as you can see in ast-explorer.

When you call path.get("body"), it returns {..., node: undefined, ...} and then, when you call unshiftContainer("body"), it tries to get "body" of node, which is undefined: container: this.node[listKey],.

Your solution works because you're finding Program object, which has body. So, this also would work if you want to use unshiftContainer:

const _program = path.findParent(p => p.isProgram());
_program.unshiftContainer("body", _node);

Also, you can do this:

const parentWithBody = path.findParent(
  (p) => p.node !== undefined && p.node.body !== undefined
);
parentWithBody.unshiftContainer("body", _node);

It will detect not only Program, but also FunctionDeclaration:

Source:

var x = 10;
var y = 5;

function test() {
  var z = 1;
}

Output:

var uid = 42;
var uid = 42;
var x = 10;
var y = 5;

function test() {
  var uid = 42;
  var z = 1;
}

Update regarding infinite loop,

Indeed, when you unshift container, you're adding new VariableDeclaration, so it will go into the infinite loop:

  1. see var x = 10 -> add var uid = 42
  2. see var uid = 42 -> add var uid = 42
  3. and so on...

In order to break this loop, add this condition, where you check if the node.kind is var:

if (path.node.declarations[0].init.value === 42 && path.node.declarations[0].id.name === "uid") {
  return;
}

(gist)

Upvotes: 6

Related Questions