Development
The repository's brief for contributors, human or AI, is CLAUDE.md. This page summarizes it.
bash
npm ci
npm run dev # development playground
npm test # unit, contract, streaming, fuzz and interaction tests
npm run typecheck
npm run buildTests
| Suite | Where | What it checks |
|---|---|---|
| Unit | test/*.test.ts | Parser, expressions, actions, layout, the model guide |
| Contract | test/contract.test.ts | Every registered component against the project rules: documentation, layout bounds at three widths, theme-only colors and fonts, hit regions and keyboard reach |
| Streaming | test/streaming.test.ts | Every prefix of every sample renders a valid, smaller screen, however it's chunked |
| Fuzz | test/fuzz.test.ts | Random and damaged specs, expressions and actions never throw (FUZZ_RUNS=5000 npm test for a longer run) |
| Website | test/site.test.ts | Every gallery example renders cleanly and behaves as described; every SayShape block in these docs parses |
| Visual | visual/ | Component examples and samples in light and dark at two widths, against baselines (Playwright) |
| Benchmarks | bench/ | Parse, layout and paint timings; pull requests are compared with their base |
Interaction tests use sayshape/testing, which runs the real engine in Node against a canvas that records what's drawn:
ts
import { render } from "sayshape/testing";
const ui = render(`state n=0
button "Add" on=inc:n
text "Clicked {n} times"`);
ui.press({ type: "button", text: "Add" });
expect(ui.state.n).toBe(1);
expect(ui.text()).toContain("Clicked 1 times");Visual baselines are made on Linux in the Playwright image, because fonts render differently on each platform: VISUAL_ARGS=--update-snapshots npm run test:visual:docker, or add the update-visual-baselines label to a pull request and CI commits them.
Rules every change keeps
- Specs can never run code. Expressions go through
src/expr.tsonly. - The parser never throws. Every problem is a diagnostic with a line number.
- Streaming keeps working: no syntax that needs a closing line.
- Components never hard-code design. Colors, fonts, radii, spacing and type sizes come from the theme.
- The model guide and component docs are generated from component definitions, never written by hand.
- Layout stays headless: it only measures text.
- Rendering stays on the canvas.
- Models generate specs, never components.
- No new runtime dependencies without a strong reason. The library has none.
Before a change is done
npm run typecheck,npm testandnpm run buildpass.- If a component definition changed,
npm run docs:componentshas been run. - Anything visual has been looked at in light and dark mode, and visual baselines are updated in the same change.
- Spec examples added anywhere parse with no diagnostics.