Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions docs/site/app/sitemap.md/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { source } from "@/lib/geistdocs/source";

export const revalidate = false;

interface PageNode {
title: string;
description: string;
url: string;
children: PageNode[];
}

function buildTree(
pages: Array<{ url: string; data: { title: string; description?: string } }>
): PageNode[] {
const root: PageNode[] = [];
const map = new Map<string, PageNode>();

// Sort pages by URL to ensure parents come before children
const sorted = [...pages].sort((a, b) => a.url.localeCompare(b.url));

for (const page of sorted) {
const node: PageNode = {
title: page.data.title,
description: page.data.description ?? "",
url: page.url,
children: []
};
map.set(page.url, node);

// Find parent by removing last segment
const segments = page.url.split("/").filter(Boolean);
if (segments.length <= 1) {
// Top-level page (e.g., /docs)
root.push(node);
} else {
// Try to find parent
const parentUrl = "/" + segments.slice(0, -1).join("/");
const parent = map.get(parentUrl);
if (parent) {
parent.children.push(node);
} else {
// No direct parent found, add to root
root.push(node);
}
}
}

return root;
}

function renderNode(node: PageNode, indent: number): string {
const prefix = " ".repeat(indent);
const lines: string[] = [];

lines.push(`${prefix}- [${node.title}](${node.url})`);
lines.push(`${prefix} - Summary: ${node.description}`);

for (const child of node.children) {
lines.push("");
lines.push(renderNode(child, indent + 1));
}

return lines.join("\n");
}

export const GET = async () => {
const pages = source.getPages("en");

const tree = buildTree(pages);

const header = `# Turborepo Documentation Sitemap

## Purpose

This file is a high-level semantic index of the documentation.
It is intended for:

- LLM-assisted navigation (ChatGPT, Claude, etc.)
- Quick orientation for contributors
- Identifying relevant documentation areas during development

It is not intended to replace individual docs.

---

`;

const body = tree.map((node) => renderNode(node, 0)).join("\n\n");

return new Response(header + body, {
headers: {
"Content-Type": "text/markdown"
}
});
};
6 changes: 5 additions & 1 deletion docs/site/lib/geistdocs/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ export const getLLMText = async (page: InferPageType<typeof source>) => {

return `# ${page.data.title}

${cleaned}`;
${cleaned}

---

[View full sitemap](/sitemap.md)`;
};

// Blog loaders
Expand Down
4 changes: 2 additions & 2 deletions docs/site/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ const proxy = (request: NextRequest, context: NextFetchEvent) => {
};

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

Expand Down