Zero-dependency glob path matching for TypeScript/JavaScript: *, **,
?, [abc]/[a-z]/[!abc] character classes, {a,b} brace alternation,
! negation, a configurable dotfile policy, case sensitivity, and a
matchList() with .gitignore-style negation-order semantics.
Agents constantly need to test paths against include/exclude patterns —
tool allow-lists, file-scoping rules, .gitignore-style filters — without
pulling in minimatch/micromatch. This package implements the common
glob subset with zero runtime dependencies and safe pattern-to-regex
conversion (every literal character is escaped; nothing from the input
pattern is ever interpreted as raw regex).
npm install @ferrow/glob-path-matcherimport { compile, match, matchList } from "glob-path-matcher";
match("src/**/*.{js,ts}", "src/deep/nested/file.js"); // true
const matcher = compile("*.log", { caseSensitive: false });
matcher.test("DEBUG.LOG"); // true
matchList(["*.log", "!important.log"], ["debug.log", "important.log", "notes.txt"]);
// ["debug.log"] — matched by *.log, not re-excluded; important.log matched then un-matched by !important.logCompiles a pattern once for reuse. CompiledMatcher = { pattern: string; negated: boolean; test(path: string): boolean }.
A leading ! sets .negated but does not affect .test() itself —
negation is a list-level concept, handled by matchList.
One-shot convenience: compile(pattern, options).test(path).
Applies patterns to paths in order, .gitignore-style: a path is
included in the result if the last pattern that matches it is not
negated. A later !pattern can un-match an earlier hit, and a later
positive pattern can re-match again after that. Paths matched by no
pattern are excluded.
The brace-expansion step used internally, exported standalone — e.g.
expandBraces("*.{js,ts}") -> ["*.js", "*.ts"].
interface GlobOptions {
caseSensitive?: boolean; // default true
dot?: boolean; // default false — see Dotfile policy below
}| Token | Meaning |
|---|---|
* |
any run of characters within one path segment (no /), including empty |
** |
any number of whole path segments (including zero) |
? |
exactly one character within a segment |
[abc], [a-z], [!abc]/[^abc] |
character class / negated class |
{a,b,c} |
brace alternation (expanded before matching; not nested) |
!pattern (as the whole pattern) |
negation marker, used by matchList |
By default (dot: false), *, **, and ? never match a path segment
starting with . — matching shell-glob/.gitignore convention — unless
the corresponding pattern segment itself literally starts with .
(e.g. .env* matches .env.local even with dot: false, but *.js does
not match .hidden.js unless dot: true).
- Patterns are relative,
/-separated segment lists — no leading-/absolute-path semantics, no Windows\separators (normalize first). {a,b}brace groups are not nested ({a,{b,c}}expands its literal text rather than recursing) — flat/multiple groups in one pattern work fine.- Character classes don't support escaped
]inside the class or POSIX classes like[[:alpha:]]. **only has its "span zero-or-more segments" meaning as a whole path segment;a**bis treated as two ordinary*wildcards, not a**span.
Part of the ferrow-toolkit collection · Sponsored by Ferrow