mrkeyoor.com_
Sat 19 Sept 23:50 UTC
npmUtilsupdated 19 Sept 2026

chevrotain review

Chevrotain 13.2.0 is a JavaScript and TypeScript toolkit for building an LL(k) lexer and parser in application code. You describe tokens, grammar rules, recovery, and CST visitors through its API; it does not read a grammar file or generate a parser. The 13.2 release changes lexer internals: switch-based strategy dispatch and faster scanning of skipped ASCII character classes improved the project's CSS and JSON lexer benchmarks. Version 13 also uses -1 for missing token and CST positions, so location checks written for NaN need updating.

Verdict

Chevrotain 13.2.0 installed in 3.8 seconds and bundled to 30.6 KB gzipped in our sandbox, making it a defensible browser parser toolkit when you need recovery and CST tooling. Skip it for standard data formats, pre-Node-22 deployments, or grammars that fight LL(k).

We installed it

Lab card: what happened when we installed chevrotainScreenshot of chevrotain documentation
Install✓ · 3.8s6 packages on disk · 3 MB
ImportESM import works · require() works · ESM package with exports map
Browser30.6 KBgzipped (111.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 chevrotain install cleanly?

Yes. In a fresh container with an empty cache, npm install chevrotain finished in 4 seconds, leaving 6 packages and 3 MB on disk. npm audit reported no known vulnerabilities.

How much does chevrotain add to a browser bundle?

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

Does chevrotain work with both ESM and CommonJS?

Yes. Both import 'chevrotain' and require('chevrotain') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does chevrotain include TypeScript types?

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

chevrotain or ohm-js: which should you use?

Pick ohm-js when ohm-js 17.5.0 fits when a declarative grammar and separate semantic operations are easier for the team to review. Chevrotain 13.2.0 installed in 3.8 seconds and bundled to 30.6 KB gzipped in our sandbox, making it a defensible browser parser toolkit when you need recovery and CST tooling.

When should you not use chevrotain?

Your production runtime is below Node 22. Package 13.2.0 declares Node >=22.0.0, even though earlier Chevrotain majors supported older releases.

API stability3/5The current tutorial still centers on createToken, Lexer, RULE, CONSUME, SUBRULE, OR, visitors, and performSelfAnalysis, so ordinary grammar code has a recognizable shape across releases. Version 13 did make a source-visible change from NaN to -1 for unavailable locations and tightened TypeScript DSL inference. Those changes can break tests or compilation even when the language grammar itself is unchanged.
Docs5/5The official documentation separates lexing, parser construction, CST visitors, embedded actions, and fault tolerance into runnable tutorial stages. It also documents token order, modes, categories, gates, position tracking, content assist, initialization cost, grammar errors, and versioned API pages. The reader still needs LL terminology, but the failure modes are named and paired with concrete grammar examples.
Maintenance5/5Version 13.2.0 was published on August 1, 2026, and the GitHub repository was pushed on August 25, 2026. It is not archived and GitHub currently lists 29 open issues and pull requests. The 13.2 changelog names two lexer performance changes and their benchmark effects, while the version 13 breaking location sentinel is documented instead of being buried in release metadata.
Ecosystem4/5npm recorded 9,987,988 downloads in the latest completed week, and GitHub reports 2,797 stars. The project provides browser use, syntax diagrams, content-assist APIs, examples, and an external LL(*) route used by Langium. It remains a parser-building niche: teams looking for ready-made JSON, YAML, or SQL parsers will find more direct packages with less grammar ownership.

Use it if

  • Chevrotain 13.2.0 fits a DSL, editor, formatter, or language server that needs token positions plus a concrete syntax tree.
  • Grammar construction should reject left recursion, duplicate DSL occurrences, unresolved rules, and ambiguous choices before requests arrive.
  • Incomplete editor input must produce errors and a partly usable CST through configurable recovery.
  • Your team wants lexer modes, token categories, syntactic content assist, and visitors in one TypeScript-facing package.
Skip it if

Setup reality

Our fresh-sandbox install of chevrotain 13.2.0 took 3.8 seconds in Node 22 Bookworm, placed 6 packages on disk, and used 3 MB. npm audit reported 0 known vulnerabilities. The package itself has 5 direct dependencies, no peer dependencies, and 1672 KB unpacked. It ships TypeScript declarations. Both require() and ESM import worked in our Node 22.23.2 checks despite the package declaring ESM with an exports map.

We measured 111.1 KB minified and 30.6 KB gzipped for a browser import of the whole package in esbuild. Define specific tokens before a catch-all Identifier and use longer_alt when a keyword may prefix an identifier. Whitespace does not disappear automatically; assign it to Lexer.SKIPPED, or keep it in a named group when comments and formatting matter.

Every parser class must register its RULE calls and then run performSelfAnalysis(). That startup pass detects grammar defects and builds lookahead functions, so construct one parser and reuse it by replacing parser.input. Lexing and parsing return different error arrays. Check both before accepting a result, including when recoveryEnabled is true.

In version 13, unavailable locations are -1 rather than NaN. Any Number.isNaN location test will silently miss the new sentinel. Recovered CST nodes can also omit children that a visitor normally receives, so editor-facing visitors need guards and malformed-input tests. The 13.2 lexer changes are internal; your grammar API remains the same, but the Node 22 floor is a deployment constraint.

Patterns

Tokenize identifiers and commas tokenize-input

import { createToken, Lexer } from 'chevrotain';

const Space = createToken({ name: 'Space', pattern: /\s+/, group: Lexer.SKIPPED });
const Comma = createToken({ name: 'Comma', pattern: /,/ });
const Identifier = createToken({ name: 'Identifier', pattern: /[A-Za-z_]\w*/ });
const vocabulary = [Space, Comma, Identifier];
const lexer = new Lexer(vocabulary);

Token order is priority order when patterns overlap. Inspect tokenize(...).errors before parsing its tokens.

Keep keyword prefixes inside identifiers match-keywords

const Identifier = createToken({ name: 'Identifier', pattern: /[A-Za-z_]\w*/ });
const Let = createToken({ name: 'Let', pattern: /let/, longer_alt: Identifier });
const vocabulary = [Space, Let, Identifier];

longer_alt makes letter one Identifier instead of Let followed by ter. Keep the keyword before Identifier.

Build and analyze a list grammar define-parser

class ListParser extends CstParser {
  constructor() {
    super(vocabulary);
    this.RULE('list', () => {
      this.AT_LEAST_ONE_SEP({ SEP: Comma, DEF: () => this.CONSUME(Identifier) });
    });
    this.performSelfAnalysis();
  }
}

performSelfAnalysis() must follow every RULE registration. Construction throws when the grammar has structural conflicts.

Check errors from both stages reuse-parser

const parser = new ListParser();
function parse(text) {
  const lex = lexer.tokenize(text);
  parser.input = lex.tokens;
  const cst = parser.list();
  const errors = [...lex.errors, ...parser.errors];
  if (errors.length) throw new Error(JSON.stringify(errors));
  return cst;
}

Assigning parser.input resets parse state for the next call. Lexer errors never appear in parser.errors.

Disambiguate repeated DSL calls number-occurrences

this.RULE('pair', () => {
  this.CONSUME(Identifier);
  this.CONSUME(Colon);
  this.CONSUME2(Identifier);
});

A second CONSUME of the same token in one rule needs the 2 suffix. Duplicate occurrence indexes fail analysis.

Turn CST tokens into an array build-cst-visitor

const Base = parser.getBaseCstVisitorConstructor();
class ListVisitor extends Base {
  constructor() { super(); this.validateVisitor(); }
  list(ctx) { return (ctx.Identifier ?? []).map((token) => token.image); }
}

CST child properties are arrays. The fallback covers a recovered node whose Identifier child is missing.

Enable recovery for editor input recover-errors

class EditorParser extends CstParser {
  constructor() {
    super(vocabulary, { recoveryEnabled: true });
    this.RULE('document', () => { /* grammar */ });
    this.performSelfAnalysis();
  }
}

Recovery records parser errors and attempts resynchronization. It does not clear lexer errors or make input valid.

Model precedence without left recursion encode-precedence

this.RULE('sum', () => {
  this.SUBRULE(this.product);
  this.MANY(() => {
    this.CONSUME(Plus);
    this.SUBRULE2(this.product);
  });
});

Chevrotain rejects a rule that calls itself before consuming input. Give each precedence level its own rule.

Alternatives

PackageRegistryPick it when
ohm-jsnpmohm-js 17.5.0 fits when a declarative grammar and separate semantic operations are easier for the team to review.
nearleynpmChoose it when an ambiguous or non-LL grammar needs Earley parsing and multiple parse results are acceptable.
peggynpmChoose it when a PEG grammar and generated parser fit better than a separate lexer plus CST visitor.

More utils guides

lru-cache · type-fest · ajv · 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.