-
Notifications
You must be signed in to change notification settings - Fork 45
packages dwar overview
@clean-jsdoc-theme/dwar owns the second half of the theme's pipeline: it is the
pure renderer. Hand it a SiteManifest and it server-renders every page to
HTML, bundles the interactive islands, emits the stylesheet and the search index,
and returns all of that as in-memory files. It is the render-side counterpart to
setu, which produces the manifest.
Note
Why the name? dwar (द्वार) is Sanskrit/Hindi for door / gateway — the
gateway the SiteManifest passes through to become the finished HTML site.
The single entry point is
render(manifest, opts):
function render(manifest: SiteManifest, opts: RenderOptions): Promise<RenderResult>;If you just want to use the theme, you never install this package directly — the JSDoc and TypeDoc bridges call it for you. It's an internal building block. See the Packages section for what you actually install, and dwar Examples for the real call shape.
A documentation build has two genuinely different jobs: understanding the
symbols and rendering pixels. setu does the first; dwar does the second.
The split lets the renderer be the one place that knows about Preact, MDX,
Shiki, esbuild, and the HTML shell — while staying ignorant of how doclets were
walked or how the sidebar was shaped. dwar consumes only the SiteManifest;
it never re-reads the doclet database.
render
(index.ts
is the engine) does the following, in order:
-
Builds the stylesheet — theme variables derived from
theme.tokensplus a pre-compiled Tailwind utility layer, stamped withmanifest.buildIdfor cache-busting (css.ts,buildThemeVariableCss+ the inlinedUTILITY_CSS). -
Bundles the islands up front in one split esbuild build — every island
is an entry point and
splitting: truehoists shared code (Preact, rang's registry) into a separatechunk-<hash>.jsthat each entry imports via relative ESM. Every emitted file is content-hashed ([name]-[hash].js) (islands-bundle.ts). -
Renders each page — compiles its MDX body to a Preact component, composes
it into rang's layout, and serializes it to an HTML document
(
mdx.ts,layout.tsx,html.ts). Pages are rendered with bounded concurrency (a worker pool capped atmin(8, cpus-1)) but assembled back in original page order, so the output is deterministic. -
Emits the fuzzy-search index — a JSON file (one entry per page plus
member/method deep-links) that the
cmdkcommand-palette island fetches at runtime. This is separate from Pagefind's full-text bundle.
A page whose frontmatter is kind: 'source' is a whole-file viewer, not prose.
dwar skips the MDX compile entirely for it and mounts a code-viewer island
instead: the SSR <pre> carries the file text, while the JSON props payload
deliberately omits the code (the hydration chunk reads it back from the DOM).
Source pages are hidden, so they contribute nothing to the search index
(index.ts,
the renderPage source branch).
These are the two properties that define dwar, and both hold in the code.
dwar does not write to disk. render allocates an array of OutputFiles in
memory and returns them; the caller persists them. There is no fs write, no
process.cwd(), no logging in the render path. The module docstring states it
outright: "render() is pure: it returns an in-memory RenderResult."
Two carefully-scoped exceptions prove the rule, and neither breaks purity for the default path:
-
runPagefindAgainstDiris the only filesystem touch in the package, and it is a separate, post-write function — never called fromrender. It operates on a directory of already-written HTML and emits the Pagefind bundle under<dir>/pagefind/(pagefind.ts). -
opts.islandCacheDiris opt-in. When (and only when) a bridge supplies it, the esbuild island bundle is read/written from an on-disk cache; omit it — the default — and bundling stays in memory. Readingos.cpus()to size the worker pool is explicitly noted as not breaking the contract (it's about no fs/cwd/logging) (islands-bundle.ts,render.tsoptions).
A single page that fails to compile (e.g. unparseable MDX) does not abort the
build. Each page renders inside a try/catch: on failure the page is skipped and
captured as a { slug, message } entry in result.errors, and the rest of the
site still renders. The error is reported, never thrown — the bridges log the
skipped pages as a warning after the build report
(index.ts,
the task closure;
publish.ts
surfaces them).
Everything comes back inside result.files (a OutputFile[], each
{ path, contents } with a forward-slash path relative to the destination root):
-
Per-page HTML —
<slug>/index.html(the root/empty slug →index.html), the full document shell fromhtml.ts. -
A co-located companion
.mdfor each content page — the page's MDX body written verbatim (<slug>/index.md), so LLMs and the copy-page button can fetch the Markdown source for the current page. Source-viewer pages have no body and emit none. - One stylesheet — the theme-variable + utility CSS, build-id stamped.
-
The fuzzy-search index —
_assets/search-index.<buildId>.json, page entries plus member deep-links. -
The island bundles — the content-hashed entry chunks plus the shared chunk,
under
_islands/. -
A per-page JSON props payload — embedded in each HTML document as
<script type="application/json" data-island-props>, read by the inline island loader at hydration time (html.ts,buildIslandsPropsPayload).
Alongside files, RenderResult carries search (the per-page entries, for any
caller that wants them), an optional errors array (present only when a page
failed), and a stats block (pageCount, assetCount, cssBytes, jsBytes,
durationMs)
(render.ts).
There is intentionally no
embedSearchIndexflag. Full-text search is the separaterunPagefindAgainstDirpost-write step — the renderer never inlines a Pagefind bundle.
dwar depends on the three sibling packages it sits downstream of, plus the render
toolchain
(package.json):
-
@clean-jsdoc-theme/utils— the boundary types (SiteManifest,RenderOptions,RenderResult,OutputFile, …); see utils Overview. -
@clean-jsdoc-theme/setu— the manifest generator (used by the smoke script); see setu Overview. -
@clean-jsdoc-theme/rang— the Preact components and island registry dwar bundles and composes; see rang Overview. -
preact/preact-render-to-stringfor SSR,@mdx-js/mdx+@shikijs/rehype/shikifor the MDX compile + highlighting,esbuildfor the island bundle, andpagefind(optional) for the post-write index.
The maintainer wants you sent to the code — start here:
- Package directory: packages/dwar · packages/dwar/src
-
The entry point + the purity/resilience logic:
index.ts(render, the per-pagetry/catch, the source-page branch) -
The option + result types:
render.ts(RenderOptions,RenderResult,OutputFile,RenderError) -
The HTML shell:
html.ts(renderHtmlDocument,htmlPathFor,mdPathFor, the props payload) -
The layout seam:
layout.tsx(SsrLayout, composing rang's slots + the island markers) -
MDX compile:
mdx.ts(compileMdxToComponent,collectUsedLangs) -
The island bundle:
islands-bundle.ts(the split esbuild build) andislands-loader.ts -
The CSS pipeline:
css.ts -
The (only) filesystem touch:
pagefind.ts(runPagefindAgainstDir) -
The runnable example:
scripts/smoke.ts
- dwar Examples — the runnable smoke script and the real call shape from the two bridges.
-
setu Overview — where the
SiteManifestdwar consumes comes from. - rang Overview — the components and islands dwar bundles.
-
Configuration — the
themeoptions that drive a render.
This wiki is auto-generated from docs-site/docs. Edit there — changes sync automatically. Full docs: https://ankdev.me/clean-jsdoc-theme/