Repository navigation
Conversation
`getPartialMatcher` decides whether a directory is worth crawling. It walked the pattern and the input in lockstep, so `**` could only ever stand for one or more segments: once its matcher rejected an input part, the whole pattern was abandoned. That is wrong whenever the segment after `**` is the one that matches. With `dot: false` picomatch's `**` does not match `.a`, so `**/.a/*.txt` never descended into `.a` and the glob returned nothing, while fast-glob returns the file. Track the pattern and input positions separately so a `**` that does not consume the current segment falls through to the next pattern part instead of ending the walk. Fixes SuperchupuDev#187
Backport the fix from SuperchupuDev/tinyglobby#215 as a yarn patch on core's bundled tinyglobby, so '**/.docs/*.stories.ts' finds '.docs/A.stories.ts' again. Add a MIGRATION.md entry for the remaining story-glob differences and commit the differential comparison script. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Storybook hits this bug while moving its story globs from globby to tinyglobby (storybookjs/storybook#37171). A I checked out this PR and it fixes our case. It also restores globby's results for patterns like Thanks for tinyglobby, and thanks @MFA-G for the fix! |
|
hello, sorry for not being active lately, been busy with real life stuff. i believe the latest picomatch version fixed this on its own? i might be wrong though |
|
Checked: this repo already resolves picomatch 4.0.7 (latest on npm), and the bug still reproduces on With On Repro on a fixture,
|
Fixes #187.
The bug
getPartialMatcheris the predicate that decides whether a directory is worth descending into. It walked the pattern parts and the input parts in lockstep with a single index, which meant**could only ever stand for one or more segments — as soon as the**matcher rejected the current input part, the whole pattern was abandoned.**also matches zero segments, and that case matters as soon as the segment right after the**is the one that matches. Withdot: false, picomatch's**does not match.foo, so:The crawler never entered
.foo, so the correct full matcher downstream never got the chance to run. Withdot: truethe**matcher accepts.foo, which is why the issue only reproduces on the default.The same shape breaks any pattern where a
**precedes a segment the**itself won't match —**/.deep/a/**/*.txtreturned[]too.The fix
Track the pattern position (
j) and the input position (k) separately. When a**doesn't consume the current input segment, advance only the pattern and retry the next pattern part against the same segment, instead of ending the walk. When it does consume it, the existing earlyreturn trueis unchanged.The two indices only diverge in that one case, so the common path costs the same.
noglobstarstill treats**as a literal part.Validation
pnpm test→ 135 pass, 0 fail. Reverting onlysrc/utils.tsfails all 5 new assertions, so they pin the fix rather than restate current behavior.pnpm typecheck,pnpm check(biome),pnpm buildall clean.pnpm benchon thetypescript-eslintfixture — no regression, tinyglobby still fastest in both:packages/*/tsconfig.json**/*Tests added: three unit tests on
getPartialMatcher(leading**, mid-pattern**, and a negative case that the looser walk doesn't turn into a false positive) plus two integration tests against the existing.a/.deepfixtures.I did not touch the
dotsemantics themselves — this is purely the traversal predicate being stricter than the matcher it guards. The other half of #187 (whetherdot: falseshould skip dot directories at all, as opposed to dot files) is a deliberate behavior choice and is left alone.🤖 Written with assistance from Claude Code.