Skip to content

Commit b208d2e

Browse files
docs: Add sitemap.md and link from all docs pages (#11515)
## Summary - Adds a `sitemap.md` file to the docs for LLM-assisted navigation and quick orientation - Appends a link to the sitemap at the bottom of all 96 docs pages The sitemap follows the format used by [Vercel docs](https://vercel.com/docs/sitemap.md), including Type, Summary, Prerequisites, and Topics metadata for each page. ## Test Visit `/docs/sitemap.md` to view the full sitemap. Each docs page now ends with: ```markdown --- [View full sitemap](/docs/sitemap.md) ``` --------- Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
1 parent 2b3c8bc commit b208d2e

3 files changed

Lines changed: 102 additions & 3 deletions

File tree

docs/site/app/sitemap.md/route.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { source } from "@/lib/geistdocs/source";
2+
3+
export const revalidate = false;
4+
5+
interface PageNode {
6+
title: string;
7+
description: string;
8+
url: string;
9+
children: PageNode[];
10+
}
11+
12+
function buildTree(
13+
pages: Array<{ url: string; data: { title: string; description?: string } }>
14+
): PageNode[] {
15+
const root: PageNode[] = [];
16+
const map = new Map<string, PageNode>();
17+
18+
// Sort pages by URL to ensure parents come before children
19+
const sorted = [...pages].sort((a, b) => a.url.localeCompare(b.url));
20+
21+
for (const page of sorted) {
22+
const node: PageNode = {
23+
title: page.data.title,
24+
description: page.data.description ?? "",
25+
url: page.url,
26+
children: []
27+
};
28+
map.set(page.url, node);
29+
30+
// Find parent by removing last segment
31+
const segments = page.url.split("/").filter(Boolean);
32+
if (segments.length <= 1) {
33+
// Top-level page (e.g., /docs)
34+
root.push(node);
35+
} else {
36+
// Try to find parent
37+
const parentUrl = "/" + segments.slice(0, -1).join("/");
38+
const parent = map.get(parentUrl);
39+
if (parent) {
40+
parent.children.push(node);
41+
} else {
42+
// No direct parent found, add to root
43+
root.push(node);
44+
}
45+
}
46+
}
47+
48+
return root;
49+
}
50+
51+
function renderNode(node: PageNode, indent: number): string {
52+
const prefix = " ".repeat(indent);
53+
const lines: string[] = [];
54+
55+
lines.push(`${prefix}- [${node.title}](${node.url})`);
56+
lines.push(`${prefix} - Summary: ${node.description}`);
57+
58+
for (const child of node.children) {
59+
lines.push("");
60+
lines.push(renderNode(child, indent + 1));
61+
}
62+
63+
return lines.join("\n");
64+
}
65+
66+
export const GET = async () => {
67+
const pages = source.getPages("en");
68+
69+
const tree = buildTree(pages);
70+
71+
const header = `# Turborepo Documentation Sitemap
72+
73+
## Purpose
74+
75+
This file is a high-level semantic index of the documentation.
76+
It is intended for:
77+
78+
- LLM-assisted navigation (ChatGPT, Claude, etc.)
79+
- Quick orientation for contributors
80+
- Identifying relevant documentation areas during development
81+
82+
It is not intended to replace individual docs.
83+
84+
---
85+
86+
`;
87+
88+
const body = tree.map((node) => renderNode(node, 0)).join("\n\n");
89+
90+
return new Response(header + body, {
91+
headers: {
92+
"Content-Type": "text/markdown"
93+
}
94+
});
95+
};

docs/site/lib/geistdocs/source.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,11 @@ export const getLLMText = async (page: InferPageType<typeof source>) => {
8686

8787
return `# ${page.data.title}
8888
89-
${cleaned}`;
89+
${cleaned}
90+
91+
---
92+
93+
[View full sitemap](/sitemap.md)`;
9094
};
9195

9296
// Blog loaders

docs/site/proxy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ const proxy = (request: NextRequest, context: NextFetchEvent) => {
4343
};
4444

4545
export const config = {
46-
// Matcher ignoring `/_next/`, `/api/`, static assets, favicon, feed.xml, sitemap.xml, robots.txt, schema JSON files, etc.
46+
// Matcher ignoring `/_next/`, `/api/`, static assets, favicon, feed.xml, sitemap.xml, sitemap.md, robots.txt, schema JSON files, etc.
4747
matcher: [
48-
"/((?!api|_next/static|_next/image|favicon.ico|feed.xml|sitemap.xml|robots.txt|schema\\.json|schema\\.v\\d+\\.json|microfrontends/schema\\.json).*)"
48+
"/((?!api|_next/static|_next/image|favicon.ico|feed.xml|sitemap.xml|sitemap.md|robots.txt|schema\\.json|schema\\.v\\d+\\.json|microfrontends/schema\\.json).*)"
4949
]
5050
};
5151

0 commit comments

Comments
 (0)