Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Array with negation pattern #9

Closed
vweevers opened this issue Feb 22, 2015 · 6 comments
Closed

Array with negation pattern #9

vweevers opened this issue Feb 22, 2015 · 6 comments
Assignees

Comments

@vweevers
Copy link

As you noted in level-glob#1 (thanks!), there's a possible bug (I'm not sure either) when using multiple patterns and negation. I did some tests with both 1.0.1 and 1.4.2, and it comes down to the following. These two lines produce different results:

var mm = require('micromatch');
console.log(mm.match(['a.md', 'b.js'], ['!*.md']));
console.log(mm.match(['a.md', 'b.js'], '!*.md'));

The first matches nothing, the second matches b.js. Is this expected behavior?

@jonschlinkert
Copy link
Member

sorry for the late reply! Ah, yes, not a bug, but... it was an implementation decision - but one I can easily change/revisit if necessary.
The thinking is that:

  • if you only pass a string pattern, then the behavior will be the same as minimatch. which is inclusive by default. Meaning '!*.md' should return everything except '*.md'. This is how minimatch works.
  • however, if you pass an array of patterns (which isn't supported by minimatch), then the results should not be inclusive by default, so you have to do ['*', '!*.md']. e.g. if you only want to negate *.md, then pass a string.

We had similar discussions on multimatch, and it seems I might be in the minority in thinking that the current behavior makes sense, so I might change it with more feedback. Thoughts?

@vweevers
Copy link
Author

That explains it, thanks. Although this behavior is counter-intuitive to me (because I always read a negation pattern as "everything but x", and multiple negation pattterns as "everything but x and y"), it's sane to keep in line with multimatch.

For now, what's missing, is a note in the readme. There's an example of multiple negation patterns:

mm(['a.js', 'b.md', 'c.txt'], '!*.{js,txt}');
//=> ['b.md']

mm(['a.md', 'b.js', 'c.txt', 'd.json'], ['*.*', '!*.{js,txt}']);
//=> ['a.md', 'd.json']

.. which kind of reveals the behavior, but it's easy to miss. An explanation or at least an explicit comment would manage expectations and thus prevent confusion.

For others, related discussion is here: sindresorhus/multimatch#7, sindresorhus/multimatch#10, robrich/gulp-match#4.

@jonschlinkert
Copy link
Member

what's missing, is a note in the readme

Good point. I'll get that inserted!

Thanks for the cross-references too!

@jonschlinkert
Copy link
Member

closing, I opened up an issue to add to docs. I'm not shutting down the idea of changing this behavior though. if you or anyone who has any thoughts on this wants to comment, please feel free

@cben
Copy link

cben commented May 1, 2023

... is counter-intuitive to me (because I always read a negation pattern as "everything but x", and multiple negation pattterns as "everything but x and y"), ...

I had the same intuition for multiple negations, but turns out it subtly depends on which API one uses.

For example I was naively assuming micromatch.isMatch() would be the boolean equivalent to micromatch() but turns out it's an alias for any() making it nearly useless with multiple negations:

> micromatch.isMatch('/a/b/c.d', ['!/a/**'])
false
> micromatch.isMatch('/a/b/c.d', ['!/x/**'])
true
> micromatch.isMatch('/a/b/c.d', ['!/a/**', '!/x/**'])
true

WAT? So the reason is that while "! a" rejects it, "! x" allows it and the result is true as long as at least one pattern allows it. Only if ALL negations fail would isMatch = any fail:

> micromatch.isMatch('/a/b/c.d', ['!/a/**'])
false
> micromatch.isMatch('/a/b/c.d', ['!/*/b/**'])
false
> micromatch.isMatch('/a/b/c.d', ['!/a/**', '!/*/b/**'])
false

The top-level micromatch() aka micromatch.match() is more intuitive:

> micromatch(['/a/b/c.d', '/x/y', '/z'], ['!/a/**'])
[ '/x/y', '/z' ]
> micromatch(['/a/b/c.d', '/x/y', '/z'], ['!/x/**'])
[ '/a/b/c.d', '/z' ]
> micromatch(['/a/b/c.d', '/x/y', '/z'], ['!/a/**', '!/*/b/**'])
[ '/x/y', '/z' ]
> micromatch(['/a/b/c.d', '/x/y', '/z'], ['!/x/**', '!/a/**'])
[ '/z' ]

but the documentation is unclear how exactly it should interpret multiple patterns.
It merely states "patterns {String|Array}: One or more glob patterns to use for matching."

BTW, I don't see evidence that "you have to do ['*', '!*.md']". It seems that array with only negations does the job.

@magicdawn
Copy link

+1 on weird behavior of current mm.isMatch when dealing with negation

Returns true if any of the given glob patterns match the specified string.

Only run into issues can i find out this api is not equal to mm()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants