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

!(*.js|*.json) does not match a.js.gz #10

Open
smbape opened this issue Dec 21, 2017 · 8 comments
Open

!(*.js|*.json) does not match a.js.gz #10

smbape opened this issue Dec 21, 2017 · 8 comments
Labels

Comments

@smbape
Copy link

smbape commented Dec 21, 2017

Hello,

I am using extglob v2.0.2 and node v8.9.0.

As the title states, there is a case where a pattern does not match where I thought it would.

extglob.makeRe("!(*.js|*.json)").test("a.js.gz") === true // KO
extglob.makeRe("!(*.js|*.json)").test("a.json.gz") === true // KO
extglob.makeRe("!(*.js|*.json)").test("a.gz") === true // OK

Should this not match or is it a bug?

@jonschlinkert
Copy link
Member

Hi, thanks for creating an issue.

Should this not match or is it a bug?

Please add a comparison of how bash handles these extglob patterns versus this library

@smbape
Copy link
Author

smbape commented Dec 21, 2017

Hello, according to bash 4.4.12, it is a bug.

shopt -s extglob
touch a.js a.js.gz a.json a.json.gz a.gz
ls !(*.js|*.json) # a.gz  a.js.gz  a.json.gz

extglob will only match a.gz

@phated
Copy link
Member

phated commented Dec 21, 2017

That seems like such incorrect behavior...

@jonschlinkert
Copy link
Member

jonschlinkert commented Dec 21, 2017

That seems like such incorrect behavior...

which result?

@jonschlinkert
Copy link
Member

jonschlinkert commented Dec 21, 2017

do the following instead:

console.log(extglob.makeRe('*.!(js|json)').test('a.js.gz')); // true
console.log(extglob.makeRe('*.!(js|json)').test('a.json.gz')); // true
console.log(extglob.makeRe('*.!(js|json)').test('a.gz')); // true

Even bash yields incorrect results sometimes when globs are nested inside negation globs. In javascript, we can't do negative lookbehinds in regular expressions, so it becomes very difficult or impossible to retroactively un-negate or un-match a substring.

edit: also, I forgot to mention that technically bash in this case is performing a "contains" match, rather than an exact match. This library is stricter in that regard. You can also use your original patterns with options.contains:

console.log(extglob.makeRe('!(*.js|*.json)', {contains: true}).test('a.js.gz')); // true
console.log(extglob.makeRe('!(*.js|*.json)', {contains: true}).test('a.json.gz')); // true
console.log(extglob.makeRe('!(*.js|*.json)', {contains: true}).test('a.gz')); // true

see https://github.com/micromatch/extglob#differences-from-bash

@jonschlinkert
Copy link
Member

btw, on a totally unrelated note, if you happen to notice this table being corrupted, that's a bug in github's markdown parser.

@smbape
Copy link
Author

smbape commented Dec 22, 2017

Hi, thanks for the comments.

Indeed it is hard without negative lookbehinds.
The contains option seems to be an alternative but matches things I really don't want to match.

console.log(extglob.makeRe('!(*.js|*.json)', {contains: true}).test('a.js')); // true, but I want it to be false
console.log(extglob.makeRe('!(*.js|*.json)', {contains: true}).test('a.js.gz')); // true
console.log(extglob.makeRe('!(*.js|*.json)', {contains: true}).test('a.json.gz')); // true
console.log(extglob.makeRe('!(*.js|*.json)', {contains: true}).test('a.gz')); // true

I also tried to use the *.!(js|json) form in the past, but *.!(js|json) did not match files without extensions.

console.log(extglob.makeRe('*.!(js|json)').test('a')); // false

I will look for a solution with 2 extglobs.

@jonschlinkert
Copy link
Member

jonschlinkert commented Dec 22, 2017

I'm thinking this looks like a bug then. I try to stick to bash behavior, but sometimes bash is unable to handle certain patterns. In this case, it seems like bash is doing the right thing.

edit: fwiw, here is what I think the results should be for all of the patterns you gave:

console.log(extglob.makeRe('!(*.js|*.json)', {contains: true}).test('a.js')); // false
console.log(extglob.makeRe('!(*.js|*.json)', {contains: true}).test('a.js.gz')); // true
console.log(extglob.makeRe('!(*.js|*.json)', {contains: true}).test('a.json.gz')); // true
console.log(extglob.makeRe('!(*.js|*.json)', {contains: true}).test('a.gz')); // true
console.log(extglob.makeRe('*.!(js|json)').test('a')); // false

I agree with your opinion on the first one, it seems like that should be false.

The last one is correct because the pattern has *. which means that a dot is expected in the path before the negation even happens.

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

No branches or pull requests

3 participants