# minimatch: the glob matcher npm itself depends on

> A small library that turns shell style glob expressions into JavaScript regular expressions, maintained by Isaac Schlueter, with an unusually blunt warning about what happens when patterns come from untrusted input.

**isaacs/minimatch** — a glob matcher in javascript

- Repository: https://github.com/isaacs/minimatch
- Website: http://isaacs.github.io/minimatch/
- Stars: 3,521 · Forks: 387
- Language: JavaScript
- License: BlueOak-1.0.0
- Published: 2026-10-06 · Updated: 2026-10-06 · Language: en
- Canonical page: https://hysenlabs.com/projects/isaacs-minimatch

## One function, and a note about who uses it

The whole public surface starts with a single call. You give it a path and a pattern, and it tells you whether they match. The README is quick to state where this shows up in practice: this is the matching library used internally by npm, which is worth knowing because it means the library is exercised against an enormous and genuinely messy corpus of real package names and file paths.

```js
// hybrid module, load with require() or import
import { minimatch } from 'minimatch'
// or:
const { minimatch } = require('minimatch')

minimatch('bar.foo', '*.foo') // true!
minimatch('bar.foo', '*.bar') // false!
minimatch('bar.foo', '*.+(bar|foo)', { debug: true }) // true, and noisy!
```

That last line is a good hint about the shape of the thing. Patterns are not limited to a cute subset of shell syntax. The `+(bar|foo)` construct is an extended glob group, and the `debug: true` option exists because when a pattern behaves unexpectedly, reading the compiled expression is the fastest way to find out why.

The package metadata tells you where the project sits today. Version 10.2.6, an ESM and CommonJS hybrid built with tshy, engines declared as Node 18, 20 or 22 and newer, and a single runtime dependency on brace-expansion at version 5. The license is BlueOak-1.0.0, a permissive license that adds an explicit patent grant rather than relying on the absence of one. The last push to the repository was on 2026-07-27.

## Globs become regular expressions, and that is the whole design

The README states the mechanism in one sentence: it works by converting glob expressions into JavaScript RegExp objects. Almost every behaviour question about this library follows from that decision, so it is worth sitting with before looking at features.

The upside is that you inherit the reach of the JavaScript regular expression engine. The feature list names brace expansion, extended glob matching, globstar `**`, and POSIX character classes with full Unicode coverage. That last point has a concrete consequence worth internalising: `[[:alpha:]]` will match `e` with an acute accent, while the character class `[a-zA-Z]` will not. Collating symbol and set matching is explicitly out of scope, so `[[=e=]]` does not match that accented character and `[[.ch.]]` does not match `ch` in locales where the two letters count as one character.

The downside is backtracking, and the README does not bury it. There is a prominent warning that any JavaScript library matching string patterns with regular expressions is subject to ReDoS if the pattern is generated from untrusted input. The stated mitigation is recursion limits and similar guards, followed by an unusually honest admission that those guards protect against accidents and not against a determined attacker.

What makes this section worth reading twice is the policy decision underneath it. The README says future ReDoS reports will be treated as working as intended and closed by the warning rather than by patching, and that a future version may use a matching algorithm without backtracking problems but that such a change would be sweeping and would not be backported to older versions. That is a maintainer deciding where the boundary of the library lies, and writing it down where users will see it.

## Brace expansion is the step that surprises people

Before any pattern becomes a regular expression, it goes through brace expansion, and that stage is the one that changes the meaning of a pattern in ways a single regexp cannot explain. The pattern `{a,b/c}/d` expands into more than one pattern, and each of those is separately compiled. The library exposes the result of that step on the `set` property of a Minimatch instance: a two dimensional array whose rows are brace-expanded patterns and whose columns are path segments.

There is a performance detail hiding in that structure which pays off in real use. If a portion of a pattern contains no magic at all, something like `foo` rather than `fo*o?`, it is left as a plain string instead of being converted into a regular expression. A matcher can then compare literals with string equality and skip the regex engine entirely for the common case where a path segment is just a name.

The `hasMagic()` method exists to answer whether a pattern needs the expensive path at all, and the README is careful to add that this does not mean the pattern string is safe to use as a literal filename. The example given is that a pattern such as an escaped asterisk is not considered magical, because the matching portion parses to the literal string containing an asterisk. The `minimatch.unescape()` method is the documented way to remove those escape characters if you need the true literal.

## The class exposes compiled state and a few public methods

For repeated matching, or for introspection, the README points at the Minimatch class rather than the single function.

```javascript
var Minimatch = require('minimatch').Minimatch
var mm = new Minimatch(pattern, options)
```

An instance carries several properties worth knowing about. `pattern` is the original string you passed in, and `options` is what the constructor received. `regexp` is what `makeRe` produced, a single expression expressing the entire pattern, which the README notes is useful when you want to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled. `negate` tells you whether the pattern is negated, and there are `comment` and `empty` flags for patterns that are comments or the empty string.

Of the methods, `makeRe()` generates the `regexp` member if it does not exist and returns it, and it returns `false` if the pattern is invalid rather than throwing, which is the detail that matters if you are accepting patterns from configuration. `match(fname)` is the boolean test. `matchOne()` takes a slash split filename and a pattern row, and is exposed mainly for glob walkers that want to avoid excessive filesystem calls by ruling out whole subtrees before descending.

