npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

zeromatch

v1.0.0

Published

Blazing-fast, picomatch-compatible glob matching, powered by a linear-time Rust bytecode VM.

Readme

zeromatch

A fast, picomatch-compatible glob matcher for Node.js, written in Rust.

bun add zeromatch

Why

Glob matching is on the hot path of nearly every JS build tool, bundler, and file watcher. picomatch is excellent - it compiles patterns to regular expressions and leans on V8's regex engine, which is hard to beat once a pattern is cached. But for uncached matching (compiling a pattern then matching once), or for batched workloads where you need to test many paths against the same pattern, there's room to do better.

zeromatch is a clean-room reimplementation: a small bytecode VM with specialized fast paths for the patterns you actually use. It's API-compatible with picomatch's common surface, so swapping it in is straightforward.

When to use it

zeromatch is a good fit when:

  • You match the same pattern against many paths. This is the case picomatch's pmFn covers; zeromatch covers it too, and the batched testMany form is significantly faster than calling a JS matcher in a loop.
  • You compile a pattern on each call. zeromatch's one-shot path is meaningfully faster than picomatch's, because pattern compilation in Rust is cheaper than building a regex source string and handing it to V8.
  • You match against Buffer or Uint8Array data (e.g. results from fs.readdir(..., { encoding: 'buffer' })). zeromatch matches bytes directly with no string conversion.

picomatch is likely a better fit if your hot loop calls a single cached matcher one path at a time and you're already at the limit there - that path is the one V8's regex engine is best at, and the FFI hop into native code is real overhead.

Benchmark

Pattern: **/needle.{js,ts,tsx,jsx,mdx} against some/a/bigger/path/to/the/crazy/needle.ts.

| Workload | picomatch | zeromatch | |---|---|---| | Compile + match once | 0.70–0.85 M ops/s | 1.25–1.45 M ops/s (≈2×) | | Cached single match | 8–10.5 M ops/s | 7.5–8.2 M ops/s | | Cached × 1000 paths | 22–23 M paths/s | 8–10.5 M paths/s |

Numbers from tinybench on Windows x64 (Node 20+). Results vary by CPU and runtime conditions.

Interpretation

  • zeromatch is consistently faster in one-shot workloads (≈2×).
  • picomatch retains an advantage in tight cached single-call loops.
  • Batched workloads depend heavily on call pattern and boundary overhead.

Usage

import { Glob, GlobSet, isMatch, scan, makeRe } from 'zeromatch'

// One-shot.
isMatch('src/index.ts', '**/*.{ts,tsx}')      // true

// Compiled matcher (recommended for hot loops).
const g = new Glob('**/*.{ts,tsx}')
g.test('src/index.ts')                         // true
g.test('src/index.css')                        // false

// Batched.
g.testMany(['a.ts', 'b.css', 'c.tsx'])         // [true, false, true]
g.filter(['a.ts', 'b.css', 'c.tsx'])           // ['a.ts', 'c.tsx']

// Many patterns at once.
const set = new GlobSet(['**/*.ts', '!**/node_modules/**'])
set.test('src/app.ts')                         // true
set.matches('src/app.ts')                      // [0]

// Direct byte input (zero-copy from Buffer / Uint8Array).
g.testBuffer(Buffer.from('src/index.ts'))      // true

// picomatch-compatible scan().
scan('src/**/*.js')
// → { base: 'src', glob: '**/*.js', isGlob: true, isGlobstar: true, ... }

// Get the underlying regex source if you need it.
makeRe('*.js')                                 // '^(?:[^/]*\\.js)$'

Compatibility

zeromatch aims to be a drop-in replacement for the picomatch surface most projects use - isMatch, the compiled matcher form, scan, makeRe. There are differences worth knowing:

  • The onMatch/onIgnore/onResult callback options aren't implemented; they're rarely used in practice.
  • makeRe returns the regex source string rather than a RegExp instance, since zeromatch doesn't use regexes internally and constructing one would only be useful to the caller. Wrap it in new RegExp(...) yourself if you need the object.
  • Negation parsing follows picomatch's rules but is slightly stricter about ambiguous cases.

If you hit a real-world pattern that picomatch handles and zeromatch doesn't, please open an issue with both inputs - that's the easiest way to keep parity tight.

Platform support

Prebuilt binaries are published for the common platforms. If you're on something unusual, the package will fall back to building from source, which requires a Rust toolchain (1.80+).

Node.js ≥ 18.17, ≥ 20.3, or ≥ 21.1.

License

MIT