create-zudo-doc 0.2.21 → 1.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 (92) hide show
  1. package/dist/compose.d.ts +6 -3
  2. package/dist/compose.js +6 -4
  3. package/dist/features/image-enlarge.d.ts +22 -12
  4. package/dist/features/image-enlarge.js +23 -191
  5. package/dist/scaffold.d.ts +11 -0
  6. package/dist/scaffold.js +51 -24
  7. package/dist/settings-gen.js +4 -0
  8. package/dist/zfb-config-gen.d.ts +16 -10
  9. package/dist/zfb-config-gen.js +34 -239
  10. package/package.json +1 -1
  11. package/templates/base/pages/_mdx-components.ts +64 -185
  12. package/templates/base/pages/index.tsx +1 -1
  13. package/templates/base/pages/lib/_body-end-islands.tsx +6 -13
  14. package/templates/base/pages/lib/_category-nav.tsx +30 -115
  15. package/templates/base/pages/lib/_category-tree-nav.tsx +38 -65
  16. package/templates/base/pages/lib/_compose-meta-title.ts +2 -6
  17. package/templates/base/pages/lib/_doc-body-end.tsx +3 -35
  18. package/templates/base/pages/lib/_doc-content-header.tsx +10 -111
  19. package/templates/base/pages/lib/_doc-history-area.tsx +19 -211
  20. package/templates/base/pages/lib/_doc-metainfo-area.tsx +10 -113
  21. package/templates/base/pages/lib/_doc-page-renderer.tsx +22 -183
  22. package/templates/base/pages/lib/_doc-page-shell.tsx +17 -251
  23. package/templates/base/pages/lib/_doc-pager.tsx +4 -74
  24. package/templates/base/pages/lib/_doc-route-entries.ts +43 -177
  25. package/templates/base/pages/lib/_doc-route-paths.ts +16 -101
  26. package/templates/base/pages/lib/_doc-tags-area.tsx +14 -77
  27. package/templates/base/pages/lib/_extract-headings.ts +21 -295
  28. package/templates/base/pages/lib/_footer-with-defaults.tsx +37 -225
  29. package/templates/base/pages/lib/_frontmatter-preview-data.ts +5 -31
  30. package/templates/base/pages/lib/_head-with-defaults.tsx +13 -138
  31. package/templates/base/pages/lib/_header-with-defaults.tsx +24 -324
  32. package/templates/base/pages/lib/_inline-version-switcher.tsx +16 -78
  33. package/templates/base/pages/lib/_math-block.tsx +4 -63
  34. package/templates/base/pages/lib/_nav-data-prep.ts +32 -51
  35. package/templates/base/pages/lib/_nav-source-cache.ts +12 -57
  36. package/templates/base/pages/lib/_nav-source-docs.ts +33 -233
  37. package/templates/base/pages/lib/_search-widget-script.ts +2 -470
  38. package/templates/base/pages/lib/_search-widget.tsx +9 -195
  39. package/templates/base/pages/lib/_sidebar-prepaint.tsx +6 -59
  40. package/templates/base/pages/lib/_sidebar-with-defaults.tsx +16 -118
  41. package/templates/base/pages/lib/_site-tree-nav.tsx +29 -80
  42. package/templates/base/pages/lib/doc-page-props.ts +26 -44
  43. package/templates/base/pages/lib/locale-merge.ts +32 -145
  44. package/templates/base/pages/lib/route-enumerators.ts +52 -286
  45. package/templates/base/src/components/ai-chat-modal.tsx +9 -8
  46. package/templates/base/src/components/content/code-group.tsx +3 -76
  47. package/templates/base/src/components/content/content-admonition.tsx +4 -56
  48. package/templates/base/src/components/doc-history.tsx +9 -8
  49. package/templates/base/src/components/image-enlarge.tsx +11 -8
  50. package/templates/base/src/components/sidebar-toggle.tsx +6 -170
  51. package/templates/base/src/components/sidebar-tree.tsx +6 -548
  52. package/templates/base/src/config/color-scheme-utils.ts +34 -158
  53. package/templates/base/src/config/i18n.ts +9 -0
  54. package/templates/base/src/config/settings-types.ts +34 -172
  55. package/templates/base/src/config/z-index-tokens.ts +5 -4
  56. package/templates/base/src/styles/global.css +40 -587
  57. package/templates/base/src/utils/base.ts +1 -1
  58. package/templates/base/src/utils/docs.ts +47 -16
  59. package/templates/base/src/utils/github.ts +12 -9
  60. package/templates/base/src/utils/nav-scope.ts +13 -42
  61. package/templates/base/src/utils/sidebar.ts +18 -86
  62. package/templates/base/src/utils/slug.ts +10 -53
  63. package/templates/base/src/utils/smart-break.tsx +12 -120
  64. package/templates/base/src/utils/tags.ts +25 -68
  65. package/templates/features/bodyFootUtil/files/src/utils/github.ts +12 -9
  66. package/templates/features/designTokenPanel/files/src/lib/design-token-panel-bootstrap.ts +13 -39
  67. package/templates/features/docHistory/files/src/components/doc-history.tsx +8 -636
  68. package/templates/features/docHistory/files/src/types/doc-history.ts +7 -23
  69. package/templates/features/docTags/files/pages/lib/_tag-pages.tsx +35 -201
  70. package/templates/features/i18n/files/pages/[locale]/index.tsx +1 -1
  71. package/templates/features/imageEnlarge/files/src/components/image-enlarge.tsx +8 -269
  72. package/templates/features/sidebarToggle/files/src/components/desktop-sidebar-toggle.tsx +6 -99
  73. package/templates/features/tagGovernance/files/scripts/tags-audit.ts +67 -515
  74. package/templates/features/versioning/files/pages/lib/_versions-page.tsx +21 -73
  75. package/templates/base/pages/404.tsx +0 -61
  76. package/templates/base/pages/robots.txt.tsx +0 -29
  77. package/templates/base/pages/sitemap.xml.tsx +0 -59
  78. package/templates/base/plugins/connect-adapter.mjs +0 -169
  79. package/templates/base/plugins/copy-public-plugin.mjs +0 -58
  80. package/templates/base/plugins/search-index-plugin.mjs +0 -66
  81. package/templates/base/scripts/gen-z-index.mjs +0 -157
  82. package/templates/base/src/components/mermaid-enlarge.tsx +0 -490
  83. package/templates/base/src/components/site-tree-nav.tsx +0 -220
  84. package/templates/features/claudeResources/files/plugins/claude-resources-plugin.mjs +0 -47
  85. package/templates/features/docHistory/files/plugins/doc-history-plugin.mjs +0 -111
  86. package/templates/features/docTags/files/pages/[locale]/docs/tags/[tag].tsx +0 -59
  87. package/templates/features/docTags/files/pages/[locale]/docs/tags/index.tsx +0 -39
  88. package/templates/features/docTags/files/pages/docs/tags/[tag].tsx +0 -43
  89. package/templates/features/docTags/files/pages/docs/tags/index.tsx +0 -20
  90. package/templates/features/llmsTxt/files/plugins/llms-txt-plugin.mjs +0 -93
  91. package/templates/features/versioning/files/pages/[locale]/docs/versions.tsx +0 -48
  92. package/templates/features/versioning/files/pages/docs/versions.tsx +0 -20
