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
960 stars 55 forks source link

Incorrectly matches trailing negation (e.g. to match `.ts` but not `.d.ts` with `**/!(*.d).ts`) #83

Closed duniul closed 3 years ago

duniul commented 3 years ago

Thanks for a great library and apologies in advance if this has been adressed somewhere else! 🙏

Issue

I'm trying to match a pattern that matches all *.ts files except *.d.ts, and while it works most of the time it does not seem to work if whatever pattern you're negating is used anywhere else in the path.

The glob **/!(*.d).ts should work for this case, but with picomatch and micromatch it excludes any file name that contains .d at all, not just at the end. With minimatch and other globbing tools it works as expected.

Example

import minimatch from 'minimatch';
import picomatch from 'micromatch';
import micromatch from 'micromatch';

const glob = '**/!(*.d).ts';

const mini = (path) => minimatch(path, glob);
mini('/file.d.ts')            // false (correct)
mini('/file.ts')              // true  (correct)
mini('/file.d.something.ts')  // true  (correct)
mini('/file.dhello.ts')       // true  (correct)

const pico = (path) => picomatch.isMatch(path, glob);
pico('/file.d.ts')            // false (correct)
pico('/file.ts')              // true  (correct)
pico('/file.d.something.ts')  // false (incorrect)
pico('/file.dhello.ts')       // false (incorrect)

const micro = (path) => micromatch.isMatch(path, glob);
micro('/file.d.ts')            // false (correct)
micro('/file.ts')              // true  (correct)
micro('/file.d.something.ts')  // false (incorrect)
micro('/file.dhello.ts')       // false (incorrect)

I also created a CodeSandbox with an example for testing any glob (see the console output).

jonschlinkert commented 3 years ago

I think I know what's causing it, but I'll double check with the sandbox, thanks for doing that!

edit: in your code snippet here, you are using micromatch in the mini (minimatch) example. Do you want to update that first so we can see what the behavior is for minimatch?

The pattern !(*.d).ts should should not match (or negate) file.d.something.ts since there is a segment between d and .js. There needs to be another *. there somewhere, probably as a second condition in the parens. I'll play around with it and see what works.

jonschlinkert commented 3 years ago

fwiw I'm still playing around with the sandbox, I think that there might be a bug here. I'll report back when I'm finished looking into it.

duniul commented 3 years ago

in your code snippet here, you are using micromatch in the mini (minimatch) example. Do you want to update that first so we can see what the behavior is for minimatch?

@jonschlinkert Oops, incorrectly edited the example to make it more readable. I've cleaned it up now, the results are the same though (minimatch shows one result, picomatch and micromatch another).

Incredibly fast response, thanks a lot 🙌

duniul commented 3 years ago

The pattern !(.d).ts should should not match (or negate) file.d.something.ts since there is a segment between d and .js. There needs to be another . there somewhere, probably as a second condition in the parens.

Exactly, out of the paths in the example only file.d.ts should be excluded.