@clidey/dory 0.27.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/bin/dist/dory.js +115 -29
  2. package/bin/dist/dory.js.map +1 -1
  3. package/package.json +12 -4
  4. package/.dockerignore +0 -4
  5. package/CLAUDE.md +0 -178
  6. package/CODE_OF_CONDUCT.md +0 -128
  7. package/CONTRIBUTING.md +0 -91
  8. package/SECURITY.md +0 -11
  9. package/bin/dory.ts +0 -433
  10. package/dist/favicon.svg +0 -75
  11. package/dist/frontmatter.json +0 -77
  12. package/dist/index.html +0 -55
  13. package/dist/llms.txt +0 -561
  14. package/dist/logo/logo.svg +0 -18
  15. package/docs/api-playground-demo.mdx +0 -119
  16. package/docs/api-reference/asyncapi.json +0 -324
  17. package/docs/api-reference/openapi.json +0 -366
  18. package/docs/api-reference/operations/create.mdx +0 -5
  19. package/docs/api-reference/operations/delete.mdx +0 -5
  20. package/docs/api-reference/operations/get.mdx +0 -5
  21. package/docs/api-reference/operations/update.mdx +0 -5
  22. package/docs/api-reference/websocket-operations/receive-chat-message.mdx +0 -5
  23. package/docs/api-reference/websocket-operations/receive-notifications.mdx +0 -5
  24. package/docs/api-reference/websocket-operations/receive-presence-update.mdx +0 -5
  25. package/docs/api-reference/websocket-operations/send-chat-message.mdx +0 -5
  26. package/docs/api-reference/websocket-operations/send-presence-update.mdx +0 -5
  27. package/docs/dory.json +0 -64
  28. package/docs/favicon.svg +0 -75
  29. package/docs/features.mdx +0 -440
  30. package/docs/introduction.mdx +0 -44
  31. package/docs/logo/logo.svg +0 -18
  32. package/docs/websocket-playground-demo.mdx +0 -135
  33. package/index.html +0 -54
  34. package/scripts/generate-frontmatter-json.js +0 -154
  35. package/scripts/generate-llm-txt.js +0 -115
  36. package/src/app.css +0 -5
  37. package/src/app.tsx +0 -13
  38. package/src/components/dropdown.tsx +0 -53
  39. package/src/components/hooks.tsx +0 -28
  40. package/src/components/loading.tsx +0 -21
  41. package/src/components/mdx-provider.tsx +0 -13
  42. package/src/components/navigation.tsx +0 -58
  43. package/src/components/notification.tsx +0 -13
  44. package/src/components/routes.tsx +0 -92
  45. package/src/components/search-index.ts +0 -43
  46. package/src/components/search.tsx +0 -435
  47. package/src/components/store.ts +0 -216
  48. package/src/config/mdx.js +0 -12
  49. package/src/config/mdx.ts +0 -12
  50. package/src/main.tsx +0 -22
  51. package/src/mdx/accordion.tsx +0 -41
  52. package/src/mdx/api-playground.tsx +0 -659
  53. package/src/mdx/api.tsx +0 -354
  54. package/src/mdx/async-api.tsx +0 -353
  55. package/src/mdx/callout.tsx +0 -64
  56. package/src/mdx/card.tsx +0 -54
  57. package/src/mdx/code.tsx +0 -289
  58. package/src/mdx/expandable.tsx +0 -36
  59. package/src/mdx/fence.tsx +0 -74
  60. package/src/mdx/icon.tsx +0 -73
  61. package/src/mdx/latex.tsx +0 -26
  62. package/src/mdx/list.tsx +0 -7
  63. package/src/mdx/mdx.tsx +0 -47
  64. package/src/mdx/mermaid.tsx +0 -147
  65. package/src/mdx/open-api.tsx +0 -311
  66. package/src/mdx/processor.ts +0 -174
  67. package/src/mdx/source.tsx +0 -58
  68. package/src/mdx/step.tsx +0 -51
  69. package/src/mdx/table.tsx +0 -20
  70. package/src/mdx/tag.tsx +0 -53
  71. package/src/mdx/text.tsx +0 -99
  72. package/src/mdx/websocket-playground.tsx +0 -824
  73. package/src/plugins/docs-assets.ts +0 -79
  74. package/src/plugins/frontmatter-dev-server.ts +0 -158
  75. package/src/plugins/frontmatter-generator.ts +0 -157
  76. package/src/plugins/llm-txt-dev-server.ts +0 -183
  77. package/src/plugins/llm-txt-generator.ts +0 -186
  78. package/src/plugins/sanitize.ts +0 -163
  79. package/src/plugins/unified-mdx-plugin.ts +0 -35
  80. package/src/theme.scss +0 -72
  81. package/src/types/config.ts +0 -47
  82. package/src/ui/header.tsx +0 -91
  83. package/src/ui/layout.tsx +0 -193
  84. package/src/ui/mobile-navigation.tsx +0 -64
  85. package/src/ui/prev-next-link.tsx +0 -74
  86. package/src/ui/table-of-content.tsx +0 -166
  87. package/src/utils/colors.ts +0 -54
  88. package/src/utils/fonts.ts +0 -23
  89. package/src/utils/functions.ts +0 -12
  90. package/src/vite-env.d.ts +0 -1
  91. package/tsconfig.app.json +0 -33
  92. package/tsconfig.bin.json +0 -24
  93. package/tsconfig.json +0 -7
  94. package/tsconfig.node.json +0 -26
  95. package/vite.config.ts +0 -41
