create-zudo-doc 0.2.20 → 0.2.22
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/dist/features/image-enlarge.d.ts +10 -2
- package/dist/features/image-enlarge.js +10 -2
- package/dist/scaffold.js +22 -22
- package/dist/zfb-config-gen.d.ts +16 -10
- package/dist/zfb-config-gen.js +34 -239
- package/package.json +1 -1
- package/templates/base/pages/lib/_body-end-islands.tsx +6 -13
- package/templates/base/pages/lib/_category-nav.tsx +30 -115
- package/templates/base/pages/lib/_category-tree-nav.tsx +38 -65
- package/templates/base/pages/lib/_compose-meta-title.ts +2 -6
- package/templates/base/pages/lib/_doc-body-end.tsx +3 -35
- package/templates/base/pages/lib/_doc-content-header.tsx +10 -111
- package/templates/base/pages/lib/_doc-history-area.tsx +19 -211
- package/templates/base/pages/lib/_doc-metainfo-area.tsx +10 -113
- package/templates/base/pages/lib/_doc-page-renderer.tsx +22 -183
- package/templates/base/pages/lib/_doc-page-shell.tsx +17 -251
- package/templates/base/pages/lib/_doc-pager.tsx +4 -74
- package/templates/base/pages/lib/_doc-route-entries.ts +43 -177
- package/templates/base/pages/lib/_doc-route-paths.ts +16 -101
- package/templates/base/pages/lib/_doc-tags-area.tsx +14 -77
- package/templates/base/pages/lib/_footer-with-defaults.tsx +37 -225
- package/templates/base/pages/lib/_frontmatter-preview-data.ts +5 -31
- package/templates/base/pages/lib/_head-with-defaults.tsx +13 -138
- package/templates/base/pages/lib/_header-with-defaults.tsx +24 -324
- package/templates/base/pages/lib/_inline-version-switcher.tsx +16 -78
- package/templates/base/pages/lib/_nav-data-prep.ts +32 -51
- package/templates/base/pages/lib/_nav-source-cache.ts +12 -57
- package/templates/base/pages/lib/_nav-source-docs.ts +33 -233
- package/templates/base/pages/lib/_search-widget-script.ts +2 -470
- package/templates/base/pages/lib/_search-widget.tsx +9 -195
- package/templates/base/pages/lib/_sidebar-prepaint.tsx +6 -47
- package/templates/base/pages/lib/_sidebar-with-defaults.tsx +16 -118
- package/templates/base/pages/lib/_site-tree-nav.tsx +29 -80
- package/templates/base/pages/lib/doc-page-props.ts +26 -44
- package/templates/base/pages/lib/locale-merge.ts +32 -145
- package/templates/base/pages/lib/route-enumerators.ts +52 -286
- package/templates/base/pages/robots.txt.tsx +2 -26
- package/templates/base/src/components/ai-chat-modal.tsx +9 -8
- package/templates/base/src/components/doc-history.tsx +9 -8
- package/templates/base/src/components/image-enlarge.tsx +11 -8
- package/templates/base/src/components/sidebar-toggle.tsx +6 -170
- package/templates/base/src/components/sidebar-tree.tsx +6 -548
- package/templates/base/src/components/site-tree-nav.tsx +6 -220
- package/templates/base/src/config/color-scheme-utils.ts +34 -158
- package/templates/base/src/config/i18n.ts +9 -0
- package/templates/base/src/config/z-index-tokens.ts +5 -4
- package/templates/base/src/styles/global.css +5 -579
- package/templates/base/src/utils/base.ts +1 -1
- package/templates/base/src/utils/docs.ts +47 -16
- package/templates/base/src/utils/github.ts +12 -9
- package/templates/base/src/utils/nav-scope.ts +13 -42
- package/templates/base/src/utils/sidebar.ts +18 -86
- package/templates/base/src/utils/slug.ts +10 -53
- package/templates/base/src/utils/smart-break.tsx +12 -120
- package/templates/base/src/utils/tags.ts +25 -68
- package/templates/features/bodyFootUtil/files/src/utils/github.ts +12 -9
- package/templates/features/designTokenPanel/files/src/lib/design-token-panel-bootstrap.ts +13 -39
- package/templates/features/docHistory/files/src/components/doc-history.tsx +8 -636
- package/templates/features/docHistory/files/src/types/doc-history.ts +7 -23
- package/templates/features/docTags/files/pages/lib/_tag-pages.tsx +35 -201
- package/templates/features/imageEnlarge/files/src/components/image-enlarge.tsx +8 -269
- package/templates/features/sidebarToggle/files/src/components/desktop-sidebar-toggle.tsx +6 -99
- package/templates/features/tagGovernance/files/scripts/tags-audit.ts +67 -515
- package/templates/features/tauri/files/src-tauri/capabilities/default.json +1 -4
- package/templates/features/tauri/files/src-tauri/tauri.conf.json +1 -1
- package/templates/features/tauriDev/files/src-tauri-dev/capabilities/default.json +1 -1
- package/templates/features/versioning/files/pages/lib/_versions-page.tsx +21 -73
- package/templates/base/plugins/connect-adapter.mjs +0 -169
- package/templates/base/plugins/search-index-plugin.mjs +0 -66
- package/templates/base/scripts/gen-z-index.mjs +0 -157
- package/templates/base/src/components/mermaid-enlarge.tsx +0 -490
- package/templates/features/claudeResources/files/plugins/claude-resources-plugin.mjs +0 -47
- package/templates/features/docHistory/files/plugins/doc-history-plugin.mjs +0 -111
- package/templates/features/llmsTxt/files/plugins/llms-txt-plugin.mjs +0 -93
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
// and
|
|
1
|
+
// Host thin-stub for nav data-prep utilities (epic #2344, S5).
|
|
2
|
+
// Imports core logic from @takazudo/zudo-doc and wires host singletons.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// Package exports the pure parameterized functions; this stub binds
|
|
5
|
+
// them to the host's `settings`, `i18n`, and `base` singletons so
|
|
6
|
+
// existing call sites (_header-with-defaults.tsx, _sidebar-with-defaults.tsx)
|
|
7
|
+
// are unchanged.
|
|
7
8
|
|
|
8
9
|
import { settings } from "@/config/settings";
|
|
9
10
|
import { t, type Locale } from "@/config/i18n";
|
|
@@ -15,6 +16,12 @@ import {
|
|
|
15
16
|
import { type NavNode } from "@/utils/docs";
|
|
16
17
|
import { buildSidebarForSection } from "@/utils/sidebar";
|
|
17
18
|
import { loadNavSourceDocs } from "./_nav-source-docs";
|
|
19
|
+
import {
|
|
20
|
+
remapVersionedHrefs as _remapVersionedHrefs,
|
|
21
|
+
buildRootMenuItems as _buildRootMenuItems,
|
|
22
|
+
buildLocaleLinksForNav as _buildLocaleLinksForNav,
|
|
23
|
+
getThemeDefaultMode as _getThemeDefaultMode,
|
|
24
|
+
} from "@takazudo/zudo-doc/nav-data-prep";
|
|
18
25
|
|
|
19
26
|
// ---------------------------------------------------------------------------
|
|
20
27
|
// remapVersionedHrefs
|
|
@@ -22,30 +29,18 @@ import { loadNavSourceDocs } from "./_nav-source-docs";
|
|
|
22
29
|
|
|
23
30
|
/**
|
|
24
31
|
* Walk the nav tree and rewrite each node's `href` to its versioned form.
|
|
25
|
-
*
|
|
26
|
-
* `buildNavTree` always emits hrefs via `docsUrl()`; when the active route
|
|
27
|
-
* lives under `/v/{version}/...` we need the same nodes pointing at the
|
|
28
|
-
* versioned URL so internal nav clicks stay inside the version. Skips
|
|
29
|
-
* nodes without an href (link-only or category placeholders).
|
|
30
32
|
*/
|
|
31
33
|
export function remapVersionedHrefs(
|
|
32
34
|
nodes: NavNode[],
|
|
33
35
|
version: string,
|
|
34
36
|
nodeLang: Locale,
|
|
35
37
|
): NavNode[] {
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (!node.href || node.slug.startsWith("__link__")) {
|
|
43
|
-
return children !== node.children ? { ...node, children } : node;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const newHref = versionedDocsUrl(node.slug, version, nodeLang);
|
|
47
|
-
return { ...node, href: newHref, children };
|
|
48
|
-
});
|
|
38
|
+
return _remapVersionedHrefs(
|
|
39
|
+
nodes,
|
|
40
|
+
version,
|
|
41
|
+
nodeLang,
|
|
42
|
+
(slug, v, lang) => versionedDocsUrl(slug, v, lang as Locale),
|
|
43
|
+
);
|
|
49
44
|
}
|
|
50
45
|
|
|
51
46
|
// ---------------------------------------------------------------------------
|
|
@@ -54,26 +49,18 @@ export function remapVersionedHrefs(
|
|
|
54
49
|
|
|
55
50
|
/**
|
|
56
51
|
* Root-menu items derived from settings.headerNav (mobile "back to menu" list).
|
|
57
|
-
*
|
|
58
|
-
* Used by both header and sidebar wrappers — the same nav data feeds both the
|
|
59
|
-
* mobile SidebarToggle (header) and the desktop SidebarTree (sidebar).
|
|
60
52
|
*/
|
|
61
53
|
export function buildRootMenuItems(
|
|
62
54
|
lang: Locale,
|
|
63
55
|
currentVersion?: string,
|
|
64
56
|
) {
|
|
65
|
-
return
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
? t(child.labelKey as Parameters<typeof t>[0], lang)
|
|
73
|
-
: child.label,
|
|
74
|
-
href: navHref(child.path, lang, currentVersion),
|
|
75
|
-
})),
|
|
76
|
-
}));
|
|
57
|
+
return _buildRootMenuItems(
|
|
58
|
+
lang,
|
|
59
|
+
currentVersion,
|
|
60
|
+
settings.headerNav,
|
|
61
|
+
(key, l) => t(key as Parameters<typeof t>[0], l as Locale),
|
|
62
|
+
(path, l, v) => navHref(path, l as Locale | undefined, v),
|
|
63
|
+
);
|
|
77
64
|
}
|
|
78
65
|
|
|
79
66
|
// ---------------------------------------------------------------------------
|
|
@@ -89,7 +76,12 @@ export function buildLocaleLinksForNav(
|
|
|
89
76
|
lang: Locale,
|
|
90
77
|
localeCount: number,
|
|
91
78
|
) {
|
|
92
|
-
return
|
|
79
|
+
return _buildLocaleLinksForNav(
|
|
80
|
+
currentPath,
|
|
81
|
+
lang,
|
|
82
|
+
localeCount,
|
|
83
|
+
(path, l) => buildLocaleLinks(path, l as Locale),
|
|
84
|
+
);
|
|
93
85
|
}
|
|
94
86
|
|
|
95
87
|
// ---------------------------------------------------------------------------
|
|
@@ -98,17 +90,6 @@ export function buildLocaleLinksForNav(
|
|
|
98
90
|
|
|
99
91
|
/**
|
|
100
92
|
* Build the resolved sidebar node list for a given section + version.
|
|
101
|
-
*
|
|
102
|
-
* Loads the nav source, filters to the active section, then optionally
|
|
103
|
-
* remaps hrefs for versioned routes.
|
|
104
|
-
*
|
|
105
|
-
* `emptyWhenUnsectioned` controls the `navSection === undefined` case —
|
|
106
|
-
* the two legacy call sites deliberately disagreed: the header's mobile
|
|
107
|
-
* drawer returned `[]` (root menu only), while the desktop sidebar fell
|
|
108
|
-
* through to `buildSidebarForSection(..., undefined)` = the FULL tree
|
|
109
|
-
* (pages whose slug matches no headerNav categoryMatch still get a
|
|
110
|
-
* sidebar). Collapsing both to `[]` shipped an empty desktop sidebar for
|
|
111
|
-
* unsectioned pages — keep the divergence explicit here.
|
|
112
93
|
*/
|
|
113
94
|
export function buildSidebarNodes(
|
|
114
95
|
lang: Locale,
|
|
@@ -133,5 +114,5 @@ export function buildSidebarNodes(
|
|
|
133
114
|
* Returns `undefined` when color mode is not configured (single-scheme projects).
|
|
134
115
|
*/
|
|
135
116
|
export function getThemeDefaultMode() {
|
|
136
|
-
return settings.colorMode
|
|
117
|
+
return _getThemeDefaultMode(settings.colorMode);
|
|
137
118
|
}
|
|
@@ -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
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
}
|