Skip to content

Commit dcc5deb

Browse files
gaearonartimath
andauthored
Add llms.txt (#8267)
Co-authored-by: artimath <ryanjhunter@gmail.com>
1 parent 3938fbf commit dcc5deb

File tree

3 files changed

+138
-0
lines changed

3 files changed

+138
-0
lines changed

next.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ const nextConfig = {
1919
scrollRestoration: true,
2020
reactCompiler: true,
2121
},
22+
async rewrites() {
23+
return [
24+
{
25+
source: '/:path*.md',
26+
destination: '/api/md/:path*',
27+
},
28+
];
29+
},
2230
env: {},
2331
webpack: (config, {dev, isServer, ...options}) => {
2432
if (process.env.ANALYZE) {

src/pages/api/md/[...path].ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import type {NextApiRequest, NextApiResponse} from 'next';
9+
import fs from 'fs';
10+
import path from 'path';
11+
12+
export default function handler(req: NextApiRequest, res: NextApiResponse) {
13+
const pathSegments = req.query.path;
14+
if (!pathSegments) {
15+
return res.status(404).send('Not found');
16+
}
17+
18+
const filePath = Array.isArray(pathSegments)
19+
? pathSegments.join('/')
20+
: pathSegments;
21+
22+
// Block /index.md URLs - use /foo.md instead of /foo/index.md
23+
if (filePath.endsWith('/index') || filePath === 'index') {
24+
return res.status(404).send('Not found');
25+
}
26+
27+
// Try exact path first, then with /index
28+
const candidates = [
29+
path.join(process.cwd(), 'src/content', filePath + '.md'),
30+
path.join(process.cwd(), 'src/content', filePath, 'index.md'),
31+
];
32+
33+
for (const fullPath of candidates) {
34+
try {
35+
const content = fs.readFileSync(fullPath, 'utf8');
36+
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
37+
res.setHeader('Cache-Control', 'public, max-age=3600');
38+
return res.status(200).send(content);
39+
} catch {
40+
// Try next candidate
41+
}
42+
}
43+
44+
res.status(404).send('Not found');
45+
}

src/pages/llms.txt.tsx

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import type {GetServerSideProps} from 'next';
9+
import {siteConfig} from '../siteConfig';
10+
import sidebarLearn from '../sidebarLearn.json';
11+
import sidebarReference from '../sidebarReference.json';
12+
import sidebarBlog from '../sidebarBlog.json';
13+
14+
interface RouteItem {
15+
title?: string;
16+
path?: string;
17+
routes?: RouteItem[];
18+
}
19+
20+
interface Sidebar {
21+
title: string;
22+
routes: RouteItem[];
23+
}
24+
25+
function extractRoutes(
26+
routes: RouteItem[],
27+
baseUrl: string
28+
): {title: string; url: string}[] {
29+
const result: {title: string; url: string}[] = [];
30+
31+
for (const route of routes) {
32+
if (route.title && route.path) {
33+
result.push({
34+
title: route.title,
35+
url: `${baseUrl}${route.path}.md`,
36+
});
37+
}
38+
if (route.routes) {
39+
result.push(...extractRoutes(route.routes, baseUrl));
40+
}
41+
}
42+
43+
return result;
44+
}
45+
46+
const sidebars: Sidebar[] = [
47+
sidebarLearn as Sidebar,
48+
sidebarReference as Sidebar,
49+
sidebarBlog as Sidebar,
50+
];
51+
52+
export const getServerSideProps: GetServerSideProps = async ({res}) => {
53+
const subdomain =
54+
siteConfig.languageCode === 'en' ? '' : siteConfig.languageCode + '.';
55+
const baseUrl = 'https://' + subdomain + 'react.dev';
56+
57+
const lines = [
58+
'# React Documentation',
59+
'',
60+
'> The library for web and native user interfaces.',
61+
];
62+
63+
for (const sidebar of sidebars) {
64+
lines.push('');
65+
lines.push(`## ${sidebar.title}`);
66+
lines.push('');
67+
68+
const routes = extractRoutes(sidebar.routes, baseUrl);
69+
for (const route of routes) {
70+
lines.push(`- [${route.title}](${route.url})`);
71+
}
72+
}
73+
74+
const content = lines.join('\n');
75+
76+
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
77+
res.write(content);
78+
res.end();
79+
80+
return {props: {}};
81+
};
82+
83+
export default function LlmsTxt() {
84+
return null;
85+
}

0 commit comments

Comments
 (0)