Skip to content

Commit e19ae07

Browse files
committed
refactor(navigation): improve navigation building logic
1 parent 0523710 commit e19ae07

File tree

7 files changed

+138
-30
lines changed

7 files changed

+138
-30
lines changed

content/docs/content/markdown/working-with-images.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ git commit -m "Update setup guide and screenshot"
204204

205205
For large images, consider Git LFS:
206206

207-
```gitattributes
207+
```shell
208208
*.jpg filter=lfs diff=lfs merge=lfs -text
209209
*.png filter=lfs diff=lfs merge=lfs -text
210210
*.webp filter=lfs diff=lfs merge=lfs -text

src/components/core/HeaderMobileMenu.astro

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Button } from "@/components/ui/button";
44
import { HEADER_SOCIAL_LINKS, HEADER_NAV_ITEMS, CONTENT, SIDEBAR_NAVIGATION } from "@data/config";
55
import DocsTabs from "@/components/docs/DocsTabs.astro";
66
import DocsContent from "@/components/docs/DocsContent.astro";
7-
import { buildNavigation, getActiveTab } from "@/lib/navigation";
7+
import { getActiveTab, buildNavigation } from "@/lib/navigation";
88
99
const activeSocials = HEADER_SOCIAL_LINKS.filter((social) => social.active);
1010
const menuPanelId = "mobile-menu";
@@ -26,7 +26,12 @@ const currentSlug = baseRoute
2626
? normalizedPath.slice(baseRoute.length).replace(/^\/+/, "") || null
2727
: null;
2828
29-
const navResult = collectionId ? await buildNavigation(SIDEBAR_NAVIGATION, collectionId) : null;
29+
// Build navigation for the current collection
30+
let navResult = null;
31+
if (collectionId) {
32+
navResult = await buildNavigation(SIDEBAR_NAVIGATION, collectionId);
33+
}
34+
3035
const activeTab = navResult
3136
? currentSlug
3237
? getActiveTab(currentSlug, navResult.tabs)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import fs from "fs/promises";
2+
import path from "path";
3+
import { fileURLToPath } from "url";
4+
5+
/**
6+
* Astro integration that prebuilds navigation for all collections at build time.
7+
* Note: The actual prebuild happens in getStaticPaths() for each page.
8+
* This integration creates a JSON export of the prebuilt navigation for reference.
9+
*/
10+
export function prebuildNavigationIntegration() {
11+
let outDir = null;
12+
13+
return {
14+
name: "prebuild-navigation",
15+
hooks: {
16+
"astro:config:done": ({ config }) => {
17+
outDir = config.outDir;
18+
},
19+
"astro:build:done": async () => {
20+
try {
21+
console.log(
22+
"[prebuild-navigation] Build completed - navigation was prebuilt during page generation",
23+
);
24+
console.log(`[prebuild-navigation] ✓ Output directory: ${outDir}`);
25+
26+
// Verify nav JSON files exist
27+
if (outDir) {
28+
const navDir = path.join(fileURLToPath(outDir), "nav");
29+
try {
30+
const files = await fs.readdir(navDir);
31+
const jsonFiles = files.filter((f) => f.endsWith(".json"));
32+
console.log(
33+
`[prebuild-navigation] ✓ Generated ${jsonFiles.length} navigation file(s): ${jsonFiles.join(", ")}`,
34+
);
35+
} catch (readErr) {
36+
console.warn(
37+
"[prebuild-navigation] Note: Nav directory not created (may be normal for dev builds)",
38+
);
39+
}
40+
}
41+
} catch (error) {
42+
console.error(
43+
"[prebuild-navigation] Error in build:done hook:",
44+
error instanceof Error ? error.message : String(error),
45+
);
46+
}
47+
},
48+
},
49+
};
50+
}

