regexpp
A dependency-free, spec-accurate ECMAScript regular expression parser and validator with a full AST, powering ESLint's regex-aware rules.
Repository Health
Technical Analysis
@eslint-community/regexpp is a dependency-free TypeScript library that parses and validates JavaScript regular expressions strictly according to the ECMAScript specification, across every ES version from ES5 through the latest Annex B and Unicode-set-mode (v flag) syntax. It exposes both simple one-call helpers (parseRegExpLiteral, validateRegExpLiteral, visitRegExpAST) and the underlying RegExpParser/RegExpValidator/RegExpVisitor classes for callers who need incremental parsing, ECMAScript-version gating, or handler-based AST traversal.
Originally created by Toru Nagashima (mysticatea) and now maintained under the eslint-community GitHub org, it is the regex-parsing engine ESLint itself and dozens of ESLint plugins rely on to write regex-aware lint rules — anywhere a rule needs to inspect a regular expression literal as a real AST instead of pattern-matching the raw string.
What You Get
- Spec-accurate parsing/validation across configurable ECMAScript versions (ES5 through the latest, including Annex B and
v-flag Unicode-set mode) - Zero runtime dependencies — a self-contained scanner, validator, parser, and visitor in one package
- A full typed AST (
AST.Nodeunion) with a structuredRegExpVisitorfor enter/leave traversal, matching the ESTree visitor conventions ESLint plugin authors already know - Dual CJS/ESM output with bundled
.d.tstypes, ready to import from either module system - Structured
RegExpSyntaxErrorobjects instead of ad hoc throw strings, so callers can handle parse failures programmatically
Common Use Cases
- Writing ESLint rules that inspect regex literals - a rule author calls
parseRegExpLiteralon aLiteralnode’s raw regex to check things like unnecessary escapes, unused capture groups, or catastrophic-backtracking-prone patterns. - Validating user-supplied regex patterns - a tool that accepts regex input from users (e.g. a config file or a search box) calls
validateRegExpLiteralto reject invalid patterns before they hit the JSRegExpconstructor. - Building regex linters or refactoring tools -
visitRegExpASTwalks the parsed tree to find, rewrite, or lint sub-patterns (character classes, backreferences, lookarounds) that string-based regex matching can’t reliably detect. - Cross-version regex compatibility checks - passing an
ecmaVersionoption lets a tool verify whether a given pattern is valid syntax under an older JS target before it ships to older runtimes.
Under The Hood
Architecture
The library is built as a strict pipeline: reader.ts walks raw source as character codes, validator.ts’s RegExpValidator scans that stream against the ECMAScript grammar as an ecma-version-gated state machine with no AST allocation, and parser.ts’s RegExpParser extends the validator’s scanning to build a typed AST (ast.ts) as it goes — so the correctness-critical grammar logic lives in exactly one place and parsing reuses it rather than re-implementing it. visitor.ts then walks the resulting tree via paired enter/leave handlers, and group-specifiers.ts/ecma-versions.ts isolate the version-specific quirks (named group re-declaration rules, Annex B legacy syntax) from the rest of the pipeline. index.ts collapses all of this into three convenience functions for the common case. The tight coupling between parser and validator (inheritance, not composition) means changes to the core grammar carry a large blast radius across both.
Tech Stack
Written in TypeScript (~5.0) with zero runtime dependencies declared in package.json. Builds via tsc plus Rollup (rollup-plugin-sourcemaps, @rollup/plugin-node-resolve) into dual CJS/ESM output (index.js/index.mjs), with .d.ts types bundled separately via dts-bundle. Ships targeting Node ^12 || ^14 || >=16. Linting runs through the org’s own @eslint-community/eslint-plugin-mysticatea config, closing the loop of the tool linting itself.
Code Quality
Tests run via Mocha with nyc coverage over an extensive fixture set in test/fixtures/parser, where test/parser.ts asserts every fixture tagged -valid parses cleanly and every one tagged -invalid throws — plus dedicated suites for validator errors and visitor traversal. A scripts/extract-test262.ts script pulls conformance fixtures directly from the official ECMAScript test262 suite to keep the validator aligned with the spec rather than relying on hand-written cases alone. CI runs on every push (ci.yml) with a separate scheduled workflow (cron.yml) for keeping Unicode data tables current. Errors are surfaced as a dedicated RegExpSyntaxError type rather than generic throws.
API Design
The public surface is intentionally minimal — three top-level functions cover the 90% case with zero configuration, while the underlying RegExpParser/RegExpValidator/RegExpVisitor classes are available for incremental parsing (start/end offsets), ECMAScript-version-gated strictness, or custom AST traversal via structured handler objects mirroring the ESTree visitor pattern ESLint rule authors already know. Every function’s parameters and return shape are documented inline in the README with links straight to the defining source line.