Operations
Content tooling
Generate, check and watch authored pages, the safety rules for source files, ownership of generated output and troubleshooting.
Commands
pnpm exec doq content generate # write the resolved pages
pnpm exec doq content check # compare, write nothing, exit 1 when stale
pnpm exec doq content watch # regenerate when a page, fragment, source file or API index changes
Run doq-api generate first when you use [[api:Name]] links: it records the symbol index the links resolve against. Then doq content generate, then the Vite build. In CI run doq-api check and doq content check before the build.
| Exit code | Meaning |
|---|---|
| 0 | success, or output is up to date |
| 1 | check found stale output |
| 2 | invalid configuration or usage |
| 3 | an output conflict: a file you wrote or edited would be overwritten |
| 4 | invalid authoring: missing source, unsafe path, bad region, unknown symbol, include cycle |
Every authoring problem is reported together, with the page, the line, the directive, the reference, the reason and a fix, and nothing is written until all are resolved. --json prints the result as one document. The programmatic form is generateContent and checkContent; options are described in DoqContentOptions.
Development
The doqContent() Vite plugin from @doqs/analog/content resolves the content when the build or dev server starts and again when a dependency changes, using Vite's file watcher (no polling):
import { doqContent } from '@doqs/analog/content';
export default defineConfig({ plugins: [doqContent(), analog(/* … */)] });
See doqContent. Without Vite, use doq content watch. doqDeterministicMarkdown() is unaffected: content is resolved before Analog loads any page.
Safety
Source files are untrusted input, so ::source is allowed to read very little:
- only text files below the configured
sourceRoots, checked on real paths, so..and symbolic links cannot leave them; - never
node_modules,.git, build output,.env*, keys, certificates or files named like credentials or secrets; - never binaries, non-UTF-8 files or files above 256 KiB; never URLs;
- nothing is executed, nothing is fetched, and nothing is read in the browser.
There is no option that turns these rules off. Fragments are limited to the authoring directory in the same way.
Ownership of generated files
doq content writes only inside out, only .md pages, and records each in .doq-content.json (in the authoring directory) with a SHA-256 hash. It never overwrites a file it did not create or one you edited after generation; it reports a conflict and changes nothing. A page removed from the authoring directory is deleted from out only if it is still unmodified. Output has no timestamps and no absolute paths, so unchanged input produces identical bytes.
Important
Authored pages are the source of truth. The Markdown below out is generated: never edit it by hand. Run doq content generate, commit the result and let CI run doq content check, which fails when the committed pages are stale.
Source links
A target can link each snippet to its origin:
{ "sourceUrl": "https://github.com/acme/lib/blob/{revision}/{path}{lines}", "revision": "v2.1.0", "sourcePathPrefix": "packages/lib/" }
{path} is the project-relative path (after sourcePathPrefix), {revision} the configured tag or commit, and {lines} the range formatted by sourceLines (default #L{start}-L{end}). Use the tag of the release that an archived version documents. The configuration used by Showcase B for its two versions is:
{
"targets": [
{
"name": "latest",
"authoring": "src/authoring/docs",
"out": "src/content/docs",
"sourceRoots": ["forge-sdk/src"],
"api": ["src/content/docs/reference/generated"]
},
{
"name": "v1",
"authoring": "src/authoring/docs-v1",
"out": "src/content/docs-v1",
"sourceRoots": ["forge-sdk-v1/src"],
"api": ["src/content/docs-v1/reference/generated"]
}
]
}
Troubleshooting
region "x" was not found: the message lists the regions the file has. Names are case-sensitive and the marker needs thedoqs:prefix.is outside the configured source roots: add the directory tosourceRoots, or move the example under an existing root."Name" is not a public symbol: rundoq-api generate; the symbol index is regenerated with it.already exists and was not generated by doq content: a hand-written page has the same path as an authored one. Rename one of them.was edited after it was generated: restore the generated page or move your edit to the authored page.