Home > Software design >  Regex for finding the name of a method containing a string
Regex for finding the name of a method containing a string

Time:12-11

I've got a Node module file containing about 100 exported methods, which looks something like this:

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

Goal: What I'd like to do is figure out how to grab the name of any method which contains a call to fooMethod, and return the correct method names: methodTwo and methodThree. I wrote a regex which gets kinda close:

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

Problem: using my example code from above, though, it would effectively match methodOne and methodThree because it finds the first instance of export and then the first instance of fooMethod and goes on from there. Here's a regex101 example.

I suspect I could make use of lookaheads or lookbehinds, but I have little experience with those parts of regex, so any guidance would be much appreciated!

CodePudding user response:

This regex matches (only) the method names that contain a call to fooMethod();

(?<=exports\.)\w (?=[^{] \{[^}] fooMethod\(\)[^}] };)

See live demo.

CodePudding user response:

If you are willing/able to not use a regular expression, and your node module doesn't include anything that would execute on its own (or at least anything that would conflict with the below), you can put this at the bottom of your module, and then run the module with node mod.js.

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

CodePudding user response:

Assuming that all methods have their body enclosed within { and }, I would make an approach to get to the final regex like this:

  • First, find a regex to get the individual methods. This can be done using this regex:
exports\.(\w )(\s|.)*?\{(\s|.)*?\}
  • Next, we are interested in those methods that have fooMethod in them before they close. So, look for } or fooMethod.*}, in that order. So, let us name the group searching for fooMethod as FOO and the name of the method calling it as METH. When we iterate the matches, if group FOO is present in a match, we will use the corresponding METH group, else we will reject it.
exports\.(?<METH>\w )(\s|.)*?\{(\s|.)*?(\}|(?<FOO>fooMethod)(\s|.)*?\})

Explanation:

  • exports\.(?<METH>\w ): Till the method name (you have already covered this)
  • (\s|.)*?\{(\s|.)*?: Some code before { and after, non-greedy so that the subsequent group is given preference
  • (\}|(?<FOO>fooMethod)(\s|.)*?\}): This has 2 parts:
    • \}: Match the method close delimiter, OR
    • (?<FOO>fooMethod)(\s|.)*?\}): The call to fooMethod followed by optional code and method close delimiter.

Here's a JavaScript code that demostrates this:

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