Package Documentation
Guide 90
How Ndigo packages document themselves — the standard every package must
meet, and the tooling that keeps it true. Applies to all
packages/*; the reference implementation of the standard is the
ndigo-generators scaffolding.
Audience: Framework contributors · Prerequisites: Workspace bootstrapped; CONTRIBUTING.md · Reference package: packages/ndigo-core
What you’ll learn
Section titled “What you’ll learn”- Meet the required package README skeleton.
- Fill the manifest metadata correctly.
- Generate and commit the API tree.
- Pass the freshness and manifest gates.
- Scaffold a new package with the generators.
Why this shape
Section titled “Why this shape”Ndigo packages are deliberately standalone-readable: a developer who only
needs @ndigo/http should never have to open the framework monorepo docs.
So every package carries its own docs:
README.md— the human-written entry point (purpose, install, usage, API pointer).api/— the generated TypeDoc reference tree, committed and CI-verified.package.jsonmetadata — registry-facing fields (description, keywords, files) that make the package self-describing on npm.
The repo-root API.md is just an index linking into the
per-package references — no root-level generated site.
The per-package README
Section titled “The per-package README”Written for someone meeting the package for the first time. Required section order (drop optional ones):
# @ndigo/<name>
> One-line tagline — what it does and who it's for.
[2–3 sentences: the problem it solves, why it exists as a separate package.]
## Installation # pnpm add @ndigo/<name> + prerequisites
## Getting Started # smallest runnable example
## Usage # common patterns; ### subsections per feature
## Configuration # (optional) options table
## API # link to api/README.md; DO NOT re-list symbols
## Development # pnpm --filter @ndigo/<name> build:lib / test
## See Also # (optional) sibling packages
## Contributing # link to root CONTRIBUTING.md
## License # ISCpnpm manifest:check enforces this skeleton: the required H2 set —
Installation, Getting Started, Usage, API, Development, Contributing,
License — must be present and in order. Configuration and See Also
stay optional.
Rules:
- No internal references: no
AD-##ids, decision-record links, commit notes, or audit citations in package READMEs — those belong indocs/. - The
## APIsection is a link, not a dump. Hand-written symbol lists (e.g.ndigo-scheduler) are allowed only alongside the generated link. - Package READMEs link packages by relative path (
../ndigo-util/README.md).
package.json metadata
Section titled “package.json metadata”| Field | Requirement | Why |
|---|---|---|
description |
≥ 25 chars, non-generic (not "Ndigo Cache"); one-sentence value statement |
npm listing, doc index, search |
keywords |
non-empty array incl. "ndigo" + domain tags |
npm discoverability |
author |
"Jeff Rose <jeff_rose@outlook.com>" |
npm profile |
license |
"ISC" |
always |
files |
["api", "lib"] for every package (enforced by scripts/check-manifests.mjs) |
npm tarball ships only these + README |
repository / homepage / bugs |
set at publish time, when a public remote exists | npm links |
exports / sideEffects / type / dep sections |
enforced by scripts/check-manifests.mjs — see ARCHITECTURE.md “Manifest Dependency Sections” |
install closure |
Generated API docs
Section titled “Generated API docs”- Generate:
pnpm doc:api(aliaspnpm doc) — runs TypeDoc per package over the package’ssrc/tree (entryPointStrategy: expand, so one module per source file), writingpackages/<name>/api/, then regenerates the rootAPI.mdindex. - Layout: the reference mirrors the source tree.
api/README.mdlists every file as a module; each module page (api/<file>.md, orapi/<dir>/README.mdfor a subpath entry point) lists that file’s symbols; symbol pages are namespaced by their file (api/<file>/classes/<Symbol>.md)._*.tsfiles are the repo’s internal-file convention and are excluded. - Package index description:
scripts/typedoc-module-summary-plugin.mjslifts the package README’s blockquote tagline onto the project reflection, soapi/README.mdopens with the same one-line description as the README.@packageDocumentationis deliberately not used: TypeDoc ignores it underentryPointStrategy: expand(probed 2026-11-06). - Function display: TypeDoc converts a
constto a Function only when the initializer is a function literal and the declaration has no type annotation. Factory-produced callables — guards such asexport const likeContentSource = like({...}) as (value) => value is ContentSource, decorator factories, and re-export stubs — therefore stayed Variables.scripts/typedoc-function-kind-plugin.mjsreclassifies a function-typed constant as a Function when its type has call signatures, no properties, and no construct signatures, so those pages move under## Functionswith the normalFunction: namerendering. Callable objects that carry members (TagMapping,ViewMapping,errorHandler,errorMapping) stay Variables so their member docs survive. No@functiontag is used — that is not a TSDoc-standard tag and STYLE_GUIDE §5 rules it out. - Build before generating: TypeDoc resolves cross-package types through
the workspace symlinks into built
lib/, sopnpm buildmust precedepnpm doc:apior the regenerated reference keeps the previous doc comments. - Zero-warning standard:
treatWarningsAsErrorsis on. Broken JSDoc links fail the lane — that’s the drift guard. Because modules are per file, an unqualified{@link Name}only resolves inside the declaring file; link across files of the same package as{@link <module>.<Name> | Name}(class members:{@link <module>.<Type>#<member>}). - Cross-package links: each package’s API reference is isolated, so a
JSDoc link to a symbol in another package needs an entry in
scripts/external-symbol-links.json({ "@ndigo/other": { "Symbol": "#" } }). TypeDoc names the needed entry in its warning message — add it there, never in a package’s own docs. - After any public API change: update the JSDoc, run
pnpm doc:api, commit the generatedapi/tree with the change. - Freshness: CI regenerates the packages a PR affects — a package’s
src/or its manifest counts, since name/version/description reach the tree — and every package on the weekly sweep, then runsgit diff --exit-code -- packages API.md, so stale docs fail the lane. A PR that changes the doc toolchain, or one with more changed files than the compare API reports (it caps the list at 300 entries, truncating it silently), regenerates everything instead of guessing. Locally:pnpm doc:check(full regeneration);node scripts/doc-api.mjs --only <pkg-dir>regenerates a single package. - Coverage gate:
pnpm doc:apifails when a top-level exported function, class, interface, or type alias has no doc comment (declaration or signature). Variables and enum members are exempt (names self-document);@internaland_-prefixed internal files are exempt. Adding a public symbol with no comment is a CI failure — write the one-liner (type aliases included). - Depth gate (zero tolerance):
pnpm doc:coverage(scripts/doc-coverage.mjs --check) enforces documentation depth per package: every source module carries a real summary (not a tag-only block), every exported class/interface member has a doc comment, every method with parameters documents each one with@param, and every function with a non-voidreturn documents@returns. Any nonzero count fails CI — there is no baseline and no ratchet. Fix the gap rather than adding an exemption; the check prints one line per package and metric. - Named exports render readable pages: TypeDoc derives each page’s name
from the declaration, so a default export renders as
default(api/<module>/classes/default.md). Export named symbols and re-export them by name fromsrc/index.ts; a strayexport defaultis a documentation defect, not just a style one. - Empty doc blocks do not count as documentation: an empty
/** */satisfies the presence gate but renders nothing. Every block must carry a real one-line summary (file header) or a member description. - Example syntax:
scripts/doc-examples.mjsparses every fenced@exampleblock as TypeScript; CI fails on syntax errors. - TSDoc syntax:
internal/tsdoc-syntax(oxlint) validates every doc comment against the TSDoc standard (@microsoft/tsdoc). - Module summaries:
scripts/typedoc-module-summary-plugin.mjslifts the canonical file-header summary onto the generated module page, soapi/<file>/README.mddescribes the module.
Adding a new package
Section titled “Adding a new package”pnpm exec nx g @ndigo/generators:package --name mypackage --description "…"pnpm exec nx g @ndigo/generators:readme --name mypackage --description "…"pnpm doc:apiThe generators emit the README template and manifest skeleton above; fill
in real content, then let pnpm manifest:check + pnpm doc:api validate.
What is deliberately gone
Section titled “What is deliberately gone”- No HTML docs —
doc-html/,typedoc.html.json, andpnpm doc:htmlwere removed (2026-09). Markdown only. - No root generated site —
typedoc.jsonentry-point wizardry is replaced byscripts/doc-api.mjs; the rootAPI.mdis the generated index. - No per-package
READMEduplication of the API — the generated reference is the single source for signatures.
See also
Section titled “See also”- Guide 46 — Upgrading Ndigo — migration-guide authoring
- CONTRIBUTING.md — the repository workflow
- Generators — package and README scaffolding
- API index — the generated package surface