cudoc

cudoc

English | 한국어

Write a fact once. Reuse it everywhere. Keep one source of truth across every document, every site and every index.

cudoc adds three features to the Markdown documentation and the site generator you already use:

Authors keep writing ordinary Markdown, with no component to import. Your documentation now has two readers: the people who open it, and the machines that read it far more often, such as retrieval pipelines, agents and models. cudoc serves both from one source.

Set it up: Next.js · Docusaurus · Nextra · VitePress · Eleventy · no generator yet · what each host asks of you

This README is also a website, built by cudoc-export from this file and docs/ with no static site generator: cudoc's documentation, exported by cudoc.


The problem: the same sentence in eleven places

A fact that belongs on several pages gets copied, and the copies drift apart as soon as one of them changes. A rate limit, a required permission, a supported version table: one copy is corrected, and the other ten quietly become wrong.

cudoc removes the copies. A fact lives in exactly one document, and every other document points at it:

```cudoc-embed
sources: [reference.md#limits]
```

At build time cudoc pulls the real content in. Readers see a complete page, and authors maintain one paragraph.


"Can't I just import a shared component?"

You can, and it removes the copies too, but an import and an embed produce different things. Every MDX host lets you write a partial and import it, and the markdown-it hosts have include directives that do the same.

An import is a reference that stays a reference: the page holds a component node, and the text appears only once the host renders it with that component registered.

import Limits from "./_limits.mdx"

<Limits />

An embed is resolved before anything renders: at build time cudoc replaces the block with the real headings, paragraphs and tables from the source document, as ordinary document nodes.

```cudoc-embed
sources: [reference.md#limits]
```

That single difference decides the rest:

Shared component cudoc-embed
Unit of reuse a whole file; VitePress can also include lines or a region marked in the file any section of a real page, by anchor, title or depth, with nothing marked
Where the fact lives a partial with no URL, no anchor, no place in the nav; a VitePress region is marked inside a page a published document readers can link to
Authoring .mdx, plus an import line on every page that uses it plain .md, nothing to import or register
Across hosts each host spells it differently the same block on all six
Adapting a copy add props, or fork the partial replace rewords this copy; the source is untouched
Derived views include only render: { type: table } builds a summary of headings
Checking a missing import fails the build, one at a time; a missing VitePress include or region passes silently cudoc check reports every broken source and anchor in one pass
What a non-host consumer gets a component name it cannot render the text

Reuse should not force you to fragment your documents

cudoc leaves the document whole: reference.md stays a page with a URL, #limits stays an anchor someone can link to, and the reuse is written where it happens. You do not predict the reuse boundary before you need it, and you do not restructure anything when it changes.

A partial makes the unit of reuse equal to the unit of file. To share one section, you cut it out into _limits.mdx and import it back into the page it came from; after a few rounds the canonical text lives in fragments no reader opens, and the real pages become assembly manifests.

And the copy is real text

cudoc's resolved output is the actual content, so every destination receives the same complete document. A component node is opaque to anything that is not the host: HTML export, an AST dataset, a retriever reading your corpus. → Document embedding


The second reader: your retrieval pipeline

Duplicated documentation now poisons your retrieval index as well as costing you maintenance. Over duplicated docs, a RAG pipeline turns the eleven near-identical copies into chunks: a query retrieves several of them, spends the context window repeating one sentence, and dilutes the relevance score of the chunk that actually answers the question. When a fact is updated in one page and not the others, stale copies compete with the fresh one, and the model has no way to tell which is current.

Single-sourcing fixes that at the root. cudoc then gives the machine side four more things:

When an agent writes the docs, cudoc check --format json hands back every broken link, anchor, image and embed in one machine-readable pass, so the loop closes without a human reading a build log. cudoc lsp runs the same check in an editor, on every save, with the likely corrections as quick fixes. → Reference checking


Pick your site generator

Each guide is a numbered walkthrough (install, configure, wire up collection, build). Every step is a copy-pasteable command or file, so you can work through one yourself or hand it to a coding agent and have it set the project up for you.

Your site Guide Demo Install
Next.js Set up Next.js → Built by Next.js @cudoment/cudoc cudoc-remark
Docusaurus Set up Docusaurus → Built by Docusaurus @cudoment/cudoc cudoc-remark cudoc-docusaurus
Nextra Set up Nextra → Built by Nextra @cudoment/cudoc cudoc-remark cudoc-nextra
VitePress Set up VitePress → Built by VitePress @cudoment/cudoc cudoc-vitepress
Eleventy Set up Eleventy → Built by Eleventy @cudoment/cudoc cudoc-eleventy
No generator yet HTML export → This README as a site @cudoment/cudoc cudoc-export

