【问题标题】:How do I traverse the scope of a Path in a babel plugin如何在 babel 插件中遍历 Path 的范围
【发布时间】:2017-11-02 17:26:07
【问题描述】:

我正在尝试编写一个简单的 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!');
                        }
                    }
                });
            }
        }
    }
});

我的方法是否有缺陷,或者从代码的角度来看我需要做些什么不同的事情?

【问题讨论】:

    标签: javascript babeljs babel-plugin


    【解决方案1】:

    我似乎在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))
                }
            }
        }
    });
    

    【讨论】:

      【解决方案2】:

      我知道这个问题已经很老了,但这个答案可能对通过 Google 来到这里的人有用。 您可以使用node.scope.traverse 在另一个遍历内部使用遍历,例如,如果您只想在try 的主体内部更改每个CallExpression

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

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          猜你喜欢
          • 2021-04-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-30
          • 1970-01-01
          • 2020-05-14
          相关资源
          最近更新 更多