docpack
Freeze structured data into document-native modules.
docpack turns external data sources into checked-in document assets.
Feed it CSV, JSON, YAML, TOML, or XLSX. Get back generated Typst or LaTeX code that you can commit, diff, review, and ship with the rest of your document sources.
It is built for the cases where runtime data loading is the wrong tradeoff:
- reproducible reports
- compliance or approval workflows
- offline or single-file document bundles
- generated artifacts that need stable diffs
Why docpack
Manifest-first: usedocpack buildfor repeatable project outputs.CLI-friendly: usedocpack emitfor one-shot pipes and previews.Backend-aware: Typst and LaTeX outputs share one normalized data model.Explicit failures: format, inference, sheet, and shape errors are surfaced with concrete diagnostics.Reviewable output: generated artifacts are plain text modules, not opaque caches.
Capability Matrix
| Area | Support |
|---|---|
| Inputs | csv, json, yaml, toml, xlsx |
| Backends | typst, latex |
| Artifacts | data-module, table-fragment |
| Typst styles | typst-official, typst-table |
| LaTeX styles | latex-expl3, latex-classic-macro, latex-booktabs-longtable, latex-plain-tabular |
| Workflow modes | build, emit, inspect, init |
Quickstart
Install
Or via Homebrew:
Emit to stdout
emit is the fast path. If you do not pass --output, generated code is written to stdout.
Example output:
#let profile = ("name": "Alice", "role": "Engineer")
Emit to a file
Pipe from stdin
Stdin requires an explicit format, and stdout requires an explicit backend.
|
Bootstrap a manifest
Build all declared outputs
Manifest-First Workflow
Minimal docpack.toml:
[]
= "quarterly-report"
= "generated"
[[]]
= "sales"
= "data/sales.csv"
= "csv"
[[]]
= "sales_typst"
= "sales"
= "sales.typ"
= "typst"
= "data-module"
= "typst-official"
= "sales"
[[]]
= "sales_tex"
= "sales"
= "sales.tex"
= "latex"
= "table-fragment"
= "latex-booktabs-longtable"
Then:
Command Surface
What each command is for
build: resolve a manifest and write all outputs in order.emit: convert one source into one artifact.inspect: show normalized shape, metadata, and resolved render defaults.init: generate a starter manifest.
Output Styles
| Backend | Artifact | Default | Alternatives |
|---|---|---|---|
| Typst | data-module |
typst-official |
none |
| Typst | table-fragment |
typst-table |
none |
| LaTeX | data-module |
latex-expl3 |
latex-classic-macro |
| LaTeX | table-fragment |
latex-booktabs-longtable |
latex-plain-tabular |
How It Works
docpack is intentionally small at the center:
source bytes
-> input normalization
-> Value + SourceMeta
-> backend render request
-> Typst / LaTeX artifact
Current library layers:
core: normalized value tree and source metadatainput: format adapters and normalizationbackend: Typst and LaTeX renderingmanifest: project resolution, inference, and build planningerror: structured failure model
Examples
Inspect before generating
This prints:
- source format
- normalized top-level shape
- tabular metadata
- inferred backend / artifact / style / root name
Force a specific sheet
Generate classic LaTeX macros
Status
What is already in place:
- manifest-driven build flow
- one-shot emit and inspect flow
- Typst data modules and table fragments
- LaTeX
expl3, classic macro,longtable, and plaintabularoutputs - reference fixture coverage across
json,yaml,toml,csv, andxlsx - real
pdflatexsmoke coverage for classic macro output - structured diagnostics for format inference, tabular shape mismatches, and missing sheets
What is still open:
- benchmark fixtures for large
csv/xlsxsources - memory profiling for larger manifest builds
Development
Maintainers
Release automation is tag-driven. See docs/releasing.md.
Design Notes
docpack is not trying to become a general-purpose ETL framework or a plugin platform.
The core idea is narrower and more useful:
- normalize external structured data once
- render it into document-native code
- keep the generated artifact reviewable and reproducible
License
docpack is licensed under AGPL-3.0-only.