# meriyah: an ESTree JavaScript parser with no backtracking

> meriyah is a self-hosted JavaScript parser written in TypeScript that emits an ESTree-compatible AST, conforms to the ECMAScript 2024 specification, and parses with no backtracking and low memory usage. It does not handle TypeScript or Flow syntax, and its RegExp support is delegated to the JavaScript runtime rather than implemented natively.

**meriyah/meriyah** — A 100% compliant, self-hosted javascript parser  - https://meriyah.github.io/meriyah

- Repository: https://github.com/meriyah/meriyah
- Stars: 1,221 · Forks: 65
- Language: TypeScript
- License: ISC
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/meriyah-meriyah

## What the feature list rules out

The README's own description is a 100% compliant, self-hosted javascript parser with high focus on both performance and stability, described as stable and already used in production.

The feature list is short enough that every entry is a decision.

It conforms to the standard ECMAScript 2024, ECMA-262 15th Edition, language specification. It supports some TC39 stage 3 proposals via the option next. It has support for additional ECMAScript features for Web Browsers, which is Annex B, the legacy annex that keeps old behaviours alive for compatibility with the web. JSX support is available via the option jsx. It tracks syntactic node locations with the option ranges or loc. It emits an ESTree-compatible abstract syntax tree.

Two entries are about what the parser will not do, and they are the ones to read twice. There is no backtracking, and memory usage is low.

And then the line that disqualifies it for a large part of the ecosystem: it does not support TypeScript or Flow syntax.

That is not a missing feature, it is a scope decision. Meriyah is a JavaScript parser. If your tool needs to handle .ts files, the typescript compiler's own parser is the tool, not this one.

The topic list names acorn, tc39, esnext, estree and typescript, which together describe the neighbourhood: Meriyah sits next to Acorn in the space of small, fast, spec-compliant JavaScript parsers, and it is judged against the TC39 test262 suite.

## Stage 3 proposals behind a single next flag

The stage 3 support is opt-in, and the README is explicit that these features need to be enabled with the next option.

Three proposals are listed as supported.

Decorators, from the TC39 decorators proposal. Source Phase Imports, from the source-phase-imports proposal. And Deferring Module Evaluation, from the defer-import-eval proposal.

Source phase imports are the one that changes parser architecture rather than just adding a syntax form. An import statement gains a source phase, meaning the module's source text is available as a string at runtime rather than being evaluated. That is a parsing problem and an evaluation problem, and a parser that does not know about it will simply fail on the syntax.

Deferring module evaluation works the same way at a different layer, since a module can declare that its evaluation is deferred until an explicit trigger.

The important point for an adopter is that the next flag is a single boolean covering three proposals, not a per-proposal switch. If your build needs decorators but must reject source phase imports for some reason, this option does not give you that granularity, and the option list in the README does not mention per-feature flags.

The result is a parser that tracks the language boundary closely. It is at ECMAScript 2024, and the three stage 3 proposals it opts into are the ones that have had a settled design for a while. The cost is that a project proposing a syntax Meriyah has not implemented, or one that has advanced past stage 3, will hit a parse error rather than a warning.

## RegExp is validated by the runtime, not by the parser

The RegExp support section is the most honest part of the README, and it is a real limitation rather than a caveat.

Meriyah doesn't parse RegExp internal syntax, because the ESTree spec didn't require internal structure of RegExp. Meriyah does use the JavaScript runtime to validate the RegExp literal by default. That means Meriyah's RegExp support is only as good as JavaScript runtime's RegExp support.

So the parser does not know what is inside a regular expression literal. It hands the literal to the host engine and asks whether the host accepts it. The consequence is that a program using a regular expression feature your Node version does not yet support will fail to parse, and the error comes from the runtime rather than from Meriyah.

There is a documented escape. Use validateRegex: false if you want consistent behavior in different environments or don't need errors for invalid RegExp. The option defaults to true.

That default is a defensible choice, since a parser that silently accepts a broken regular expression is worse than one that rejects it. But it means the parser's behaviour is not purely a function of its own code, it is a function of the runtime underneath. For a build tool that runs on several Node versions, that is a difference in behaviour across environments.

The README also notes that as of May 2025, some latest RegExp features require Node.js at or above 24. The two features named are RegExp modifiers and RegExp duplicate named capturing groups.

## One options object, and a ranges flag that grew teeth

The parse method takes an optional options object, and the full list is in the README as a TypeScript block.

The mode is sourceType, which is script, module or commonjs, defaulting to script. Stage 3 support is the next boolean. Line and column information comes from loc, and the raw property on literals and identifiers from raw. Comment extraction is onComment, which accepts either a function or an array, and token extraction is onToken with the same shape. Automatic semicolon insertion is reported through onInsertedSemicolon. There is a webcompat flag, an impliedStrict flag, a preserveParens flag for the non-standard parenthesized expression node, a lexical flag for lexical binding and scope tracking, a source string, and the jsx flag.

The ranges option is the one with a story. It used to be a boolean, and it can still be one, where true means emit start, end and the range array, which is described as backwards compatible. But it also accepts an object that selectively enables start, end and range, for example { start: true, end: true }. That is a small API addition with a real effect, since a tool that only wants offsets can now avoid materialising the range array, and one that wants the array can still have it.

The distinction between the callback and array forms of onComment and onToken is documented with the same care. If an array is supplied, comments and tokens are pushed to it, and the item contains start, end and range information when the ranges flag is true, and loc information when the loc flag is true. If a function callback is supplied, the start, end and loc information are provided to the function callback regardless of the settings on the ranges and loc flags. The onComment callback has one extra argument, the value string for the body of the comment.