src/layouts/DocsLayout.astro

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,25 @@ import TableOfContents from "@/components/docs/TableOfContents.astro";
55
import Breadcrumb from "@/components/docs/Breadcrumb.astro";
66
import SourceButton from "@/components/docs/SourceButton.astro";
77
import CodeCopyButton from "@/components/docs/CodeCopyButton.astro";
8-
import { buildNavigation, getActiveTab } from "@/lib/navigation";
8+
import { getActiveTab } from "@/lib/navigation";
99
import { buildBreadcrumbItems } from "@/lib/docs/breadcrumb";
10-
import { SIDEBAR_NAVIGATION } from "@data/config";
1110
import type { DocsLayoutProps, Heading } from "@/lib/docs/types";
11+
import type { NavigationResult } from "@/lib/navigation/types";
1212
13-
const props = Astro.props as DocsLayoutProps;
14-
const { headings = [], frontmatter, collection } = props;
13+
interface Props extends DocsLayoutProps {
14+
nav?: NavigationResult;
15+
}
16+
17+
const props = Astro.props as Props;
18+
const { headings = [], frontmatter, collection, nav } = props;
1519
const collectionId = collection ?? "docs";
16-
const navResult = await buildNavigation(SIDEBAR_NAVIGATION, collectionId);
17-
const activeTab = getActiveTab(frontmatter.id, navResult.tabs);
20+
21+
// Use prebuilt nav or handle missing nav gracefully
22+
if (!nav) {
23+
throw new Error(`Navigation data missing for collection: ${collectionId}`);
24+
}
25+
26+
const activeTab = getActiveTab(frontmatter.id, nav.tabs);
1827
1928
// Build breadcrumb items
2029
const breadcrumbItems = buildBreadcrumbItems(frontmatter.id, collectionId);
@@ -33,8 +42,8 @@ const tableOfContentsHeadings = headings
3342
<div class="flex min-h-screen">
3443
<!-- Sidebar with tabs inside -->
3544
<Sidebar
36-
tabs={navResult.tabs}
37-
showTabs={navResult.showTabs}
45+
tabs={nav.tabs}
46+
showTabs={nav.showTabs}
3847
activeTab={activeTab}
3948
currentSlug={frontmatter.id}
4049
collectionId={collectionId}

