用于查找包含字符串的方法名称的正则表达式

Regex for finding the name of a method containing a string

我有一个包含大约 100 个导出方法的 Node 模块文件,它看起来像这样:

exports.methodOne = async user_id => {
    // other method contents
};
exports.methodTwo = async user_id => {
    // other method contents
    fooMethod();
};
exports.methodThree = async user_id => {
    // other method contents
    fooMethod();
};

目标:我想做的是弄清楚如何获取包含对 fooMethod 和 [=39 的调用的任何方法的名称=] 正确的方法名称:methodTwo 和 methodThree。我写了一个正则表达式 kinda close:

exports\.(\w+).*(\n.*?){1,}fooMethod

问题:不过,使用上面的示例代码,它会有效地匹配 methodOne 和 methodThree,因为它会找到 export 的第一个实例,然后是第一个实例fooMethod 并从那里继续。 Here's a regex101 example.

我想我可以使用先行或后行,但我对正则表达式的这些部分没有什么经验,所以任何指导将不胜感激!


编辑:事实证明正则表达式不适合此类任务。 @ctcherry 建议使用解析器,并将其用作跳板,我能够了解抽象语法树 (AST) 和 recast tool which lets you traverse the tree after using various tools (acorn 以及其他)将您的代码解析为树形式。

有了这些工具,我成功地构建了一个脚本来解析和遍历我的节点应用程序的文件,并且能够按预期找到包含 fooMethod 的所有方法。

假设所有方法的主体都包含在 {} 中,我会采用这样的方法来获得最终的正则表达式:

  • 首先,找到一个正则表达式来获取各个方法。这可以使用这个正则表达式来完成:
exports\.(\w+)(\s|.)*?\{(\s|.)*?\}
  • 接下来,我们感兴趣的是那些在关闭之前里面有fooMethod的方法。因此,按此顺序查找 }fooMethod.*}。因此,让我们将搜索 fooMethod 的组命名为 FOO,并将调用它的方法的名称命名为 METH。当我们迭代匹配时,如果组 FOO 出现在匹配中,我们将使用相应的 METH 组,否则我们将拒绝它。
exports\.(?<METH>\w+)(\s|.)*?\{(\s|.)*?(\}|(?<FOO>fooMethod)(\s|.)*?\})

解释:

  • exports\.(?<METH>\w+): 直到方法名(你已经覆盖了这个)
  • (\s|.)*?\{(\s|.)*?{之前和之后的一些代码,非贪心,以便后面的组优先
  • (\}|(?<FOO>fooMethod)(\s|.)*?\}):这有两部分:
    • \}:匹配方法结束分隔符,或
    • (?<FOO>fooMethod)(\s|.)*?\}):调用 fooMethod 后跟可选代码和方法关闭分隔符。

这里有一个 JavaScript 代码来演示这个:

let p = /exports\.(?<METH>\w+)(\s|.)*?\{(\s|.)*?(\}|(?<FOO>fooMethod)(\s|.)*?\})/g
let input = `exports.methodOne = async user_id => {
    // other method contents
};
exports.methodTwo = async user_id => {
    // other method contents
    fooMethod();
};
exports.methodThree = async user_id => {
    // other method contents
    fooMethod();
};';`
let match = p.exec( input );
while( match !== null) {
    if( match.groups.FOO !== undefined ) console.log( match.groups.METH );
    match = p.exec( input )
}

此正则表达式(仅)匹配包含对 fooMethod();

的调用的方法名称
(?<=exports\.)\w+(?=[^{]+\{[^}]+fooMethod\(\)[^}]+};)

参见live demo

正则表达式并不是解决这个问题所有部分的最佳工具,理想情况下我们可以依赖更高级别的东西,一个解析器。

一种方法是让 javascript 在加载和执行期间自行解析。如果您的节点模块不包含任何可自行执行的内容(或至少不包含任何与以下内容冲突的内容),您可以将其放在模块的底部,然后 运行 使用 node mod.js.

console.log(Object.keys(exports).filter(fn => exports[fn].toString().includes("fooMethod(")));

(在下面的评论中显示以上是不可能的。)

另一种选择是使用像 https://github.com/acornjs/acorn 这样的库(还有其他选项)来编写一些其他 javascript 来解析您的原始目标 javascript,那么您将拥有您可以使用树结构来执行匹配,并最终 return 您之后的函数名称。我不是该库的专家,所以很遗憾我没有适合您的示例代码。