## Error handling with isParseError, so a real crash stays a crash

There is one exported helper that tells you about the error model, and it is small but important.

The isParseError function is exposed for error instance checking.

```js
import { parse, isParseError } from './meriyah';

try {
  parse('invalid code');
} catch (error) {
  if (isParseError(error)) {
    console.error(error.description);
  } else {
    throw error;
  }
}
```

The pattern is the point. A parse failure is an expected outcome of parsing untrusted or user-supplied code, so it gets its own error type with a description property, and the else branch rethrows anything that is not a parse error. That is the correct structure for a parser used as a filter, where a malformed file is a normal event and an out-of-memory or a bug in your own code is not.

The example usage section then shows the shape of the output. Parsing the destructuring assignment '({x: [y] = 0} = 1)' returns a Program node with sourceType script and a body containing an ExpressionStatement whose expression is an AssignmentExpression with an ObjectPattern on the left, and the properties array holds a Property node.

That example is not arbitrary. A destructuring assignment nested inside parentheses with a default value and an array pattern is exactly the kind of construct where a hand-written parser is likely to get the grammar wrong, and the output shows the ESTree node types are the ones the rest of the JavaScript tooling ecosystem already expects.

## The toolchain around the parser

The package.json and the repository layout show how the project is built and checked, which matters if you are depending on it.

The package is meriyah at version 7.4.0, with a main pointing at a CommonJS bundle, a module field at a minified ESM bundle, browser and unpkg fields at the minified UMD build, and types at dist/types/meriyah.d.ts. The exports map gives types, module-sync, import, require and default entries, so both module systems and bundlers resolve the right artefact.

The lint script is a chain of six checks: eslint, then tsc for types, then prettier --check, then cspell with --gitignore, then knip for unused exports and dependencies, then a unicode check that runs node scripts/generate-unicode.mjs --check. That last one is interesting, and it is the kind of check a parser project needs. The Unicode tables in a JavaScript parser are versioned data, and a script that regenerates and verifies them is how you know the parser's idea of the Unicode version matches the one it claims.

The repository has a test262/ directory, which is the TC39 test suite, and the presence of a dedicated directory for it is a stronger signal about conformance than the 100% compliant claim in the description.

There are also knip.json, cspell.json, eslint.config.mjs, prettier.config.mjs, _config.yml, .husky/ and a types/ directory, plus CHANGELOG.md, which is where you look when a version bump changes parsing behaviour.

## Release cadence and the stability of the 7.x line

The release history is recent and steady. v7.4.0 was tagged on 2026-10-05, v7.3.3 on 2026-09-13 and v7.3.2 on 2026-08-17, and the last push came on 2026-10-17.

The version number is at 7, which is worth pausing on. A parser that has gone through seven major versions has a history of changing its output shape, and for a tool that consumes the AST, a major bump is a migration event.

The README's stability claim, that it is stable and already used in production, is a claim about the library's behaviour rather than about its API surface being frozen. The practical consequence for an adopter is that you should read the CHANGELOG.md between the version you are on and the version you are moving to, and you should treat a major version bump as something that needs a test run against your own fixtures.

The licence is ISC, in LICENSE.md, which is a short permissive licence with no copyleft obligation, so vendoring the parser into a product that ships to browsers is straightforward. The author is listed as Kenny F., with Chunpeng Huo as a contributor.

The playground and the benchmark page are both hosted at meriyah.github.io/meriyah, and the benchmark link is the place to check the parser's throughput on your own shape of input rather than trusting a number from elsewhere.

## Conclusion

meriyah suits a tool that needs to parse JavaScript itself, such as a linter, a transformer or a code analysis service, and specifically suits one that already speaks ESTree and wants a parser with predictable performance and no backtracking. It is the wrong tool if you need TypeScript or Flow syntax, since the README states plainly that it does not support them, and it is the wrong choice if you need regular expression internals parsed, because that is delegated to the host runtime. Before adopting it, check the test262/ directory in the repository to see how much of the specification suite it runs, and decide about validateRegex first, since the default behaviour ties your parser's error reporting to the Node version you happen to run.

## FAQ

### Does meriyah support TypeScript or Flow syntax?

No. The README states in the feature list that meriyah does not support TypeScript or Flow syntax. It parses JavaScript, with optional JSX via the jsx option.

### How do I install meriyah?

Run npm install meriyah --save-dev. The package is published as meriyah at version 7.4.0 with CommonJS, ESM and UMD builds and TypeScript declarations.

### How does meriyah handle regular expression literals?

It does not parse RegExp internal syntax, and by default it uses the JavaScript runtime to validate the literal, so its RegExp support is only as good as the runtime's. Set validateRegex to false for consistent behaviour across environments.

### Which stage 3 JavaScript proposals does meriyah support?

Decorators, Source Phase Imports and Deferring Module Evaluation, all of which need to be enabled with the next option. The next flag is a single boolean covering all three.

### How do I tell a parse error apart from other exceptions in meriyah?

Use the exported isParseError helper, which is exposed for error instance checking. A parse error has a description property, and any error that is not a parse error should be rethrown.

## Sources

- [Issues](https://github.com/meriyah/meriyah/issues)
- [License: ISC](https://github.com/meriyah/meriyah/blob/main/LICENSE)
- [meriyah/meriyah on GitHub](https://github.com/meriyah/meriyah)
- [README](https://github.com/meriyah/meriyah/blob/main/README.md)
- [Releases](https://github.com/meriyah/meriyah/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/meriyah-meriyah
