import fs from 'fs/promises'; import path from 'path'; import matter from 'gray-matter'; import { config } from '@/lib/config'; import { mapSlugToFileSlug } from './slugs'; export interface PageFrontmatter { title: string; excerpt: string; featuredImage: string | null; focalX?: number; focalY?: number; layout?: 'default' | 'fullBleed'; public?: boolean; } export interface PageData { slug: string; redirectUrl?: string; redirectPermanent?: boolean; frontmatter: PageFrontmatter; content: any; // Lexical AST Document } function mapDoc(doc: any): PageData { return { slug: doc.slug, frontmatter: { title: doc.title, excerpt: doc.excerpt || '', featuredImage: typeof doc.featuredImage === 'object' && doc.featuredImage !== null ? doc.featuredImage.sizes?.card?.url || doc.featuredImage.url : null, focalX: typeof doc.featuredImage === 'object' && doc.featuredImage !== null ? doc.featuredImage.focalX : 50, focalY: typeof doc.featuredImage === 'object' && doc.featuredImage !== null ? doc.featuredImage.focalY : 50, layout: doc.layout || 'default', } as PageFrontmatter, content: doc.content as any, }; } export async function getPageBySlug(slug: string, locale: string): Promise { try { const fileSlug = await mapSlugToFileSlug(slug, locale); const filePath = path.join(process.cwd(), 'content', 'pages', `${fileSlug}.mdx`); const fileContent = await fs.readFile(filePath, 'utf-8'); const { data, content } = matter(fileContent); // Fix MDX data props dropped by next-mdx-remote // Payload serializes as data={{"items":...}} which Acorn treats as a Block statement. const fixedContent = content.replace(/data=\{\{([\s\S]*?)\}\}/g, (match, p1) => { return 'data="{' + p1.replace(/"/g, '"') + '}"'; }); let parsedContent = fixedContent; try { if (content.trim().startsWith('{')) { parsedContent = JSON.parse(content); } } catch (e) { // Not JSON } return { slug, redirectUrl: data.redirectUrl, redirectPermanent: data.redirectPermanent ?? true, frontmatter: { title: data.title || '', excerpt: data.excerpt || '', featuredImage: data.featuredImage?.url || data.featuredImage || null, focalX: data.featuredImage?.focalX || data.focalX || 50, focalY: data.featuredImage?.focalY || data.focalY || 50, layout: data.layout === 'fullBleed' || data.layout === 'default' ? data.layout : 'default', public: data.public ?? true, }, content: parsedContent, }; } catch (error) { console.error(`getPageBySlug failed for ${slug}:`, error); return null; } } export async function getAllPages(locale: string): Promise { try { const dir = path.join(process.cwd(), 'content', 'pages'); const files = await fs.readdir(dir); const slugs = files.filter((f) => f.endsWith('.mdx')).map((f) => f.replace('.mdx', '')); const pages = await Promise.all(slugs.map((slug) => getPageBySlug(slug, locale))); return pages.filter((p): p is PageData => p !== null); } catch (error) { console.error(`getAllPages failed for ${locale}:`, error); return []; } } export async function getAllPagesMetadata(locale: string): Promise[]> { const pages = await getAllPages(locale); return pages.map((p) => ({ slug: p.slug, frontmatter: p.frontmatter, })); }