Skip to content

Commit 6d60744

Browse files
dmealingclaude
andauthored
feat(docs-site): HTML documentation-site generator wired as meta docs --site (#171)
* docs(spec): docs-site — HTML documentation site as a meta docs --site surface (3-phase design) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(plan): docs-site Phase 1 — port as @metaobjectsdev/docs-site + meta docs --site (3 tasks) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(docs-site): port HTML documentation-site generator as @metaobjectsdev/docs-site Verbatim port of the model-agnostic generator (loader, link-graph, page builders, mermaid emitters, site orchestrator, link-check) + templates + assets + generic acme fixture + tests, wired as a workspace package. 29 tests pass; golden byte-identical. Phase 1 of docs-site (meta docs --site); CLI wiring in a follow-up task. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * build(docs-site): strict-mode type fixes + confirm dist build/template resolution The monorepo's tsconfig.base.json is stricter than the source bun project (noUncheckedIndexedAccess + exactOptionalPropertyTypes). Applied minimal type-only fixes so `tsc -p .` (build) and typecheck pass: non-null assertions on in-bounds array/match accesses, default-destructure on guaranteed splits, and `| undefined` on the optional interface props that receive explicit undefined. No generator behavior change — golden byte-identical, 29 tests pass. Verified the BUILT dist package resolves templates/assets via resolve(import.meta.dir, "../templates") from dist/ (18 pages, 0 dangling on the acme fixture). bun.lock registers the workspace package. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(cli): meta docs --site surface — emit the HTML documentation site Adds a `--site` surface to `meta docs` that renders the browsable HTML documentation site via @metaobjectsdev/docs-site, under `<out>/site`. - `--site` is additive: combined with --model/--api it emits both the markdown surfaces AND the site; when it is the ONLY surface flag, markdown emission is suppressed (parseDocsArgs passes an explicit empty markdown surface list, which resolveDocsConfig honors via `??`). - The site has its OWN model loader (docs-site loadModel), so a site-only run returns before the sdk loadMemory/GenContext path — decoupled, one fewer failure surface, and no gen config required. - `--templates <dir>` is threaded as the docs-site templatesDir override (the scaffold-and-own seam; a consumer template of the same basename wins). - Help text (top-level + `docs --help` FLAGS) documents --site/--model/--api/ --metamodel; cli.test.ts help snapshot updated for the one changed line. - docs-command.test.ts covers site-only (markdown suppressed), --site --model additive, and site determinism. CLI typecheck green; 361 pass / 0 fail. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(spec): docs-site Phase 2 — consolidate the graph onto the shared relationship IR Design for re-sourcing the site's relationship edges from the metadata relationship PRIMITIVES (deriveM2MFields / findReferenceBetween / MetaRelationship) rather than the lossy buildApiModel output or the Drizzle-shaped buildRelationMap. Keeps LinkGraph as the graph shell; enriches relationship edges (M:N-through-junction with the junction kept as a node, belongs-to direction, directed + symmetric self-joins, onDelete); makes structural edges inheritance-aware via resolved accessors; dedupes relationship-vs-bare-FK. Extends the acme fixture (golden regenerates); additive mermaid connector/dashed-M:N rendering; presentation + gates preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(plan): docs-site Phase 2 implementation plan (6 tasks, TDD) Task-by-task plan: extend the acme fixture (M:N + directed/symmetric self-joins + onDelete); extend Ref + belongs-to enrichment + relationship-vs-FK dedupe; M:N-through-junction edges via deriveM2MFields; }o--o{ / dashed-flowchart rendering with junction kept as a node; genericize CURATED palette keys; full verification. Golden regenerated per output-affecting task; determinism + link-check + typecheck gates at each step. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(docs-site): extend acme fixture with M:N + directed/symmetric self-joins + onDelete Adds Product, OrderProduct, CustomerReferral, CustomerFriend to the acme::shop fixture: Order.products (M:N via OrderProduct), Order.customer gains @onDelete: cascade, Customer.referrals (directed self-join via CustomerReferral, @sourceRefField: referrerId), Customer.friends (symmetric self-join via CustomerFriend). Golden regenerated (4 new pages), determinism verified via two independent regenerations (byte-identical). Also updates load.test.ts and package-index-data.test.ts hardcoded object name/count lists — pure structural fallout of the 4 new fixture entities, unrelated to relationship-edge derivation. link-graph.test.ts's pre-existing backlink/degree assertions on Customer (refsTo length/degree == 3) are left failing on purpose: they assume the FK-vs-relationship dedupe that Task 2 of this plan implements. * feat(docs-site): enrich belongs-to relationship edges (cardinality/onDelete/subtype) + dedupe the bare FK * fix(metadata): resolve M:N @through junction by FQN as well as bare name (deriveM2MFields) deriveM2MFields resolved the @through junction via root.findObject, which matches by bare object name only. Since @through is conventionally written fully-qualified (package::Name) and the loader's own FQN-aware validation (refMatchesObject) already accepts that form, every FQN @through passed loading but then threw M2MDerivationError at derivation time, silently dropping the M:N edge for any caller that swallows the error. Fall back to the bare suffix after the last "::" when the direct lookup misses, mirroring the existing resolvedTargetPkField idiom in meta-identity.ts. Adds a regression test exercising a fully-qualified @through. * feat(docs-site): derive M:N through-junction edges (hetero/directed/symmetric) via deriveM2MFields Replaces the Task 2 temporary addM2mEdge stub with the real derivation: resolves the junction FQN via resolveRef, then calls deriveM2MFields to compute sourceJoinField/targetJoinField (hetero match-by-target-entity, directed self-join via @sourceRefField, symmetric self-join in declaration order). A derivation failure (ambiguous self-join) skips the logical edge rather than throwing out of generation — the junction's two FK edges still render. Unblocked by the upstream deriveM2MFields FQN-junction-lookup fix in the metadata package (prior commit) — @through is conventionally written fully qualified, which the bare-name-only lookup could not resolve. * feat(docs-site): render M:N with }o--o{ (ER) + dashed flowchart edge; label onDelete/junction * chore(docs-site): drop the hardcoded domain→color map; hash every package into a neutral palette The CURATED map keyed diagram colors by domain-specific package names carried over from the source project — a soft vocabulary leak in a public repo. Replace it with a plain fixed PALETTE array and hash every package's leaf name into a slot (the same deterministic slot the previously-unmapped packages already used). No hardcoded domain vocabulary remains, and the fixture golden is byte-identical (packages that matched a curated key now hash to an equivalent slot). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(docs-site): explain the MetaData→MetaObject cast in the relationship derivation Addresses the Task 2 review's Minor: the double-cast (as unknown as MetaObject) now carries a comment noting the dn.kind === "object" runtime guarantee and why the unknown bridge is required for tsc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(spec): docs-site Phase 3 — scaffold-and-own + rollout program Sub-project A (this spec): meta docs --scaffold-site copies templates+assets into codegen/docs-site/ (ADR-0034 write-only-if-absent); assetsDir override so owned CSS/JS are used; auto-detect on meta docs --site; docs; validation via the real meta CLI (scaffold→edit→regen→owned-copy-wins) + a scale/parity smoke against a substantial model. Documents the sequenced rollout: cross-port deriveM2MFields FQN sync (Python/Java in-repo), prerelease, and two downstream adopter conversions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(plan): docs-site Phase 3 sub-project A implementation plan (4 tasks, TDD) Task-by-task: assetsDir override in generateSite; export SITE_TEMPLATE_NAMES/SITE_ASSET_NAMES/readSiteFile; meta docs --scaffold-site (copy to codegen/docs-site, write-only-if-absent) + auto-detect owned copies on --site; docs + real-CLI scaffold→edit→owned-wins validation + a scale smoke against an in-repo conformance model. Determinism/link-check/typecheck gates per task. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(docs-site): assetsDir override in generateSite (owned CSS/JS wins over bundled) * feat(docs-site): export SITE_TEMPLATE_NAMES/SITE_ASSET_NAMES/readSiteFile for scaffolding * feat(cli): meta docs --scaffold-site + auto-detect owned templates/assets on --site * docs(docs-site): README + own-your-theme workflow; drop unused emitSite param + test import Adds the docs-site README documenting `meta docs --scaffold-site` (copy templates+ assets into codegen/docs-site/, edit, regenerate — owned copies win, engine stays a dep). Folds in two review Minors: emitSite's now-unused `flags` param dropped (+ both call sites), and an unused mkdirSync import removed from the assetsDir test. Validated end-to-end via the real meta CLI: --scaffold-site writes 11 files; an edited owned template AND asset both win in the output; re-scaffold reports 0 created / 11 preserved (never clobbers); and generateSite renders a real in-repo conformance model to 20 pages with 0 dangling links. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * no-mistakes(review): fix Node import.meta.dir crash and wire docs-site core config * no-mistakes(document): sync docs for docs-site package and --site CLI flags --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 5e809f8 commit 6d60744

96 files changed

Lines changed: 7570 additions & 8 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bun.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/RELEASING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ previous version" (a sed/grep over `*/package.json`):
3636
| Tier | Packages |
3737
|---|---|
3838
| 0 | `metadata`, `render` |
39-
| 1 | `codegen-ts`, `runtime-ts`, `migrate-ts`, `sdk`, `runtime-web` |
39+
| 1 | `codegen-ts`, `runtime-ts`, `migrate-ts`, `sdk`, `runtime-web`, `docs-site` |
4040
| 2 | `codegen-ts-react`, `codegen-ts-tanstack`, `react` |
4141
| 3 | `tanstack` |
4242
| 4 | `cli`, `ai-runtime` (leaves — nothing depends on them; publish last) |
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
# Docs Site — Phase 1 (Port as `meta docs --site`) Implementation Plan
2+
3+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4+
5+
**Goal:** Bring the proven HTML documentation-site generator into the metaobjects monorepo as `@metaobjectsdev/docs-site`, wired as a new `meta docs --site` surface, keeping its current graph/output verbatim.
6+
7+
**Architecture:** A verbatim port of a model-agnostic bun/TypeScript generator (loader → link-graph → page builders → mermaid emitters → site orchestrator → link-check) into a new workspace package, plus a thin `--site` branch in the `meta docs` CLI command that calls the package's `generateSite()`. No behavior change to the generator; the shared-relationship-IR consolidation is Phase 2.
8+
9+
**Tech Stack:** bun, TypeScript (monorepo `tsconfig.base.json`), `@metaobjectsdev/metadata` + `@metaobjectsdev/render` (workspace deps), `yaml`, mustache templates, Tailwind/daisyUI + mermaid (CDN, referenced by the site's chrome).
10+
11+
## Global Constraints
12+
13+
- **Public repo:** metaobjects is public. The package ships ONLY the generic `acme` fixture + generic docs — no downstream/consumer/client names, no absolute local paths. Verify `git diff` before every commit.
14+
- **Verbatim port:** the generator's `src/`, `templates/`, `assets/`, `test/`, and `test/fixture/` are copied UNCHANGED from the reference implementation. Do not "improve" them here — Phase 2 owns graph changes. The ONLY new/edited files are the package manifest/tsconfig, `src/index.ts`, and the CLI `--site` wiring.
15+
- **Determinism + link-check preserved:** the byte-identical golden test and the link checker (generation throws on dangling links) must stay green; two regenerations must be byte-identical.
16+
- **Escaping invariant:** `@metaobjectsdev/render`'s `render()` does not HTML-escape; the ported builders already escape at the boundary via the single canonical `esc`. Do not change this.
17+
- **Package conventions:** match the sibling TS packages (`codegen-ts-react` etc.): `"type": "module"`, `exports` with a `bun → ./src/index.ts` condition, `tsc -p .` build to `dist`, `bun test`, workspace deps as `workspace:*`, `"license": "Apache-2.0"`, `publishConfig.access: public`.
18+
19+
## Source of the ported files
20+
21+
The verbatim-port files come from the reference implementation's `metadata-docs` tree. The exact file list is fixed (below). If a reviewer/implementer does not have that tree mounted, the files are available on the `feat/docs-site` branch history where the controller placed them; treat the on-branch copies as the source of truth. Do NOT re-derive them.
22+
23+
**Port these UNCHANGED** into `server/typescript/packages/docs-site/`:
24+
- `src/`: `badges.ts`, `coverage.ts`, `link-check.ts`, `link-graph.ts`, `load.ts`, `mermaid.ts`, `mustache-highlight.ts`, `package-docs.ts`, `site.ts`, `yaml-comments.ts`, and `src/builders/`: `extras.ts`, `index-data.ts`, `object-data.ts`, `output-data.ts`, `package-data.ts`, `prompt-data.ts`.
25+
- `templates/`: the 9 `*.mustache` files (`chrome-head`, `chrome-foot`, `index.html`, `package.html`, `object.html`, `prompt.html`, `output.html`, `enums.html`, `coverage.html`).
26+
- `assets/`: `site.css`, `site.js`.
27+
- `test/`: the 14 `*.test.ts` files (`badges`, `coverage`, `golden`, `graph-v2`, `index-package-v2`, `link-check`, `link-graph`, `load`, `mermaid`, `mustache-highlight`, `object-data`, `object-data-v2`, `package-docs`, `package-index-data`, `prompt-output-extras`, `site` — note some map 1:1) and `test/fixture/` (`input/acme/**` + `golden/**`).
28+
- **Do NOT port** the reference `generate.ts` (it is the downstream entry point; the CLI replaces it) or `package.json`/`bun.lock` (the monorepo manifest replaces them).
29+
30+
---
31+
32+
## File Structure
33+
34+
New package `server/typescript/packages/docs-site/`:
35+
- `package.json` — NEW (manifest; deps `@metaobjectsdev/metadata` + `@metaobjectsdev/render` `workspace:*`, `yaml`).
36+
- `tsconfig.json`, `tsconfig.typecheck.json` — NEW (extend `../../tsconfig.base.json`, matching siblings).
37+
- `README.md`, `LICENSE` — NEW (LICENSE copied from a sibling package).
38+
- `src/**` — ported verbatim + `src/index.ts` NEW (public API barrel).
39+
- `templates/**`, `assets/**` — ported verbatim (live at package root so `resolve(import.meta.dir, "../templates")` resolves from both `src/` and `dist/`).
40+
- `test/**` — ported verbatim.
41+
42+
Modified:
43+
- `server/typescript/packages/cli/src/commands/docs.ts` — add the `--site` surface.
44+
45+
---
46+
47+
## Task 1: Scaffold the package + port the generator; `bun test` green
48+
49+
**Files:**
50+
- Create: `server/typescript/packages/docs-site/package.json`, `tsconfig.json`, `tsconfig.typecheck.json`, `README.md`, `LICENSE`, `src/index.ts`
51+
- Create (verbatim port): all `src/**`, `templates/**`, `assets/**`, `test/**` per "Source of the ported files"
52+
- Test: the ported `test/**` (run via `bun test`)
53+
54+
**Interfaces:**
55+
- Produces: `@metaobjectsdev/docs-site` exporting `generateSite(opts: SiteOptions): Promise<SiteResult>`, `SiteOptions = { sourceDirs: string[]; outDir: string; title: string; stamp: string; commit: string; core?: { n?: number }; templatesDir?: string }`, `SiteResult = { pages: string[]; coverage: CoverageReport; anomalies: Anomaly[]; dangling: string[] }` (re-exported from `./src/site`).
56+
57+
- [ ] **Step 1: Create the package dir + port the files verbatim.**
58+
59+
```bash
60+
cd server/typescript/packages
61+
mkdir -p docs-site
62+
# from the reference tree (or the branch copies), copy UNCHANGED:
63+
cp -R <ref>/src docs-site/src
64+
cp -R <ref>/templates docs-site/templates
65+
cp -R <ref>/assets docs-site/assets
66+
cp -R <ref>/test docs-site/test
67+
# LICENSE from a sibling:
68+
cp codegen-ts-react/LICENSE docs-site/LICENSE
69+
```
70+
Confirm no `generate.ts`, no `package.json`, no `bun.lock`, no `node_modules`, no `mockup/` were copied.
71+
72+
- [ ] **Step 2: Write `docs-site/package.json`** (manifest matching siblings; deps as workspace):
73+
74+
```json
75+
{
76+
"name": "@metaobjectsdev/docs-site",
77+
"version": "0.15.6",
78+
"description": "HTML documentation-site generator for metaobjects models — a browsable multi-page site (nav, search, per-object/package pages, kind-aware ER diagrams). The `meta docs --site` surface.",
79+
"type": "module",
80+
"main": "./dist/index.js",
81+
"types": "./dist/index.d.ts",
82+
"exports": {
83+
".": { "bun": "./src/index.ts", "types": "./dist/index.d.ts", "default": "./dist/index.js" }
84+
},
85+
"files": ["dist", "src", "templates", "assets", "README.md", "LICENSE"],
86+
"scripts": { "build": "tsc -p .", "typecheck": "tsc -p tsconfig.typecheck.json", "test": "bun test" },
87+
"license": "Apache-2.0",
88+
"author": "Doug Mealing <doug@dougmealing.com>",
89+
"homepage": "https://metaobjects.dev",
90+
"bugs": { "url": "https://github.com/metaobjectsdev/metaobjects/issues" },
91+
"repository": { "type": "git", "url": "https://github.com/metaobjectsdev/metaobjects.git", "directory": "server/typescript/packages/docs-site" },
92+
"keywords": ["metaobjects", "docs", "documentation", "html", "site", "erd"],
93+
"publishConfig": { "access": "public" },
94+
"dependencies": {
95+
"@metaobjectsdev/metadata": "workspace:*",
96+
"@metaobjectsdev/render": "workspace:*",
97+
"yaml": "^2.9.0"
98+
},
99+
"devDependencies": { "bun-types": "latest", "typescript": "^5.6.0" }
100+
}
101+
```
102+
103+
- [ ] **Step 3: Write `docs-site/tsconfig.json` and `tsconfig.typecheck.json`** (match siblings):
104+
105+
`tsconfig.json`:
106+
```json
107+
{ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "./dist", "rootDir": "./src" }, "include": ["src/**/*"], "exclude": ["dist", "test", "node_modules"] }
108+
```
109+
`tsconfig.typecheck.json` (copy the sibling's — typically includes `src` + `test` with `noEmit`; mirror `codegen-ts-react/tsconfig.typecheck.json` exactly).
110+
111+
- [ ] **Step 4: Write `docs-site/src/index.ts`** (public API barrel):
112+
113+
```ts
114+
export { generateSite } from "./site";
115+
export type { SiteOptions, SiteResult } from "./site";
116+
```
117+
(If `site.ts` does not already export `SiteOptions`/`SiteResult` as named types, confirm it does — the reference version exports both. Do not add new logic.)
118+
119+
- [ ] **Step 5: Install + run the ported tests.** From the repo root:
120+
121+
```bash
122+
bun install
123+
bun test server/typescript/packages/docs-site
124+
```
125+
Expected: all ported tests pass (the golden byte-identical test + graph/builder/mermaid/link-check unit tests). If the golden test fails, the FIRST suspect is template/asset resolution: `resolve(import.meta.dir, "../templates")` must resolve to `docs-site/templates`. Since bun runs `src/site.ts` directly, `import.meta.dir` = `docs-site/src`, so `../templates` = `docs-site/templates` ✓ and `../assets` = `docs-site/assets` ✓. If a test references a path that assumed the old repo layout, fix the TEST's fixture path only (never the generator's behavior); report any such change.
126+
127+
- [ ] **Step 6: Hygiene check + commit.**
128+
129+
```bash
130+
grep -rniE "<downstream-name>|/home/" server/typescript/packages/docs-site/ && echo "LEAK — stop" || echo clean
131+
git add server/typescript/packages/docs-site
132+
git commit -m "feat(docs-site): port HTML documentation-site generator as @metaobjectsdev/docs-site (verbatim, acme fixture, tests green)"
133+
```
134+
135+
---
136+
137+
## Task 2: Build to `dist` + typecheck green (dist template/asset resolution)
138+
139+
**Files:**
140+
- Modify: none (verifies the Task-1 package builds); may adjust `files`/paths only if the build reveals a gap.
141+
142+
**Interfaces:**
143+
- Consumes: the Task-1 package.
144+
- Produces: a built `dist/` with working template/asset resolution, so downstream consumers importing the built package (not the `bun` src condition) still find templates.
145+
146+
- [ ] **Step 1: Build.** `bun run --cwd server/typescript/packages/docs-site build` (i.e. `tsc -p .`). Expected: `dist/` emitted, no type errors.
147+
- [ ] **Step 2: Typecheck.** `bun run --cwd server/typescript/packages/docs-site typecheck`. Expected: clean.
148+
- [ ] **Step 3: Verify dist template resolution.** From a scratch script, import the BUILT package (force the `default`/`dist` condition, not `bun`) and generate against the fixture into a tmp dir; confirm it does not throw and writes pages. Because `dist/site.js` sits at `docs-site/dist/`, `resolve(import.meta.dir, "../templates")` = `docs-site/templates` ✓ — templates/assets are NOT under `dist`, and are included in `files`, so both bun-src and dist runs resolve them. If the built run cannot find templates, the fix is to ensure `templates`/`assets` are in `package.json` `files` (already are) and that the resolution is `../templates` from `dist` (it is) — do NOT copy templates into `dist`.
149+
- [ ] **Step 4: Commit** (only if any manifest/path adjustment was needed):
150+
151+
```bash
152+
git commit -am "build(docs-site): confirm dist build + template/asset resolution from built output"
153+
```
154+
155+
---
156+
157+
## Task 3: Wire the `meta docs --site` surface + end-to-end verify
158+
159+
**Files:**
160+
- Modify: `server/typescript/packages/cli/src/commands/docs.ts` (add `--site` flag + emit branch)
161+
- Modify: `server/typescript/packages/cli/package.json` (add `@metaobjectsdev/docs-site` as a `workspace:*` dependency)
162+
- Modify: `server/typescript/packages/cli/src/index.ts` (help text: mention `--site`)
163+
164+
**Interfaces:**
165+
- Consumes: `generateSite` from `@metaobjectsdev/docs-site`.
166+
167+
- [ ] **Step 1: Add the dependency.** In `cli/package.json` dependencies add `"@metaobjectsdev/docs-site": "workspace:*"`; run `bun install` from the repo root.
168+
169+
- [ ] **Step 2: Parse the `--site` flag.** In `docs.ts`'s arg loop (alongside `--model`/`--api`/`--metamodel`), add:
170+
171+
```ts
172+
} else if (a === "--site") {
173+
wantSite = true;
174+
```
175+
Declare `let wantSite = false;` next to `wantModel`/`wantApi`/`wantMetamodel`, and return it from the parse result (extend the options type with `site: boolean` mirroring `metamodel: boolean`).
176+
177+
- [ ] **Step 3: Emit the site when `--site` is set.** In the command's run body, AFTER the existing surface handling, add a branch that resolves the metadata source dir(s) the command already computes (the `<metadata>` root's `metaobjects/` source path used to load the model — reuse the SAME dirs the model loader reads), then calls `generateSite`:
178+
179+
```ts
180+
import { generateSite } from "@metaobjectsdev/docs-site";
181+
// ... after model/api/metamodel handling, before the final summary:
182+
if (opts.site) {
183+
const outDir = /* the resolved out dir, default ./docs/site when --site */;
184+
const r = await generateSite({
185+
sourceDirs: /* the resolved metadata source dirs (same the model loader uses) */,
186+
outDir,
187+
title: /* project/model title — reuse the command's existing title source, else "Metadata" */,
188+
stamp: new Date().toISOString().slice(0, 10),
189+
commit: "", // CLI has no repo commit context by default; empty is fine
190+
core: { n: 15 },
191+
});
192+
// reuse the command's existing "wrote N files" reporting shape for the site
193+
}
194+
```
195+
Notes for the implementer: (a) find how the command resolves the metadata source directory it feeds to the loader — pass those exact dirs as `sourceDirs`; (b) default the site out dir to `./docs/site` (parallel to the markdown `./docs`); (c) `--templates <dir>` (already parsed) should be threaded as `templatesDir` so the scaffold-and-own override works for the site too; (d) `--site` is additive — if combined with `--model`/`--api`, emit both.
196+
197+
- [ ] **Step 4: Help text.** In `cli/src/index.ts` `docs` help, add a line: ` --site generate the browsable HTML documentation site (docs/site/)`.
198+
199+
- [ ] **Step 5: End-to-end verify.** Build the cli (`bun run --cwd server/typescript/packages/cli build` if it builds; else run via bun src). Run the CLI's `docs --site` against the ported `acme` fixture model into a tmp dir:
200+
- it MUST NOT throw (link-check green),
201+
- it writes `index.html` + per-package/object pages + `assets/`,
202+
- two runs are byte-identical (determinism).
203+
Grep the output for `party.?lore`/`/home/`none.
204+
205+
- [ ] **Step 6: Hygiene + commit.**
206+
207+
```bash
208+
grep -rniE "<downstream-name>|/home/" server/typescript/packages/cli/src/commands/docs.ts && echo "LEAK — stop" || echo clean
209+
git add server/typescript/packages/cli
210+
git commit -m "feat(cli): meta docs --site surface — emit the HTML documentation site via @metaobjectsdev/docs-site"
211+
```
212+
213+
---
214+
215+
## Self-Review
216+
217+
**Spec coverage (Phase 1):** new `@metaobjectsdev/docs-site` packageTask 1; `meta docs --site` surface, additive to markdownTask 3; verbatim port of generator + `acme` fixture + testsTask 1; deterministic + link-checkedTasks 1 & 3; public-repo hygienehygiene checks in Tasks 1 & 3; dist build + template resolutionTask 2; escaping/determinism invariants preserved (no generator edits) → Global Constraints. Phase 2 (shared-IR graph) and Phase 3 (scaffold-and-own + consumers) are out of scope by design.
218+
219+
**Placeholder scan:** the `/* ... */` comments in Task 3 Step 3 are integration-point pointers, not code placeholdersthe surrounding code + notes tell the implementer exactly what to substitute (the command's own resolved source dirs / out / title). They are unavoidable because they reference the CLI command's existing local variables, which the implementer reads in-file. Everything else is concrete.
220+
221+
**Type consistency:** `generateSite`/`SiteOptions`/`SiteResult` names match across Tasks 1 and 3; `wantSite`/`site: boolean` mirror the existing `wantMetamodel`/`metamodel: boolean` pattern in `docs.ts`; `templatesDir` matches `SiteOptions`.

0 commit comments

Comments
 (0)