Wit

How to add a node or renderer

A practical guide to extending Wit's vocabulary and emitting new output formats. For developers.

Three ways to get a new node

  • A user definition — no code. Write #name … name# and use it. This is the right answer for almost every "new node."
  • A core-vocabulary addition — code in the runtime and every renderer. Reserve this for shapes that belong to the language itself.
  • The opaque pass-through@node(type …) carries its parameters straight to the renderer, which dispatches on the typeconvention. This is the seam for renderer-specific widgets.

Adding to the core vocabulary

A core node is a commitment: every renderer must handle it. The cadence is test-first.

  • Register the name in core-vocab.ts so the resolver stops requiring a definition for it.
  • Add a handler in each renderer — the HTML and Markdown renderers both need a branch.
  • Add a fixture under the core-vocab fixtures, with its snapshot, beforethe change — the fixtures are the executable spec, and a core node without one breaks that contract.

Writing a custom renderer

Walk the expanded AST and dispatch on each node's kind. The kinds form a stable, serializable, exhaustive union — you can enumerate them with wit tour. A minimal skeleton switches on kind, recurses into children, and handles a node use by its name.

The opaque escape valve

When you need a renderer-specific widget — a chart, a map, a game-state panel — @node(type …) passes its parameters through untouched. The resolver skips binding lookup and the expander emits the node intact, so your renderer sees every parameter and can build whatever it likes. User-defined wrappers compose over it cleanly, so authors get a friendly name and the renderer gets the raw payload.

Wiring it in

Whether you drive the CLI or your own program, the shape is the same: parse, resolve, expand, then your renderer. A document that loads external data adds loadExternalData before resolve.

Common mistakes

  • A core node without a fixture. It breaks the executable-spec contract — add the fixture and snapshot first.
  • Lifting stale prose. Older reference files predate recent features; verify any snippet against current renderer code and a real build before copying it.

See also

Architecture

maps the stages you hook into; Write a custom renderer and Embed the Wit parser are the worked versions; Design principles explains why the core stays small.