/packages/create-zudo-doc/CLAUDE.md
CLAUDE.md at /packages/create-zudo-doc/CLAUDE.md
Path: packages/
create-zudo-doc
CLI scaffold tool for creating new zudo-doc documentation sites. Generates a project with configurable features, a Default light/dark color scheme, and i18n support.
Eject CLI contract: the per-component
zudo-doc eject <component>swizzle CLI +.zudo-doc.jsonprovenance marker are specified indocs/(C0 #2359; implemented by C1 #2362; Decision 5 revised by S4 #2373 — theeject- contract. md zudo-docbin now ships from@takazudo/zudo-doc, notcreate-zudo-doc, so it is reachable in generated projects).
Architecture (minimal-scaffold, epic zudolab/zudo-doc#2651 Wave 6 #2660)
The generator emits the locked ~17-file minimal manifest — one config file (zfb.config.ts, zudoDoc({ ...only fields you chose })) plus markdown content plus a handful of unavoidable root files. Everything else (layout, chrome, islands, default @theme tokens, even the doc ROUTES themselves via packageOwnedRoutes) ships from @takazudo/zudo-doc in node_modules.
Copy the minimal base template (
templates/base/) — no injection anchors left; every file is either shipped verbatim or (global.css) patched by exactly one feature'spostProcesshook.Generate the ONE
zfb.config.tsprogrammatically (zfb-config-gen.ts) — diff-from-defaults: only fields the user actually chose are emitted.Compose selected features — copy feature files (only five features have a
templates/features/<name>/files/directory:claudeSkills,claudeSkillsWriting,i18n,tauri,tauriDev) and runpostProcesshooks for the handful of cases that need a small source patch (docHistorythreads itself into the doc stub(s);designTokenPanelinserts the one conditional zdtp CSS import line;tagGovernancewrites a tinysrc/config/pair inline — it has no template directory — see each module's header comment for why).
Both base and i18n document stubs unconditionally consume virtual:zudo-doc-chrome-bindings. The doc-history post-processor spreads the configured object first and replaces only DocHistory, so it must never drop primary slots, headerRightComponents, or mdxExtras. Presentational chrome customization is config + one typed module; it does not require a route fork.
Most feature modules are now pure settings-field emission: the feature only changes what zfb-config-gen.ts writes into zudoDoc({...}), because the corresponding UI/behavior is entirely package-owned already (package-first migration, epics #2321/#2344/#2356). src/features/<name>.ts's header comment says, per feature, exactly why there's nothing (or almost nothing) left to inject or copy.
Key Files
| File | Role |
|---|---|
src/ | Orchestrates the scaffold pipeline: copy base, seed content, generate zfb.config.ts + package.json, compose features |
src/ | Composition engine: injection system (mostly unused now — see below), feature resolution |
src/features/*.ts | Feature modules — settings-field emission via zfb-config-gen.ts + a handful of genuine file copies / postProcess patches. 19 module files, 19 featureModules keys in index.ts — one of which (footer) is a pseudo-feature triggered by footerNavGroup/footerCopyright/footerTaglist. sidebarFilter has no module (built into the package's sidebar tree); skillSymlinker, claudeSkills, claudeSkillsWriting, and changelog are handled directly in scaffold.ts |
src/ | The SINGLE config generator — emits the one zfb.config.ts (defineConfig(zudoDoc({...}))), diff-from-defaults against a local mirror of packages/'s DEFAULT_SETTINGS. Replaces the former settings-gen.ts + zfb-config-gen.ts two-file split — there is no more src/ in a fresh scaffold |
src/ | Generates the per-project CLAUDE.md for the scaffolded site, including the current zfb semantic-highlighting contract, chrome bindings, and binding-aware eject guidance |
src/ | Resolves a JSON --preset file (or CLI flags) into UserChoices — unrelated to the package's own @takazudo/, despite the similar name |
src/ | Feature definitions, supported langs, header-right labels, and the current Default light/dark scheme pairing |
src/ | Shared utilities (patchFile, patchDefaultLang, getSecondaryLang) |
src/ | CLI argument parsing (minimist) |
src/ | Programmatic API (createZudoDoc()). CreateOptions must stay in sync with PresetJson (preset.ts) — a field added to one and not the other is a type-level parity gap (#2922). Shape validation for headerRightItems/metaTags is shared via preset.ts's exported validateHeaderRightItems()/validateMetaTags() — extend those, don't re-implement the allowlists here. |
src/ | Interactive prompts (@clack/prompts) |
src/ | Entry point |
Template Directories
| Directory | Role |
|---|---|
templates/base/ | The locked ~17-file minimal manifest (barebone, EN-only): pages/ (1-line re-export), pages/docs/[[...slug]].tsx (self-contained doc stub — see its header comment for why it's required), src/ (~20-line @import chain + token-override slot), tsconfig.json (5-line extends form), public//favicon.ico/favicon-32x32.png/favicon-16x16.png (byte-identical copies of the repo-root public/ files — #3186). zfb.config.ts/package.json/CLAUDE.md/.gitignore/.npmrc/pnpm-workspace.yaml are generated programmatically, not copied from here. |
templates/features/*/files/ | Feature-specific files copied when a feature is selected. Exactly five have a template directory: claudeSkills (skill copies, driven from scaffold.ts), claudeSkillsWriting (the zudo-doc-writing skill, also driven from scaffold.ts), i18n (locale doc stub), tauri and tauriDev (Rust shells). tagGovernance has no template directory — it writes one explicit tag vocabulary/config module inline in its postProcess; all audit/suggest behavior comes from package-owned bins. |
Injection anchors — mostly retired
src/'s ANCHOR_FILES is now an empty array. Every anchor target the old system targeted — pages/, src/, src/'s @slot:global-css:* comments — is gone from templates/base/ (the package owns chrome/islands/ settings-types entirely now; global.css shrank to a fixed @import chain with no anchors). The Injection/applyInjections/cleanAnchors machinery in compose.ts is kept as infrastructure for a future feature that genuinely needs it, not because anything uses it today — a few feature modules (docHistory, designTokenPanel, tagGovernance) instead use a plain postProcess hook that does a small, targeted string patch (see each module's source).
Testing
Unit tests
pnpm testRuns vitest tests in src/__tests__/.
Generator CLI integration tests
Two Claude Code skills test the full scaffold-build-run cycle:
/— Test a single generation patternl- generator- cli- tester <pattern> /— Run all 9 patterns, fix bugs, verify everythingl- run- generator- cli- whole- test
Test patterns
| Pattern | Description |
|---|---|
barebone | Everything OFF — minimal project |
search | Only search enabled |
i18n | Only i18n enabled |
sidebar-filter | Only sidebar filter enabled |
claude-resources | Only Claude Resources enabled |
design-token-panel | Only design token panel enabled (uses --design-token-panel CLI flag) |
light-dark | Light-dark color scheme mode |
lang-ja | Japanese as default language |
all-features | Everything ON |
Always rebuild the CLI before testing:
pnpm buildAdding a New Feature
Follow the "Feature Change Checklist" in the repo-root CLAUDE.md — that is the single canonical ordering, and it starts at packages/ (the ONE field census) rather than at this package. This file deliberately keeps no second copy: the two orderings used to be hand-maintained side by side and drifted (they disagreed about which features have a templates/features/ directory).
Generator-side touchpoints, for orientation only — the root checklist has the authoritative order and the reasons: src/ (CLI flag), src/features/<name>.ts + src/ (module + registration), src/ (DEFAULT_MIRROR + buildDesiredConfig() + FIELD_ORDER), src/ (deps), src/.
After changes, run / to verify no drift remains between the main project and the generator.