mrkeyoor.com_
Sat 08 Aug 22:01 UTC
npmUtilsupdated 08 Aug 2026

regexp-to-ast

regexp-to-ast parses the text form of an older JavaScript regular-expression literal, including its slash delimiters and flags, into a compact AST. The tree represents alternatives, assertions, groups, backreferences, character sets, code-unit values, and quantifier bounds, with numeric source offsets on every node. It also ships a base visitor for walking the result. It analyzes syntax only; it does not execute, optimize, validate safety, or regenerate a RegExp.

Verdict

Its tiny AST and visitor remain understandable, but the accepted grammar stopped before several now-common JavaScript RegExp features. Keep it for compatible legacy consumers; choose @eslint-community/regexpp or regjsparser for new analysis tools.

API stability4/5The public surface is only RegExpParser, BaseRegExpVisitor, VERSION, and a compact set of declared node shapes. Version 0.5.0 added loc ranges without otherwise disrupting the model, and no later release has changed it. Existing consumers of the supported subset should see deterministic trees. The missing point comes from a declaration mismatch where VERSION is typed as number but exported as a string, plus a grammar whose frozen shape cannot represent newer syntax.
Docs3/5The README shows parsing, parser reuse, and a complete visitor subclass, links directly to the TypeScript API, states ES5 compatibility, and honestly lists gaps in Unicode handling and diagnostics. The declaration file clearly enumerates nodes and fields. Documentation loses points because it does not define literal-input escaping, offset semantics, visitor traversal order, Infinity serialization, supported ECMAScript edition, or examples for sets and quantifiers, and its browser script path names a file absent from the published tarball.
Maintenance1/5npm shows 0.5.0 published in December 2019. GitHub's most recent default-branch commits returned by the repository API are from February 2020 and concern examples or development dependencies, while the repository push timestamp is June 2023. The project is not archived and still has 28 issues and pull requests combined, but no release has delivered the README's listed Unicode, error-message, position-error, octal, or edge-case work.
Ecosystem2/5The package recorded 4,086,142 downloads in the measured week and is dependency-free, so it remains common inside larger parser and tooling dependency graphs. Deliberate adoption signals are much smaller: the repository has 28 stars, exposes one bespoke AST rather than ESTree, and provides no generator, transformer plugins, safety rules, or integrations. Modern lint and compiler tools more often converge on regexpp-family or regjsparser ASTs.

Use it if

  • You maintain a tool that already consumes this package's small, stable AST shape
  • Your input is limited to the pre-lookbehind JavaScript RegExp subset documented by version 0.5.0
  • You need ES5-style browser code with no runtime dependencies and can load the actual distributed UMD file
  • You want a simple subclassable visitor and numeric begin/end offsets without a larger ESTree-compatible parser
Skip it if

Setup reality

npm install regexp-to-ast has no runtime dependencies, native build, peer dependency, credential, or configuration step. In CommonJS, require the package and construct RegExpParser. The parser does not accept a bare source such as a|b; pattern expects literal text beginning with / and ending with / plus flags. A RegExp instance is not accepted directly, so call toString() first. The same parser instance can be reused sequentially because each pattern call resets its mutable index, input, and capture counter, but do not share one instance across overlapping asynchronous work. Version 0.5.0 recognizes only g, i, m, u, and y flags, throws on duplicates, and rejects trailing input. Its TypeScript declaration describes the shipped AST well, but VERSION is incorrectly declared as number even though runtime exports the string "0.5.0". Characters and set members are numeric UTF-16 code units; the README warns that characters outside the Basic Multilingual Plane need unfinished support. Quantifier infinity is represented by JavaScript Infinity, which becomes null if you JSON.stringify the AST. The base visitor calls your type-specific method and then always traverses children, including quantifiers attached to atoms. Override visitChildren if you need pruning. Browser use relies on the UMD build at lib/regexp-to-ast.js and creates window.regexpToAst; the README's lib/parser.js path does not exist in the tarball. There is no parser option object, recovery mode, custom grammar, AST generator, or safety analysis for catastrophic backtracking.

Patterns

Parse regular-expression literal textparse-literal

const { RegExpParser } = require('regexp-to-ast');
const parser = new RegExpParser();

const ast = parser.pattern('/^(cat|dog)s?$/i');
console.log(ast.type); // Pattern

Input must include opening and closing slashes plus any flags. A bare source string such as ^cat$ is rejected immediately.

Parse an existing RegExp objectparse-regexp-object

const expression = /a|b/gi;
const ast = parser.pattern(expression.toString());

pattern accepts only text. RegExp.prototype.toString supplies escaped slash delimiters and flags in the format the parser expects.

Reuse one parser sequentiallyreuse-parser

const inputs = ['/one/', '/(two)+/g', '/three|four/i'];
const trees = inputs.map((input) => parser.pattern(input));

Each call resets mutable parser state. Sequential reuse is documented; do not interleave calls by wrapping or extending the parser with asynchronous callbacks.

Inspect parsed flagsread-flags

const { flags } = parser.pattern('/name/gimuy');
console.log({
  global: flags.global,
  ignoreCase: flags.ignoreCase,
  multiLine: flags.multiLine,
  unicode: flags.unicode,
  sticky: flags.sticky,
});

These are the only accepted flags. Inputs containing s, d, or v are rejected as redundant trailing input.

Read top-level alternativesinspect-alternatives

const ast = parser.pattern('/red|green|blue/');
const alternatives = ast.value.value;
console.log(alternatives.length); // 3
console.log(alternatives.map((alt) => alt.type));

Pattern.value is a Disjunction, and Disjunction.value is an array of Alternative nodes even when there is only one branch.

Convert character nodes back to textdecode-characters

const ast = parser.pattern('/abc/');
const terms = ast.value.value[0].value;
const text = terms
  .filter((node) => node.type === 'Character')
  .map((node) => String.fromCharCode(node.value))
  .join('');
console.log(text); // abc

Character.value stores a UTF-16 code unit, not a string or full Unicode code point. Non-BMP characters are a documented limitation.

Expand character set entriesinspect-character-set

const ast = parser.pattern('/[^a-c0-2]/');
const set = ast.value.value[0].value[0];

for (const item of set.value) {
  if (typeof item === 'number') console.log('code unit', item);
  else console.log('range', item.from, item.to);
}
console.log('negated', set.complement);

Set values mix numeric code units and { from, to } ranges. Expanded escapes such as \d can add many numeric entries.

Inspect repetition bounds and greedinessread-quantifier

const ast = parser.pattern('/a{2,}?/');
const atom = ast.value.value[0].value[0];
console.log(atom.quantifier);
// { type: 'Quantifier', atLeast: 2, atMost: Infinity, greedy: false, loc: ... }

Unbounded maxima use Infinity. JSON.stringify turns Infinity into null, so encode it explicitly before persisting an AST.

Collect numbered capture groupsfind-capturing-groups

const { BaseRegExpVisitor } = require('regexp-to-ast');

class CaptureVisitor extends BaseRegExpVisitor {
  constructor() {
    super();
    this.groups = [];
  }
  visitGroup(node) {
    if (node.capturing) this.groups.push(node.idx);
  }
}

const visitor = new CaptureVisitor();
visitor.visit(parser.pattern('/(a)(?:b)(c)/'));
console.log(visitor.groups); // [1, 2]

Only numbered captures exist in this AST. The base visitor traverses children automatically after visitGroup returns.

Count every AST node typecount-node-types

class CountingVisitor extends BaseRegExpVisitor {
  constructor() {
    super();
    this.counts = Object.create(null);
  }
  visit(node) {
    this.counts[node.type] = (this.counts[node.type] || 0) + 1;
    super.visit(node);
  }
}

const visitor = new CountingVisitor();
visitor.visit(parser.pattern('/a+(b|c)/'));
console.log(visitor.counts);

Call super.visit(node) or traversal stops at the current node. Quantifiers are child nodes and receive their own visits.

Map a node back to input textslice-source-location

const input = '/foo(bar)+/g';
const ast = parser.pattern(input);
const group = ast.value.value[0].value[3];
console.log(input.slice(group.loc.begin, group.loc.end)); // (bar)+

loc uses zero-based begin and exclusive end offsets into the complete literal text, including delimiters and flags. An atom's range includes its attached quantifier.

Load the actual browser bundleload-in-browser

<script src="https://unpkg.com/regexp-to-ast@0.5.0/lib/regexp-to-ast.js"></script>
<script>
  const parser = new regexpToAst.RegExpParser();
  console.log(parser.pattern('/hello/i'));
</script>

The README shows lib/parser.js, but that file is absent from 0.5.0. The distributed UMD file is lib/regexp-to-ast.js and exposes regexpToAst globally.

Alternatives

PackageRegistryPick it when
@eslint-community/regexppnpmUse a maintained ECMAScript parser when current flags, named groups, lookbehind, Unicode properties, and lint tooling matter
regjsparsernpmUse the parser from the Babel and regjs stack when broad modern JavaScript RegExp syntax coverage is the priority
regexp-treenpmUse a parser plus traversal, transformation, optimization, and generation toolkit rather than an AST reader alone
regexppnpmUse the older standalone ECMAScript parser only when an existing dependency requires its established API; prefer the community fork for new work