mrkeyoor.com_
Wed 23 Sept 00:37 UTC
npmUtilsupdated 22 Sept 2026

regexp-to-ast review

regexp-to-ast 0.5.0 parses JavaScript regular-expression literal text, including the surrounding slashes and supported flags, into its own small AST. Nodes represent disjunctions, alternatives, anchors, lookaheads, characters, sets, numbered groups, backreferences, and quantifiers; a base visitor walks the tree. The 0.5.0 release added zero-based `loc.begin` and exclusive `loc.end` offsets to every AST node. The parser does no matching, code generation, ReDoS analysis, or optimization. Its grammar predates named groups, lookbehind, Unicode property escapes, and the `s`, `d`, and `v` flags now found in current JavaScript.

Verdict

regexp-to-ast 0.5.0 installed in 0.5 seconds and bundled to 3.2 KB gzipped in our sandbox, with both module loaders working and no audit findings. Its frozen pre-modern grammar makes sense for compatible legacy AST consumers; new JavaScript regex tooling should start with `@eslint-community/regexpp` or regjsparser.

We installed it

Lab card: what happened when we installed regexp-to-astScreenshot of regexp-to-ast documentation
Install✓ · 0.5s1 package on disk · 1 MB
ImportESM import works · require() works · CommonJS package
Browser3.2 KBgzipped (12.1 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does regexp-to-ast install cleanly?

Yes. In a fresh container with an empty cache, npm install regexp-to-ast finished in 0.5s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does regexp-to-ast add to a browser bundle?

3.2 KB gzipped (12.1 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does regexp-to-ast work with both ESM and CommonJS?

Yes. Both import 'regexp-to-ast' and require('regexp-to-ast') worked in Node 22 in our run. The package is published as CommonJS.

Does regexp-to-ast include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

regexp-to-ast or @eslint-community/regexpp: which should you use?

@eslint-community/regexpp: Choose it for a maintained ECMAScript grammar with modern flags, groups, lookbehind, Unicode properties, and lint-tool use. regexp-to-ast 0.5.0 installed in 0.5 seconds and bundled to 3.2 KB gzipped in our sandbox, with both module loaders working and no audit findings.

When should you not use regexp-to-ast?

Inputs can contain modern syntax. The flag parser accepts only g, i, m, u, and y, and the AST cannot represent named captures, lookbehind, Unicode properties, or Unicode sets.

API stability4/5The exports are `RegExpParser`, `BaseRegExpVisitor`, `VERSION`, and a concise set of node interfaces. Release 0.5.0 added source locations without replacing existing node shapes, and no later package has moved them. Consumers of the accepted grammar get a predictable tree. The last point is lost because the declaration says `VERSION` is numeric while runtime returns a string, and the stable node model has no extension point for current syntax.
Docs3/5The README demonstrates literal parsing, RegExp conversion, parser reuse, and a full visitor subclass. The declaration file lists each node field, and the limitations section admits unfinished Unicode and diagnostic work. It never specifies the ECMAScript edition, exact location boundaries, Infinity serialization, or rejected modern constructs. Its browser installation path is also wrong, which is a concrete setup failure for anyone copying the example.
Maintenance1/5npm published 0.5.0 on December 11, 2019, and the changelog lists source locations as that release's only feature. GitHub's newest default-branch commits are from February 14, 2020 and update examples or development packages; the later 2023 push timestamp does not correspond to a release. The repository is unarchived and GitHub reports 28 issues and pull requests combined, with six issues on the first page after pull requests were removed.
Ecosystem2/5The npm endpoint counted 4,759,828 downloads in the latest completed week, so this package remains embedded in widely installed dependency graphs. Direct adoption signals are far smaller: GitHub has 28 stars, the AST is package-specific, and no generator, transform plugin, linter rules, or ESTree bridge is published with it. Current compiler and lint tooling is more likely to share regexpp or regjsparser conventions.

Use it if

  • An existing tool already stores or visits this package's AST and its regex inputs stay within the older supported grammar.
  • You need numeric source ranges for a compact parser whose entire public model is declared in one TypeScript file.
  • A dependency-free CommonJS parser and UMD browser global fit a legacy JavaScript environment.
  • Numbered capture groups and UTF-16 code-unit character values are sufficient for the analysis.
Skip it if

Setup reality

We installed regexp-to-ast 0.5.0 in a clean Node 22 sandbox in 0.5 seconds. npm left one package and 1 MB on disk; the package is 60 KB unpacked with zero direct and peer dependencies. It uses the MIT license, bundles a declaration file, and had zero known vulnerabilities in npm audit. CommonJS require() and ESM import both worked. Our esbuild browser result was 12.1 KB minified and 3.2 KB gzipped.

Construct RegExpParser and pass literal text such as /cat+/gi. A bare source string and a RegExp object are not accepted; call .toString() on an existing RegExp. Version 0.5.0 recognizes five flags and throws on duplicates or trailing characters. The package has no configuration file, credentials, native build, or runtime cache. One parser can handle consecutive calls because pattern() resets its input index and capture counter each time.

Characters and set members are stored as numeric UTF-16 code units. The README explicitly leaves non-BMP Unicode and Unicode-flag escapes unfinished, so an AST is not a faithful model for all u patterns. Unbounded quantifiers use JavaScript Infinity; ordinary JSON.stringify converts that number to null. Encode it deliberately before persisting trees. The declaration also types VERSION as a number even though runtime exports the string 0.5.0.

The base visitor dispatches to a node-specific method and then walks children, including a quantifier attached to an atom. Override visitChildren when pruning is needed; a callback return value does not stop descent by itself. For direct browser loading, use the UMD file lib/regexp-to-ast.js, which writes regexpToAst on the global object. The README's lib/parser.js URL points to no published file.

Patterns

Parse literal text into an AST parse-regex-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 includes both slash delimiters and any flags. Passing bare text such as `^cat$` fails at the first character.

Convert a RegExp object before parsing parse-regexp-instance

const expression = /red|blue/gi;
const ast = parser.pattern(expression.toString());

`pattern()` takes a string only. `RegExp.prototype.toString()` supplies delimiters and flags in the expected form.

Parse several literals with one instance reuse-parser-sequentially

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

Each synchronous call resets the input cursor and capture count before parsing the next literal.

Read the five flag booleans inspect-supported-flags

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

Only `g`, `i`, `m`, `u`, and `y` are accepted. The newer `s`, `d`, or `v` flag leaves redundant input and throws.

Inspect disjunction branches list-alternatives

const ast = parser.pattern('/red|green|blue/');
const branches = ast.value.value;
console.log(branches.length); // 3

`Pattern.value` is a Disjunction, whose `value` is always an array of Alternative nodes.

Turn code units back into text decode-character-nodes

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

Character values are UTF-16 code units. `String.fromCharCode` does not repair the package's documented non-BMP limitation.

Read a negated set and its ranges inspect-character-class

const set = parser.pattern('/[^a-c0-2]/').value.value[0].value[0];
for (const item of set.value) {
  if (typeof item === 'number') console.log('unit', item);
  else console.log('range', item.from, item.to);
}
console.log(set.complement);

Set contents mix individual numeric units with `{from, to}` objects; `complement` records the leading caret.

Preserve an unbounded quantifier serialize-quantifier-bounds

const atom = parser.pattern('/a{2,}?/').value.value[0].value[0];
const quantifier = {
  ...atom.quantifier,
  atMost: atom.quantifier.atMost === Infinity ? 'Infinity' : atom.quantifier.atMost,
};

The AST uses `Infinity` for an open upper bound. JSON would silently store that value as null without conversion.

Visit numbered capturing groups collect-capture-groups

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

class Captures extends BaseRegExpVisitor {
  groups = [];
  visitGroup(node) {
    if (node.capturing) this.groups.push(node.idx);
  }
}
const visitor = new Captures();
visitor.visit(parser.pattern('/(a)(?:b)(c)/'));

This AST has numbered captures only. Base visitor traversal continues into the group after `visitGroup` returns.

Count nodes while retaining traversal count-ast-node-types

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

Calling `super.visit(node)` is required here; omitting it stops dispatch and child traversal at that node.

Map a group back to literal text slice-node-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));

Release 0.5.0 added zero-based begin and exclusive end offsets over the complete literal string.

Use the file that actually ships load-umd-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 published UMD file creates `regexpToAst`. The README's `lib/parser.js` path does not exist in 0.5.0.

Alternatives

PackageRegistryPick it when
@eslint-community/regexppnpmChoose it for a maintained ECMAScript grammar with modern flags, groups, lookbehind, Unicode properties, and lint-tool use.
regjsparsernpmUse it when Babel-style tooling needs broad current syntax coverage and configurable feature flags.
regexp-treenpmChoose it when parsing must be followed by traversal, transforms, optimization, or generation.
regexppnpmKeep it only for consumers tied to the older package API; new work should compare its maintained community fork first.

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.