`hasMagic()` returns true if the parsed pattern contains any magic characters, and returns false when every comparator part is a string literal. The `magicalBraces` option changes that: set on the constructor, it makes brace expansions count as magic even when their expansions are otherwise plain, so a pattern like `a{b,c}d` reports magic under that flag. Everything not listed here is internal and called as needed.

## Windows path handling is where projects lose an afternoon

The Windows section of the README is the densest part of the documentation and it exists because path separator conventions collide. The rule is simple and absolute: use forward slashes only in glob expressions. Backslashes in a pattern are always escape characters, never path separators, no matter what the host operating system does. On the path argument side, Windows will still accept either separator, and those separators match against forward slashes in the pattern.

UNC paths get their own rules because they start with a double slash and a naive parser would treat that as a globstar. A pattern beginning with a double slash followed by non slash characters preserves it, so `//*` matches `//x` but not `/x`. A pattern starting with `//?/` followed by a drive letter does not treat the question mark as a wildcard at all; it is read as a literal character. Longer forms starting with `//?/<drive letter>:/` match filesystem paths that start with just the drive and colon, as though the prefix were not present, and that equivalence only holds when the drive letters match case insensitively. Everything after that point compares case sensitively unless `nocase:true` is set.

One asymmetry is easy to miss: backslashes are always permitted as separators in the path argument, but in the pattern argument they are only allowed when `windowsPathsNoEscape: true` is set. The default is the safe reading, and the escape is opt in.

## How the project is built and tested

The repository is small and its toolchain is visible in the manifest. `prepare` runs tshy, which is what produces the dual ESM and CommonJS output with matching type declarations, and the tshy config points the exports map back at `./src/index.ts` during development so the source is what gets typed against. `pretest` and `presnap` both run that prepare step, so a test run cannot accidentally exercise a stale build.

Tests use tap, and snapshots are stored in a `tap-snapshots/` directory, which is the right shape for a library whose output is largely data: the parsed set, the compiled regexp, the flags. Snapshot testing makes an unintended change in compilation output visible as a diff rather than as a silent behaviour change. `postsnap` runs lint and `postlint` runs format, so the snapshot and lint steps are chained deliberately.

Linting has moved to oxlint with its TypeScript-aware plugin, and formatting is prettier. There is a dedicated `typedoc` script pointed at the source files with a `typedoc.json` in the tree, and a `benchmark` script pointing at a benchmark directory. Publishing is tied to versioning: `preversion` runs the tests, `postversion` publishes, and `prepublishOnly` pushes the tag. The last detail worth noting is `.tshy/` in the repository tree, which is where the build tool keeps its generated configuration, a reminder that the dual format build is the part of this project most likely to need attention during an upgrade.

## Conclusion

minimatch is worth reading closely precisely because it is small and because its author documents its limits instead of hiding them. The design decision that shapes everything else is that a glob is compiled to a RegExp, which buys you bash semantics and the entire JavaScript regexp engine at the price of backtracking, and the README says so in bold. The parts most projects actually hit are the forward slash rule on Windows, the brace expansion that turns one pattern into several, and the fact that a pattern without magic is kept as a plain string rather than being needlessly compiled. If you take one practical thing away, take this: treat patterns as code, not as data, and never build them from untrusted input.

## FAQ

### What is minimatch used for?

It answers one question: does this path match this glob pattern. That is what npm uses it for internally, and it is the same question asked by ignore file matching, file finders, and any tool that lets users write shell style patterns. It matches patterns only, it does not walk the filesystem.

### How does minimatch handle brace expansion?

Braces are expanded before anything becomes a regular expression, so a pattern like {a,b/c}/d produces more than one pattern. The expanded results are exposed on the set property of a Minimatch instance as a two dimensional array, with one row per expanded pattern and one entry per path segment.

### Is it safe to build minimatch patterns from user input?

The documentation says no. Because patterns compile to JavaScript regular expressions, an attacker who controls the pattern can create a denial of service through catastrophic backtracking. The library applies limits that guard against accidents, but the author states they do not defend against a determined attacker, so patterns should be treated as code rather than data.

### Why does my glob fail on Windows?

Use forward slashes only in the pattern. Backslashes are always read as escape characters by this library, regardless of the host operating system, while the path argument itself may use either separator. UNC paths with a leading double slash have additional documented rules.

### How do I tell whether a pattern needs the regex engine?

Call hasMagic() on a Minimatch instance. It returns false when every part of the parsed pattern is a plain string, and segments with no magic are kept as literal strings instead of being compiled. The magicalBraces constructor option makes brace expansions count as magic even when their expansions are plain.

## Sources

- [isaacs/minimatch on GitHub](https://github.com/isaacs/minimatch)
- [Issues](https://github.com/isaacs/minimatch/issues)
- [License: BlueOak-1.0.0](https://github.com/isaacs/minimatch/blob/main/LICENSE)
- [Project website](http://isaacs.github.io/minimatch/)
- [README](https://github.com/isaacs/minimatch/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/isaacs-minimatch
