Skip to content

docs: migrate the site to doc-kit (proof of concept) - #11072

Closed
ovflowd wants to merge 14 commits into
rolldown:mainfrom
ovflowd:docs/doc-kit
Closed

ovflowd wants to merge 14 commits into
rolldown:mainfrom
ovflowd:docs/doc-kit

Conversation

@ovflowd

@ovflowd ovflowd commented Oct 1, 2026 •

Copy link
Copy Markdown

Important

This is a proof of concept, not ready to merge. It depends on doc-kit pieces that aren't released yet, so CI will fail until they are (pnpm-workspace.yaml points @doc-kit/* at a local clone).

Description

This PR migrates rolldown.rs from VitePress to doc-kit, keeping the site looking and behaving the same, URLs and anchors included.

Why doc-kit

doc-kit is the documentation generator behind the Node.js API docs, and it's built around one thing: the experience of reading and writing API documentation. VitePress is a great general-purpose site generator, but an API reference on top of it is something we bolt on ourselves (TypeDoc → Markdown → custom components). doc-kit understands APIs natively:

  • API entries are first-class. Signatures, typed parameter and return lists, defaults, stability indices (experimental, deprecated), source links, events and class members are part of doc-kit's Markdown specification, and are rendered and cross-linked consistently, with types linking to their pages.
  • One pipeline for guides and the API reference. With @doc-kit/typedoc, the reference comes straight from Rolldown's TypeScript sources and JSDoc, and is just more doc-kit Markdown next to the guides: same components, search, navigation and llms.txt.
  • A good writing experience. Pages are plain Markdown (MDX when they need components), with GitHub alerts and standard fenced code instead of site-specific syntax, so docs read well on GitHub and in editors too.
  • Light by default. Pages are server-rendered static HTML, with JavaScript only for the few interactive islands (search, theme toggle, code tabs, outline).
  • Shared tooling. It's maintained by the Node.js project and adopted by other projects (webpack's docs are moving to it), so improvements made here (like the TypeDoc plugin, diagrams and llms-full.txt support) benefit everyone, and vice versa.

What it depends on

It needs a few counterparts, which are up for review:

  • A stack of doc-kit PRs, each reviewable on its own:
  • ovflowd/voidzero-dev-doc-kit-theme: the VoidZero design (styles and components) for doc-kit, installed from Git at a commit. It's a temporary home, meant to move under @voidzero-dev if this goes anywhere. The VoidZero fonts and brand assets are VoidZero's: the theme takes them from @voidzero-dev/vitepress-theme rather than copying them.

Screenshots

Before is rolldown.rs, after is this branch (1440×900).

Page Before After
Home
Getting Started
Plugin API
Reference index
build()
treeshake option

Where doc-kit shines

The API pages are where the difference shows the most: doc-kit renders signatures, typed parameter tables with their return values, stability indices, source links and events natively, and options are documented once and linked everywhere else.

Page Before After
Bundler API
InputOptions (now a linked list of options instead of ~1,460 lines repeating their docs)
checks option, with its nested options
this.resolve() in the plugin context
RolldownWatcher events (doc-kit Event: entries)

What changed

  • VitePress is gone, and so are custom build scripts: the docs' scripts are plain typedoc, doc-kit generate and serve calls.
  • The API reference is generated by TypeDoc with doc-kit's TypeDoc plugin (docs/typedoc.config.mjs), using doc-kit's own signatures, typed lists, stability and source links. Options are documented once, on their own page, and listed with a link everywhere else: before, each input option's docs were repeated on four pages (Interface.InputOptions, RolldownOptions, WatchOptions were ~1,460 lines each, now ~40).
  • Pages and JSDoc comments are written in doc-kit Markdown: GitHub alerts instead of VitePress containers, displayName code block titles, adjacent code blocks as tabs.
  • Diagrams are plain dot blocks rendered by doc-kit, with light and dark colors. The two hook graphs were converted from their custom DSL, with the legend as a Graphviz table.
  • llms.txt, each page's .md and llms-full.txt are still served, now generated by doc-kit.
  • Navigation is data: docs/navigation.json, in VitePress' themeConfig shape (nav, sidebar, socialLinks, footer, editLink).
  • The theme keeps one file per layout (doc, home, 404). The mobile navigation, sidebar drawer and outline work with CSS only, no JavaScript. Styles are Tailwind, and the reusable parts live in the theme package.
  • URLs and anchors are kept, including the reference's member anchors (/reference/Interface.Plugin#load): 0 broken internal links out of 58,227 across 277 pages.

Build performance

Clean builds, three alternating runs each, on the same main commit (6fbe999), medians:

xychart-beta
    title "Clean docs build (seconds)"
    x-axis ["Reference (VitePress)", "Reference (doc-kit)", "Site (VitePress)", "Site (doc-kit)", "Total (VitePress)", "Total (doc-kit)"]
    y-axis "Seconds" 0 --> 10
    bar [1.45, 1.60, 6.00, 7.52, 7.45, 9.12]
Loading
VitePress (main) doc-kit (this PR)
API reference 1.45s 1.60s
Site 6.00s 7.52s
Total 7.45s 9.12s
CPU time (site) 14.4s 23.8s
Peak memory (site) ~1.5 GB ~2.2 GB