@@ -32,11 +32,21 @@
32
32
  // HMR intent preserved: a content edit produces a NEW snapshot object (new
33
33
  // `collections[name]` array identity), so every memo here misses and
34
34
  // recomputes — matching the old content-keyed cache's change detection.
35
+ //
36
+ // PACKAGE SPLIT (epic #2344, S6):
37
+ // `memoizeDerived` — the generic two-level WeakMap memo — is now in the
38
+ // package at @takazudo/zudo-doc/nav-source-cache (no zfb imports needed).
39
+ // `stableDocs` stays here because it imports `getCollection` /
40
+ // `getContentSnapshot` from "zfb/content", which is a virtual module
41
+ // provided by the zfb SSG build system at compile time — not an npm package.
35
42
 
36
43
  import { getCollection, getContentSnapshot } from "zfb/content";
37
44
  import { bridgeDocsEntries, type ZfbDocsData } from "../_data";
38
45
  import type { DocPageEntry } from "./doc-page-props";
39
46
 
47
+ // Re-export memoizeDerived from the package (pure, no zfb dep).
48
+ export { memoizeDerived } from "@takazudo/zudo-doc/nav-source-cache";
49
+
40
50
  // ---------------------------------------------------------------------------
41
51
  // Snapshot anchor → stable bridged arrays
