@chevrotain/regexp-to-ast review
`@chevrotain/regexp-to-ast` 13.2.0 turns a slash-delimited JavaScript-style regular expression into the AST used by Chevrotain's lexer analysis. The result records alternatives, groups, assertions, character sets, numeric backreferences, quantifiers, flags, and source offsets; a base visitor walks those nodes. Our install also showed why this is a specialist package: it is a 148 KB ESM-only module whose CommonJS entry fails, and its accepted regex dialect omits several newer ECMAScript features.
`@chevrotain/regexp-to-ast` 13.2.0 installed in 1.3 seconds and bundled to 2.9 KB gzipped in our sandbox, but `require()` failed and modern regex constructs are absent. Install it for Chevrotain AST compatibility; choose `@eslint-community/regexpp` for general ECMAScript regex tooling.
We installed it
| Install | ✓ · 1.3s | 1 package on disk · 1 MB |
| Import | ½ | ESM import works · require() fails · ESM package with exports map |
| Browser | 2.9 KB | gzipped (10.6 KB minified), bundled with esbuild |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does @chevrotain/regexp-to-ast install cleanly?
Yes. In a fresh container with an empty cache, npm install @chevrotain/regexp-to-ast finished in 1 seconds, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.
How much does @chevrotain/regexp-to-ast add to a browser bundle?
2.9 KB gzipped (10.6 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.
Does @chevrotain/regexp-to-ast work with both ESM and CommonJS?
ESM only. import '@chevrotain/regexp-to-ast' worked, require('@chevrotain/regexp-to-ast') failed in our run, so CommonJS projects need a dynamic import or a build step.
Does @chevrotain/regexp-to-ast include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
@chevrotain/regexp-to-ast or @eslint-community/regexpp: which should you use?
@eslint-community/regexpp: Use it when current ECMAScript regex parsing and validation matter more than matching Chevrotain's private tree shape. @chevrotain/regexp-to-ast 13.2.0 installed in 1.3 seconds and bundled to 2.9 KB gzipped in our sandbox, but require() failed and modern regex constructs are absent.
When should you not use @chevrotain/regexp-to-ast?
Your tool must understand current ECMAScript regex syntax. Version 13.2.0 does not model the d, s, or v flags, named captures, named backreferences, Unicode property escapes, or Unicode set operations.
Use it if
- You are extending Chevrotain lexer analysis and must consume the exact regex tree that the parent project expects.
- Your inputs stay within numbered captures, classic sets, lookarounds, quantifiers, backreferences, and the `gimuy` flag set.
- A dependency-free parser with source offsets and a recursive visitor is enough, and you can treat the declarations and tests as the API reference.
- Your tool must understand current ECMAScript regex syntax. Version 13.2.0 does not model the `d`, `s`, or `v` flags, named captures, named backreferences, Unicode property escapes, or Unicode set operations.
- Your runtime loads dependencies with `require()`. Node 22.23.2 rejected the package in our sandbox because its exports map only supplies an ESM import target.
- You expect a maintained standalone manual. The subpackage ships no README or syntax table, and the monorepo documentation describes Chevrotain rather than this AST contract.
- Your caller holds `RegExp` objects or bare source strings. `RegExpParser.pattern()` expects a string containing the opening slash, closing slash, and any flags.
- You only need to declare Chevrotain tokens. The main `chevrotain` package accepts regular expressions directly, so importing this internal analysis layer adds an AST contract you do not need.
Setup reality
We installed @chevrotain/regexp-to-ast 13.2.0 in 1.3 seconds on Node 22 Bookworm. The clean install left 1 package and 1 MB on disk. The published package is 148 KB unpacked, declares 0 direct and 0 peer dependencies, bundles TypeScript declarations, and produced 0 npm audit findings. Our browser build measured 10.6 KB minified and 2.9 KB gzipped.
Module loading is the first hard boundary. This package has type: module and an exports map with an import condition only. ESM import worked under Node 22.23.2, while require() failed. Migrate the caller to ESM or choose another parser; a compatibility wrapper cannot make Node resolve a CommonJS target that the package does not publish.
pattern() takes text shaped like '/[a-z]+/i', not a RegExp instance and not '[a-z]+'. Locations are offsets into that complete input. Character nodes store numeric UTF-16 values, ranges use {from, to}, and an open quantifier uses Infinity. Convert that value before JSON serialization because JSON writes non-finite numbers as null. Parse errors arrive as synchronous exceptions rather than a list of recoverable diagnostics.
Version 13.2.0 recognizes 5 flags: g, i, m, u, and y. Its node declarations do not cover named groups, property escapes, dotAll, indices, or Unicode Sets. The base visitor descends after each specialized hook, so override visitChildren() when traversal must stop at a node. Pin the package if stored data depends on exact node shapes; releases follow the larger Chevrotain monorepo and there is no separate AST compatibility policy.
Patterns
Parse a complete regex literal parse-regex-literal
import { RegExpParser } from '@chevrotain/regexp-to-ast';
const parser = new RegExpParser();
const ast = parser.pattern('/^[a-z]+$/i');
console.log(ast.type, ast.flags.ignoreCase);`pattern()` requires slash delimiters and trailing flags in the string. Version 13.2.0 does not accept a `RegExp` object or bare `.source` value.
Enumerate top-level branches inspect-alternatives
const ast = parser.pattern('/cat|dog|fox/');
const alternatives = ast.value.value;
console.log(alternatives.length); // 3A Pattern contains a Disjunction, whose value is the Alternative array. Groups and lookarounds add nested Disjunction nodes.
Recover text from node offsets read-source-locations
const input = '/ab+c/';
const ast = parser.pattern(input);
const first = ast.value.value[0].value[0];
console.log(input.slice(first.loc.begin, first.loc.end)); // aThe 0-based offsets refer to the full slash-delimited string, including the opening delimiter, rather than `RegExp.source`.
Collect literal character values visit-every-character
import { BaseRegExpVisitor } from '@chevrotain/regexp-to-ast';
import type { Character } from '@chevrotain/regexp-to-ast';
class CharacterCollector extends BaseRegExpVisitor {
values: number[] = [];
visitCharacter(node: Character) { this.values.push(node.value); }
}
const visitor = new CharacterCollector();
visitor.visit(parser.pattern('/abc/'));
console.log(visitor.values.map(String.fromCharCode));Each Character node holds a numeric UTF-16 code unit. The base visitor continues into children after it calls a specialized hook.
Report members of a character set inspect-character-ranges
import type { Set } from '@chevrotain/regexp-to-ast';
class SetReporter extends BaseRegExpVisitor {
visitSet(node: Set) {
console.log({ complement: node.complement, entries: node.value });
}
}
new SetReporter().visit(parser.pattern('/[^a-z0-9]/'));Ranges use `{from, to}` and single entries are numbers. Escapes such as `\d` are expanded into entries rather than retained as source tokens.
Read repetition limits and greediness inspect-quantifiers
import type { Quantifier } from '@chevrotain/regexp-to-ast';
class QuantifierReporter extends BaseRegExpVisitor {
visitQuantifier(node: Quantifier) {
console.log(node.atLeast, node.atMost, node.greedy);
}
}
new QuantifierReporter().visit(parser.pattern('/a+?b{2,}/'));An unlimited `atMost` is `Infinity`. Map it to a string or sentinel before JSON serialization, which otherwise turns it into `null`.
Count lookaround assertions find-lookarounds
import type { Assertion } from '@chevrotain/regexp-to-ast';
class LookaroundCounter extends BaseRegExpVisitor {
count = 0;
visitLookahead(_node: Assertion) { this.count += 1; }
visitNegativeLookahead(_node: Assertion) { this.count += 1; }
visitLookbehind(_node: Assertion) { this.count += 1; }
visitNegativeLookbehind(_node: Assertion) { this.count += 1; }
}
const visitor = new LookaroundCounter();
visitor.visit(parser.pattern('/(?<=a)b(?!c)/'));
console.log(visitor.count); // 2Each lookaround owns a nested Disjunction. Version 13.2.0 handles these 4 assertion kinds but does not parse named captures.
List numbered capture indexes inspect-capturing-groups
import type { Group } from '@chevrotain/regexp-to-ast';
class GroupCollector extends BaseRegExpVisitor {
groups: number[] = [];
visitGroup(node: Group) {
if (node.capturing && node.idx !== undefined) this.groups.push(node.idx);
}
}
const visitor = new GroupCollector();
visitor.visit(parser.pattern('/(a)(?:b)(c)/'));
console.log(visitor.groups); // [1, 2]Capturing groups receive numeric indexes. A noncapturing group has `capturing: false` and no `idx`; named capture fields do not exist.
Limit input to the supported flags validate-supported-flags
function parseSupported(input: string) {
if (!/^\/(?:\\.|[^/])*\/[gimuy]*$/.test(input)) {
throw new Error('Expected a simple /pattern/gimuy literal');
}
return parser.pattern(input);
}The guard deliberately accepts only 5 flags and does not solve every escaped-slash case. The parser rejects `d`, `s`, and `v`.
Convert a thrown parse failure handle-parse-error
try {
const ast = parser.pattern(candidate);
analyze(ast);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.error({ candidate, message });
}The parser throws synchronously at the first invalid construct. It does not return error codes, recover an AST, or collect several diagnostics.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @eslint-community/regexpp | npm | Use it when current ECMAScript regex parsing and validation matter more than matching Chevrotain's private tree shape. |
| regexp-tree | npm | Use it for parsing, traversal, transformation, optimization, and printing in one toolkit. |
| regexpp | npm | Use it only when an older ESLint integration already depends on its established AST format. |
| chevrotain | npm | Use the parent package when you are building a lexer or parser rather than inspecting regex syntax directly. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

