Как мне пройти путь Path в плагине Babel

Я пытаюсь написать простой плагин Babel, но мне трудно пройти соответствующий узел с вложенным посетителем. Я хотел бы найти все require вызывает в модуле, который требует определенного модуля и затем применяет некоторое преобразование в той же области.

Чтобы проиллюстрировать это на надуманном примере, я хотел бы преобразовать исходный код, например:

const f = require('foo-bar');
const result = f() * 2;

в нечто вроде:

const result = 99 * 2; // as i "know" that calling f will always return 99

Я пытался сделать следующее:

module.exports = ({ types: t }) => ({
    visitor: {
        CallExpression(path) {
            if (path.node.callee.name === 'require'
                && path.node.arguments.length === 1
                && t.isStringLiteral(p.node.arguments[0])
                && path.node.arguments[0].value === 'foo-bar'
            ) {
                const localIdentifier = path.parent.id.name;
                // if i print here it will show me that it successfully
                // found all require calls
                p.scope.traverse({
                    Identifier(subp) {
                        // this will never run at all
                        if (subp.name === localIdentifier) {
                            console.log('MATCH!');
                        }
                    }
                });
            }
        }
    }
});

Мой подход ошибочен или мне нужно что-то сделать иначе, чем с точки зрения кода?

3 ответа

Я знаю, что это очень старый вопрос, но этот ответ может быть полезен тем, кто прибыл сюда через Google. Вы можете использовать траверс внутри другого траверса, используяnode.scope.traverse, например, если вы хотите изменить каждый CallExpression только если внутри в теле try:

module.exports = ({ types: t }) => ({
  visitor: {
    TryStatement(path) {
      const { scope, node } = path

      const traversalHandler = {
        CallExpression(path) {
          path.replaceWith(t.Identifier('foo'))
        }
      }

      scope.traverse(node, traversalHandler, this)
    }
  }
})

Я не могу найти много документации по path.scope.traverse,

Прошло почти 2 года, но я надеюсь, что это решит вашу проблему.

module.exports = ({ types: t }) => ({
    visitor: {
        CallExpression(path) {
            if (path.node.callee.name === 'require'
                && path.node.arguments.length === 1
                && t.isStringLiteral(path.node.arguments[0])
                && path.node.arguments[0].value === 'foo-bar'
            ) {
                this.localIdentifier = path.parent.id.name;
            }
            if(path.node.callee.name === this.localIdentifier){
                path.replaceWith(t.NumericLiteral(99))
            }
        }
    }
});

Пройдите через parent scope, найдите узел:

      function inScope(scope, nodeName) {
  if (!scope || !scope.bindings) return false

  let ret = false
  let cur = scope
  while (cur) {
    ret = cur.bindings[nodeName]
    if (cur === scope.parent || ret) {
      break
    }
    cur = scope.parent
  }

  return ret
}


// your visitor.js
  return {
    visitor: {
      CallExpression(path) {
        inScope(path.scope, path.node.name)
      }
  }

Другие вопросы по тегам