isaacs / minimatch

a glob matcher in javascript

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Feature Request: Testing against multiple patterns

matwilko opened this issue · comments

At the moment, if developers want to test a path against multiple minimatch patterns at once, there's not really a good option to both maximise correctness and performance - the two options I can see are:

  1. Create a minimatch instance for each pattern, and run match with the path on each of them. This is the most correct/compatible method, but comes with a lot of overhead in match related to repeating the same pre-processing of the path into the split path.
  2. Extract the pre-processing code from match, generate the split path array manually, and then extract this.set from each instance and run matchOne against the parse results. This is more performant, but requires poking at internals and copying code that may well change in future versions.

I'd like to suggest adding an API surface to handle the (I would expect pretty common) case of needing to match paths against multiple patterns at once.

My initial thoughts of what this could look like would be to add either-or-both some simple static helper methods to Minimatch, or go all out and create an explicit MinimatchSet (or similar) class that encapsulates multiple patterns together, and could in the future be potentially expanded to do more optimization among the multiple patterns (e.g. if multiple patterns have a shared prefix, they could be partially combined)

Thoughts on potential API shape:

class Minimatch {
    public static matchAll(path: string, patterns: Minimatch[], partial: boolean): boolean;
    public static matchAny(path: string, patterns: Minimatch[], partial: boolean): boolean;
}
class MinimatchSet {
    public constructor(patterns: string[], , options: MinimatchOptions = {});
    public matchAll(path: string): boolean;
    public matchAny(path: string): boolean;
    public getFirstMatchingPattern(path: string): string;
    public getMatchingPatterns(path: string): string[];
}

I'm happy to put some time into fleshing this out if it's something you'd like to pursue adding to the library 😄

commented

Sure, this is already useful in glob, where you can pass in an array of patterns. If minimatch could take an array of patterns, then it could get away with having just one of them instead of instantiating multiple Minimatches.

I'm not sure that matchAll is all that valuable, tbh. The common behavior in other glob matchers seems to be that if you give it an array of patterns, it'll return true if any of them match.

The important interfaces are:

minimatch(path: string, pattern: string | string[], options: MinimatchOptions): true
class Minimatch {
  constructor(pattern: string | string[], options: MinimatchOptions)
}
filter(pattern: string | string[], options: MinimatchOptions = {}): (p: string) => boolean

The simple naive approach would be to leverage the fact that minimatch already actually does handle an array of patterns (expanded via brace-expand), so you could just if (Array.isArray(pattern)) pattern = `{${pattern.join(',')}}` and let braceExpand do the lifting. A slightly more clever way would be, when it does the brace expansion, to do it on all the elements of the array, and then flatten them into a single set. Nothing else would need to change, I'd imagine.

Do you have a real use case today where you have a set of patterns and need to ensure that all of them match?