teasel
A JavaScript and TypeScript parser written in Rust, made to be called from JavaScript. You get an ESTree tree, the shape every tool already knows how to walk, and you get the scopes, bindings and references with it, worked out during the same parse.
Getting the tree back to JavaScript
A parser written in Rust has to get its tree to JavaScript, and that is usually done one of two ways. Write the tree out as text and parse that text again on the other side: you paid for one tree and built two, with a string in between that grows with your file. Or keep the tree where it was built and hand JavaScript a handle: every question you ask a node, its type, its children, where it starts, is a trip across the boundary and back.
teasel does neither. Your text goes in once, when you make the Source. What you want read, a whole program, an expression at some offset, a piece of a template that stops at your own token, is the slot the engine reads it through. The engine lexes, parses and resolves scopes in one pass and answers with a compact stream of words. The decoder on the JavaScript side turns that stream into ESTree objects, one per node, and nothing in them points back at the engine. You can walk the tree, change it and serialize it without the engine.
Plain objects, with scopes
Every node is an ordinary object with start and end, nothing hidden behind a getter. Turn on scopes and the same parse also knows what every identifier declares or refers to, with no second pass over the tree.
What parse returns lists what each option adds. How it works follows one parse across the boundary and back.
import { Source, referenceOf } from '@teasel/parser';
const { node, scopes } = new Source('let x = 1; x++', { scopes: true }).parse();
node.type; // 'Program'
referenceOf(node.body[0].declarations[0].id).binding; // { name: 'x', kind: 'let', scope, … }
scopes.length; // 1Who it is for
If you're writing a compiler or a bundler, you get an ESTree tree fast, scopes included, and TypeScript read or stripped on the way through.
If you're writing a template language, you can pull one expression out of the middle of a document and stop at your own tokens. See Embedded JavaScript.
If you're writing a tool that runs in a browser, the same import is a WebAssembly build with the same API. Under Node the package is a native addon, and the WebAssembly build where the addon does not load.
npm install @teasel/parserGetting started is the first parse, four lines long.
A teasel is the spiky seed head of a tall thistle-looking plant. For a few hundred years, cloth makers dragged them across woven wool to raise the nap.