How to add a node or renderer
Extending Wit's vocabulary and output formats. For developers.
Three ways to get a new node
- A definition — no code. Write
#name(…) … name#and use it. The right answer for almost every new node. - The opaque node —
@node(type: x, …)passes every parameter to the renderer, which dispatches ontype. The seam for renderer-specific widgets. - A core name — code in the parser, runtime and every renderer. Only for shapes that belong to the language.
A definition can give the opaque node a friendly name, so writers type @chart(…) and the renderer receives the raw payload:
#chart(literal src, kind) @node(type: chart, src: ::src::, kind: ::kind::) node@ chart# @chart(src: data/q3.csv, kind: bar)
<div data-type="chart" data-src="data/q3.csv" data-kind="bar"></div>
Adding a core name
A core name is a commitment every renderer must keep. Test first:
- Write the case in
docs/spec.mdortests/spec/, and add it totests/spec/pending.txt. - Add the name to
CORE_VOCAB_NAMES, and any parameter it reads toCORE_PARAMS, inpackages/parser/src/core-names.ts. The parser splits a core use's parens againstCORE_PARAMS. - Add a handler to the HTML and Markdown renderers.
- Add prose-safety cases to
packages/render-html/src/prose-safety.test.ts— a new name must not turn ordinary sentences into structure (a known name glues to a word, and opens the parenthesis form). - Delete the pending line; refresh snapshots with
WIT_SNAPSHOT_UPDATE=1 pnpm testand readgit diff tests/rendered.
Writing a renderer
Walk the expanded AST and switch on each node's kind; handle a nodeUse by its name and params. The kinds
are a closed, serialisable union — wit tour lists the 24 and shows
which a document uses.
Wiring it in
The CLI and your own program run the same steps: scanCloseless, parse, loadExternalData if the document loads data, resolve, expand, then your renderer. See API.
Common mistakes
- A core name without a spec case. Write the case first.
- A parameter missing from CORE_PARAMS. Without it, the previous value does not stop at that key and swallows it.
- Copying 0.2 snippets. Pipes,
{ … }calls and...are gone; runwit migrateon old examples.
See also
Architecture · Custom renderer · Embed the parser · Design principles.