|
| 1 | +import fg from 'fast-glob'; |
| 2 | +import matter from 'gray-matter'; |
| 3 | +import * as fs from 'node:fs/promises'; |
| 4 | +import path from 'node:path'; |
| 5 | +import { remark } from 'remark'; |
| 6 | +import remarkGfm from 'remark-gfm'; |
| 7 | +import remarkStringify from 'remark-stringify'; |
| 8 | + |
| 9 | +export const revalidate = false; |
| 10 | + |
| 11 | +// Regular expressions for cleaning up the content |
| 12 | +const IMPORT_REGEX = /import\s+?(?:(?:{[^}]*}|\*|\w+)\s+from\s+)?['"](.*?)['"];?\n?/g; |
| 13 | +const COMPONENT_USAGE_REGEX = /<[A-Z][a-zA-Z]*(?:\s+[^>]*)?(?:\/?>|>[^<]*<\/[A-Z][a-zA-Z]*>)/g; |
| 14 | +const NEXTRA_COMPONENT_REGEX = /<(?:Callout|Steps|Tabs|Tab|FileTree)[^>]*>[^<]*<\/(?:Callout|Steps|Tabs|Tab|FileTree)>/g; |
| 15 | +const MDX_EXPRESSION_REGEX = /{(?:[^{}]|{[^{}]*})*}/g; |
| 16 | +const EXPORT_REGEX = /export\s+(?:default\s+)?(?:const|let|var|function|class|interface|type)?\s+[a-zA-Z_$][0-9a-zA-Z_$]*[\s\S]*?(?:;|\n|$)/g; |
| 17 | + |
| 18 | +export async function GET() { |
| 19 | + try { |
| 20 | + const files = await fg(['content/en/patterns/**/*.mdx']); |
| 21 | + |
| 22 | + const scan = files.map(async (file) => { |
| 23 | + try { |
| 24 | + const fileContent = await fs.readFile(file); |
| 25 | + const { content, data } = matter(fileContent.toString()); |
| 26 | + |
| 27 | + // Get the filename without extension to use as fallback title |
| 28 | + const basename = path.basename(file, '.mdx'); |
| 29 | + |
| 30 | + // Extract category from file path |
| 31 | + const pathParts = path.dirname(file).split(path.sep); |
| 32 | + let category = 'general'; |
| 33 | + if (pathParts.length > 3 && pathParts[3]) { |
| 34 | + category = pathParts[3]; |
| 35 | + } |
| 36 | + |
| 37 | + // Skip if the file is marked as hidden or draft |
| 38 | + if (data.draft || data.hidden) { |
| 39 | + return null; |
| 40 | + } |
| 41 | + |
| 42 | + // Use filename as title if no title in frontmatter, and convert to Title Case |
| 43 | + const title = data.title || basename.split('-') |
| 44 | + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) |
| 45 | + .join(' '); |
| 46 | + |
| 47 | + const processed = await processContent(content); |
| 48 | + return `File: ${file} |
| 49 | +# ${category.toUpperCase()}: ${title} |
| 50 | +
|
| 51 | +${data.description || ''} |
| 52 | +
|
| 53 | +${processed}`; |
| 54 | + } catch (error) { |
| 55 | + console.error(`Error processing file ${file}:`, error); |
| 56 | + return null; |
| 57 | + } |
| 58 | + }); |
| 59 | + |
| 60 | + const scanned = (await Promise.all(scan)).filter(Boolean); |
| 61 | + |
| 62 | + if (!scanned.length) { |
| 63 | + return new Response('No content found', { status: 404 }); |
| 64 | + } |
| 65 | + |
| 66 | + return new Response(scanned.join('\n\n')); |
| 67 | + } catch (error) { |
| 68 | + console.error('Error generating LLM content:', error); |
| 69 | + return new Response('Internal Server Error', { status: 500 }); |
| 70 | + } |
| 71 | +} |
| 72 | + |
| 73 | +async function processContent(content: string): Promise<string> { |
| 74 | + try { |
| 75 | + // Multi-step cleanup to handle different MDX constructs |
| 76 | + let cleanContent = content |
| 77 | + // Remove imports first |
| 78 | + .replace(IMPORT_REGEX, '') |
| 79 | + // Remove exports |
| 80 | + .replace(EXPORT_REGEX, '') |
| 81 | + // Remove Nextra components with their content |
| 82 | + .replace(NEXTRA_COMPONENT_REGEX, '') |
| 83 | + // Remove other React components |
| 84 | + .replace(COMPONENT_USAGE_REGEX, '') |
| 85 | + // Remove MDX expressions |
| 86 | + .replace(MDX_EXPRESSION_REGEX, '') |
| 87 | + // Clean up multiple newlines |
| 88 | + .replace(/\n{3,}/g, '\n\n') |
| 89 | + // Remove empty JSX expressions |
| 90 | + .replace(/{[\s]*}/g, '') |
| 91 | + // Clean up any remaining JSX-like syntax |
| 92 | + .replace(/<>[\s\S]*?<\/>/g, '') |
| 93 | + .replace(/{\s*\/\*[\s\S]*?\*\/\s*}/g, '') |
| 94 | + .trim(); |
| 95 | + |
| 96 | + // Simple markdown processing without MDX |
| 97 | + const file = await remark() |
| 98 | + .use(remarkGfm) |
| 99 | + .use(remarkStringify) |
| 100 | + .process(cleanContent); |
| 101 | + |
| 102 | + return String(file); |
| 103 | + } catch (error) { |
| 104 | + console.error('Error processing content:', error); |
| 105 | + // If processing fails, return a basic cleaned version |
| 106 | + return content |
| 107 | + .replace(IMPORT_REGEX, '') |
| 108 | + .replace(COMPONENT_USAGE_REGEX, '') |
| 109 | + .replace(MDX_EXPRESSION_REGEX, '') |
| 110 | + .trim(); |
| 111 | + } |
| 112 | +} |
0 commit comments