@ferrow/glob-path-matcher
v1.0.0
Published
Zero-dependency glob path matching: *, **, ?, [abc]/[a-z] classes, {a,b} braces, negation, dotfile policy, case sensitivity, a compile() reusable matcher, and matchList() with .gitignore-style negation-order semantics.
Maintainers
Readme
glob-path-matcher
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.
Why
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).
Install
npm install glob-path-matcherQuickstart
import { 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.logAPI
compile(pattern: string, options?: GlobOptions): CompiledMatcher
Compiles 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.
match(pattern: string, path: string, options?: GlobOptions): boolean
One-shot convenience: compile(pattern, options).test(path).
matchList(patterns: string[], paths: string[], options?: GlobOptions): string[]
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.
expandBraces(pattern: string): string[]
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
}Glob syntax supported
| 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 |
Dotfile policy
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).
Limits
- 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
