|
| 1 | +import { components, guides, overviews, snippets, utilities } from ".velite" |
| 2 | +import { expandComponentContent } from "lib/mdx-expansion" |
| 3 | +import { NextRequest, NextResponse } from "next/server" |
| 4 | + |
| 5 | +// Default finder function for most collections |
| 6 | +const defaultFindBy = (d: any, slug: string) => d.slug === slug |
| 7 | + |
| 8 | +// Define collection mappings for cleaner lookups |
| 9 | +const collections: Record< |
| 10 | + string, |
| 11 | + { |
| 12 | + data: any[] |
| 13 | + findBy?: (d: any, slug: string) => boolean |
| 14 | + } |
| 15 | +> = { |
| 16 | + overview: { data: overviews }, |
| 17 | + components: { data: components }, |
| 18 | + guides: { data: guides }, |
| 19 | + utilities: { data: utilities }, |
| 20 | + snippets: { |
| 21 | + data: snippets, |
| 22 | + findBy: (d, slug) => d.frontmatter?.slug === `/snippets/${slug}`, |
| 23 | + }, |
| 24 | +} |
| 25 | + |
| 26 | +type CollectionType = keyof typeof collections |
| 27 | + |
| 28 | +function findDocument(category: string, slug: string) { |
| 29 | + // First, try to find in the specified category |
| 30 | + const collection = collections[category as CollectionType] |
| 31 | + if (collection) { |
| 32 | + const findBy = collection.findBy || defaultFindBy |
| 33 | + const doc = collection.data.find((d) => findBy(d, slug)) |
| 34 | + if (doc) { |
| 35 | + return { |
| 36 | + doc, |
| 37 | + contentType: |
| 38 | + category === "components" ? "component" : (category as string), |
| 39 | + } |
| 40 | + } |
| 41 | + } |
| 42 | + |
| 43 | + // If not found or invalid category, search all collections using the category as the slug |
| 44 | + for (const [type, collection] of Object.entries(collections)) { |
| 45 | + if (type === "snippets") continue // Skip snippets for direct slug search |
| 46 | + |
| 47 | + const findBy = collection.findBy || defaultFindBy |
| 48 | + const doc = collection.data.find((d) => findBy(d, category)) |
| 49 | + if (doc) { |
| 50 | + return { doc, contentType: type === "components" ? "component" : type } |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + return { doc: null, contentType: null } |
| 55 | +} |
| 56 | + |
| 57 | +export async function GET( |
| 58 | + _request: NextRequest, |
| 59 | + context: { params: Promise<{ slug: string[] }> }, |
| 60 | +) { |
| 61 | + const { slug } = await context.params |
| 62 | + |
| 63 | + if (!slug || slug.length === 0) { |
| 64 | + return NextResponse.json({ error: "No slug provided" }, { status: 400 }) |
| 65 | + } |
| 66 | + |
| 67 | + let category = slug[0] |
| 68 | + let rest = slug.slice(1) |
| 69 | + let framework: string | undefined |
| 70 | + |
| 71 | + // Check if the path is like /components/react/avatar |
| 72 | + // If second segment is a framework, extract it |
| 73 | + if (category === "components" && rest.length >= 2) { |
| 74 | + const possibleFramework = rest[0] |
| 75 | + if (["react", "vue", "solid", "svelte"].includes(possibleFramework)) { |
| 76 | + framework = possibleFramework |
| 77 | + rest = rest.slice(1) // Remove framework from the path |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + const searchSlug = rest.join("/") |
| 82 | + const { doc, contentType } = findDocument(category, searchSlug) |
| 83 | + |
| 84 | + if (!doc) { |
| 85 | + return NextResponse.json({ error: "Content not found" }, { status: 404 }) |
| 86 | + } |
| 87 | + |
| 88 | + // Get the raw content |
| 89 | + let rawContent = doc.body?.raw || doc.raw || doc.content || "" |
| 90 | + |
| 91 | + // Expand component content (CodeSnippets, ApiTable, ContextTable, etc.) if content type is component |
| 92 | + if (contentType === "component" && rawContent) { |
| 93 | + const componentId = doc.slug // The slug is the component ID (e.g., "accordion", "dialog", etc.) |
| 94 | + rawContent = expandComponentContent(rawContent, componentId, doc, framework) |
| 95 | + } |
| 96 | + |
| 97 | + // Return all available data from velite |
| 98 | + const response = { |
| 99 | + // Core fields |
| 100 | + slug: doc.slug, |
| 101 | + title: doc.title || doc.frontmatter?.title, |
| 102 | + description: doc.description || doc.frontmatter?.description, |
| 103 | + contentType, |
| 104 | + framework, // Include the framework if specified |
| 105 | + |
| 106 | + // Content with expanded snippets |
| 107 | + content: rawContent, |
| 108 | + html: doc.body?.html || doc.html, |
| 109 | + |
| 110 | + // Package info |
| 111 | + package: doc.package || doc.frontmatter?.package, |
| 112 | + |
| 113 | + // URLs |
| 114 | + editUrl: doc.editUrl, |
| 115 | + permalink: doc.permalink, |
| 116 | + } |
| 117 | + |
| 118 | + return NextResponse.json(response) |
| 119 | +} |
0 commit comments