npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

codemirror-grammar-mode

v0.1.10

Published

Experimental approach to writing CodeMirror modes

Readme

CodeMirror grammar mode

This is an experimental tool for building CodeMirror modes from grammar descriptions.

You write a grammar like this:

skip (" " | "\t" | "\n")* {
  Expr { (num | var | ParenExpr) (op Expr)? }
  context ParenExpr { "(" Expr ")" }
}
tokens {
  num="number" { digit+ }
  var="variable" { letter (letter | digit)* }
  op { "+" | "-" | "/" | "*" }
}
digit { "0"-"9" }
letter { "a"-"z" | "A"-"Z" }

And then run grammar-mode on it to convert it into a JavaScript file. This file will export a set of bindings that can be given to the accompanying interpreter (in src/mode.js) to create a CodeMirror mode.

Grammar syntax

A grammar is a set of rules. Rules may appear on the top level or within tokens or skip blocks. The rules within tokens are considered the base token types of the language, and will be fallen back on when nothing else matches. A skip block is used to automatically insert whitespace-like productions between the elements of the rules inside of it.

Each rule has a name, optionally followed by the keyword context to mark it as a rule for which a context has to be pushed onto the context stack. Contexts can be used by external code to do things like computing indentation based on what rules are currently active.

After the rule name, you can add an equals sign and a quoted string to set a token type for the rule (for example num="number" in the example). That token type will be used to highlight the text that matches the rule.

Each rule contains a match expression, which is built up like this:

  • A "literal string" (using JSON string syntax) matches that exact text.

  • An underscore matches any character, and a period matches any character except newlines.

  • A character range is written as two single-character strings with a dash in between.

  • An unquoted word is a reference to another rule.

  • Multiple expressions separated by whitespace indicate that these things must match in sequence.

  • Parentheses can be used around expressions to group them.

  • Multiple expressions separated by pipe characters indicate a choice between those expressions. The first choice that matches is taken.

  • A +, *, or ? after an expression allows that expression to occur one or more (+), zero or more (*), or zero or one (?) times. This is done greedily — as many repetitions as possible are matched.

  • A ~ or ! character followed by an expression denotes a lookahead — positive lookahead for ~ and negative for !.

  • An & followed by a name is a call to a predicate. This is an external function that will be called to determine whether a given position matches.

Single-edge lookahead

A grammar is compiled to a set of state machines, whose edges are regular expressions, possibly extended with predicate calls and lookaheads, or calls to rules. When parsing, the interpreter will take the first edge that matches and consumes input, without looking ahead further.

The catch is that you have somehow write your grammar so that the right choice is made at every point. If something is ambiguous, the parser will just always take the first path. So, depending on your grammar, you might have to insert lookaheads to disambiguate things. For example, to distinguish between a variable and a label in a C-like language, you'd need rules something like this:

Statement {
  label ":" |
  variable |
  otherThing
}

label="meta" { letter+ ~(spaceChar* ":") }
variable="variable" { letter+ }

Command-line parameters

The grammar-mode command expects a file as argument, or will read from standard input when not given one. Other, optional, arguments include:

  • --output file specifies a file to write the output to (defaults to standard output).

  • --es-module tells the tool to output an ES6 module (default is a CommonJS module).

  • --graph will cause it to output a graph in .dot format instead of a JavaScript module. Can be useful for debugging.

  • --names will cause the JavaScript output to be more verbose but easier to read, using string names rather than numbers for the nodes.