Each host demo is the same two Markdown files, built by that host from the runnable example; Host demos links the files beside each one. With no generator yet, the last row starts from an empty directory: two commands and two documents give you a complete site. → Your first site

The install column lists cudoc's own packages, and a guide's first step installs anything else its host needs. Every package is ESM and needs Node.js 22 or later. Supported versions lists the host releases every change is tested against, and gives the details of what each host asks of your setup:

Every host takes .md, and the three MDX hosts also take .mdx, decided per file by its extension. Every cudoc feature works in both; only on Nextra are a code title and named tab components drawn differently in .md. → Choosing .md or .mdx

HTML, PDF and Word export is an extra output available from every host, reusing the same collected documents, rather than a seventh choice you make instead of the others. → Export alongside an existing site


What you get

A site takes up the three features in this order: syntax extensions within the pages you have, document embedding between those pages, and export of the same documents to other formats.

Markdown that does more, without components

Authors never import or register a React component; the page stays Markdown.

## Requirements (#requirements) (@New)

> [!NOTE] Before you start
> Prepare your account and access token.

| Item   | What you need                                        |
| ------ | ---------------------------------------------------- |
| Access | - Account<br />-- Verified email<br />- Access token |

The extensions are explicit heading anchors, badges, callouts with real titles, nested lists inside table cells, merged cells, table notes, titled code blocks with highlighted lines, tabs, shared variables and shared link definitions. Each feature your host also spells its own way accepts cudoc syntax, your host's native syntax, or both, so you can adopt cudoc without rewriting a single existing page. A feature you do not use can be turned off, and its syntax stays the text it is written as. → Markdown syntax

Embedding that selects, not just includes

An embed names a whole document, one section or a folder, and can summarize, outline or reword what it brings in.

You want You write
A whole document sources: [reference.md]
One section and its children sources: [reference.md#limits]
A section without its children includeChildren: false
A summary table of headings select: { depth: 2 } with render: { type: table }
A folding tree of a folder's documents sources: [guides/] with render: { type: tree }
The same content, worded for this page replace: [{ find: "...", replace: "..." }]

The source document never changes. → Document embedding

Edit the source, and every copy follows while you write

cudoc collect --watch beside your dev server collects a saved document again in the time it takes to compile that one file. Only the documents whose text changed are compiled, only the embeds that read one of them are resolved again, and everything else is reused.

cudoc collect --watch --config cudoc.config.mjs

On the MDX hosts the loader you registered tells the dev server that the pages embedding it changed, so the copy in the browser updates without a restart. A document that fails to compile is a message in the terminal while the last good result stays in place. VitePress and Eleventy load the library when their configuration is evaluated, so there the dev server is restarted after the change. → Collection setup, Watching

One set of documents, three destinations

Your site, HTML to hand over and a machine-readable AST corpus all come from the same Markdown, resolved the same way.

Output Command Use it for
Your existing site your normal build production docs, for people
HTML export cudoc-export build offline handoff, one-file attachments, review rounds, static deployment
AST dataset cudoc dataset RAG pipelines, search indexes, MCP servers, agent context

HTML export writes a site, single pages that carry their own styles and pictures, or pages that collect review notes. Hyperlinks follow one of three policies: links to local files, links to deployed URLs, or no links at all. So the same content works browsed from disk, attached to a message or published.

Complete samples are committed with the repository. A six-document API handbook is exported from one configuration into a site in two languages, per-document files and a bound volume with a cover and a contents page. It is exported from three more configurations into single pages, a page to review and a hosted review. Open the PDF or the Word file directly, browse the site on the documentation site or from a clone, and compare them with the sources that produced them. → Complete examples


Reference

Guides teach the workflow, and the reference is where signatures, defaults and contracts live.

Looking for Go to
Syntax modes, callout types, native forms, column layouts Markdown syntax
Collection setup, selection, replacement, refresh rules Document embedding
HTML modes, site structure, link policies, PDF and Word Export
Broken links, anchors, images and embeds across a document set Reference checking
Projection options, manifest format, consumer contract AST datasets
Imports, signatures, option defaults, AST metadata, internals API reference
Which package does what, and what each one exports Packages
A real site you can run Runnable examples

Contributing

A clone builds and tests with three commands:

npm ci
npm run build
npm test

npm test runs three tiers: package unit tests, the shared fixtures compiled by each example's real compiler, and assertions on built example sites. A tier whose prerequisite is missing skips with the command that satisfies it, and CUDOC_STRICT=1 turns any skip into a failure. See tests/README.md.

Host examples install separately with their own lockfiles. Update the affected guide and API reference in the same change as any API or workflow change. CONTRIBUTING.md covers reporting a problem, making a change and writing a page.

Release notes

What changed in each version is in the release notes.

License

cudoc is released under the MIT License.

cudoc