create-zudo-doc 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/api.d.ts +6 -0
  2. package/dist/api.js +5 -2
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +4 -0
  5. package/dist/index.js +26 -1
  6. package/dist/scaffold.d.ts +1 -1
  7. package/dist/scaffold.js +10 -6
  8. package/dist/utils.d.ts +27 -0
  9. package/dist/utils.js +72 -1
  10. package/package.json +1 -1
  11. package/templates/base/pages/index.tsx +6 -4
  12. package/templates/base/pages/lib/_chrome.ts +166 -0
  13. package/templates/base/pages/lib/_doc-route-entries.ts +7 -51
  14. package/templates/base/pages/lib/_nav-source-docs.ts +9 -29
  15. package/templates/base/pages/lib/_route-context.ts +32 -0
  16. package/templates/base/src/config/tag-vocabulary-types.ts +4 -39
  17. package/templates/features/claudeResources/files/src/integrations/claude-resources/__tests__/generate.test.ts +137 -22
  18. package/templates/features/claudeResources/files/src/integrations/claude-resources/generate.ts +115 -28
  19. package/templates/features/i18n/files/pages/[locale]/index.tsx +6 -4
  20. package/templates/base/pages/_mdx-components.ts +0 -106
  21. package/templates/base/pages/lib/_category-nav.tsx +0 -34
  22. package/templates/base/pages/lib/_category-tree-nav.tsx +0 -45
  23. package/templates/base/pages/lib/_compose-meta-title.ts +0 -25
  24. package/templates/base/pages/lib/_doc-body-end.tsx +0 -8
  25. package/templates/base/pages/lib/_doc-content-header.tsx +0 -17
  26. package/templates/base/pages/lib/_doc-history-area.tsx +0 -34
  27. package/templates/base/pages/lib/_doc-metainfo-area.tsx +0 -26
  28. package/templates/base/pages/lib/_doc-page-renderer.tsx +0 -31
  29. package/templates/base/pages/lib/_doc-page-shell.tsx +0 -29
  30. package/templates/base/pages/lib/_doc-pager.tsx +0 -9
  31. package/templates/base/pages/lib/_doc-route-paths.ts +0 -16
  32. package/templates/base/pages/lib/_doc-tags-area.tsx +0 -30
  33. package/templates/base/pages/lib/_footer-with-defaults.tsx +0 -58
  34. package/templates/base/pages/lib/_head-with-defaults.tsx +0 -22
  35. package/templates/base/pages/lib/_header-with-defaults.tsx +0 -45
  36. package/templates/base/pages/lib/_inline-version-switcher.tsx +0 -23
  37. package/templates/base/pages/lib/_math-block.tsx +0 -4
  38. package/templates/base/pages/lib/_nav-data-prep.ts +0 -118
  39. package/templates/base/pages/lib/_search-widget-script.ts +0 -2
  40. package/templates/base/pages/lib/_sidebar-prepaint.tsx +0 -11
  41. package/templates/base/pages/lib/_sidebar-with-defaults.tsx +0 -26
  42. package/templates/base/pages/lib/_site-tree-nav.tsx +0 -37
  43. package/templates/base/pages/lib/_toc-title.ts +0 -3
  44. package/templates/base/pages/lib/route-enumerators.ts +0 -58
  45. package/templates/base/src/components/tree-nav-shared.tsx +0 -71
  46. package/templates/base/src/utils/content-files.ts +0 -110
  47. package/templates/features/docTags/files/pages/lib/_tag-pages.tsx +0 -47
  48. package/templates/features/versioning/files/pages/lib/_versions-page.tsx +0 -33
package/dist/api.d.ts CHANGED
@@ -16,5 +16,11 @@ export interface CreateOptions {
16
16
  packageManager: "pnpm" | "npm" | "yarn" | "bun";
17
17
  /** Install dependencies after scaffolding (default: false) */
18
18
  install?: boolean;
19
+ /**
20
+ * Initialize a git repository + initial commit after scaffolding
21
+ * (default: false). The CLI defaults this on; the programmatic API defaults
22
+ * it off so automation / test callers never create unexpected repos.
23
+ */
24
+ git?: boolean;
19
25
  }
20
26
  export declare function createZudoDoc(options: CreateOptions): Promise<string>;
package/dist/api.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import path from "path";
2
2
  import { scaffold } from "./scaffold.js";
