A report with live data
Build a release note whose version, contributor list and metrics table come from programs and files run at build time. Write the prose once; the numbers refresh on every build. A developer wires the sources once.
Step 1 — Name each input by alias
The document names only an alias, never a command. That is what keeps it safe to share.
@load meta load@ @load team load@ @load downloads load@ @load env load@
Each line binds the data to a name of the same spelling. Use @load(as: stats) downloads load@ to bind it under another
name.
Step 2 — Wire the aliases
A developer maps each alias to a program in wit.sources.json. Every
entry has a run (the command as an argument array) and a format — json or csv.
{
"sources": {
"meta": { "run": ["node", "scripts/meta.js"], "format": "json" },
"team": { "run": ["node", "scripts/team.js"], "format": "json" },
"downloads": { "run": ["cat", "data/downloads.csv"], "format": "csv" }
}
}A JSON object becomes a record, a JSON array a collection, and a CSV file a
collection of records keyed by its header row. The built-in env alias needs no entry: it exposes environment variables, read from the file you
pass with --env.
Step 3 — A loaded record in prose
(h1 Release @meta.version h1) Cut @meta.date on branch @env.WIT_BRANCH.
Step 4 — Iterate a loaded collection
(h2 Contributors h2) @ul (each @team as person) (li @person.name — @person.commits commits li) (end) ul@
Step 5 — A CSV straight into a table
@table(rows: @downloads)
Step 6 — An image beside prose
row is an invisible flex band; a col with a
fixed size sits beside one that flows.
@row @col(size: 260) @img(src: assets/downloads.svg, alt: Downloads chart) col@ @col macOS leads this cycle, with Windows a steady second and Linux climbing. col@ row@
Step 7 — Build with the sources config
wit build report.wit --sources wit.sources.json --env .env --allow-exec --fragmentResult. With meta.js printing {"version":"0.4.0","date":"2026-07-05"} and WIT_BRANCH=main in .env:
<h1>Release 0.4.0</h1> <p>Cut 2026-07-05 on branch main.</p> <h2>Contributors</h2> <ul><li>TauraJ — 42 commits</li><li>Ada Lovelace — 17 commits</li></ul> <table><thead><tr><th>platform</th><th>downloads</th><th>change</th></tr></thead>…</table>
Swap --fragment for -o report.pdf to get a styled PDF.
The trust boundary
The document names only an alias. Only the operator's wit.sources.json maps it to a command, and --allow-exec must be passed for any program to run. Without it, only env resolves. A document you receive can never run a program you didn't wire
yourself.
Pitfalls
- Forgetting --allow-exec. The build stops with
E_LOAD_FAILEDat the first@loadof a program alias. - Forgetting
@load env load@. Every alias,envincluded, must be loaded before@env.NAMEresolves; otherwise it isE_UNRESOLVED_REFERENCE. - Noisy sources. A JSON source must print only JSON to standard output — a stray log line breaks the parse.
Variations
Any program that prints JSON or CSV works — a Python script, a SQL client, a compiled binary. To run the same data flow inside your own app, see Embed the Wit parser.
See also
Load a CSV into a table is a focused excerpt; External data documents the loader seam.