micromatch / picomatch

Blazing fast and accurate glob matcher written JavaScript, with no dependencies and full support for standard and extended Bash glob features, including braces, extglobs, POSIX brackets, and regular expressions. Used by GraphQL, Jest, Astro, Snowpack, Storybook, bulma, Serverless, fdir, Netlify, AWS Amplify, Revogrid, rollup, routify, open-wc, imba, ava, docusaurus, fast-glob, globby, chokidar, anymatch, cloudflare/miniflare, pts, and more than 5 million projects! Please follow picomatch's author: https://github.com/jonschlinkert
https://github.com/micromatch
MIT License
972 stars 56 forks source link

Missing function or option to match all given globs (option to create conjunction match instead of disjunction) #122

Closed nedredmond closed 1 year ago

nedredmond commented 1 year ago

As the documentation says, isMatch will match any of the provided globs when provided an array. This creates an issue for a use case in which an array of negative matchers are provided.

For example, if a developer wanted to match a path without given matches, each glob would negate the other (unless a path had all of the strings): picomatch(["!(**/__tests__/**)", "!(**/dist/**)"])

The above would match any path with /__tests__/ in it as long as it did not also have /dist/, and vice versa, and ends up matching all files (since test files don't often end up in dist directories).

Of course, we could change this to a single (but more difficult to write and grok) glob, but it would be more intuitive to use a "match all" option-- a function like isMatchAll() or an option like matchAll: true.

nedredmond commented 1 year ago

I solved this locally:

// picomatch does does inclusive disjunctions (ORs) by default,
//   so we need to do some extra work to get conjunctive (AND) matches.
if (globsList.length > 1 && matchAllGlobs) {
    // conjunctive glob match
    const matchers = globsList.map((glob) => picomatch(glob));
    filters.push((path) => matchers.every((matcher) => matcher(path)));
} else {
    // disjunctive glob match
    filters.push((path) => picomatch(globsList)(path));
}

Would love to see a built-in solution.

nedredmond commented 1 year ago

I see that this is exactly how it's handled in micromatch, which I wasn't aware of before I made this issue! I'll go ahead and close it.

https://github.com/micromatch/micromatch/blob/2c56a8604b68c1099e7bc0f807ce0865a339747a/index.js#L334-L340

micromatch.all = (str, patterns, options) => {
  if (typeof str !== 'string') {
    throw new TypeError(`Expected a string: "${util.inspect(str)}"`);
  }

  return [].concat(patterns).every(p => picomatch(p, options)(str));
};