-
Notifications
You must be signed in to change notification settings - Fork 140
Expand file tree
/
Copy pathllms-plugin.ts
More file actions
107 lines (90 loc) · 3.98 KB
/
llms-plugin.ts
File metadata and controls
107 lines (90 loc) · 3.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import path from 'node:path';
import fs from 'node:fs';
import type {LoadContext} from '@docusaurus/types';
type DocsIndexRecord = {
title?: string;
description?: string;
};
type VersionedDocsRoute = {
props?: {
version?: {
docs?: Record<string, DocsIndexRecord>;
};
};
priority?: number;
};
type PluginRouteConfig = {
plugin?: {
name?: string;
};
routes?: VersionedDocsRoute[];
};
function isDocsPluginRouteConfig(route: unknown): route is PluginRouteConfig {
if (!route || typeof route !== 'object') {
return false;
}
const maybeRoute = route as PluginRouteConfig;
return maybeRoute.plugin?.name === 'docusaurus-plugin-content-docs' && Array.isArray(maybeRoute.routes);
}
const pluginLlmsTxt = async (context: LoadContext) => {
return {
name: 'llms-txt-plugin',
loadContent: async () => {
const { siteDir } = context;
const contentDir = path.join(siteDir, 'docs');
const markdownFiles: string[] = [];
const collectMarkdownFiles = async (dir: string): Promise<void> => {
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
await collectMarkdownFiles(fullPath);
} else if (entry.name.endsWith('.mdx') || entry.name.endsWith('.md')) {
markdownFiles.push(fullPath);
}
}
};
await collectMarkdownFiles(contentDir);
markdownFiles.sort();
return { markdownFiles };
},
postBuild: async ({ content, routes, outDir }: {content: {markdownFiles: string[]}; routes: unknown[]; outDir: string}) => {
const { markdownFiles } = content;
// Write concatenated markdown content without keeping all file bodies in memory.
const concatenatedPath = path.join(outDir, 'llms-full.txt');
const output = fs.createWriteStream(concatenatedPath, { encoding: 'utf8' });
for (const [index, filePath] of markdownFiles.entries()) {
const markdown = await fs.promises.readFile(filePath, 'utf8');
if (index > 0) {
output.write('\n\n---\n\n');
}
output.write(markdown);
}
await new Promise<void>((resolve, reject) => {
output.end(() => resolve());
output.on('error', reject);
});
// console.log('routes', routes);
// we need to dig down several layers:
// find PluginRouteConfig marked by plugin.name === "docusaurus-plugin-content-docs"
const docsPluginRouteConfigs = routes.filter(isDocsPluginRouteConfig);
// console.log('docsPluginRouteConfigs', docsPluginRouteConfigs);
const docsRecords = docsPluginRouteConfigs.map((config) => {
return (config.routes ?? []).filter((route) => Boolean(route?.props?.version?.docs))
.filter((route) => route?.priority !== undefined)
.map((route) => {
const currentVersionDocsRoutes = route.props?.version?.docs ?? {};
return Object.entries(currentVersionDocsRoutes).map(([docPath, record]) => {
return `- [${record.title ?? 'Untitled'}](${docPath}): ${record.description ?? ''}`;
});
}).flat();
}).flat();
// Build up llms.txt file
const llmsTxt = `# ${context.siteConfig.title}\n\n## Docs\n\n${docsRecords.join('\n')}`;
// Write llms.txt file
const llmsTxtPath = path.join(outDir, 'llms.txt');
await fs.promises.writeFile(llmsTxtPath, llmsTxt);
},
};
};
export default pluginLlmsTxt;