Futuristic control room with holographic code displays and syntax trees

Meriyah: The JavaScript Parser That Chose Violence (Against Slow Parsing)

The Gray Cat
The Gray Cat

If you have ever built a linter, a code transformer, or any tool that needs to understand JavaScript source code, you know the first step is always the same: parse it into an Abstract Syntax Tree. The quality and speed of that parser determines how snappy your tool feels. meriyah is a self-hosted JavaScript parser that takes this responsibility seriously, delivering full ECMAScript 2024 compliance while consistently outperforming the usual suspects in raw speed.

Why Your Parser Matters More Than You Think

Parsing is one of those foundational operations that touches everything. Every time webpack processes a module, every time ESLint checks a file, every time a code formatter reformats your source, a parser is doing the heavy lifting underneath. Meriyah occupies an interesting niche: it produces standard ESTree-compatible ASTs (the same format Acorn and Esprima output), so it can slot into existing toolchains as a drop-in replacement, but it does the work noticeably faster.

The benchmarks tell a clear story. Meriyah clocks in at roughly 2,054 operations per second on typical parsing workloads, compared to Acorn at 1,411 and Esprima at 1,346. That is about 45% faster than Acorn and over 50% faster than Esprima. For a single file that difference is invisible, but when your build tool processes thousands of modules, those savings compound quickly.

What You Get Out of the Box

Meriyah ships with zero runtime dependencies. That means no transitive supply chain concerns, no bloated node_modules contribution, and no version conflict headaches. Here is what it supports:

  • Full ECMAScript 2024 (ES15) compliance
  • ESTree-compatible AST output
  • JSX parsing for React and similar frameworks
  • Stage 3 proposal support (decorators, JSON modules)
  • Web compatibility mode (Annex B) for legacy browser quirks
  • Comment and token extraction
  • Location tracking with ranges and line/column info
  • Lexical binding and scope tracking
  • Automatic semicolon insertion detection
  • Unicode 17 support (as of v7)

One notable limitation: Meriyah does not parse TypeScript or Flow. If you need type annotation support, you will want @babel/parser instead. But for pure JavaScript and JSX, Meriyah has you covered.

Getting Started

Install with your package manager of choice:

npm install meriyah
yarn add meriyah

Your First Parse

The API could not be simpler. Import one function, pass it a string of JavaScript, and get back an AST:

import { parse } from 'meriyah';

const ast = parse('const greeting = "hello world";', {
  sourceType: 'module',
  ranges: true,
  loc: true,
});

console.log(ast.body[0].type);
// "VariableDeclaration"

The sourceType option tells the parser how to treat the input. Use 'module' for ES modules, 'script' for classic scripts, or 'commonjs' for Node.js-style files with top-level return statements.

Parsing JSX

If you are working with React code, flip the jsx flag:

import { parse } from 'meriyah';

const jsxCode = `
  function App() {
    return <div className="container"><h1>Hello</h1></div>;
  }
`;

const ast = parse(jsxCode, {
  sourceType: 'module',
  jsx: true,
  loc: true,
});

const returnStatement = ast.body[0].body.body[0];
console.log(returnStatement.argument.type);
// "JSXElement"

Extracting Comments and Tokens

Sometimes you need more than just the AST. Meriyah can collect comments and tokens as it parses:

import { parse } from 'meriyah';

const comments: any[] = [];
const tokens: any[] = [];

const ast = parse(
  `// This is important
  const x = 42; /* inline */`,
  {
    sourceType: 'script',
    onComment: comments,
    onToken: tokens,
  }
);

console.log(comments.length); // 2
console.log(tokens.length);   // 5

You can also pass callback functions instead of arrays for onComment and onToken if you want to process them on the fly rather than collecting them.

Bending the Parser to Your Will

Stage 3 Proposals

Want to parse code that uses decorators or JSON module imports? The next flag enables Stage 3 proposal support:

import { parse } from 'meriyah';

const code = `
  @logged
  class UserService {
    @memoize
    getUser(id: string) {
      return fetch('/api/users/' + id);
    }
  }
`;

const ast = parse(code, {
  sourceType: 'module',
  next: true,
});

console.log(ast.body[0].type);
// "ClassDeclaration"

Building a Simple Code Analyzer

Here is a practical example: a function that finds all function declarations in a file and reports their names and parameter counts.

import { parse } from 'meriyah';
import type { Program, FunctionDeclaration } from 'estree';

function analyzeFunctions(source: string) {
  const ast = parse(source, {
    sourceType: 'module',
    loc: true,
  }) as unknown as Program;

  const functions: Array<{ name: string; params: number; line: number }> = [];

  function walk(node: any) {
    if (node.type === 'FunctionDeclaration' && node.id) {
      functions.push({
        name: node.id.name,
        params: node.params.length,
        line: node.loc?.start.line ?? 0,
      });
    }
    for (const key of Object.keys(node)) {
      const child = node[key];
      if (child && typeof child === 'object') {
        if (Array.isArray(child)) {
          child.forEach((c) => c?.type && walk(c));
        } else if (child.type) {
          walk(child);
        }
      }
    }
  }

  walk(ast);
  return functions;
}

const report = analyzeFunctions(`
  function add(a, b) { return a + b; }
  function greet(name) { return "Hello, " + name; }
  function noop() {}
`);

console.log(report);
// [
//   { name: 'add', params: 2, line: 2 },
//   { name: 'greet', params: 1, line: 3 },
//   { name: 'noop', params: 0, line: 4 }
// ]

Because Meriyah produces standard ESTree output, you can use it with any ESTree-compatible walker like estree-walker or acorn-walk rather than writing your own traversal.

Scope Tracking and Error Handling

For more advanced tooling, enable lexical scope tracking and use the built-in error utilities:

import { parse, isParseError } from 'meriyah';

try {
  const ast = parse('const x = 1; const x = 2;', {
    sourceType: 'module',
    lexical: true,
  });
} catch (error) {
  if (isParseError(error)) {
    console.log('Parse error at line', error.line, 'column', error.column);
    console.log(error.description);
  }
}

The lexical option enables scope tracking, which means the parser will catch duplicate declarations and other scope-related errors that would normally only surface at runtime.

What Changed in Version 7

The recent v7 release brought some breaking changes worth knowing about. Node.js versions below 20 are no longer supported. The module option has been replaced by the more flexible sourceType option that accepts 'script', 'module', or 'commonjs'. The globalReturn option is gone in favor of sourceType: 'commonjs'. And the uniqueKeyInPattern option was removed since patterns now always enforce unique keys.

On the positive side, v7 added Unicode 17 support and the ability to disable RegExp validation with validateRegex: false, which is useful for consistent cross-environment behavior when your code might run on older Node.js versions that do not support the latest RegExp features.

When to Reach for Meriyah

Meriyah is the right choice when you need a fast, standards-compliant JavaScript parser with zero dependencies. It shines in custom linters, code analyzers, build tools, and any scenario where you are processing many files and parsing speed directly affects user experience. Its ESTree output means you can swap it in wherever you are currently using Acorn or Esprima without changing any downstream code.

If you need TypeScript support, reach for @babel/parser. If you need a plugin system, Acorn has you covered. But if you want the fastest pure JavaScript parser available and you value a clean dependency tree, meriyah is a strong pick that has earned its nearly half-million weekly downloads.