Skip to content

Commit 717a385

Browse files
madster456N2D4Madison KennedyBilalG1
authored
Llms.txt (stack-auth#724)
<!-- Adds llms.txt compatibility to the docs pages. --> --------- Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com> Co-authored-by: Madison Kennedy <madison@Madisons-MacBook-Pro.local> Co-authored-by: BilalG1 <bg2002@gmail.com>
1 parent 9f79485 commit 717a385

File tree

6 files changed

+115
-0
lines changed

6 files changed

+115
-0
lines changed

docs/lib/get-llm-text.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { InferPageType } from 'fumadocs-core/source';
2+
import { remarkInclude } from 'fumadocs-mdx/config';
3+
import { remark } from 'remark';
4+
import remarkGfm from 'remark-gfm';
5+
import remarkMdx from 'remark-mdx';
6+
import { apiSource, source } from './source';
7+
8+
const processor = remark()
9+
.use(remarkMdx)
10+
// needed for Fumadocs MDX
11+
.use(remarkInclude)
12+
.use(remarkGfm);
13+
14+
export async function getLLMText(page: InferPageType<typeof source> | InferPageType<typeof apiSource>) {
15+
// Remove the Fumadocs generated comment before processing
16+
let content = page.data.content;
17+
18+
// Remove the specific Fumadocs comment that appears in generated API docs
19+
content = content.replace(
20+
/\{\s*\/\*\s*This file was generated by Fumadocs\. Do not edit this file directly\. Any changes should be made by running the generation command again\.\s*\*\/\s*\}/g,
21+
''
22+
);
23+
24+
const processed = await processor.process({
25+
path: page.data._file.absolutePath,
26+
value: content,
27+
});
28+
29+
return `# ${page.data.title}
30+
URL: ${page.url}
31+
Source: ${page.data._file.absolutePath}
32+
33+
${page.data.description || ''}
34+
35+
${processed.value}`;
36+
}

docs/next.config.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ const config = {
2626
},
2727
async rewrites() {
2828
return [
29+
// Redirect .mdx requests to the llms.mdx route handler
30+
{
31+
source: '/docs/:path*.mdx',
32+
destination: '/llms.mdx/:path*',
33+
},
34+
{
35+
source: '/api/:path*.mdx',
36+
destination: '/llms.mdx/:path*',
37+
},
2938
// Serve OpenAPI files from the openapi directory
3039
{
3140
source: '/openapi/:path*',

docs/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
"react": "^18.3.1",
4242
"react-dom": "^18.3.1",
4343
"react-remove-scroll": "^2.7.0",
44+
"remark": "^15.0.1",
45+
"remark-gfm": "^4.0.1",
46+
"remark-mdx": "^3.1.0",
4447
"shiki": "^3.4.2",
4548
"tailwind-merge": "^3.3.0"
4649
},
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { getLLMText } from 'lib/get-llm-text';
2+
import { apiSource, source } from 'lib/source';
3+
import { notFound } from 'next/navigation';
4+
import { type NextRequest, NextResponse } from 'next/server';
5+
6+
export const revalidate = false;
7+
8+
export async function GET(
9+
_req: NextRequest,
10+
{ params }: { params: Promise<{ slug?: string[] }> },
11+
) {
12+
const { slug } = await params;
13+
14+
// Try to find the page in either source
15+
let page = source.getPage(slug);
16+
17+
// If not found in main docs, try API docs
18+
if (!page) {
19+
page = apiSource.getPage(slug);
20+
}
21+
22+
if (!page) notFound();
23+
24+
return new NextResponse(await getLLMText(page));
25+
}
26+
27+
export function generateStaticParams() {
28+
// Generate static params for both main docs and API docs
29+
const docsParams = source.generateParams();
30+
const apiParams = apiSource.generateParams();
31+
32+
return [...docsParams, ...apiParams];
33+
}

docs/src/app/llms.txt/route.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { getLLMText } from 'lib/get-llm-text';
2+
import { apiSource, source } from 'lib/source';
3+
4+
// cached forever
5+
export const revalidate = false;
6+
7+
export async function GET() {
8+
// Get all pages from both main docs and API docs
9+
const docsPages = source.getPages();
10+
const apiPages = apiSource.getPages();
11+
12+
// Process all pages
13+
const docsPromises = docsPages.map(getLLMText);
14+
const apiPromises = apiPages.map(getLLMText);
15+
16+
const [docsContent, apiContent] = await Promise.all([
17+
Promise.all(docsPromises),
18+
Promise.all(apiPromises)
19+
]);
20+
21+
// Combine all content
22+
const allContent = [...docsContent, ...apiContent];
23+
24+
return new Response(allContent.join('\n\n'));
25+
}

pnpm-lock.yaml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)