create-zudo-doc 0.1.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.
- package/LICENSE +21 -0
- package/README.md +146 -0
- package/bin/create-zudo-doc.js +2 -0
- package/dist/api.d.ts +20 -0
- package/dist/api.js +13 -0
- package/dist/claude-md-gen.d.ts +2 -0
- package/dist/claude-md-gen.js +113 -0
- package/dist/cli.d.ts +39 -0
- package/dist/cli.js +157 -0
- package/dist/compose.d.ts +95 -0
- package/dist/compose.js +206 -0
- package/dist/constants.d.ts +20 -0
- package/dist/constants.js +224 -0
- package/dist/features/body-foot-util.d.ts +10 -0
- package/dist/features/body-foot-util.js +12 -0
- package/dist/features/claude-resources.d.ts +2 -0
- package/dist/features/claude-resources.js +6 -0
- package/dist/features/design-token-panel.d.ts +14 -0
- package/dist/features/design-token-panel.js +27 -0
- package/dist/features/doc-history.d.ts +9 -0
- package/dist/features/doc-history.js +11 -0
- package/dist/features/doc-tags.d.ts +19 -0
- package/dist/features/doc-tags.js +33 -0
- package/dist/features/footer-taglist.d.ts +14 -0
- package/dist/features/footer-taglist.js +17 -0
- package/dist/features/footer.d.ts +8 -0
- package/dist/features/footer.js +10 -0
- package/dist/features/i18n.d.ts +22 -0
- package/dist/features/i18n.js +41 -0
- package/dist/features/image-enlarge.d.ts +11 -0
- package/dist/features/image-enlarge.js +13 -0
- package/dist/features/index.d.ts +15 -0
- package/dist/features/index.js +53 -0
- package/dist/features/llms-txt.d.ts +11 -0
- package/dist/features/llms-txt.js +13 -0
- package/dist/features/search.d.ts +9 -0
- package/dist/features/search.js +11 -0
- package/dist/features/sidebar-resizer.d.ts +14 -0
- package/dist/features/sidebar-resizer.js +16 -0
- package/dist/features/sidebar-toggle.d.ts +13 -0
- package/dist/features/sidebar-toggle.js +15 -0
- package/dist/features/tag-governance.d.ts +14 -0
- package/dist/features/tag-governance.js +16 -0
- package/dist/features/tauri-dev.d.ts +2 -0
- package/dist/features/tauri-dev.js +25 -0
- package/dist/features/tauri.d.ts +11 -0
- package/dist/features/tauri.js +52 -0
- package/dist/features/versioning.d.ts +27 -0
- package/dist/features/versioning.js +43 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +150 -0
- package/dist/preset.d.ts +37 -0
- package/dist/preset.js +156 -0
- package/dist/prompts.d.ts +32 -0
- package/dist/prompts.js +248 -0
- package/dist/scaffold.d.ts +4 -0
- package/dist/scaffold.js +344 -0
- package/dist/settings-gen.d.ts +2 -0
- package/dist/settings-gen.js +237 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.js +34 -0
- package/dist/zfb-config-gen.d.ts +19 -0
- package/dist/zfb-config-gen.js +222 -0
- package/package.json +65 -0
- package/templates/base/.htmlvalidate.json +5 -0
- package/templates/base/.zfb/doc-history-meta.json +1 -0
- package/templates/base/pages/404.tsx +55 -0
- package/templates/base/pages/_data.ts +179 -0
- package/templates/base/pages/_mdx-components.ts +249 -0
- package/templates/base/pages/docs/[...slug].tsx +448 -0
- package/templates/base/pages/index.tsx +158 -0
- package/templates/base/pages/lib/_body-end-islands.tsx +201 -0
- package/templates/base/pages/lib/_category-nav.tsx +148 -0
- package/templates/base/pages/lib/_category-tree-nav.tsx +104 -0
- package/templates/base/pages/lib/_compose-meta-title.ts +29 -0
- package/templates/base/pages/lib/_details.tsx +30 -0
- package/templates/base/pages/lib/_doc-history-area.tsx +178 -0
- package/templates/base/pages/lib/_doc-metainfo-area.tsx +100 -0
- package/templates/base/pages/lib/_doc-tags-area.tsx +89 -0
- package/templates/base/pages/lib/_extract-headings.ts +81 -0
- package/templates/base/pages/lib/_footer-with-defaults.tsx +234 -0
- package/templates/base/pages/lib/_frontmatter-preview-data.ts +53 -0
- package/templates/base/pages/lib/_head-with-defaults.tsx +113 -0
- package/templates/base/pages/lib/_header-with-defaults.tsx +386 -0
- package/templates/base/pages/lib/_inline-version-switcher.tsx +84 -0
- package/templates/base/pages/lib/_math-block.tsx +63 -0
- package/templates/base/pages/lib/_nav-source-docs.ts +68 -0
- package/templates/base/pages/lib/_preset-generator.tsx +81 -0
- package/templates/base/pages/lib/_search-widget-script.ts +388 -0
- package/templates/base/pages/lib/_search-widget.tsx +196 -0
- package/templates/base/pages/lib/_sidebar-with-defaults.tsx +176 -0
- package/templates/base/pages/lib/_site-tree-nav.tsx +128 -0
- package/templates/base/pages/lib/locale-merge.ts +58 -0
- package/templates/base/pages/lib/route-enumerators.ts +302 -0
- package/templates/base/pages/sitemap.xml.tsx +51 -0
- package/templates/base/plugins/connect-adapter.mjs +144 -0
- package/templates/base/plugins/copy-public-plugin.mjs +50 -0
- package/templates/base/plugins/search-index-plugin.mjs +54 -0
- package/templates/base/scripts/run-b4push.sh +102 -0
- package/templates/base/src/components/ai-chat-modal.tsx +15 -0
- package/templates/base/src/components/client-router-bootstrap.tsx +14 -0
- package/templates/base/src/components/content/component-map.ts +25 -0
- package/templates/base/src/components/content/content-blockquote.tsx +16 -0
- package/templates/base/src/components/content/content-code.tsx +117 -0
- package/templates/base/src/components/content/content-link.tsx +83 -0
- package/templates/base/src/components/content/content-ol.tsx +19 -0
- package/templates/base/src/components/content/content-paragraph.tsx +10 -0
- package/templates/base/src/components/content/content-strong.tsx +16 -0
- package/templates/base/src/components/content/content-table.tsx +18 -0
- package/templates/base/src/components/content/content-ul.tsx +18 -0
- package/templates/base/src/components/content/heading-h2.tsx +26 -0
- package/templates/base/src/components/content/heading-h3.tsx +26 -0
- package/templates/base/src/components/content/heading-h4.tsx +26 -0
- package/templates/base/src/components/design-token-panel-bootstrap.tsx +15 -0
- package/templates/base/src/components/desktop-sidebar-toggle.tsx +15 -0
- package/templates/base/src/components/doc-history.tsx +18 -0
- package/templates/base/src/components/html-preview/highlighted-code.tsx +74 -0
- package/templates/base/src/components/html-preview/html-preview.tsx +108 -0
- package/templates/base/src/components/html-preview/preflight.ts +112 -0
- package/templates/base/src/components/html-preview/preview-base.tsx +159 -0
- package/templates/base/src/components/image-enlarge.tsx +19 -0
- package/templates/base/src/components/mobile-toc.tsx +94 -0
- package/templates/base/src/components/preset-generator.tsx +14 -0
- package/templates/base/src/components/sidebar-toggle.tsx +98 -0
- package/templates/base/src/components/sidebar-tree.tsx +543 -0
- package/templates/base/src/components/site-tree-nav.tsx +233 -0
- package/templates/base/src/components/theme-toggle.tsx +93 -0
- package/templates/base/src/components/toc.tsx +63 -0
- package/templates/base/src/components/tree-nav-shared.tsx +71 -0
- package/templates/base/src/config/color-scheme-utils.ts +182 -0
- package/templates/base/src/config/color-schemes.ts +128 -0
- package/templates/base/src/config/frontmatter-preview-defaults.ts +24 -0
- package/templates/base/src/config/frontmatter-preview-renderers.tsx +46 -0
- package/templates/base/src/config/i18n.ts +225 -0
- package/templates/base/src/config/settings-types.ts +162 -0
- package/templates/base/src/config/sidebars.ts +66 -0
- package/templates/base/src/config/tag-vocabulary-types.ts +39 -0
- package/templates/base/src/config/tag-vocabulary.ts +20 -0
- package/templates/base/src/hooks/use-active-heading.ts +133 -0
- package/templates/base/src/plugins/docs-source-map.ts +103 -0
- package/templates/base/src/plugins/hast-utils.ts +10 -0
- package/templates/base/src/plugins/rehype-code-title.ts +50 -0
- package/templates/base/src/plugins/rehype-heading-links.ts +53 -0
- package/templates/base/src/plugins/rehype-image-enlarge.ts +113 -0
- package/templates/base/src/plugins/rehype-mermaid.ts +41 -0
- package/templates/base/src/plugins/rehype-strip-md-extension.ts +58 -0
- package/templates/base/src/plugins/remark-admonitions.ts +99 -0
- package/templates/base/src/plugins/remark-resolve-markdown-links.ts +127 -0
- package/templates/base/src/plugins/url-utils.ts +4 -0
- package/templates/base/src/styles/global.css +1066 -0
- package/templates/base/src/types/docs-entry.ts +39 -0
- package/templates/base/src/types/heading.ts +5 -0
- package/templates/base/src/types/locale.ts +10 -0
- package/templates/base/src/utils/base.ts +139 -0
- package/templates/base/src/utils/content-files.ts +106 -0
- package/templates/base/src/utils/dedent.ts +24 -0
- package/templates/base/src/utils/docs.ts +335 -0
- package/templates/base/src/utils/git-info.ts +70 -0
- package/templates/base/src/utils/github.ts +19 -0
- package/templates/base/src/utils/header-right-items.ts +38 -0
- package/templates/base/src/utils/nav-scope.ts +63 -0
- package/templates/base/src/utils/sidebar.ts +104 -0
- package/templates/base/src/utils/slug.ts +10 -0
- package/templates/base/src/utils/smart-break.tsx +126 -0
- package/templates/base/src/utils/tags.ts +126 -0
- package/templates/base/tsconfig.json +36 -0
- package/templates/features/bodyFootUtil/files/src/utils/github.ts +19 -0
- package/templates/features/claudeResources/files/plugins/claude-resources-plugin.mjs +137 -0
- package/templates/features/claudeResources/files/src/integrations/claude-resources/__tests__/escape-for-mdx.test.ts +34 -0
- package/templates/features/claudeResources/files/src/integrations/claude-resources/__tests__/generate.test.ts +376 -0
- package/templates/features/claudeResources/files/src/integrations/claude-resources/escape-for-mdx.ts +93 -0
- package/templates/features/claudeResources/files/src/integrations/claude-resources/generate.ts +586 -0
- package/templates/features/designTokenPanel/files/src/components/design-token-panel-bootstrap.tsx +15 -0
- package/templates/features/designTokenPanel/files/src/config/design-token-panel-config.ts +99 -0
- package/templates/features/designTokenPanel/files/src/config/design-tokens-manifest.ts +177 -0
- package/templates/features/designTokenPanel/files/src/lib/design-token-panel-bootstrap.ts +50 -0
- package/templates/features/docHistory/files/plugins/doc-history-plugin.mjs +99 -0
- package/templates/features/docHistory/files/src/components/doc-history.tsx +598 -0
- package/templates/features/docHistory/files/src/types/doc-history.ts +23 -0
- package/templates/features/docHistory/files/src/utils/doc-history.ts +180 -0
- package/templates/features/docTags/files/pages/[locale]/docs/tags/[tag].tsx +116 -0
- package/templates/features/docTags/files/pages/[locale]/docs/tags/index.tsx +99 -0
- package/templates/features/docTags/files/pages/docs/tags/[tag].tsx +101 -0
- package/templates/features/docTags/files/pages/docs/tags/index.tsx +86 -0
- package/templates/features/i18n/files/pages/[locale]/docs/[...slug].tsx +467 -0
- package/templates/features/i18n/files/pages/[locale]/index.tsx +213 -0
- package/templates/features/imageEnlarge/files/src/components/image-enlarge.tsx +248 -0
- package/templates/features/llmsTxt/files/plugins/llms-txt-plugin.mjs +74 -0
- package/templates/features/sidebarResizer/files/src/scripts/sidebar-resizer.ts +185 -0
- package/templates/features/sidebarToggle/files/src/components/desktop-sidebar-toggle.tsx +126 -0
- package/templates/features/tagGovernance/files/scripts/tags-audit.ts +576 -0
- package/templates/features/tagGovernance/files/scripts/tags-suggest.ts +428 -0
- package/templates/features/tauri/files/src/components/find-bar.tsx +122 -0
- package/templates/features/tauri/files/src/components/find-in-page-init.tsx +53 -0
- package/templates/features/tauri/files/src/utils/find-in-page.ts +175 -0
- package/templates/features/tauri/files/src-tauri/Cargo.toml +14 -0
- package/templates/features/tauri/files/src-tauri/build.rs +3 -0
- package/templates/features/tauri/files/src-tauri/capabilities/default.json +11 -0
- package/templates/features/tauri/files/src-tauri/src/main.rs +250 -0
- package/templates/features/tauri/files/src-tauri/tauri.conf.json +25 -0
- package/templates/features/tauriDev/files/src-tauri-dev/Cargo.toml +15 -0
- package/templates/features/tauriDev/files/src-tauri-dev/build.rs +3 -0
- package/templates/features/tauriDev/files/src-tauri-dev/capabilities/default.json +7 -0
- package/templates/features/tauriDev/files/src-tauri-dev/frontend/index.html +187 -0
- package/templates/features/tauriDev/files/src-tauri-dev/icons/icon.png +0 -0
- package/templates/features/tauriDev/files/src-tauri-dev/src/main.rs +995 -0
- package/templates/features/tauriDev/files/src-tauri-dev/tauri.conf.json +22 -0
- package/templates/features/tauriDev/files/src-tauri-dev/test-launch.sh +65 -0
- package/templates/features/versioning/files/pages/[locale]/docs/versions.tsx +100 -0
- package/templates/features/versioning/files/pages/docs/versions.tsx +78 -0
- package/templates/features/versioning/files/pages/v/[version]/docs/[...slug].tsx +451 -0
- package/templates/features/versioning/files/pages/v/[version]/ja/docs/[...slug].tsx +490 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Programmatically generate zfb.config.ts from user choices.
|
|
3
|
+
*
|
|
4
|
+
* W7A (#1736): emits zfb plugins in the host's INLINE-OBJECT shape —
|
|
5
|
+
* `{ name: "./plugins/<plugin>.mjs", options: {...} }` — not the
|
|
6
|
+
* pre-cutover factory-import pattern (`import { fooPlugin } from
|
|
7
|
+
* "./src/integrations/foo"`). Inline functions are not supported by zfb's
|
|
8
|
+
* plugin runtime (see `@takazudo/zfb/plugins` source); plugins MUST be
|
|
9
|
+
* authored as standalone `.mjs` modules referenced from `zfb.config.ts`
|
|
10
|
+
* by `name`. The plugin source files are shipped by the base/feature
|
|
11
|
+
* templates under `plugins/<plugin>.mjs` and `templates/features/<feature>/
|
|
12
|
+
* files/plugins/<plugin>.mjs`.
|
|
13
|
+
*
|
|
14
|
+
* Replaces the former astro-config-gen.ts + content-config-gen.ts pair.
|
|
15
|
+
* In the zfb world, content-collection schemas live inside zfb.config.ts
|
|
16
|
+
* itself — there is no separate content.config.ts.
|
|
17
|
+
*/
|
|
18
|
+
export function generateZfbConfig(choices) {
|
|
19
|
+
const hasDocHistory = choices.features.includes("docHistory");
|
|
20
|
+
const hasLlmsTxt = choices.features.includes("llmsTxt");
|
|
21
|
+
const hasClaudeResources = choices.features.includes("claudeResources");
|
|
22
|
+
const hasTagGovernance = choices.features.includes("tagGovernance");
|
|
23
|
+
const lines = [];
|
|
24
|
+
// --- Imports ---
|
|
25
|
+
lines.push(`import { z } from "zod";`);
|
|
26
|
+
lines.push(`import { defineConfig } from "zfb/config";`);
|
|
27
|
+
lines.push(`import { settings } from "./src/config/settings";`);
|
|
28
|
+
if (hasTagGovernance) {
|
|
29
|
+
lines.push(`import { tagVocabulary } from "./src/config/tag-vocabulary";`);
|
|
30
|
+
}
|
|
31
|
+
lines.push(``);
|
|
32
|
+
// --- Tags schema builder (only when tagGovernance is selected) ---
|
|
33
|
+
if (hasTagGovernance) {
|
|
34
|
+
lines.push(`function buildTagsSchema() {`);
|
|
35
|
+
lines.push(` const vocabularyActive = settings.tagVocabulary && settings.tagGovernance === "strict";`);
|
|
36
|
+
lines.push(` if (!vocabularyActive) return z.array(z.string()).optional();`);
|
|
37
|
+
lines.push(` const allowed = new Set<string>();`);
|
|
38
|
+
lines.push(` for (const entry of tagVocabulary) {`);
|
|
39
|
+
lines.push(` allowed.add(entry.id);`);
|
|
40
|
+
lines.push(` for (const alias of entry.aliases ?? []) allowed.add(alias);`);
|
|
41
|
+
lines.push(` }`);
|
|
42
|
+
lines.push(` const allowedList = [...allowed];`);
|
|
43
|
+
lines.push(` if (allowedList.length === 0) return z.array(z.string()).optional();`);
|
|
44
|
+
lines.push(` const [first, ...rest] = allowedList;`);
|
|
45
|
+
lines.push(` return z.array(z.enum([first, ...rest] as [string, ...string[]])).optional();`);
|
|
46
|
+
lines.push(`}`);
|
|
47
|
+
lines.push(``);
|
|
48
|
+
}
|
|
49
|
+
// --- Schema definition ---
|
|
50
|
+
lines.push(`const docsSchema = z`);
|
|
51
|
+
lines.push(` .object({`);
|
|
52
|
+
lines.push(` title: z.string(),`);
|
|
53
|
+
lines.push(` description: z.string().optional(),`);
|
|
54
|
+
lines.push(` category: z.string().optional(),`);
|
|
55
|
+
lines.push(` sidebar_position: z.number().optional(),`);
|
|
56
|
+
lines.push(` sidebar_label: z.string().optional(),`);
|
|
57
|
+
if (hasTagGovernance) {
|
|
58
|
+
lines.push(` tags: buildTagsSchema(),`);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
lines.push(` tags: z.array(z.string()).optional(),`);
|
|
62
|
+
}
|
|
63
|
+
lines.push(` search_exclude: z.boolean().optional(),`);
|
|
64
|
+
lines.push(` pagination_next: z.string().nullable().optional(),`);
|
|
65
|
+
lines.push(` pagination_prev: z.string().nullable().optional(),`);
|
|
66
|
+
lines.push(` draft: z.boolean().optional(),`);
|
|
67
|
+
lines.push(` unlisted: z.boolean().optional(),`);
|
|
68
|
+
lines.push(` hide_sidebar: z.boolean().optional(),`);
|
|
69
|
+
lines.push(` hide_toc: z.boolean().optional(),`);
|
|
70
|
+
lines.push(` doc_history: z.boolean().optional(),`);
|
|
71
|
+
lines.push(` standalone: z.boolean().optional(),`);
|
|
72
|
+
lines.push(` slug: z.string().optional(),`);
|
|
73
|
+
lines.push(` generated: z.boolean().optional(),`);
|
|
74
|
+
lines.push(` })`);
|
|
75
|
+
lines.push(` .passthrough();`);
|
|
76
|
+
lines.push(``);
|
|
77
|
+
lines.push(`const docsSchemaJson = z.toJSONSchema(docsSchema) as Record<string, unknown>;`);
|
|
78
|
+
lines.push(``);
|
|
79
|
+
// --- Collection type ---
|
|
80
|
+
lines.push(`interface CollectionEntryShape {`);
|
|
81
|
+
lines.push(` name: string;`);
|
|
82
|
+
lines.push(` path: string;`);
|
|
83
|
+
lines.push(` schema: Record<string, unknown>;`);
|
|
84
|
+
lines.push(`}`);
|
|
85
|
+
lines.push(``);
|
|
86
|
+
// --- Collections array ---
|
|
87
|
+
lines.push(`const collections: CollectionEntryShape[] = [];`);
|
|
88
|
+
lines.push(``);
|
|
89
|
+
lines.push(`collections.push({ name: "docs", path: settings.docsDir, schema: docsSchemaJson });`);
|
|
90
|
+
lines.push(``);
|
|
91
|
+
// Locale collections — empty loop when locales is {} (i18n disabled).
|
|
92
|
+
lines.push(`for (const [code, config] of Object.entries(settings.locales)) {`);
|
|
93
|
+
lines.push(` collections.push({ name: \`docs-\${code}\`, path: config.dir, schema: docsSchemaJson });`);
|
|
94
|
+
lines.push(`}`);
|
|
95
|
+
lines.push(``);
|
|
96
|
+
// Version collections — outer `if` short-circuits when versions is false.
|
|
97
|
+
lines.push(`if (settings.versions) {`);
|
|
98
|
+
lines.push(` for (const version of settings.versions) {`);
|
|
99
|
+
lines.push(` collections.push({`);
|
|
100
|
+
lines.push(` name: \`docs-v-\${version.slug}\`,`);
|
|
101
|
+
lines.push(` path: version.docsDir,`);
|
|
102
|
+
lines.push(` schema: docsSchemaJson,`);
|
|
103
|
+
lines.push(` });`);
|
|
104
|
+
lines.push(` if (version.locales) {`);
|
|
105
|
+
lines.push(` for (const [code, config] of Object.entries(version.locales)) {`);
|
|
106
|
+
lines.push(` collections.push({`);
|
|
107
|
+
lines.push(` name: \`docs-v-\${version.slug}-\${code}\`,`);
|
|
108
|
+
lines.push(` path: config.dir,`);
|
|
109
|
+
lines.push(` schema: docsSchemaJson,`);
|
|
110
|
+
lines.push(` });`);
|
|
111
|
+
lines.push(` }`);
|
|
112
|
+
lines.push(` }`);
|
|
113
|
+
lines.push(` }`);
|
|
114
|
+
lines.push(`}`);
|
|
115
|
+
lines.push(``);
|
|
116
|
+
// --- Locale helpers used by integrationPlugins (always emitted because
|
|
117
|
+
// search-index + copy-public are always-on; locale-shaped data is
|
|
118
|
+
// consumed by search-index even when there's only the default locale).
|
|
119
|
+
lines.push(`const localeArray = Object.entries(settings.locales).map(([code, locale]) => ({`);
|
|
120
|
+
lines.push(` code,`);
|
|
121
|
+
lines.push(` dir: locale.dir,`);
|
|
122
|
+
lines.push(`}));`);
|
|
123
|
+
lines.push(`const localeRecord = Object.fromEntries(`);
|
|
124
|
+
lines.push(` Object.entries(settings.locales).map(([code, locale]) => [code, { dir: locale.dir }]),`);
|
|
125
|
+
lines.push(`);`);
|
|
126
|
+
lines.push(``);
|
|
127
|
+
// --- Plugins — inline-object shape matches host. Each entry's `name`
|
|
128
|
+
// is a relative path to a `.mjs` plugin module shipped by the
|
|
129
|
+
// base/feature templates. zfb's plugin runtime resolves the module
|
|
130
|
+
// and dispatches lifecycle hooks (preBuild / postBuild / devMiddleware)
|
|
131
|
+
// on its default export. ---
|
|
132
|
+
lines.push(`const integrationPlugins = [`);
|
|
133
|
+
if (hasClaudeResources) {
|
|
134
|
+
lines.push(` ...(settings.claudeResources`);
|
|
135
|
+
lines.push(` ? [`);
|
|
136
|
+
lines.push(` {`);
|
|
137
|
+
lines.push(` name: "./plugins/claude-resources-plugin.mjs",`);
|
|
138
|
+
lines.push(` options: {`);
|
|
139
|
+
lines.push(` claudeDir: settings.claudeResources.claudeDir,`);
|
|
140
|
+
lines.push(` projectRoot: settings.claudeResources.projectRoot,`);
|
|
141
|
+
lines.push(` docsDir: settings.docsDir,`);
|
|
142
|
+
lines.push(` },`);
|
|
143
|
+
lines.push(` },`);
|
|
144
|
+
lines.push(` ]`);
|
|
145
|
+
lines.push(` : []),`);
|
|
146
|
+
}
|
|
147
|
+
if (hasDocHistory) {
|
|
148
|
+
lines.push(` ...(settings.docHistory`);
|
|
149
|
+
lines.push(` ? [`);
|
|
150
|
+
lines.push(` {`);
|
|
151
|
+
lines.push(` name: "./plugins/doc-history-plugin.mjs",`);
|
|
152
|
+
lines.push(` options: {`);
|
|
153
|
+
lines.push(` docsDir: settings.docsDir,`);
|
|
154
|
+
lines.push(` locales: localeRecord,`);
|
|
155
|
+
lines.push(` },`);
|
|
156
|
+
lines.push(` },`);
|
|
157
|
+
lines.push(` ]`);
|
|
158
|
+
lines.push(` : []),`);
|
|
159
|
+
}
|
|
160
|
+
// search-index is always-on (matches host) — emits dist/search-index.json
|
|
161
|
+
// even when no <Search /> widget mounts; ~few-KB cost is acceptable and
|
|
162
|
+
// keeps the dev-middleware route registered for the always-mounted
|
|
163
|
+
// search widget in pages/lib/_header-with-defaults.tsx.
|
|
164
|
+
lines.push(` {`);
|
|
165
|
+
lines.push(` name: "./plugins/search-index-plugin.mjs",`);
|
|
166
|
+
lines.push(` options: {`);
|
|
167
|
+
lines.push(` docsDir: settings.docsDir,`);
|
|
168
|
+
lines.push(` locales: localeRecord,`);
|
|
169
|
+
lines.push(` base: settings.base,`);
|
|
170
|
+
lines.push(` },`);
|
|
171
|
+
lines.push(` },`);
|
|
172
|
+
if (hasLlmsTxt) {
|
|
173
|
+
lines.push(` ...(settings.llmsTxt`);
|
|
174
|
+
lines.push(` ? [`);
|
|
175
|
+
lines.push(` {`);
|
|
176
|
+
lines.push(` name: "./plugins/llms-txt-plugin.mjs",`);
|
|
177
|
+
lines.push(` options: {`);
|
|
178
|
+
lines.push(` siteName: settings.siteName,`);
|
|
179
|
+
lines.push(` siteDescription: settings.siteDescription,`);
|
|
180
|
+
lines.push(` base: settings.base,`);
|
|
181
|
+
lines.push(` siteUrl: settings.siteUrl,`);
|
|
182
|
+
lines.push(` defaultLocaleDir: settings.docsDir,`);
|
|
183
|
+
lines.push(` locales: localeArray,`);
|
|
184
|
+
lines.push(` },`);
|
|
185
|
+
lines.push(` },`);
|
|
186
|
+
lines.push(` ]`);
|
|
187
|
+
lines.push(` : []),`);
|
|
188
|
+
}
|
|
189
|
+
// copy-public is always-on (matches host) — workaround for upstream zfb
|
|
190
|
+
// gap where `zfb build` does not copy `public/` to dist/. Empty/missing
|
|
191
|
+
// public/ is a no-op, so the cost to projects without public/ is zero.
|
|
192
|
+
lines.push(` {`);
|
|
193
|
+
lines.push(` name: "./plugins/copy-public-plugin.mjs",`);
|
|
194
|
+
lines.push(` options: {`);
|
|
195
|
+
lines.push(` publicDir: "public",`);
|
|
196
|
+
lines.push(` },`);
|
|
197
|
+
lines.push(` },`);
|
|
198
|
+
lines.push(`];`);
|
|
199
|
+
lines.push(``);
|
|
200
|
+
// --- Export ---
|
|
201
|
+
lines.push(`export default defineConfig({`);
|
|
202
|
+
lines.push(` framework: "preact",`);
|
|
203
|
+
lines.push(` tailwind: { enabled: true },`);
|
|
204
|
+
lines.push(` collections,`);
|
|
205
|
+
lines.push(` stripMdExt: true,`);
|
|
206
|
+
lines.push(` resolveMarkdownLinks: {`);
|
|
207
|
+
lines.push(` enabled: true,`);
|
|
208
|
+
lines.push(` dirs: [`);
|
|
209
|
+
lines.push(` { dir: settings.docsDir, routePrefix: "/docs/" },`);
|
|
210
|
+
lines.push(` ...Object.entries(settings.locales).map(([code, locale]) => ({`);
|
|
211
|
+
lines.push(` dir: locale.dir,`);
|
|
212
|
+
lines.push(` routePrefix: \`/\${code}/docs/\`,`);
|
|
213
|
+
lines.push(` })),`);
|
|
214
|
+
lines.push(` ],`);
|
|
215
|
+
lines.push(` onBrokenLinks: "warn",`);
|
|
216
|
+
lines.push(` },`);
|
|
217
|
+
lines.push(` base: settings.base,`);
|
|
218
|
+
lines.push(` trailingSlash: settings.trailingSlash,`);
|
|
219
|
+
lines.push(` plugins: integrationPlugins,`);
|
|
220
|
+
lines.push(`});`);
|
|
221
|
+
return lines.join("\n") + "\n";
|
|
222
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-zudo-doc",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Create a new zudo-doc documentation site",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Takeshi Takatsudo",
|
|
7
|
+
"homepage": "https://zudo-doc.takazudomodular.com",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/zudolab/zudo-doc.git",
|
|
11
|
+
"directory": "packages/create-zudo-doc"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/zudolab/zudo-doc/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"documentation",
|
|
18
|
+
"zfb",
|
|
19
|
+
"mdx",
|
|
20
|
+
"scaffold",
|
|
21
|
+
"create",
|
|
22
|
+
"cli"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=20"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public",
|
|
29
|
+
"provenance": true
|
|
30
|
+
},
|
|
31
|
+
"type": "module",
|
|
32
|
+
"bin": {
|
|
33
|
+
"create-zudo-doc": "./bin/create-zudo-doc.js"
|
|
34
|
+
},
|
|
35
|
+
"main": "./dist/api.js",
|
|
36
|
+
"exports": {
|
|
37
|
+
".": "./dist/api.js"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"bin",
|
|
41
|
+
"dist",
|
|
42
|
+
"templates",
|
|
43
|
+
"!dist/__tests__"
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc",
|
|
47
|
+
"dev": "tsc --watch",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:slow": "vitest run --config vitest.slow.config.ts",
|
|
50
|
+
"prepublishOnly": "pnpm build && pnpm test"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@clack/prompts": "^0.9.1",
|
|
54
|
+
"fs-extra": "^11.3.0",
|
|
55
|
+
"minimist": "^1.2.8",
|
|
56
|
+
"picocolors": "^1.1.1"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/fs-extra": "^11.0.4",
|
|
60
|
+
"@types/minimist": "^1.2.5",
|
|
61
|
+
"@types/node": "^22.0.0",
|
|
62
|
+
"typescript": "^5.9.0",
|
|
63
|
+
"vitest": "^4.1.0"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** @jsxRuntime automatic */
|
|
2
|
+
/** @jsxImportSource preact */
|
|
3
|
+
// Port of src/pages/404.astro → zfb page module.
|
|
4
|
+
//
|
|
5
|
+
// The 404 page is a static route with no dynamic params. zfb emits it as
|
|
6
|
+
// dist/404.html so the host platform (Cloudflare Pages, Netlify, etc.) can
|
|
7
|
+
// serve it for unmatched requests. No paths() export needed.
|
|
8
|
+
//
|
|
9
|
+
// The original Astro page rendered the full html/head/body inline without
|
|
10
|
+
// DocLayout because the 404 page intentionally has no sidebar/TOC/header.
|
|
11
|
+
// This port wraps via DocLayoutWithDefaults with hideSidebar/hideToc plus
|
|
12
|
+
// a noindex meta so search engines do not index it.
|
|
13
|
+
|
|
14
|
+
import { defaultLocale } from "@/config/i18n";
|
|
15
|
+
import { settings } from "@/config/settings";
|
|
16
|
+
import { withBase } from "@/utils/base";
|
|
17
|
+
import { DocLayoutWithDefaults } from "@takazudo/zudo-doc/doclayout";
|
|
18
|
+
import type { JSX } from "preact";
|
|
19
|
+
import { FooterWithDefaults } from "./lib/_footer-with-defaults";
|
|
20
|
+
import { HeaderWithDefaults } from "./lib/_header-with-defaults";
|
|
21
|
+
import { HeadWithDefaults } from "./lib/_head-with-defaults";
|
|
22
|
+
import { composeMetaTitle } from "./lib/_compose-meta-title";
|
|
23
|
+
import { BodyEndIslands } from "./lib/_body-end-islands";
|
|
24
|
+
|
|
25
|
+
export const frontmatter = { title: "404" };
|
|
26
|
+
|
|
27
|
+
export default function NotFoundPage(): JSX.Element {
|
|
28
|
+
const locale = defaultLocale;
|
|
29
|
+
const title = "Page Not Found";
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<DocLayoutWithDefaults
|
|
33
|
+
title={composeMetaTitle(title)}
|
|
34
|
+
head={<HeadWithDefaults title={title} />}
|
|
35
|
+
lang={locale}
|
|
36
|
+
noindex={true}
|
|
37
|
+
hideSidebar={true}
|
|
38
|
+
hideToc={true}
|
|
39
|
+
headerOverride={<HeaderWithDefaults lang={locale} />}
|
|
40
|
+
footerOverride={<FooterWithDefaults lang={locale} />}
|
|
41
|
+
bodyEndComponents={<BodyEndIslands basePath={settings.base ?? "/"} />}
|
|
42
|
+
>
|
|
43
|
+
<div class="min-h-[60vh] flex flex-col items-center justify-center px-hsp-2xl py-vsp-xl">
|
|
44
|
+
<h1 class="text-display font-bold mb-vsp-md">404</h1>
|
|
45
|
+
<p class="text-title text-muted mb-vsp-xl">Page not found.</p>
|
|
46
|
+
<a
|
|
47
|
+
href={withBase("/")}
|
|
48
|
+
class="bg-accent px-hsp-lg py-vsp-xs font-medium text-bg hover:bg-accent-hover"
|
|
49
|
+
>
|
|
50
|
+
Back to Home
|
|
51
|
+
</a>
|
|
52
|
+
</div>
|
|
53
|
+
</DocLayoutWithDefaults>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// pages/_data.ts — zfb-compatible data helpers for doc page modules.
|
|
2
|
+
//
|
|
3
|
+
// Provides the bridge between zfb's CollectionEntry (from "zfb/content") and
|
|
4
|
+
// the utility functions in @/utils/docs that expect DocsEntry (which carries
|
|
5
|
+
// an `id` field mirroring Astro's collection entry id).
|
|
6
|
+
//
|
|
7
|
+
// Sync convention (ADR-004):
|
|
8
|
+
// getCollection() resolves from the pre-loaded ContentSnapshot without an
|
|
9
|
+
// async boundary. paths() exports call getDocs() without await. The Promise
|
|
10
|
+
// wrapper on the type is a v0 artefact — the synchronous snapshot path is
|
|
11
|
+
// the production contract.
|
|
12
|
+
|
|
13
|
+
import { getCollection } from "zfb/content";
|
|
14
|
+
import type { CollectionEntry } from "zfb/content";
|
|
15
|
+
import type { DocsEntry } from "@/types/docs-entry";
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Types
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Frontmatter shape shared by all docs collections (EN, locale, versioned).
|
|
23
|
+
* Matches the zod schema in zfb.config.ts field-for-field.
|
|
24
|
+
* `.passthrough()` equivalent: the index signature [key: string]: unknown
|
|
25
|
+
* keeps custom frontmatter keys available (e.g. for frontmatter-preview).
|
|
26
|
+
*/
|
|
27
|
+
export type ZfbDocsData = {
|
|
28
|
+
title: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
category?: string;
|
|
31
|
+
sidebar_position?: number;
|
|
32
|
+
sidebar_label?: string;
|
|
33
|
+
tags?: string[];
|
|
34
|
+
search_exclude?: boolean;
|
|
35
|
+
pagination_next?: string | null;
|
|
36
|
+
pagination_prev?: string | null;
|
|
37
|
+
draft?: boolean;
|
|
38
|
+
unlisted?: boolean;
|
|
39
|
+
hide_sidebar?: boolean;
|
|
40
|
+
hide_toc?: boolean;
|
|
41
|
+
doc_history?: boolean;
|
|
42
|
+
standalone?: boolean;
|
|
43
|
+
slug?: string;
|
|
44
|
+
generated?: boolean;
|
|
45
|
+
[key: string]: unknown;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* zfb collection entry augmented with the `id` and `collection` fields that
|
|
50
|
+
* @/utils/docs utility functions (buildNavTree, buildBreadcrumbs, …) expect
|
|
51
|
+
* from DocsEntry.
|
|
52
|
+
*
|
|
53
|
+
* `id` is bridged from `slug` — in Astro, `id` was the file-path identifier
|
|
54
|
+
* (e.g. "getting-started/intro"); in zfb, the same role is played by `slug`.
|
|
55
|
+
* Mapping them keeps the utility functions working without modification.
|
|
56
|
+
*/
|
|
57
|
+
export type ZfbDocsEntry = CollectionEntry<ZfbDocsData> & {
|
|
58
|
+
/** Bridged from `slug` for @/utils/docs compat. */
|
|
59
|
+
id: string;
|
|
60
|
+
/** Collection name, e.g. "docs", "docs-ja", "docs-v-1.0". */
|
|
61
|
+
collection: string;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Loaders
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Load docs from a named collection synchronously (ADR-004 sync contract).
|
|
70
|
+
*
|
|
71
|
+
* `getCollection` resolves from the ContentSnapshot when called inside a
|
|
72
|
+
* paths() evaluation. The `as unknown as` cast converts the nominal Promise
|
|
73
|
+
* wrapper to a plain array — safe because the snapshot path is synchronous.
|
|
74
|
+
*
|
|
75
|
+
* The returned entries include:
|
|
76
|
+
* - All CollectionEntry fields (slug, data, body, module_specifier, Content)
|
|
77
|
+
* - `id` — same value as `slug`, for @/utils/docs compat
|
|
78
|
+
* - `collection` — the collection name, for DocsEntry compat
|
|
79
|
+
*/
|
|
80
|
+
export function getDocs(collectionName: string): ZfbDocsEntry[] {
|
|
81
|
+
const entries = getCollection(collectionName) as unknown as CollectionEntry<ZfbDocsData>[];
|
|
82
|
+
return entries.map((e) => ({
|
|
83
|
+
...e,
|
|
84
|
+
// Astro-compat: strip a trailing `/index` from the entry id so
|
|
85
|
+
// `getting-started/index.mdx` → id "getting-started" (matching
|
|
86
|
+
// Astro 5's `glob()` collection loader). Downstream nav helpers
|
|
87
|
+
// (`buildNavTree`, `buildBreadcrumbs`, …) keyed off the stripped
|
|
88
|
+
// form long before zfb existed; emitting the unstripped slug here
|
|
89
|
+
// produces ambiguous-URL collisions at paths()-expansion time.
|
|
90
|
+
id: stripIndexSuffix(e.slug),
|
|
91
|
+
collection: collectionName,
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function stripIndexSuffix(slug: string): string {
|
|
96
|
+
if (slug === "index") return "";
|
|
97
|
+
return slug.endsWith("/index") ? slug.slice(0, -"/index".length) : slug;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Augment a raw zfb collection result with the Astro-style
|
|
102
|
+
* `id`/`collection` fields that downstream `@/utils/docs` helpers
|
|
103
|
+
* (and the `DocPageEntry` extender shape used by `[...slug].tsx`
|
|
104
|
+
* pages) expect. Use this when a page needs a typed array more
|
|
105
|
+
* specific than `DocsEntry` — pages that only need `DocsEntry[]`
|
|
106
|
+
* can use [`loadDocs`] / [`getDocs`] directly.
|
|
107
|
+
*/
|
|
108
|
+
export function bridgeEntries<T = ZfbDocsData>(
|
|
109
|
+
entries: ReadonlyArray<CollectionEntry<T>>,
|
|
110
|
+
collectionName: string,
|
|
111
|
+
): Array<CollectionEntry<T> & { id: string; collection: string }> {
|
|
112
|
+
return entries.map((e) => ({
|
|
113
|
+
...e,
|
|
114
|
+
id: stripIndexSuffix(e.slug),
|
|
115
|
+
collection: collectionName,
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Cast ZfbDocsEntry[] to DocsEntry[] for passing to @/utils/docs utilities.
|
|
121
|
+
*
|
|
122
|
+
* The types are structurally compatible: ZfbDocsEntry has every required field
|
|
123
|
+
* of DocsEntry (id, collection, data, body). The optional `rendered` and
|
|
124
|
+
* `filePath` fields of DocsEntry are absent but not required.
|
|
125
|
+
*/
|
|
126
|
+
export function asDocsEntries(entries: ZfbDocsEntry[]): DocsEntry[] {
|
|
127
|
+
return entries as unknown as DocsEntry[];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* One-shot helper for paths()/render-time pages that just need a
|
|
132
|
+
* `DocsEntry[]` for `@/utils/docs` consumption — wraps `getDocs` and
|
|
133
|
+
* the `asDocsEntries` cast so call sites stay one-line. Use this from
|
|
134
|
+
* any page that previously did
|
|
135
|
+
* `getCollection("docs") as unknown as DocsEntry[]` — that idiom
|
|
136
|
+
* silently dropped the `id`/`collection` fields the utility helpers
|
|
137
|
+
* read, which threw `Cannot read properties of undefined` at runtime.
|
|
138
|
+
*/
|
|
139
|
+
export function loadDocs(collectionName: string): DocsEntry[] {
|
|
140
|
+
return asDocsEntries(getDocs(collectionName));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Filter out draft entries.
|
|
145
|
+
* Drafts are always excluded in static-build paths() context.
|
|
146
|
+
*/
|
|
147
|
+
export function filterDrafts(entries: ZfbDocsEntry[]): ZfbDocsEntry[] {
|
|
148
|
+
return entries.filter((e) => !e.data.draft);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Merge locale docs with base (EN) fallbacks.
|
|
153
|
+
*
|
|
154
|
+
* Strategy (mirrors src/utils/locale-docs.ts):
|
|
155
|
+
* 1. Load locale docs (e.g. "docs-ja")
|
|
156
|
+
* 2. Load base docs ("docs")
|
|
157
|
+
* 3. Locale docs take priority; base docs fill in missing slugs.
|
|
158
|
+
* 4. Track which slugs came from base (fallbackSlugs).
|
|
159
|
+
*
|
|
160
|
+
* Returns { allDocs, fallbackSlugs }.
|
|
161
|
+
* categoryMeta is not merged here — callers use loadCategoryMeta() directly.
|
|
162
|
+
*/
|
|
163
|
+
export function mergeLocaleDocs(
|
|
164
|
+
locale: string,
|
|
165
|
+
): { allDocs: ZfbDocsEntry[]; fallbackSlugs: Set<string> } {
|
|
166
|
+
const localeDocs = filterDrafts(getDocs(`docs-${locale}`));
|
|
167
|
+
const baseDocs = filterDrafts(getDocs("docs"));
|
|
168
|
+
|
|
169
|
+
const localeSlugSet = new Set(localeDocs.map((d) => d.data.slug ?? d.id));
|
|
170
|
+
|
|
171
|
+
const fallbackDocs = baseDocs.filter(
|
|
172
|
+
(d) => !localeSlugSet.has(d.data.slug ?? d.id),
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
allDocs: [...localeDocs, ...fallbackDocs],
|
|
177
|
+
fallbackSlugs: new Set(fallbackDocs.map((d) => d.data.slug ?? d.id)),
|
|
178
|
+
};
|
|
179
|
+
}
|