@@ -1,79 +0,0 @@
1
- import type { Plugin } from 'vite';
2
- import { cpSync, existsSync, readdirSync, statSync } from 'fs';
3
- import { resolve, extname } from 'path';
4
-
5
- /**
6
- * Plugin to copy docs assets to the build output
7
- * Since we can't use publicDir for docs (we need to import from it),
8
- * this plugin manually copies asset files during build
9
- */
10
- export function docsAssetsPlugin(): Plugin {
11
- return {
12
- name: 'docs-assets',
13
- apply: 'build', // Only run during build
14
- closeBundle() {
15
- const docsDir = resolve(process.cwd(), 'docs');
16
- const outDir = resolve(process.cwd(), 'dist');
17
-
18
- if (!existsSync(docsDir)) {
19
- console.warn('[docs-assets] docs directory not found, skipping asset copy');
20
- return;
21
- }
22
-
23
- // Define asset extensions to copy
24
- const assetExtensions = new Set([
25
- '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico',
26
- '.pdf', '.mp4', '.webm', '.mp3', '.wav',
27
- '.woff', '.woff2', '.ttf', '.eot',
28
- ]);
29
-
30
- // Define file/folder patterns to skip
31
- const skipPatterns = new Set([
32
- 'dory.json',
33
- 'node_modules',
34
- '.DS_Store',
35
- ]);
36
-
37
- const shouldSkip = (name: string) => {
38
- if (skipPatterns.has(name)) return true;
39
- const ext = extname(name).toLowerCase();
40
- // Skip MDX and JSON files (they're processed by Vite)
41
- if (ext === '.mdx' || ext === '.md' || ext === '.json') return true;
42
- return false;
43
- };
44
-
45
- const copyAssets = (srcDir: string, destDir: string, relativePath: string = '') => {
46
- const items = readdirSync(srcDir);
47
-
48
- for (const item of items) {
49
- if (shouldSkip(item)) continue;
50
-
51
- const srcPath = resolve(srcDir, item);
52
- const destPath = resolve(destDir, item);
53
- const itemRelativePath = relativePath ? `${relativePath}/${item}` : item;
54
-
55
- try {
56
- const stats = statSync(srcPath);
57
-
58
- if (stats.isDirectory()) {
59
- // Recursively copy directories
60
- copyAssets(srcPath, destPath, itemRelativePath);
61
- } else if (stats.isFile()) {
62
- const ext = extname(item).toLowerCase();
63
- // Copy if it's an asset file
64
- if (assetExtensions.has(ext)) {
65
- cpSync(srcPath, destPath, { force: true });
66
- console.log(`[docs-assets] Copied ${itemRelativePath}`);
67
- }
68
- }
69
- } catch (error) {
70
- console.warn(`[docs-assets] Failed to copy ${itemRelativePath}:`, error);
71
- }
72
- }
73
- };
74
-
75
- console.log('[docs-assets] Copying assets from docs to dist...');
76
- copyAssets(docsDir, outDir);
77
- },
78
- };
79
- }
@@ -1,158 +0,0 @@
1
- import type { Plugin } from 'vite';
2
- import fs from 'fs';
3
- import path from 'path';
4
-
5
- // Read dory.json to get navigation order (flat list, in order)
6
- function getNavigationOrder(docsDir: string): string[] {
7
- const doryJsonPath = path.join(docsDir, 'dory.json');
8
- if (!fs.existsSync(doryJsonPath)) return [];
9
- const doryJson = JSON.parse(fs.readFileSync(doryJsonPath, 'utf-8'));
10
- const order: string[] = [];
11
-
12
- function walkPages(pages: any[]) {
13
- for (const page of pages) {
14
- if (typeof page === 'string') {
15
- order.push(page);
16
- } else if (typeof page === 'object' && page.pages) {
17
- walkPages(page.pages);
18
- }
19
- }
20
- }
21
-
22
- if (doryJson.navigation && Array.isArray(doryJson.navigation.tabs)) {
23
- for (const tab of doryJson.navigation.tabs) {
24
- if (tab.groups && Array.isArray(tab.groups)) {
25
- for (const group of tab.groups) {
26
- if (group.pages && Array.isArray(group.pages)) {
27
- walkPages(group.pages);
28
- }
29
- }
30
- }
31
- }
32
- }
33
- return order;
34
- }
35
-
36
- // Function to parse frontmatter from MDX content
37
- function parseFrontmatter(content: string) {
38
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
39
- if (!frontmatterMatch) return { data: {}, content };
40
-
41
- const frontmatter = frontmatterMatch[1];
42
- const data: Record<string, any> = {};
43
-
44
- frontmatter.split('\n').forEach(line => {
45
- const [key, ...valueParts] = line.split(':');
46
- if (key && valueParts.length) {
47
- const value = valueParts.join(':').trim();
48
- data[key.trim()] = value.replace(/^["']|["']$/g, '');
49
- }
50
- });
51
-
52
- return { data, content: content.slice(frontmatterMatch[0].length) };
53
- }
54
-
55
- // Convert filename to path format used by the store
56
- function pathFromFilename(filename: string, docsDir: string): string {
57
- return filename
58
- .replace(docsDir, '')
59
- .replace(/\/?index\.mdx$/, '/')
60
- .replace(/\.mdx$/, '')
61
- .replace(/\\/g, '/')
62
- .toLowerCase();
63
- }
64
-
65
- // Function to recursively find all MDX files and map by relative path (without .mdx)
66
- function findMdxFilesMap(dir: string, baseDir: string): Record<string, string> {
67
- const files: Record<string, string> = {};
68
- const items = fs.readdirSync(dir, { withFileTypes: true });
69
-
70
- for (const item of items) {
71
- const fullPath = path.join(dir, item.name);
72
- if (item.isDirectory()) {
73
- Object.assign(files, findMdxFilesMap(fullPath, baseDir));
74
- } else if (item.isFile() && item.name.endsWith('.mdx')) {
75
- // Key: relative path without .mdx, with forward slashes
76
- let rel = path.relative(baseDir, fullPath).replace(/\\/g, '/');
77
- if (rel.endsWith('.mdx')) rel = rel.slice(0, -4);
78
- files[rel] = fullPath;
79
- }
80
- }
81
-
82
- return files;
83
- }
84
-
85
- // Function to generate frontmatter JSON in dory.json order, then append any extra files
86
- function generateFrontmatterJson(docsDir: string): Array<Record<string, any>> {
87
- const navOrder = getNavigationOrder(docsDir);
88
- const mdxFilesMap = findMdxFilesMap(docsDir, docsDir);
89
- const usedKeys = new Set<string>();
90
- const frontmatterArray: Array<Record<string, any>> = [];
91
-
92
- // Process files in navigation order first
93
- for (const navKey of navOrder) {
94
- // Try both with and without "index"
95
- let fileKey = navKey;
96
- if (mdxFilesMap[fileKey]) {
97
- usedKeys.add(fileKey);
98
- } else if (mdxFilesMap[`${fileKey}/index`]) {
99
- fileKey = `${fileKey}/index`;
100
- usedKeys.add(fileKey);
101
- } else {
102
- continue;
103
- }
104
-
105
- const filePath = mdxFilesMap[fileKey];
106
- const rawContent = fs.readFileSync(filePath, 'utf-8');
107
- const { data } = parseFrontmatter(rawContent);
108
- const pathname = pathFromFilename(filePath, docsDir);
109
-
110
- frontmatterArray.push({
111
- ...data,
112
- path: pathname
113
- });
114
- }
115
-
116
- // Process any extra files not in navigation order, sorted
117
- const extraKeys = Object.keys(mdxFilesMap).filter(k => !usedKeys.has(k)).sort();
118
- for (const fileKey of extraKeys) {
119
- const filePath = mdxFilesMap[fileKey];
120
- const rawContent = fs.readFileSync(filePath, 'utf-8');
121
- const { data } = parseFrontmatter(rawContent);
122
- const pathname = pathFromFilename(filePath, docsDir);
123
-
124
- frontmatterArray.push({
125
- ...data,
126
- path: pathname
127
- });
128
- }
129
-
130
- return frontmatterArray;
131
- }
132
-
133
- export function frontmatterDevServer(): Plugin {
134
- return {
135
- name: 'frontmatter-dev-server',
136
- configureServer(server) {
137
- server.middlewares.use('/frontmatter.json', (req, res, next) => {
138
- if (req.method !== 'GET') {
139
- return next();
140
- }
141
-
142
- try {
143
- const docsDir = path.join(process.cwd(), 'docs');
144
- const frontmatterArray = generateFrontmatterJson(docsDir);
145
- const jsonContent = JSON.stringify(frontmatterArray, null, 2);
146
-
147
- res.setHeader('Content-Type', 'application/json; charset=utf-8');
148
- res.setHeader('Content-Length', Buffer.byteLength(jsonContent));
149
- res.end(jsonContent);
150
- } catch (error) {
151
- console.error('Error generating frontmatter JSON:', error);
152
- res.statusCode = 500;
153
- res.end('Internal Server Error');
154
- }
155
- });
156
- }
157
- };
158
- }
@@ -1,157 +0,0 @@
1
- import type { Plugin } from 'vite';
2
- import fs from 'fs';
3
- import path from 'path';
4
-
5
- interface FrontmatterGeneratorOptions {
6
- docsDir?: string;
7
- outputPath?: string;
8
- }
9
-
10
- // Read dory.json to get navigation order (flat list, in order)
11
- function getNavigationOrder(docsDir: string): string[] {
12
- const doryJsonPath = path.join(docsDir, 'dory.json');
13
- if (!fs.existsSync(doryJsonPath)) return [];
14
- const doryJson = JSON.parse(fs.readFileSync(doryJsonPath, 'utf-8'));
15
- const order: string[] = [];
16
-
17
- function walkPages(pages: any[]) {
18
- for (const page of pages) {
19
- if (typeof page === 'string') {
20
- order.push(page);
21
- } else if (typeof page === 'object' && page.pages) {
22
- walkPages(page.pages);
23
- }
24
- }
25
- }
26
-
27
- if (doryJson.navigation && Array.isArray(doryJson.navigation.tabs)) {
28
- for (const tab of doryJson.navigation.tabs) {
29
- if (tab.groups && Array.isArray(tab.groups)) {
30
- for (const group of tab.groups) {
31
- if (group.pages && Array.isArray(group.pages)) {
32
- walkPages(group.pages);
33
- }
34
- }
35
- }
36
- }
37
- }
38
- return order;
39
- }
40
-
41
- // Function to parse frontmatter from MDX content
42
- function parseFrontmatter(content: string) {
43
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
44
- if (!frontmatterMatch) return { data: {}, content };
45
-
46
- const frontmatter = frontmatterMatch[1];
47
- const data: Record<string, any> = {};
48
-
49
- frontmatter.split('\n').forEach(line => {
50
- const [key, ...valueParts] = line.split(':');
51
- if (key && valueParts.length) {
52
- const value = valueParts.join(':').trim();
53
- data[key.trim()] = value.replace(/^["']|["']$/g, '');
54
- }
55
- });
56
-
57
- return { data, content: content.slice(frontmatterMatch[0].length) };
58
- }
59
-
60
- // Convert filename to path format used by the store
61
- function pathFromFilename(filename: string, docsDir: string): string {
62
- return filename
63
- .replace(docsDir, '')
64
- .replace(/\/?index\.mdx$/, '/')
65
- .replace(/\.mdx$/, '')
66
- .replace(/\\/g, '/')
67
- .toLowerCase();
68
- }
69
-
70
- // Function to recursively find all MDX files and map by relative path (without .mdx)
71
- function findMdxFilesMap(dir: string, baseDir: string): Record<string, string> {
72
- const files: Record<string, string> = {};
73
- const items = fs.readdirSync(dir, { withFileTypes: true });
74
-
75
- for (const item of items) {
76
- const fullPath = path.join(dir, item.name);
77
- if (item.isDirectory()) {
78
- Object.assign(files, findMdxFilesMap(fullPath, baseDir));
79
- } else if (item.isFile() && item.name.endsWith('.mdx')) {
80
- // Key: relative path without .mdx, with forward slashes
81
- let rel = path.relative(baseDir, fullPath).replace(/\\/g, '/');
82
- if (rel.endsWith('.mdx')) rel = rel.slice(0, -4);
83
- files[rel] = fullPath;
84
- }
85
- }
86
-
87
- return files;
88
- }
89
-
90
- // Function to generate frontmatter JSON in dory.json order, then append any extra files
91
- function generateFrontmatterJson(docsDir: string): Array<Record<string, any>> {
92
- const navOrder = getNavigationOrder(docsDir);
93
- const mdxFilesMap = findMdxFilesMap(docsDir, docsDir);
94
- const usedKeys = new Set<string>();
95
- const frontmatterArray: Array<Record<string, any>> = [];
96
-
97
- // Process files in navigation order first
98
- for (const navKey of navOrder) {
99
- // Try both with and without "index"
100
- let fileKey = navKey;
101
- if (mdxFilesMap[fileKey]) {
102
- usedKeys.add(fileKey);
103
- } else if (mdxFilesMap[`${fileKey}/index`]) {
104
- fileKey = `${fileKey}/index`;
105
- usedKeys.add(fileKey);
106
- } else {
107
- continue;
108
- }
109
-
110
- const filePath = mdxFilesMap[fileKey];
111
- const rawContent = fs.readFileSync(filePath, 'utf-8');
112
- const { data } = parseFrontmatter(rawContent);
113
- const pathname = pathFromFilename(filePath, docsDir);
114
-
115
- frontmatterArray.push({
116
- ...data,
117
- path: pathname
118
- });
119
- }
120
-
121
- // Process any extra files not in navigation order, sorted
122
- const extraKeys = Object.keys(mdxFilesMap).filter(k => !usedKeys.has(k)).sort();
123
- for (const fileKey of extraKeys) {
124
- const filePath = mdxFilesMap[fileKey];
125
- const rawContent = fs.readFileSync(filePath, 'utf-8');
126
- const { data } = parseFrontmatter(rawContent);
127
- const pathname = pathFromFilename(filePath, docsDir);
128
-
129
- frontmatterArray.push({
130
- ...data,
131
- path: pathname
132
- });
133
- }
134
-
135
- return frontmatterArray;
136
- }
137
-
138
- export function frontmatterGenerator(options: FrontmatterGeneratorOptions = {}): Plugin {
139
- return {
140
- name: 'frontmatter-generator',
141
- generateBundle() {
142
- const docsDir = options.docsDir || path.join(process.cwd(), 'docs');
143
- const frontmatterArray = generateFrontmatterJson(docsDir);
144
- const jsonContent = JSON.stringify(frontmatterArray, null, 2);
145
-
146
- this.emitFile({
147
- type: 'asset',
148
- fileName: 'frontmatter.json',
149
- source: jsonContent
150
- });
151
-
152
- console.log(`✅ Generated frontmatter JSON file`);
153
- console.log(`📄 Processed ${frontmatterArray.length} MDX files`);
154
- console.log(`📝 Total JSON size: ${jsonContent.length} characters`);
155
- }
156
- };
157
- }
@@ -1,183 +0,0 @@
1
- import type { Plugin } from 'vite';
2
- import fs from 'fs';
3
- import path from 'path';
4
-
5
- // Read dory.json to get navigation order (flat list, in order)
6
- function getNavigationOrder(docsDir: string): string[] {
7
- const doryJsonPath = path.join(docsDir, 'dory.json');
8
- if (!fs.existsSync(doryJsonPath)) return [];
9
- const doryJson = JSON.parse(fs.readFileSync(doryJsonPath, 'utf-8'));
10
- const order: string[] = [];
11
-
12
- function walkPages(pages: any[]) {
13
- for (const page of pages) {
14
- if (typeof page === 'string') {
15
- order.push(page);
16
- } else if (typeof page === 'object' && page.pages) {
17
- walkPages(page.pages);
18
- }
19
- }
20
- }
21
-
22
- if (doryJson.navigation && Array.isArray(doryJson.navigation.tabs)) {
23
- for (const tab of doryJson.navigation.tabs) {
24
- if (tab.groups && Array.isArray(tab.groups)) {
25
- for (const group of tab.groups) {
26
- if (group.pages && Array.isArray(group.pages)) {
27
- walkPages(group.pages);
28
- }
29
- }
30
- }
31
- }
32
- }
33
- return order;
34
- }
35
-
36
- // Function to parse frontmatter from MDX content
37
- function parseFrontmatter(content: string) {
38
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n/);
39
- if (!frontmatterMatch) return { data: {}, content };
40
-
41
- const frontmatter = frontmatterMatch[1];
42
- const data: Record<string, string> = {};
43
-
44
- frontmatter.split('\n').forEach(line => {
45
- const [key, ...valueParts] = line.split(':');
46
- if (key && valueParts.length) {
47
- const value = valueParts.join(':').trim();
48
- data[key.trim()] = value.replace(/^["']|["']$/g, '');
49
- }
50
- });
51
-
52
- return { data, content: content.slice(frontmatterMatch[0].length) };
53
- }
54
-
55
- // Function to recursively find all MDX files and map by relative path (without .mdx)
56
- function findMdxFilesMap(dir: string, baseDir: string): Record<string, string> {
57
- const files: Record<string, string> = {};
58
- const items = fs.readdirSync(dir, { withFileTypes: true });
59
-
60
- for (const item of items) {
61
- const fullPath = path.join(dir, item.name);
62
- if (item.isDirectory()) {
63
- Object.assign(files, findMdxFilesMap(fullPath, baseDir));
64
- } else if (item.isFile() && item.name.endsWith('.mdx')) {
65
- // Key: relative path without .mdx, with forward slashes
66
- let rel = path.relative(baseDir, fullPath).replace(/\\/g, '/');
67
- if (rel.endsWith('.mdx')) rel = rel.slice(0, -4);
68
- files[rel] = fullPath;
69
- }
70
- }
71
-
72
- return files;
73
- }
74
-
75
- // Function to clean MDX content for LLM consumption
76
- function cleanMdxContent(content: string): string {
77
- // Remove JSX components but keep their content
78
- let cleaned = content
79
- // Remove JSX opening/closing tags but keep content
80
- .replace(/<(\w+)([^>]*?)>/g, '')
81
- .replace(/<\/\w+>/g, '')
82
- // Remove code block language specifiers
83
- .replace(/```(\w+)/g, '```')
84
- // Remove HTML comments
85
- .replace(/<!--[\s\S]*?-->/g, '')
86
- // Remove excessive whitespace
87
- .replace(/\n\s*\n\s*\n/g, '\n\n')
88
- // Remove leading/trailing whitespace
89
- .trim();
90
-
91
- return cleaned;
92
- }
93
-
94
- // Function to generate LLM text content in dory.json order, then append any extra files
95
- function generateLlmContent(docsDir: string): string {
96
- const navOrder = getNavigationOrder(docsDir);
97
- const mdxFilesMap = findMdxFilesMap(docsDir, docsDir);
98
- const usedKeys = new Set<string>();
99
- let aggregatedContent = '';
100
-
101
- // Output files in navigation order, in order as in dory.json
102
- for (const navKey of navOrder) {
103
- // Try both with and without "index"
104
- let fileKey = navKey;
105
- if (mdxFilesMap[fileKey]) {
106
- usedKeys.add(fileKey);
107
- } else if (mdxFilesMap[`${fileKey}/index`]) {
108
- fileKey = `${fileKey}/index`;
109
- usedKeys.add(fileKey);
110
- } else {
111
- continue;
112
- }
113
- const filePath = mdxFilesMap[fileKey];
114
- const rawContent = fs.readFileSync(filePath, 'utf-8');
115
- const { data, content } = parseFrontmatter(rawContent);
116
- const cleanedContent = cleanMdxContent(content);
117
-
118
- // Add file header
119
- aggregatedContent += `\n# ${fileKey}.mdx\n`;
120
-
121
- // Add title if available
122
- if (data.title) {
123
- aggregatedContent += `## ${data.title}\n\n`;
124
- }
125
-
126
- // Add description if available
127
- if (data.description) {
128
- aggregatedContent += `${data.description}\n\n`;
129
- }
130
-
131
- // Add cleaned content
132
- aggregatedContent += cleanedContent + '\n\n';
133
- aggregatedContent += '---\n\n';
134
- }
135
-
136
- // Output any extra files not in navigation order, sorted
137
- const extraKeys = Object.keys(mdxFilesMap).filter(k => !usedKeys.has(k)).sort();
138
- for (const fileKey of extraKeys) {
139
- const filePath = mdxFilesMap[fileKey];
140
- const rawContent = fs.readFileSync(filePath, 'utf-8');
141
- const { data, content } = parseFrontmatter(rawContent);
142
- const cleanedContent = cleanMdxContent(content);
143
-
144
- aggregatedContent += `\n# ${fileKey}.mdx\n`;
145
- if (data.title) {
146
- aggregatedContent += `## ${data.title}\n\n`;
147
- }
148
- if (data.description) {
149
- aggregatedContent += `${data.description}\n\n`;
150
- }
151
- aggregatedContent += cleanedContent + '\n\n';
152
- aggregatedContent += '---\n\n';
153
- }
154
-
155
- // Remove leading newlines for the very first file, so the first file is at the very top
156
- return aggregatedContent.replace(/^\n+/, '').trim();
157
- }
158
-
159
- export function llmTxtDevServer(): Plugin {
160
- return {
161
- name: 'llm-txt-dev-server',
162
- configureServer(server) {
163
- server.middlewares.use('/llms.txt', (req, res, next) => {
164
- if (req.method !== 'GET') {
165
- return next();
166
- }
167
-
168
- try {
169
- const docsDir = path.join(process.cwd(), 'docs');
170
- const llmContent = generateLlmContent(docsDir);
171
-
172
- res.setHeader('Content-Type', 'text/plain; charset=utf-8');
173
- res.setHeader('Content-Length', Buffer.byteLength(llmContent));
174
- res.end(llmContent);
175
- } catch (error) {
176
- console.error('Error generating LLM text:', error);
177
- res.statusCode = 500;
178
- res.end('Internal Server Error');
179
- }
180
- });
181
- }
182
- };
183
- }