如何在babel插件中遍历Path的范围

时间:2017-06-01 14:00:36

标签: javascript babeljs babel-plugin

我正在尝试编写一个简单的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 个答案:

答案 0 :(得分:1)

我知道这个问题很老,但是这个答案对Google到这里来的人可能有用。 例如,如果您只想在node.scope.traverse的主体内部进行更改,则可以使用CallExpression在另一个导线的内部使用导线。

try

答案 1 :(得分:0)

我似乎找不到关于path.scope.traverse的文档。

已经快两年了,但我希望这能解决您的问题。

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))
            }
        }
    }
});

答案 2 :(得分:0)

遍历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)
      }
  }