import fs from 'fs/promises'; import path from 'path'; import matter from 'gray-matter'; import { config } from '@/lib/config'; export interface ProductFrontmatter { title: string; sku: string; description: string; categories: string[]; images: string[]; focalX?: number; focalY?: number; isFallback?: boolean; } export interface ProductData { slug: string; frontmatter: ProductFrontmatter; content: any; // Lexical AST from Payload application?: any; // Lexical AST for Application field } export async function getProductMetadata( slug: string, locale: string, ): Promise | null> { const p = await getProductBySlug(slug, locale); if (!p) return null; return { slug: p.slug, frontmatter: p.frontmatter, }; } export async function getProductBySlug(slug: string, locale: string): Promise { try { const filePath = path.join(process.cwd(), 'content', 'products', `${slug}.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 } // Filter images const resolvedImages = ((data.images as any[]) || []) .map((img) => (typeof img === 'string' ? img : img?.url)) .filter(Boolean); if (resolvedImages.length === 0) return null; return { slug, frontmatter: { title: data.title || '', sku: data.sku || '', description: data.description || '', categories: Array.isArray(data.categories) ? data.categories.map((c: any) => c?.category || c) : [], images: resolvedImages, focalX: data.focalX || 50, focalY: data.focalY || 50, }, content: parsedContent, application: data.application, }; } catch (error) { console.error(`getProductBySlug failed for ${slug}:`, error); return null; } } export async function getAllProductSlugs(locale: string): Promise { try { const dir = path.join(process.cwd(), 'content', 'products'); const files = await fs.readdir(dir); return files.filter((f) => f.endsWith('.mdx')).map((f) => f.replace('.mdx', '')); } catch (error) { console.error(`getAllProductSlugs failed for ${locale}:`, error); return []; } } export async function getAllProducts(locale: string): Promise { try { const slugs = await getAllProductSlugs(locale); const products = await Promise.all(slugs.map((slug) => getProductBySlug(slug, locale))); return products.filter( (p): p is ProductData => p !== null && p.frontmatter.images && p.frontmatter.images.length > 0, ); } catch (error) { console.error(`getAllProducts failed for ${locale}:`, error); return []; } } export async function getAllProductsMetadata(locale: string): Promise[]> { const products = await getAllProducts(locale); return products.map((p) => ({ slug: p.slug, frontmatter: p.frontmatter, })); }