mrkeyoor.com_
Sat 08 Aug 21:02 UTC
npmUtilsupdated 08 Aug 2026

@chevrotain/regexp-to-ast

`@chevrotain/regexp-to-ast` parses a JavaScript-regex-like literal string into the compact syntax tree Chevrotain uses to analyze lexer token patterns. Its tree represents alternatives, characters, character sets and ranges, capturing or noncapturing groups, numeric backreferences, assertions, lookarounds, quantifiers, source offsets, and the `g`, `i`, `m`, `u`, and `y` flags. It also exports a recursive base visitor with one hook per node kind. It does not execute regexes, build a Chevrotain grammar, or cover every current ECMAScript regular-expression feature.

Verdict

Install this directly only when compatibility with Chevrotain's own regex AST is the point. For general JavaScript regex tooling, its undocumented subset and missing modern syntax make `@eslint-community/regexpp` or `regexp-tree` the safer choice.

API stability3/5The public export surface is only RegExpParser and BaseRegExpVisitor, and the node interfaces are explicit in one declaration file. That simplicity helps. However, the package has no standalone compatibility statement, its AST is primarily an implementation tool for the parent lexer, and the model visibly lags modern ECMAScript syntax. Consumers that switch exhaustively on node types or flags should expect source changes when missing syntax is eventually added.
Docs2/5The shipped types clearly enumerate node shapes, locations, flags, assertions, groups, sets, ranges, quantifiers, and visitor hooks, and the repository tests provide exact AST examples. There is no package README, usage tutorial, dedicated API page, supported-syntax table, or explicit limitations list. The monorepo README explains Chevrotain as a whole but never teaches this subpackage's required slash-delimited input or ESM-only import.
Maintenance5/5Version 13.2.0 was published on 2026-08-01, and the Chevrotain repository was pushed on 2026-08-07. GitHub reported 29 open issues and pull requests across the monorepo, which has active CI and coordinated package releases. The subpackage has dependency-free source, tests for each supported AST construct, and current TypeScript declarations. Maintenance is strong even though most decisions are driven by the parent lexer.
Ecosystem3/5npm recorded 4,377,249 downloads for 2026-07-31 through 2026-08-06, but this is largely transitive use through Chevrotain rather than broad direct adoption. The parent toolkit has 2,793 GitHub stars and is used by Langium, Prettier Java, HyperFormula, and JHipster tooling. Direct consumers get no plugins or standalone community documentation, and the AST contract is specific to Chevrotain rather than ESTree.

Use it if

  • You extend Chevrotain's lexer analysis and need the same AST shapes and character-set expansion that Chevrotain expects internally
  • You analyze a controlled regex subset limited to numbered groups, classic character classes, lookarounds, quantifiers, and `gimuy` flags
  • You want a small dependency-free AST plus a subclassable visitor and are comfortable treating the TypeScript declaration and tests as the API reference
Skip it if

Setup reality

There are no runtime dependencies, peers, native builds, or configuration files, and TypeScript declarations ship in the package. Importing is the first trap: version 13.2.0 is ESM-only and exposes an `import` export but no CommonJS target, so `require('@chevrotain/regexp-to-ast')` is unsupported. The parser accepts a string formatted like a regex literal, including opening and closing slashes; passing `/abc/` as a RegExp object or passing only `'abc'` fails. The output uses numeric UTF-16 character values and `{from, to}` ranges, with `loc.begin` and `loc.end` offsets into the input string. Infinity represents an unbounded quantifier, which needs special handling before JSON serialization because JSON turns Infinity into null. The supported flag model is only global, ignoreCase, multiLine, unicode, and sticky. Source inspection shows no parser paths for dotAll, hasIndices, Unicode Sets, named captures, named backreferences, Unicode property escapes, or braced Unicode code points, so validate the accepted dialect before using this with arbitrary user regexes. Error messages are synchronous parser errors rather than a structured diagnostic list. The base visitor automatically calls `visitChildren()` after a specialized hook; calling `super.visitCharacter()` is harmless but does not control traversal. Override `visitChildren()` if you must prune or extend traversal. Finally, pin the version when consuming AST shapes directly because this package is released as part of the larger Chevrotain monorepo and has no standalone compatibility guide.

Patterns

Parse a slash-delimited patternparse-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);

Pass a string containing both slashes and flags. A RegExp object or bare source string is not accepted by pattern().

Read top-level alternativesinspect-alternatives

const ast = parser.pattern('/cat|dog|fox/');
const alternatives = ast.value.value;
console.log(alternatives.length); // 3

The tree is Pattern -> Disjunction -> Alternative[]. Nested groups and lookarounds contain their own Disjunction nodes.

Map an AST node back to source textread-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)); // a

Locations are zero-based offsets into the complete slash-delimited input, not offsets into RegExp.source alone.

Collect literal character code unitsvisit-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));

Character values are numeric UTF-16 units. Specialized visitor hooks are followed automatically by recursive child traversal.

Read character-class rangesinspect-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]/'));

Range entries use `{ from, to }`; individual members are numbers. Predefined classes such as `\d` are expanded into set entries.

Find greedy and lazy quantifiersinspect-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,}/'));

Unbounded maximums are Infinity. Convert that explicitly before JSON.stringify, which otherwise serializes Infinity as null.

Detect lookahead and lookbehind assertionsfind-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); // 2

Lookaround nodes contain a Disjunction in `value`. Named capture syntax is not supported by this parser.

List numbered capturing groupsinspect-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]

Only numbered captures are represented. Noncapturing groups set capturing false and omit idx.

Reject syntax outside the supported flag setvalidate-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 is intentionally conservative and does not parse every valid slash occurrence. The package itself rejects `d`, `s`, and `v` flags.

Turn a parser exception into a diagnostichandle-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 });
}

Parsing stops at the first synchronous error. There is no structured error code, recovery mode, or list of diagnostics.

Alternatives

PackageRegistryPick it when
@eslint-community/regexppnpmChoose it for a parser maintained around current ECMAScript regular-expression syntax and validator behavior
regexp-treenpmChoose it when you need parsing plus traversal, transforms, optimization, and regeneration of regular expressions
regexppnpmChoose it when an existing ESLint-era integration already depends on its ESTree-like regex AST contract
chevrotainnpmChoose the parent toolkit when the actual goal is defining lexer tokens and parsing a language rather than inspecting regex syntax