src/pages/[collection]/[...slug].astro

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,38 @@ import type { CollectionKey } from "astro:content";
44
import DocsLayout from "@/layouts/DocsLayout.astro";
55
import MdxImage from "@/components/docs/markdown/MdxImage.astro";
66
import MdxLink from "@/components/docs/markdown/MdxLink.astro";
7-
import { CONTENT } from "@data/config";
7+
import { CONTENT, SIDEBAR_NAVIGATION } from "@data/config";
8+
import { buildNavigation } from "@/lib/navigation";
89
910
export async function getStaticPaths() {
1011
const paths: {
1112
params: { collection: CollectionKey; slug: string };
1213
props: {
1314
doc: Awaited<ReturnType<typeof getCollection>>[number];
1415
collection: CollectionKey;
16+
nav: Awaited<ReturnType<typeof buildNavigation>>;
1517
};
1618
}[] = [];
1719
1820
for (const sys of CONTENT.systems) {
21+
const nav = await buildNavigation(SIDEBAR_NAVIGATION, sys.id);
22+
1923
const entries = await getCollection(sys.id as CollectionKey);
2024
for (const doc of entries) {
2125
paths.push({
2226
params: { collection: sys.id as CollectionKey, slug: doc.id },
23-
props: { doc, collection: sys.id as CollectionKey },
27+
props: { doc, collection: sys.id as CollectionKey, nav },
2428
});
2529
}
2630
}
2731
2832
return paths;
2933
}
3034
31-
const { doc, collection } = Astro.props as {
35+
const { doc, collection, nav } = Astro.props as {
3236
doc: Awaited<ReturnType<typeof getCollection>>[number];
3337
collection: CollectionKey;
38+
nav: Awaited<ReturnType<typeof buildNavigation>>;
3439
};
3540
const { Content, headings } = await render(doc);
3641
@@ -40,6 +45,6 @@ const components = {
4045
};
4146
---
4247

43-
<DocsLayout frontmatter={doc} headings={headings} collection={collection}>
48+
<DocsLayout frontmatter={doc} headings={headings} collection={collection} nav={nav}>
4449
<Content components={components} />
4550
</DocsLayout>

src/pages/[collection]/index.astro

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,65 @@
11
---
22
import { getCollection, render } from "astro:content";
33
import type { CollectionKey } from "astro:content";
4-
import { CONTENT } from "@data/config";
4+
import { CONTENT, SIDEBAR_NAVIGATION } from "@data/config";
55
import DocsLayout from "@/layouts/DocsLayout.astro";
66
import MdxImage from "@/components/docs/markdown/MdxImage.astro";
77
import MdxLink from "@/components/docs/markdown/MdxLink.astro";
8+
import { buildNavigation } from "@/lib/navigation";
89
910
export async function getStaticPaths() {
10-
const paths: { params: { collection: CollectionKey }; props: { collection: CollectionKey } }[] =
11-
[];
11+
const paths: {
12+
params: { collection: CollectionKey };
13+
props: {
14+
collection: CollectionKey;
15+
nav: Awaited<ReturnType<typeof buildNavigation>>;
16+
doc: Awaited<ReturnType<typeof getCollection>>[number] | undefined;
17+
};
18+
}[] = [];
19+
1220
for (const sys of CONTENT.systems) {
21+
const nav = await buildNavigation(SIDEBAR_NAVIGATION, sys.id);
22+
const entries = await getCollection(sys.id as CollectionKey);
23+
24+
// Check if index doc exists, if not use the default redirect target
25+
let indexDoc = entries.find((entry) => entry.id === "index");
26+
27+
if (!indexDoc) {
28+
// Find the default doc to render instead
29+
const defaultRedirect = sys.defaultDocRedirect;
30+
if (defaultRedirect) {
31+
// Extract slug from redirect path (e.g., "/docs/introduction" -> "introduction")
32+
const baseRoute = sys.route ?? `/${sys.id}`;
33+
const defaultSlug = defaultRedirect
34+
.replace(baseRoute + "/", "")
35+
.replace(/^\/+/, "");
36+
indexDoc = entries.find((entry) => entry.id === defaultSlug);
37+
}
38+
}
39+
1340
paths.push({
1441
params: { collection: sys.id as CollectionKey },
15-
props: { collection: sys.id as CollectionKey },
42+
props: {
43+
collection: sys.id as CollectionKey,
44+
nav,
45+
doc: indexDoc,
46+
},
1647
});
1748
}
49+
1850
return paths;
1951
}
2052
2153
const collection = Astro.params.collection as CollectionKey;
22-
23-
const entries = await getCollection(collection);
24-
const doc = entries.find((entry) => entry.id === "index");
54+
const { nav, doc } = Astro.props as {
55+
collection: CollectionKey;
56+
nav: Awaited<ReturnType<typeof buildNavigation>>;
57+
doc: Awaited<ReturnType<typeof getCollection>>[number] | undefined;
58+
};
2559
2660
if (!doc) {
27-
return Astro.redirect(
28-
(CONTENT.systems ?? []).find((system) => system.id === collection)?.defaultDocRedirect ??
29-
"/",
30-
);
61+
// Fallback to home if no doc found at all
62+
return Astro.redirect("/");
3163
}
3264
3365
const { Content, headings } = await render(doc);
@@ -38,6 +70,6 @@ const components = {
3870
};
3971
---
4072

41-
<DocsLayout frontmatter={doc} headings={headings} collection={collection}>
73+
<DocsLayout frontmatter={doc} headings={headings} collection={collection} nav={nav}>
4274
<Content components={components} />
4375
</DocsLayout>

src/pages/debug/navigation.json.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import type { APIRoute } from "astro";
22
import { buildNavigation } from "@/lib/navigation";
3-
import { SIDEBAR_NAVIGATION } from "@data/config";
3+
import { SIDEBAR_NAVIGATION, CONTENT } from "@data/config";
44

55
export const GET: APIRoute = async () => {
66
try {
7-
const nav = await buildNavigation(SIDEBAR_NAVIGATION);
8-
return new Response(JSON.stringify(nav, null, 2), {
7+
const allNav: Record<string, unknown> = {};
8+
9+
// Build navigation for all collections
10+
for (const system of CONTENT.systems) {
11+
const nav = await buildNavigation(SIDEBAR_NAVIGATION, system.id);
12+
allNav[system.id] = nav;
13+
}
14+
15+
return new Response(JSON.stringify(allNav, null, 2), {
916
headers: {
1017
"Content-Type": "application/json; charset=utf-8",
1118
},

0 commit comments

Comments
 (0)