Skip to content

perf: match static patterns without picomatch - #212

Open
webpro wants to merge 2 commits into
SuperchupuDev:mainfrom
webpro:static-patterns
Open

perf: match static patterns without picomatch#212
webpro wants to merge 2 commits into
SuperchupuDev:mainfrom
webpro:static-patterns

Conversation

@webpro

@webpro webpro commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Every walked entry is tested against every compiled pattern. That is fine for globs, but a static pattern can only ever match one exact path, so running it through picomatch costs O(entries × patterns) for no benefit.

This surfaces when patterns are generated rather than hand-written. In knip for example, package.json#exports/imports maps expand into entry paths; in a large monorepo with generated-types workspaces a single map can expand to tens of thousands of literal paths (webpro-nl/knip#1876).

So I took a stab at this, since I believe it's tinyglobby's job rather than consumer to optimize this. And it will benefit other consumers as well.

The idea is that processPatterns now splits positive patterns into static and match (dynamic). The crawler matches statics with two Sets instead of picomatch:

  • an exact set, for the full matcher
  • a prefix set (every ancestor of each pattern), for the partial matcher, so traversal still descends toward literal targets and prunes everything else

When both kinds are present the matchers are OR'd. When there are no static patterns the original matchers are used untouched, so "pure glob input" is unaffected.

A false negative here only costs speed, while a false positive would change results. So anything outside that set (glob syntax, escapes, quotes, |, $$, ++, non-ascii) keeps the matcher path. It also keeps no picomatch-specific knowledge, so it stays correct if the matcher is ever swapped (looking at #178).

Benchmarks

npm run bench — this adds two high-cardinality cases to the existing suite:

500 static patterns (typescript-eslint) latency avg
tinyglobby (before) 82.6 ms
tinyglobby (after) 6.2 ms
fast-glob 8.6 ms
glob 13.2 ms

Worth noting tinyglobby was behind fast-glob and glob on this input before, and is ahead after.

It also helps mixed input, in proportion to how many literals are in the mix (4000-file tree, **/* plus N literals):

literals before after
0 3.4 ms 3.5 ms
100 29.7 ms 4.0 ms
1000 321 ms 5.1 ms
4000 882 ms 7.8 ms

Pure-literal input scales linearly instead of quadratically: 1000 literals 81 ms → 4.7 ms, 5000 literals 2308 ms → 21 ms, and the new path stays linear (~4 µs/pattern) out to 50k.

Correctness

No behaviour change — this only changes which matcher a pattern is routed to.

Adds 30 tests covering the static path: missing files, overlap with dynamic patterns, crawler ordering, case sensitivity, deep, abort signals, ignore, onlyDirectories, absolute patterns and absolute input, symlinks and followSymbolicLinks, escaped/quoted/repeated matcher syntax, dotted paths, parent and sibling directories, the fs option, root hoisting with many patterns, prefix collisions, and pipe alternation.

Results were also diffed against main over an adversarial corpus (names built from $ ^ + " ' @ ! % ~ # & , ; |, spaces, unicode, nested and space-containing directories) and over randomized inputs across the option matrix (dot, caseSensitiveMatch, onlyFiles, onlyDirectories, deep, ignore, absolute, expandDirectories) — identical in every case.

Disclaimer: I've used Claude and Codex to assist, such as for writing extensive tests. This PR is likely "a bit much", kept it together to ensure it does improve performance and is otherwise complete and correct. Happy to tune it down any way you like.

@pkg-pr-new

pkg-pr-new Bot commented Jul 24, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/tinyglobby@212

commit: dfb9164

@webpro
webpro force-pushed the static-patterns branch from 03f783f to dfb9164 Compare July 24, 2026 17:20
@SuperchupuDev

Copy link
Copy Markdown
Owner

i will need some time to review this pull request, but i want to tell you in advance that this is an insane improvement, thanks a lot for taking your time to look into this ❤️

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

Successfully merging this pull request may close these issues.

2 participants