Being honest here: doc-kit is ~22% slower end to end right now (the recent vite-plus v1 update made the VitePress build quite a bit faster). Most of doc-kit's time is its jsx-ast stage (~5.3s of the site build), so we plan to improve that and the TypeDoc plugin on the doc-kit side, and maybe move doc-kit's bundling to Vite+.

Trade-offs

  • No live reload in dev: refresh the page after a rebuild.
  • "Last updated" still comes from git log.
  • External deep links to ~35 page headings whose anchor changed (#import-meta is now #importmeta) land at the top of the page. Links within the site are updated.
  • The hook graphs' legend sits above the graph instead of overlaid on it.
  • Escape and clicking outside no longer close the mobile panels (they're CSS only).

Validation

To run it locally, clone doc-kit next to this repository, at the top of the stack:

git clone https://github.com/nodejs/doc-kit.git ../doc-kit
cd ../doc-kit && git switch feat/llms-full && pnpm install && cd -
pnpm install

Then:

pnpm docs          # TypeDoc and doc-kit in watch mode, served at http://localhost:5173
pnpm docs:build    # a production build into docs/dist
pnpm docs:preview  # serves docs/dist

Checked: format, lint and knip pass, every internal link and anchor resolves, and pages were compared with rolldown.rs side by side (light and dark).

VitePress containers become GitHub alerts, code group wrappers go (adjacent code blocks are tabs in doc-kit), code block titles become `displayName`s, and pages with components become MDX. Links to headings use doc-kit's anchors.

Assisted-by: Claude Opus 5.5 <[email protected]>
The JSDoc comments and the option docs they include are rendered into the API reference: their VitePress containers become GitHub alerts and their code block titles `displayName`s, and separate adjacent code blocks get a comment between them, as doc-kit shows adjacent code blocks as tabs.

Assisted-by: Claude Opus 5.5 <[email protected]>
doc-kit drops the dot from the heading's anchor: #import-meta is #importmeta.

Assisted-by: Claude Opus 5.5 <[email protected]>
The layouts (documentation, home, 404), the home page and the API reference's sidebar and index, built on @voidzero-dev/doc-kit-theme, which holds the VoidZero styles and components. The navigation is data, in VitePress' themeConfig shape (navigation.json).

Assisted-by: Claude Opus 5.5 <[email protected]>
doc-kit replaces VitePress, configured in doc-kit.config.mjs; the API reference is generated by TypeDoc with doc-kit's TypeDoc plugin (typedoc.config.mjs). The scripts are plain: `reference`, `build`, `dev` (TypeDoc and doc-kit in watch mode, served by `serve`) and `preview`.

doc-kit comes from a clone next to this repository, at the top of the stack of pull requests adding what the site needs (nodejs/doc-kit#1127), until they are released.

Assisted-by: Claude Opus 5.5 <[email protected]>
knip entries, editor settings, format ignores, the root docs scripts and the deploy workflow's reference fingerprint follow the new layout.

Assisted-by: Claude Opus 5.5 <[email protected]>
@socket-security

socket-security Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​node-core/​ui-components@​1.7.6971008295100
Addednpm/​serve@​14.2.6991009986100
Addednpm/​preact@​10.29.810010010098100

View full report

@ovflowd

ovflowd commented Oct 1, 2026

Copy link
Copy Markdown
Author

cc @TheAlexLichter

The example had no tsconfig, so type-aware lint checked its TSX with default options. With React's types installed (they come with the docs' UI components, as optional peers of Radix UI), `React.JSX` started resolving and its JSX failed to type-check. The tsconfig extends the root one, which only loads Node's types, and preserves JSX for the bundler.

Assisted-by: Claude Opus 5.5 <[email protected]>
Each entry point TypeDoc documents is named with `@module` after the path it's imported from (`rolldown/utils`), which the reference shows on exports the main entry point lacks. The main one is merged into the project with `@mergeModuleWith <project>`, so its exports keep URLs of their own (`Function.build`), and the lint config allows that tag.

Assisted-by: Claude Opus 5.5 <[email protected]>
A linked package keeps its own dependencies, so the docs loaded a second TypeDoc next to the plugin's, and the plugin's router, which extends TypeDoc's, wrote no pages. TypeDoc now comes from the linked plugin, until doc-kit is released.

Assisted-by: Claude Opus 5.5 <[email protected]>
The plugin now builds on TypeDoc's router and options (nodejs/doc-kit#1125). The config uses `docKit`, TypeDoc's `basePath` for source links, and `docKitUrlAdapter` to keep rolldown.rs' URLs (`Interface.Plugin`, `InputOptions.input`), so every existing link and redirect still works. Plugin hooks get the signatures of `FunctionPluginHooks` from a small TypeDoc plugin, as `Plugin` types them as `ObjectHook<…>`, and the sidebar reads the new page list.

Assisted-by: Claude Opus 5.5 <[email protected]>
@ovflowd

ovflowd commented Oct 8, 2026

Copy link
Copy Markdown
Author

Despite being a nice PoC, the VoidZero team let me know that migrating away from VitePress would be complicated. Closing this PR and hopefully in the future it gets adopted :)

@ovflowd ovflowd closed this Oct 8, 2026
@TheAlexLichter

Copy link
Copy Markdown
Collaborator

@ovflowd Thank you so much for the effort though @ovflowd! This will come handy for future evaluations for sure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants