micromatch / micromatch/picomatch

`*.*` does not match names ending in a dot with default options (fastpaths vs full-parse divergence)

Open Beginner friendly
#207 0 comments 0 reactions 0 assignees View on GitHub

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.js wrapper and with windows: true / windows: false explicitly, 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.