42
52
  // ---------------------------------------------------------------------------
@@ -70,6 +80,8 @@ function buildBridged(collectionName: string): DocPageEntry[] {
70
80
  * `buildNavTree` — can rely on reference equality. The entries carry the full
71
81
  * `DocPageEntry` shape (Content, body, module_specifier, id, collection) the
72
82
  * route `paths()` props need.
83
+ *
84
+ * Passed as `stableDocs` to `createNavSourceDocs` in `_nav-source-docs.ts`.
73
85
  */
74
86
  export function stableDocs(collectionName: string): DocPageEntry[] {
75
87
  const anchor = snapshotAnchor(collectionName);
@@ -86,60 +98,3 @@ export function stableDocs(collectionName: string): DocPageEntry[] {
86
98
  bridgedByAnchor.set(anchor, built);
87
99
  return built;
88
100
  }
89
-
90
- // ---------------------------------------------------------------------------
91
- // Generic derived-array memo (merge / filter results)
92
- // ---------------------------------------------------------------------------
93
-
94
- // Two-level memo: a WeakMap on the FIRST stable input array (so a new snapshot
95
- // drops the whole sub-map), then a string key combining the remaining stable
96
- // inputs' identities and the option signature.
97
- const derivedMemo = new WeakMap<object, Map<string, unknown>>();
98
- // Per-build incrementing ids for stable arrays, so a multi-array key can be a
99
- // cheap string. Lives in a WeakMap so it does not retain arrays past their
100
- // snapshot.
101
- const arrayId = new WeakMap<object, number>();
102
- let nextArrayId = 1;
103
- function idOf(arr: object): number {
104
- let id = arrayId.get(arr);
105
- if (id === undefined) {
106
- id = nextArrayId++;
107
- arrayId.set(arr, id);
108
- }
109
- return id;
110
- }
111
-
112
- // Anchor object for the rare case of an empty `inputs` array (e.g. an absent
113
- // locale collection). Lets the WeakMap still key the derived memo.
114
- const EMPTY_INPUT_ANCHOR: object = {};
115
-
116
- /**
117
- * Memoize a derived array (e.g. a locale merge or an `isNavVisible` filter)
118
- * on the identity of its stable inputs plus a caller-supplied option
119
- * signature.
120
- *
121
- * `inputs` MUST be the stable arrays returned by {@link stableDocs} (or arrays
122
- * derived from them through this same helper) — passing a fresh array defeats
123
- * the memo. `optionSig` distinguishes call sites that differ only in filter
124
- * options (e.g. `applyDefaultLocaleOnlyFilter`, `keepUnlisted`), so they never
125
- * collide on the same key.
126
- */
127
- export function memoizeDerived<T>(
128
- inputs: readonly object[],
129
- optionSig: string,
130
- compute: () => T,
131
- ): T {
132
- const primary = inputs[0] ?? EMPTY_INPUT_ANCHOR;
133
- let sub = derivedMemo.get(primary);
134
- if (!sub) {
135
- sub = new Map();
136
- derivedMemo.set(primary, sub);
137
- }
138
- const key = `${inputs.map((a) => idOf(a)).join("/")}::${optionSig}`;
139
- // Use has(): a legitimately-`undefined` computed result must still register
140
- // as a cache hit, otherwise it recomputes every call.
141
- if (sub.has(key)) return sub.get(key) as T;
142
- const computed = compute();
143
- sub.set(key, computed);
144
- return computed;
145
- }
@@ -1,237 +1,37 @@
1
- // Shared, identity-stable nav-source resolver.
2
- //
3
- // Picks the right collection(s) and category-meta dir for an active
4
- // (locale, version) pair, applying the locale-first + EN-fallback merge that
5
- // the doc-route `paths()` passes use, so every nav surface (desktop sidebar,
6
- // mobile SidebarToggle, route enumeration, MDX nav wrappers) sees the same
7
- // data the pages enumerate.
8
- //
9
- // IDENTITY STABILITY (why this is not a thin wrapper)
10
- // ---------------------------------------------------
11
- // Every returned `docs` / `navDocs` array and `categoryMeta` Map is memoized
12
- // so repeat callers within one build get the SAME instances. That is what lets
13
- // `buildNavTree`'s identity fast-path (`src/utils/docs.ts`) skip the O(n log n)
14
- // key computation on the ~900 calls a 251-page build makes. See
15
- // `_nav-source-cache.ts` for the snapshot-anchored memo and the original-issue
16
- // trace (zudolab/zudo-doc#1902 / #1882).
17
- //
18
- // Used by:
19
- // - _sidebar-with-defaults.tsx / _header-with-defaults.tsx (per-page nav)
20
- // - the 4 doc-route paths() files (route enumeration + nav tree)
21
- // - route-enumerators.ts (sitemap) and the MDX nav wrappers
22
- // each picking the `NavSourceVariant` matching its filter needs.
23
-
24
- import { defaultLocale, getLocaleConfig, type Locale } from "@/config/i18n";
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.
5
+
6
+ import { createNavSourceDocs } from "@takazudo/zudo-doc/nav-source-docs";
7
+ export type { NavSourceDocs, NavSourceOptions } from "@takazudo/zudo-doc/nav-source-docs";
8
+ import { defaultLocale, getLocaleConfig } from "@/config/i18n";
25
9
  import { settings } from "@/config/settings";
26
- import {
10
+ import { loadCategoryMeta, isNavVisible } from "@/utils/docs";
11
+ import { isDefaultLocaleOnlyPath } from "@/utils/base";
12
+ import { stableDocs } from "./_nav-source-cache";
13
+
14
+ const {
15
+ resolveNavSource,
16
+ resolveVersionedLocaleSource,
17
+ loadNavSourceDocs,
18
+ stableMergeCategoryMeta,
19
+ stableNavDocs,
20
+ } = createNavSourceDocs({
21
+ defaultLocale,
22
+ docsDir: settings.docsDir,
23
+ getVersions: () => settings.versions,
24
+ getLocaleConfig,
27
25
  loadCategoryMeta,
28
26
  isNavVisible,
29
- type CategoryMeta,
30
- } from "@/utils/docs";
31
- import type { DocsEntry } from "@/types/docs-entry";
32
- import type { DocPageEntry } from "./doc-page-props";
33
- import { stableDocs, memoizeDerived } from "./_nav-source-cache";
34
- import { mergeLocaleDocs } from "./locale-merge";
35
-
36
- // ---------------------------------------------------------------------------
37
- // Stable category-meta merge
38
- // ---------------------------------------------------------------------------
39
-
40
- // `loadCategoryMeta(dir)` is already memoized by dir (returns the same Map).
41
- // The locale merge of two such Maps, however, was minted fresh on every call —
42
- // breaking the `categoryMeta === categoryMeta` identity check in the nav-tree
43
- // fast-path. Memoize the merge on the (baseDir, localeDir) pair.
44
- const mergedCategoryMetaCache = new Map<string, Map<string, CategoryMeta>>();
45
-
46
- /** Base metadata first, locale overrides win on overlapping keys — same merge
47
- * order the locale doc-route paths() use. Returns a STABLE Map per
48
- * (baseDir, localeDir) pair. */
49
- export function stableMergeCategoryMeta(
50
- baseDir: string,
51
- localeDir: string,
52
- ): Map<string, CategoryMeta> {
53
- const key = `${baseDir}\n${localeDir}`;
54
- const cached = mergedCategoryMetaCache.get(key);
55
- if (cached) return cached;
56
- const merged = new Map<string, CategoryMeta>([
57
- ...loadCategoryMeta(baseDir),
58
- ...loadCategoryMeta(localeDir),
59
- ]);
60
- mergedCategoryMetaCache.set(key, merged);
61
- return merged;
62
- }
63
-
64
- // ---------------------------------------------------------------------------
65
- // Stable navDocs (isNavVisible filter) derived from a stable docs array
66
- // ---------------------------------------------------------------------------
67
-
68
- /** `docs.filter(isNavVisible)`, memoized on the stable `docs` identity so the
69
- * filtered array also has stable identity for the nav-tree fast-path. */
70
- export function stableNavDocs<T extends DocsEntry>(docs: T[]): T[] {
71
- return memoizeDerived([docs], "navVisible", () => docs.filter(isNavVisible));
72
- }
73
-
74
- // ---------------------------------------------------------------------------
75
- // Resolved nav source
76
- // ---------------------------------------------------------------------------
77
-
78
- export type NavSourceDocs = {
79
- /** Full doc list (merged + draft-filtered; unlisted retained per options). */
80
- docs: DocPageEntry[];
81
- /** `docs.filter(isNavVisible)` — stable instance for buildNavTree. */
82
- navDocs: DocPageEntry[];
83
- /** Stable category-meta Map for the active (locale, version). */
84
- categoryMeta: Map<string, CategoryMeta>;
85
- /** Slugs that came from the locale collection (for isFallback). Empty for
86
- * default-locale / single-collection cases. */
87
- localeSlugSet: ReadonlySet<string>;
27
+ isDefaultLocaleOnlyPath,
28
+ stableDocs,
29
+ });
30
+
31
+ export {
32
+ resolveNavSource,
33
+ resolveVersionedLocaleSource,
34
+ loadNavSourceDocs,
35
+ stableMergeCategoryMeta,
36
+ stableNavDocs,
88
37
  };
89
-
90
- /**
91
- * How to filter the merged doc list. The locale merge takes different options
92
- * at different call sites; the option signature is folded into the memo key so
93
- * variants never collide on a shared cache entry.
94
- */
95
- export interface NavSourceOptions {
96
- /** Drop base docs matching `defaultLocaleOnlyPrefixes` (route/sidebar nav). */
97
- applyDefaultLocaleOnlyFilter?: boolean;
98
- /** Retain `unlisted: true` docs (route enumeration + nav). */
99
- keepUnlisted?: boolean;
100
- }
101
-
102
- function optionSig(o: NavSourceOptions): string {
103
- return `dlo=${o.applyDefaultLocaleOnlyFilter ? 1 : 0};ku=${o.keepUnlisted ? 1 : 0}`;
104
- }
105
-
106
- const EMPTY_SLUG_SET: ReadonlySet<string> = new Set();
107
-
108
- /**
109
- * Resolve the identity-stable nav source for an EN/locale (optionally
110
- * versioned) context. Every field is memoized so repeated calls with the same
111
- * logical inputs return the same instances.
112
- */
113
- export function resolveNavSource(
114
- lang: Locale,
115
- currentVersion: string | undefined,
116
- options: NavSourceOptions = {},
117
- ): NavSourceDocs {
118
- const sig = optionSig(options);
119
-
120
- // --- Versioned. For a non-default locale the version IS configured for,
121
- // delegate to resolveVersionedLocaleSource so every nav surface uses the
122
- // SAME version-scoped locale-first merge the page body / route
123
- // enumeration use (#1909) — keeping nav labels and locale-only version
124
- // pages in sync. Otherwise (default locale, or the version not configured
125
- // for this locale) fall back to the version's EN base collection.
126
- if (currentVersion) {
127
- // `versions` is `VersionConfig[] | false` — `false?.find` would throw
128
- // (optional chaining only short-circuits on null/undefined).
129
- const versionConfig = Array.isArray(settings.versions)
130
- ? settings.versions.find((v) => v.slug === currentVersion)
131
- : undefined;
132
- const localeDir = versionConfig?.locales?.[lang]?.dir;
133
- if (lang !== defaultLocale && localeDir) {
134
- return resolveVersionedLocaleSource(
135
- currentVersion,
136
- versionConfig?.docsDir ?? settings.docsDir,
137
- lang,
138
- localeDir,
139
- options,
140
- );
141
- }
142
- const docs = stableDocs(`docs-v-${currentVersion}`);
143
- const categoryMeta = loadCategoryMeta(versionConfig?.docsDir ?? settings.docsDir);
144
- const navDocs = stableNavDocs(docs);
145
- return { docs, navDocs, categoryMeta, localeSlugSet: EMPTY_SLUG_SET };
146
- }
147
-
148
- // --- Default locale: the "docs" collection directly.
149
- if (lang === defaultLocale) {
150
- const docs = stableDocs("docs");
151
- const categoryMeta = loadCategoryMeta(settings.docsDir);
152
- const navDocs = stableNavDocs(docs);
153
- return { docs, navDocs, categoryMeta, localeSlugSet: EMPTY_SLUG_SET };
154
- }
155
-
156
- // --- Non-default locale: locale-first merge with EN fallback.
157
- const baseDocs = stableDocs("docs");
158
- const localeDocs = stableDocs(`docs-${lang}`);
159
-
160
- const merged = memoizeDerived([baseDocs, localeDocs], `merge;${sig}`, () =>
161
- mergeLocaleDocs<DocPageEntry>({
162
- baseDocs,
163
- localeDocs,
164
- applyDefaultLocaleOnlyFilter: options.applyDefaultLocaleOnlyFilter,
165
- keepUnlisted: options.keepUnlisted,
166
- }),
167
- );
168
- const docs = merged.docs;
169
-
170
- const localeDir = getLocaleConfig(lang)?.dir ?? settings.docsDir;
171
- const categoryMeta = stableMergeCategoryMeta(settings.docsDir, localeDir);
172
- const navDocs = stableNavDocs(docs);
173
-
174
- return { docs, navDocs, categoryMeta, localeSlugSet: merged.localeSlugSet };
175
- }
176
-
177
- /**
178
- * Resolve the identity-stable nav source for a versioned non-default-locale
179
- * context — locale-specific version collection over the version's EN base.
180
- */
181
- export function resolveVersionedLocaleSource(
182
- versionSlug: string,
183
- versionDocsDir: string,
184
- lang: Locale,
185
- localeDir: string | undefined,
186
- options: NavSourceOptions = {},
187
- ): NavSourceDocs {
188
- const sig = optionSig(options);
189
- const baseDocs = stableDocs(`docs-v-${versionSlug}`);
190
- const localeDocs = localeDir ? stableDocs(`docs-v-${versionSlug}-${lang}`) : EMPTY_DOCS;
191
-
192
- const merged = memoizeDerived(
193
- localeDir ? [baseDocs, localeDocs] : [baseDocs],
194
- `vmerge;${lang};${sig}`,
195
- () =>
196
- mergeLocaleDocs<DocPageEntry>({
197
- baseDocs,
198
- localeDocs,
199
- applyDefaultLocaleOnlyFilter: options.applyDefaultLocaleOnlyFilter,
200
- keepUnlisted: options.keepUnlisted,
201
- }),
202
- );
203
- const docs = merged.docs;
204
-
205
- const categoryMeta = localeDir
206
- ? stableMergeCategoryMeta(versionDocsDir, localeDir)
207
- : loadCategoryMeta(versionDocsDir);
208
- const navDocs = stableNavDocs(docs);
209
-
210
- return { docs, navDocs, categoryMeta, localeSlugSet: merged.localeSlugSet };
211
- }
212
-
213
- // Shared empty array for the "no locale collection" branch — stable identity
214
- // keeps the merge memo consistent.
215
- const EMPTY_DOCS: DocPageEntry[] = [];
216
-
217
- // ---------------------------------------------------------------------------
218
- // Back-compat: loadNavSourceDocs (sidebar + header consumers)
219
- // ---------------------------------------------------------------------------
220
-
221
- /**
222
- * Per-page nav source for the desktop sidebar and mobile SidebarToggle.
223
- *
224
- * Thin adapter over {@link resolveNavSource} with the sidebar's filter options
225
- * (`applyDefaultLocaleOnlyFilter`, `keepUnlisted`). Returns `navDocs` already
226
- * filtered so callers pass it straight to `buildSidebarForSection` without a
227
- * fresh `.filter(isNavVisible)` that would defeat the nav-tree fast-path.
228
- */
229
- export function loadNavSourceDocs(
230
- lang: Locale,
231
- currentVersion: string | undefined,
232
- ): NavSourceDocs {
233
- return resolveNavSource(lang, currentVersion, {
234
- applyDefaultLocaleOnlyFilter: true,
235
- keepUnlisted: true,
236
- });
237
- }