micromatch / micromatch/picomatch
`*.*` does not match names ending in a dot with default options (fastpaths vs full-parse divergence)
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 1.3k
- Forks
- 135
- Avg merge
- 23h 38m
- Merged PRs (30d)
- 7
Description
Summary
With default options, *.* does not match names that end in a dot (a., foo.), but it does match them when fastpaths is disabled. Since fastpaths is documented as a pure speed optimization ("full parsing is skipped for a handful common glob patterns"), the two paths should agree.
Bash — which the README cites as picomatch's reference behavior — matches these names.
Reproduction
const pm = require('picomatch');
pm.isMatch('a.', '*.*'); // false
pm.isMatch('a.', '*.*', { fastpaths: false }); // true
Same for foo. and ab..
Expected
true in both cases.
The README states results "are based on Bash's unit tests and the Bash 4.3 specification", with two documented exceptions (* not crossing /, and non-greedy negated extglobs). This case is neither.
Bash agrees:
$ touch 'a.' 'ab.' 'foo.' 'x.y'
$ shopt -s nullglob; echo *.*
a. ab. foo. x.y
$ [[ "a." == *.* ]] && echo TRUE
TRUE
Actual
false on the default path, true with fastpaths: false.
Where it comes from
lib/parse.js, in the fastpaths '*.*' case:
case '*.*':
return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
ONE_CHAR is (?=.) and sits after DOT_LITERAL, requiring at least one character after the dot. The full parser instead places the non-empty guard at the start and leaves the post-dot star free to match empty:
- fastpaths:
/^(?:(?!\.)[^/]*?\.(?=.)[^/]*?\/?)$/ - full parse:
/^(?:(?!\.)(?=.)[^/]*?\.[^/]*?\/?)$/
Notes
- Reproduced on picomatch 4.0.7 (current release from npm).
- Identical result via the
index.jswrapper and withwindows: true/windows: falseexplicitly, so it is not an artifact of Windows path normalization. - No test in
test/pins'a.'against'*.*', so there does not appear to be an intended behavior contradicting the above. - I am not proposing which side should change — that is a maintainer call. The report is about the two paths disagreeing.
A second, weaker divergence of the same shape exists for prefix globs (pm.isMatch('foo/', 'foo*') is false by default, true with fastpaths: false). I am not filing that one as a spec violation, because the README only documents the strictSlashes: true direction and says nothing about the default — but it is the same fastpaths/full-parse disagreement, so it may be worth looking at together.
Found with an automated specification-conformance pass (rules extracted from this repo's README and tests, then exercised against the implementation) and verified by hand against Bash and the published package before filing.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in lib/parse.js at the fastpaths '.' case and compare its result with the full parser. Reproduce the mismatch with pm.isMatch for 'a.' and '.', then inspect the existing tests in test/. Done means both default and fastpaths-disabled matching agree with Bash, with regression coverage for names ending in a dot.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100