3
- import { installDependencies, validateProjectName } from "./utils.js";
3
+ import { initGitRepo, installDependencies, validateProjectName } from "./utils.js";
4
4
  export async function createZudoDoc(options) {
5
- const { install = false, ...rest } = options;
5
+ const { install = false, git = false, ...rest } = options;
6
6
  const nameError = validateProjectName(rest.projectName);
7
7
  if (nameError)
8
8
  throw new Error(`Invalid projectName: ${nameError}`);
@@ -12,5 +12,8 @@ export async function createZudoDoc(options) {
12
12
  if (install) {
13
13
  installDependencies(targetDir, choices.packageManager);
14
14
  }
15
+ if (git) {
16
+ initGitRepo(targetDir);
17
+ }
15
18
  return targetDir;
16
19
  }
package/dist/cli.d.ts CHANGED
@@ -33,6 +33,8 @@ export interface CliArgs {
33
33
  preset?: string;
34
34
  pm?: "pnpm" | "npm" | "yarn" | "bun";
35
35
  install?: boolean;
36
+ /** Initialize a git repository + initial commit after scaffolding (default on). */
37
+ git?: boolean;
36
38
  yes?: boolean;
37
39
  help?: boolean;
38
40
  }
package/dist/cli.js CHANGED
@@ -67,6 +67,8 @@ export function parseArgs(argv = process.argv.slice(2)) {
67
67
  }
68
68
  if (wasPassed("install"))
69
69
  args.install = raw["install"] !== false;
70
+ if (wasPassed("git"))
71
+ args.git = raw["git"] !== false;
70
72
  if (raw.yes || raw.y)
71
73
  args.yes = true;
72
74
  if (raw.help || raw.h)
@@ -95,6 +97,8 @@ ${featureHelp}
95
97
  --preset <path> Load settings from a JSON preset file (use "-" for stdin)
96
98
  --pm <manager> pnpm | npm | yarn | bun
97
99
  --[no-]install Install dependencies after scaffolding
100
+ --[no-]git Initialize a git repository + initial commit
101
+ (default: on; enables doc-history metadata)
98
102
  -y, --yes Use defaults for unspecified options, skip prompts
99
103
  -h, --help Show this help message
100
104
 
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { FEATURES } from "./constants.js";
6
6
  import { loadPreset } from "./preset.js";
7
7
  import { runPrompts } from "./prompts.js";
8
8
  import { scaffold } from "./scaffold.js";
9
- import { installDependencies } from "./utils.js";
9
+ import { installDependencies, initGitRepo } from "./utils.js";
10
10
  async function main() {
11
11
  const args = parseArgs();
12
12
  // Handle --help
@@ -144,6 +144,31 @@ async function main() {
144
144
  s2.stop("Installation failed. Run install manually.");
145
145
  }
146
146
  }
147
+ // Initialize a git repository (default on; --no-git to opt out). doc-history
148
+ // reads `git log` for each page's Created/Updated/Author block, so a project
149
+ // without git renders empty history. Runs after install so the lockfile is
150
+ // part of the initial commit; auto-skips if the target is already inside a
151
+ // repo or if git is unavailable.
152
+ const shouldInitGit = args.git ?? true;
153
+ if (shouldInitGit) {
154
+ const s3 = p.spinner();
155
+ s3.start("Initializing git repository...");
156
+ const result = initGitRepo(targetDir);
157
+ switch (result.status) {
158
+ case "ok":
159
+ s3.stop("Initialized git repository with an initial commit.");
160
+ break;
161
+ case "skipped-existing-repo":
162
+ s3.stop("Already inside a git repository — skipped git init.");
163
+ break;
164
+ case "skipped-no-git":
165
+ s3.stop("git not found — skipped git init.");
166
+ break;
167
+ case "failed":
168
+ s3.stop("Could not initialize git — continuing without it.");
169
+ break;
170
+ }
171
+ }
147
172
  p.outro(`${pc.green("Done!")} Your project is ready at ${pc.cyan(choices.projectName)}`);
148
173
  console.log();
149
174
  console.log(` ${pc.bold("Next steps:")}`);
@@ -11,5 +11,5 @@ export { getSecondaryLang };
11
11
  *
12
12
  * Bumped in lockstep by scripts/release-create-zudo-doc.sh.
13
13
  */
14
- export declare const ZUDO_DOC_PIN = "^1.2.0";
14
+ export declare const ZUDO_DOC_PIN = "^2.0.0";
15
15
  export declare function scaffold(choices: UserChoices): Promise<void>;
package/dist/scaffold.js CHANGED
@@ -18,7 +18,7 @@ export { getSecondaryLang };
18
18
  *
19
19
  * Bumped in lockstep by scripts/release-create-zudo-doc.sh.
20
20
  */
21
- export const ZUDO_DOC_PIN = "^1.2.0";
21
+ export const ZUDO_DOC_PIN = "^2.0.0";
22
22
  /**
23
23
  * Files in `templates/base/**` that must never be copied into a generated
24
24
  * project. Each entry is matched against the path relative to `templates/base/`
@@ -477,13 +477,17 @@ function generatePackageJson(choices) {
477
477
  // No consumer-facing / CLI breaking change.
478
478
  // next.68: routine toolchain bump from next.67, adopted in
479
479
  // lockstep with the root package.json pins. No consumer-facing / CLI change.
480
- // next.69 (current pin): routine toolchain bump from next.68, adopted in
480
+ // next.69: routine toolchain bump from next.68, adopted in
481
481
  // lockstep with the root package.json pins. No consumer-facing / CLI change.
482
- "@takazudo/zfb": "0.1.0-next.69",
483
- "@takazudo/zfb-runtime": "0.1.0-next.69",
482
+ // next.70 (current pin): routine toolchain bump from next.69, re-aligned
483
+ // with the root package.json pins (root was bumped in b5489acf; this
484
+ // scaffold pin lagged at next.69 and broke check-pin-parity). No
485
+ // consumer-facing / CLI change.
486
+ "@takazudo/zfb": "0.1.0-next.70",
487
+ "@takazudo/zfb-runtime": "0.1.0-next.70",
484
488
  // zfb-adapter-cloudflare — required for any route with `prerender = false`.
485
489
  // Pinned in lockstep with @takazudo/zfb.
486
- "@takazudo/zfb-adapter-cloudflare": "0.1.0-next.69",
490
+ "@takazudo/zfb-adapter-cloudflare": "0.1.0-next.70",
487
491
  // @takazudo/zudo-doc — published from this monorepo via
488
492
  // .github/workflows/publish-zudo-doc.yml. The pin here is bumped in
489
493
  // lockstep by scripts/release-create-zudo-doc.sh whenever zudo-doc's
@@ -566,7 +570,7 @@ function generatePackageJson(choices) {
566
570
  // @takazudo/zudo-doc/integrations/doc-history which in turn imports
567
571
  // @takazudo/zudo-doc-history-server/git-history. Without this dep the
568
572
  // plugin host fails at init with ERR_MODULE_NOT_FOUND — W8A (#1739).
569
- deps["@takazudo/zudo-doc-history-server"] = "^1.2.0";
573
+ deps["@takazudo/zudo-doc-history-server"] = "^2.0.0";
570
574
  // tsx is no longer needed here: the relocated package plugin imports the
571
575
  // runner directly (no `tsx -e` spawn) since the package ships compiled
572
576
  // dist/ — package-first migration #2321 (#2337).
package/dist/utils.d.ts CHANGED
@@ -7,6 +7,33 @@
7
7
  */
8
8
  export declare function validateProjectName(name: string): string | null;
9
9
  export declare function installDependencies(dir: string, pm: string): void;
10
+ export type GitInitResult = {
11
+ status: "ok";
12
+ } | {
13
+ status: "skipped-existing-repo";
14
+ } | {
15
+ status: "skipped-no-git";
16
+ } | {
17
+ status: "failed";
18
+ message: string;
19
+ };
20
+ /**
21
+ * Initialize a git repository in `dir` and create an initial commit.
22
+ *
23
+ * Why: zudo-doc's doc-history feature reads `git log` for each page's
24
+ * Created/Updated/Author block. A scaffolded project with no git repo renders
25
+ * empty history — and on older `@takazudo/zudo-doc-history-server` it crashed
26
+ * `pnpm dev` outright (the preBuild hook ran `git rev-parse` and threw).
27
+ * Initializing git here makes the feature work out of the box.
28
+ *
29
+ * Safe by construction:
30
+ * - `skipped-no-git` when git is not installed;
31
+ * - `skipped-existing-repo` when `dir` is already inside a git work tree (e.g.
32
+ * scaffolding into an existing monorepo) — never nest repositories;
33
+ * - the initial commit only falls back to a neutral identity when the user has
34
+ * none configured, so a normal user's commit keeps their own identity.
35
+ */
36
+ export declare function initGitRepo(dir: string): GitInitResult;
10
37
  export declare function capitalize(str: string): string;
11
38
  /** Get a short uppercase label for a language code (e.g. "en" → "EN", "zh-cn" → "ZH-CN"). */
12
39
  export declare function getLangLabel(langCode: string): string;
package/dist/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- import { execSync } from "child_process";
1
+ import { execSync, execFileSync } from "child_process";
2
2
  import fs from "fs-extra";
3
3
  // Project-name grammar (locked by F4 — S4 #2013):
4
4
  // /^[a-z0-9][a-z0-9._-]*$/, max 214 chars, unscoped, used as both directory
@@ -36,6 +36,77 @@ export function installDependencies(dir, pm) {
36
36
  // Use pipe to avoid garbled output when used alongside spinner
37
37
  execSync(cmd, { cwd: dir, stdio: "pipe" });
38
38
  }
39
+ /**
40
+ * Initialize a git repository in `dir` and create an initial commit.
41
+ *
42
+ * Why: zudo-doc's doc-history feature reads `git log` for each page's
43
+ * Created/Updated/Author block. A scaffolded project with no git repo renders
44
+ * empty history — and on older `@takazudo/zudo-doc-history-server` it crashed
45
+ * `pnpm dev` outright (the preBuild hook ran `git rev-parse` and threw).
46
+ * Initializing git here makes the feature work out of the box.
47
+ *
48
+ * Safe by construction:
49
+ * - `skipped-no-git` when git is not installed;
50
+ * - `skipped-existing-repo` when `dir` is already inside a git work tree (e.g.
51
+ * scaffolding into an existing monorepo) — never nest repositories;
52
+ * - the initial commit only falls back to a neutral identity when the user has
53
+ * none configured, so a normal user's commit keeps their own identity.
54
+ */
55
+ export function initGitRepo(dir) {
56
+ // Is git available at all?
57
+ try {
58
+ execFileSync("git", ["--version"], { stdio: "ignore" });
59
+ }
60
+ catch {
61
+ return { status: "skipped-no-git" };
62
+ }
63
+ // Already inside a git work tree? Don't create a nested repository.
64
+ try {
65
+ const inside = execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
66
+ cwd: dir,
67
+ encoding: "utf-8",
68
+ stdio: ["ignore", "pipe", "ignore"],
69
+ }).trim();
70
+ if (inside === "true")
71
+ return { status: "skipped-existing-repo" };
72
+ }
73
+ catch {
74
+ // git exits non-zero when `dir` is not inside a repo — the expected case.
75
+ }
76
+ const COMMIT_MESSAGE = "Initial commit from create-zudo-doc";
77
+ try {
78
+ execFileSync("git", ["init", "-q"], { cwd: dir, stdio: "ignore" });
79
+ execFileSync("git", ["add", "-A"], { cwd: dir, stdio: "ignore" });
80
+ try {
81
+ execFileSync("git", ["commit", "-q", "-m", COMMIT_MESSAGE], {
82
+ cwd: dir,
83
+ stdio: "ignore",
84
+ });
85
+ }
86
+ catch {
87
+ // No git identity configured on this machine — retry with a neutral
88
+ // fallback so the initial commit still succeeds. Users with a configured
89
+ // identity never reach this branch (their identity is used above).
90
+ execFileSync("git", [
91
+ "-c",
92
+ "user.name=create-zudo-doc",
93
+ "-c",
94
+ "user.email=create-zudo-doc@users.noreply.github.com",
95
+ "commit",
96
+ "-q",
97
+ "-m",
98
+ COMMIT_MESSAGE,
99
+ ], { cwd: dir, stdio: "ignore" });
100
+ }
101
+ return { status: "ok" };
102
+ }
103
+ catch (err) {
104
+ return {
105
+ status: "failed",
106
+ message: err instanceof Error ? err.message : String(err),
107
+ };
108
+ }
109
+ }
39
110
  export function capitalize(str) {
40
111
  return str.replace(/\b\w/g, (c) => c.toUpperCase());
41
112
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-zudo-doc",
3
- "version": "1.2.0",
3
+ "version": "2.0.0",
4
4
  "description": "Create a new zudo-doc documentation site",
5
5
  "license": "MIT",
6
6
  "author": "Takeshi Takatsudo",
@@ -28,10 +28,12 @@ import type { JSX } from "preact";
28
28
  import type { VNode } from "preact";
29
29
  import { Island } from "@takazudo/zfb";
30
30
  import { SiteTreeNav } from "@takazudo/zudo-doc/site-tree-nav-island";
31
- import { FooterWithDefaults } from "./lib/_footer-with-defaults";
32
- import { HeaderWithDefaults } from "./lib/_header-with-defaults";
33
- import { HeadWithDefaults } from "./lib/_head-with-defaults";
34
- import { composeMetaTitle } from "./lib/_compose-meta-title";
31
+ import {
32
+ FooterWithDefaults,
33
+ HeaderWithDefaults,
34
+ HeadWithDefaults,
35
+ composeMetaTitle,
36
+ } from "./lib/_chrome";
35
37
  import { BodyEndIslands } from "./lib/_body-end-islands";
36
38
 
37
39
  export const frontmatter = { title: "Home" };
@@ -0,0 +1,166 @@
1
+ /** @jsxRuntime automatic */
2
+ /** @jsxImportSource preact */
3
+ // Host chrome adapter — THE single host adapter for the page-chrome surface
4
+ // (epic Collapse Wiring Shells #2420; HOSTCOLLAPSE #2427 collapsed the former
5
+ // per-component `pages/lib/_*` chrome shells into this one module).
6
+ //
7
+ // It builds the host's REAL bindings ONCE and threads them through the public
8
+ // `createChrome(routeContext, hostBindings)` builder, then re-exports the wired
9
+ // page-chrome barrel (renderDocPage, HeaderWithDefaults, FooterWithDefaults,
10
+ // HeadWithDefaults, composeMetaTitle, …). Every doc route + homepage chrome
11
+ // import resolves here.
12
+ //
13
+ // - the reconstructed route context comes from the retained, vitest-safe
14
+ // `./_route-context` seam (settings + i18n + URL/nav/slug helpers +
15
+ // nav-source resolvers + route enumerators), fed the host content bridge
16
+ // (`stableDocs`) so the content read + nav enumeration stay byte-identical;
17
+ // - the genuinely host-bound slots (`hostBindings`): the real SearchWidget,
18
+ // git-history manifest, sidebars config, frontmatter renderers/builder,
19
+ // footer tag loader + vocabulary, body-end islands, DocHistory island, and
20
+ // the showcase MDX content overrides (`mdxExtras`).
21
+ //
22
+ // Island-scanner contract (load-bearing): this module MUST keep the STATIC
23
+ // imports of DocHistory (`@takazudo/zudo-doc/doc-history`), BodyEndIslands
24
+ // (`./_body-end-islands`) and PresetGeneratorFallback (`./_preset-generator`).
25
+ // After the collapse the only page→island import chain is
26
+ // `pages/docs/*.tsx → _chrome.ts → {DocHistory, BodyEndIslands, PresetGenerator}`;
27
+ // if any of these became a type-only or dynamic import the zfb island scanner
28
+ // would stop walking it and the marker/bundle would silently drop.
29
+
30
+ import type { ComponentChildren } from "preact";
31
+ import type { ChromeHostBindings } from "@takazudo/zudo-doc/factory-context";
32
+
33
+ import { createChrome } from "@takazudo/zudo-doc/chrome";
34
+
35
+ import { settings } from "@/config/settings";
36
+ import { defaultLocale } from "@/config/i18n";
37
+ import { tagVocabulary } from "@/config/tag-vocabulary";
38
+ import sidebars from "@/config/sidebars";
39
+ import { frontmatterRenderers } from "@/config/frontmatter-preview-renderers";
40
+ import { collectTags } from "@/utils/tags";
41
+ import { toRouteSlug } from "@/utils/slug";
42
+ import type { DocsEntry } from "@/types/docs-entry";
43
+
44
+ import { routeContext } from "./_route-context";
45
+ import { stableDocs, memoizeDerived } from "./_nav-source-cache";
46
+ import { mergeLocaleDocs } from "./locale-merge";
47
+ import { SearchWidget } from "./_search-widget";
48
+ import { BodyEndIslands as BodyEndIslandsSeam } from "./_body-end-islands";
49
+ import { buildFrontmatterPreviewEntries } from "./_frontmatter-preview-data";
50
+ import { DetailsWrapper } from "./_details";
51
+ import { PresetGeneratorFallback } from "./_preset-generator";
52
+ import { DocHistory } from "@takazudo/zudo-doc/doc-history";
53
+ import { HtmlPreviewWrapper, type HtmlPreviewWrapperProps } from "@takazudo/zudo-doc/html-preview-wrapper";
54
+ // SSR author + date metadata — `#doc-history-meta` is the build-time manifest
55
+ // alias (esbuild-inlined, no fs). Static import is load-bearing for the island
56
+ // scanner chain noted above.
57
+ import docHistoryMeta from "#doc-history-meta";
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // Footer tag loader (host-side; moved verbatim from the former
61
+ // _chrome-context.ts / _footer-with-defaults.tsx). Reads collections via
62
+ // stableDocs / memoizeDerived and aggregates tags per locale. Threaded as
63
+ // hostBindings.loadTagsForLocale.
64
+ // ---------------------------------------------------------------------------
65
+
66
+ function loadTagsForLocale(lang: string) {
67
+ if (lang === defaultLocale) {
68
+ const baseDocs = stableDocs("docs");
69
+ return memoizeDerived([baseDocs], "footerTaglist;default", () => {
70
+ const docs: DocsEntry[] = baseDocs.filter(
71
+ (d) => !d.data.draft && !d.data.unlisted && !d.data.category_no_page,
72
+ );
73
+ const tagMap = collectTags(docs, (id, data) => data.slug ?? toRouteSlug(id));
74
+ return [...tagMap.values()].sort((a, b) => a.tag.localeCompare(b.tag, lang));
75
+ });
76
+ }
77
+ const baseDocs = stableDocs("docs");
78
+ const localeDocs = stableDocs(`docs-${lang}`);
79
+ return memoizeDerived([baseDocs, localeDocs], `footerTaglist;${lang}`, () => {
80
+ const result = mergeLocaleDocs({
81
+ baseDocs: baseDocs.filter((d) => !d.data.draft),
82
+ localeDocs: localeDocs.filter((d) => !d.data.draft),
83
+ applyDefaultLocaleOnlyFilter: true,
84
+ });
85
+ const docs: DocsEntry[] = result.docs.filter((d) => !d.data.category_no_page);
86
+ const tagMap = collectTags(docs, (id, data) => data.slug ?? toRouteSlug(id));
87
+ return [...tagMap.values()].sort((a, b) => a.tag.localeCompare(b.tag, lang));
88
+ });
89
+ }
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // Showcase MDX content overrides (host-bound; identical to the former
93
+ // pages/_mdx-components.ts `extras` block — kept in lockstep). Threaded via
94
+ // hostBindings.mdxExtras; the package factory merges them over its defaults.
95
+ // ---------------------------------------------------------------------------
96
+
97
+ /** MDX-tag stub: renders nothing (Preact null-vnode path). */
98
+ const MdxStub = (_props: unknown) => null;
99
+
100
+ /** SSR pass-through for `<Island when=…>` — renders children, ignores `when`. */
101
+ function IslandWrapper(props: {
102
+ when?: "load" | "idle" | "visible" | "media";
103
+ children?: ComponentChildren;
104
+ }): ComponentChildren {
105
+ return props.children ?? null;
106
+ }
107
+
108
+ const HtmlPreviewWithGlobalConfig = (props: HtmlPreviewWrapperProps) =>
109
+ HtmlPreviewWrapper({ globalConfig: settings.htmlPreview ?? null, ...props });
110
+
111
+ const mdxExtras = {
112
+ HtmlPreview: HtmlPreviewWithGlobalConfig,
113
+ Details: DetailsWrapper,
114
+ SmartBreak: MdxStub,
115
+ Island: IslandWrapper,
116
+ PresetGenerator: PresetGeneratorFallback,
117
+ Avatar: MdxStub,
118
+ Button: MdxStub,
119
+ Card: MdxStub,
120
+ MyComponent: MdxStub,
121
+ PageLayout: MdxStub,
122
+ } as unknown as Record<string, (props: Record<string, unknown>) => unknown>;
123
+
124
+ // ---------------------------------------------------------------------------
125
+ // The host's real bindings (the 10 ChromeHostBindings slots).
126
+ // ---------------------------------------------------------------------------
127
+
128
+ const hostBindings: ChromeHostBindings = {
129
+ SearchWidget: SearchWidget as ChromeHostBindings["SearchWidget"],
130
+ docHistoryMeta: docHistoryMeta as Record<string, unknown>,
131
+ sidebarsConfig: sidebars as unknown as Record<string, unknown>,
132
+ frontmatterRenderers: frontmatterRenderers as unknown as ChromeHostBindings["frontmatterRenderers"],
133
+ buildFrontmatterPreviewEntries:
134
+ buildFrontmatterPreviewEntries as unknown as ChromeHostBindings["buildFrontmatterPreviewEntries"],
135
+ loadTagsForLocale: loadTagsForLocale as unknown as ChromeHostBindings["loadTagsForLocale"],
136
+ tagVocabulary,
137
+ BodyEndIslands: BodyEndIslandsSeam as unknown as ChromeHostBindings["BodyEndIslands"],
138
+ DocHistory: DocHistory as unknown as ChromeHostBindings["DocHistory"],
139
+ mdxExtras: mdxExtras as ChromeHostBindings["mdxExtras"],
140
+ };
141
+
142
+ // ---------------------------------------------------------------------------
143
+ // Build the wired page-chrome surface ONCE from the route context + real host
144
+ // bindings, and re-export the barrel. Every omitted slot falls back to the
145
+ // package stub (byte-identical to the injected package-routes path); each REAL
146
+ // slot above overrides it.
147
+ // ---------------------------------------------------------------------------
148
+
149
+ const chrome = createChrome(routeContext, hostBindings);
150
+
151
+ export type { RenderDocPageOptions } from "@takazudo/zudo-doc/doc-page-renderer";
152
+
153
+ export const {
154
+ composeMetaTitle,
155
+ HeadWithDefaults,
156
+ HeaderWithDefaults,
157
+ FooterWithDefaults,
158
+ SidebarWithDefaults,
159
+ renderDocPage,
160
+ VersionsPageView,
161
+ collectTagMapForLocale,
162
+ TagDetailPageView,
163
+ TagsIndexPageView,
164
+ SiteTreeNavWrapper,
165
+ BodyEndIslands,
166
+ } = chrome;
@@ -1,54 +1,10 @@
1
- // Thin stub — doc-route-entries moved to the package (epic #2344, S6).
2
- // Calls `createDocRouteEntries(ctx)` from @takazudo/zudo-doc/doc-route-entries
3
- // with the host utilities injected, then re-exports the resulting builder
4
- // function so all existing call sites continue to work unchanged.
1
+ // Thin shimthe memoized doc-route-entry builder rides on the unified
2
+ // ChromeContext now (epic Collapse Wiring Shells #2420, FACTORIES #2424).
3
+ // `createRouteContext` (in _chrome-context.ts) assembles `buildDocRouteEntries`
4
+ // as part of the route context; this module just re-exports it so the doc-route
5
+ // page files keep importing it from here unchanged.
5
6
 
6
- import { createDocRouteEntries } from "@takazudo/zudo-doc/doc-route-entries";
7
- import type {
8
- DocPageEntry,
9
- DocNavNode,
10
- BreadcrumbItem,
11
- } from "@takazudo/zudo-doc/doc-route-entries";
12
7
  export type { DocRouteEntry, BuildDocRouteEntriesArgs } from "@takazudo/zudo-doc/doc-route-entries";
13
- import {
14
- buildNavTree,
15
- buildBreadcrumbs,
16
- collectAutoIndexNodes,
17
- type NavNode,
18
- type CategoryMeta,
19
- } from "@/utils/docs";
20
- import { getNavSectionForSlug, getNavSubtree } from "@/utils/nav-scope";
21
- import { toRouteSlug, toSlugParams } from "@/utils/slug";
22
- import type { DocsEntry } from "@/types/docs-entry";
23
- import type { Locale } from "@/config/i18n";
24
- import { extractHeadings } from "./_extract-headings";
8
+ import { routeContext } from "./_route-context";
25
9
 
26
- export const { buildDocRouteEntries } = createDocRouteEntries({
27
- // The factory describes its injected nav builders with the package's own
28
- // structural counterparts (DocPageEntry / DocNavNode / Map<string, unknown>)
29
- // and a plain `locale: string`. The host's buildNavTree / buildBreadcrumbs
30
- // are typed against the concrete project types (DocsEntry / NavNode / the
31
- // Locale union / Map<string, CategoryMeta>). They are runtime-identical, so
32
- // the stub adapts them with thin wrappers that cast at the injection boundary
33
- // where the host owns the type knowledge.
34
- buildNavTree: (docs: DocPageEntry[], locale: string, categoryMeta: Map<string, unknown>) =>
35
- buildNavTree(
36
- docs as unknown as DocsEntry[],
37
- locale as Locale,
38
- categoryMeta as Map<string, CategoryMeta>,
39
- ) as DocNavNode[],
40
- buildBreadcrumbs: (
41
- tree: DocNavNode[],
42
- slug: string,
43
- locale: string,
44
- urlFor?: (slug: string) => string,
45
- ): BreadcrumbItem[] =>
46
- buildBreadcrumbs(tree as NavNode[], slug, locale as Locale, urlFor),
47
- collectAutoIndexNodes: (tree: DocNavNode[]) =>
48
- collectAutoIndexNodes(tree as NavNode[]) as DocNavNode[],
49
- getNavSectionForSlug,
50
- getNavSubtree,
51
- toRouteSlug,
52
- toSlugParams,
53
- extractHeadings,
54
- });
10
+ export const { buildDocRouteEntries } = routeContext;
@@ -1,37 +1,17 @@
1
- // Thin stub — nav-source-docs moved to the package (epic #2344, S6).
2
- // Calls `createNavSourceDocs(ctx)` from @takazudo/zudo-doc/nav-source-docs
3
- // with the host singletons injected, then re-exports the resulting resolver
4
- // functions so all existing call sites continue to work unchanged.
1
+ // Thin shim — nav-source resolvers ride on the unified ChromeContext now
2
+ // (epic Collapse Wiring Shells #2420, FACTORIES #2424). The reconstructed
3
+ // `createRouteContext` (in _chrome-context.ts) builds the identity-stable
4
+ // nav-source API as part of the route context; this module just re-exports the
5
+ // bindings so the existing call sites (route files, nav wrappers, _nav-data-prep,
6
+ // route-enumerators) keep importing them from here unchanged.
5
7
 
6
- import { createNavSourceDocs } from "@takazudo/zudo-doc/nav-source-docs";
7
8
  export type { NavSourceDocs, NavSourceOptions } from "@takazudo/zudo-doc/nav-source-docs";
8
- import { defaultLocale, getLocaleConfig } from "@/config/i18n";
9
- import { settings } from "@/config/settings";
10
- import { loadCategoryMeta, isNavVisible } from "@/utils/docs";
11
- import { isDefaultLocaleOnlyPath } from "@/utils/base";
12
- import { stableDocs } from "./_nav-source-cache";
9
+ import { routeContext } from "./_route-context";
13
10
 
14
- const {
11
+ export const {
15
12
  resolveNavSource,
16
13
  resolveVersionedLocaleSource,
17
14
  loadNavSourceDocs,
18
15
  stableMergeCategoryMeta,
19
16
  stableNavDocs,
20
- } = createNavSourceDocs({
21
- defaultLocale,
22
- docsDir: settings.docsDir,
23
- getVersions: () => settings.versions,
24
- getLocaleConfig,
25
- loadCategoryMeta,
26
- isNavVisible,
27
- isDefaultLocaleOnlyPath,
28
- stableDocs,
29
- });
30
-
31
- export {
32
- resolveNavSource,
33
- resolveVersionedLocaleSource,
34
- loadNavSourceDocs,
35
- stableMergeCategoryMeta,
36
- stableNavDocs,
37
- };
17
+ } = routeContext;
@@ -0,0 +1,32 @@
1
+ // Host route context — the reconstructed RouteContext (settings + i18n + URL
2
+ // helpers + nav/slug helpers + identity-stable nav-source resolvers + doc-route
3
+ // + route enumerators), built ONCE via the public `createRouteContext`
4
+ // (epic Collapse Wiring Shells #2420, FACTORIES #2424).
5
+ //
6
+ // Kept SEPARATE from `_chrome-context.ts` so the data shells (`_nav-source-docs`
7
+ // / `_doc-route-entries` / `route-enumerators`) — and the unit tests that import
8
+ // them — depend ONLY on this lightweight module, NOT on the chrome host bindings
9
+ // (which pull the build-time `#doc-history-meta` alias + island components that
10
+ // don't resolve under vitest).
11
+ //
12
+ // The host content bridge (`stableDocs`) is injected so the docs read + nav
13
+ // enumeration match the project's existing `pages/*` paths() exactly (host
14
+ // `bridgeDocsEntries` over the `zfb/content` snapshot), not the package default.
15
+
16
+ import { createRouteContext } from "@takazudo/zudo-doc/route-context";
17
+ import type { ColorScheme } from "@takazudo/zudo-doc/color-scheme-utils";
18
+ import { settings } from "@/config/settings";
19
+ import { translations } from "@/config/i18n";
20
+ import { tagVocabulary } from "@/config/tag-vocabulary";
21
+ import { colorSchemes } from "@/config/color-schemes";
22
+ import { stableDocs } from "./_nav-source-cache";
23
+
24
+ export const routeContext = createRouteContext(
25
+ {
26
+ settings,
27
+ translations,
28
+ tagVocabulary,
29
+ colorSchemes: colorSchemes as unknown as Record<string, ColorScheme>,
30
+ },
31
+ { stableDocs },
32
+ );
@@ -1,39 +1,4 @@
1
- /**
2
- * Tag governance enforcement level.
3
- *
4
- * - `"off"` — no vocabulary-aware enforcement. The `tags` schema stays a
5
- * free-form `string[]`. Identical to pre-vocabulary behaviour.
6
- * - `"warn"` — `tags` schema stays free-form so builds pass, but the tag
7
- * audit script (see Sub 2) reports unknown tags as warnings and
8
- * exits non-zero under `--ci`.
9
- * - `"strict"` — `tags` schema is tightened to `z.enum([...allowedIds])`, so
10
- * unknown tags fail `pnpm check` / `pnpm build`.
11
- *
12
- * Orthogonal to `tagVocabulary` (the on/off switch for consulting the
13
- * vocabulary file at runtime). See `settings-types.ts` for details.
14
- */
15
- export type TagGovernanceMode = "off" | "warn" | "strict";
16
-
17
- /**
18
- * A single entry in the tag vocabulary.
19
- *
20
- * - `id` — canonical tag id. What content files should ideally use.
21
- * - `label` — optional human-readable label (falls back to `id`).
22
- * - `description`— optional short description for tooling / tag index pages.
23
- * - `group` — optional grouping key used by the grouped tag footer
24
- * (e.g. `"type"`, `"level"`, `"topic"`).
25
- * - `aliases` — alternate strings that content files may use. Alias
26
- * resolution rewrites these to `id` before aggregation.
27
- * - `deprecated` — `true` marks the tag as deprecated with no redirect: the
28
- * canonical id is dropped from aggregation. Pass
29
- * `{ redirect: "<other-id>" }` to rewrite this tag to another
30
- * canonical id when it appears in content.
31
- */
32
- export interface TagVocabularyEntry {
33
- id: string;
34
- label?: string;
35
- description?: string;
36
- group?: string;
37
- aliases?: readonly string[];
38
- deprecated?: boolean | { redirect?: string };
39
- }
1
+ // Re-export from the shared package — canonical definitions live in
2
+ // @takazudo/zudo-doc/settings (epic #2321). Consumers that import from
3
+ // this local path continue to work unchanged.
4
+ export type { TagGovernanceMode, TagVocabularyEntry } from "@takazudo/zudo-doc/settings";