import fs from 'fs'; import path from 'path'; import matter from 'gray-matter'; import readingTime from 'reading-time'; import { notFound } from 'next/navigation'; const postsDirectory = path.join(process.cwd(), 'posts'); export interface Heading { level: number; text: string; slug: string; } export interface PostData { id: string; date: string; title: string; description: string; content: string; readingTime: string; headings: Heading[]; wip: boolean; } export interface PostMeta { id: string; date: string; title: string; description: string; readingTime: string; wip: boolean; } function extractHeadings(content: string): Heading[] { const regex = /^(#{2,3})\s+(.+)$/gm; const headings: Heading[] = []; let match; while ((match = regex.exec(content)) !== null) { const text = match[2].trim(); const slug = text .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/(^-|-$)/g, ''); headings.push({ level: match[1].length, text, slug }); } return headings; } export function getSortedPostsData(): PostMeta[] { try { const fileNames = fs.readdirSync(postsDirectory); const allPostsData = fileNames .filter(fileName => fileName.endsWith('.mdx')) .map(fileName => { const id = fileName.replace(/\.mdx$/, ''); const fullPath = path.join(postsDirectory, fileName); const fileContents = fs.readFileSync(fullPath, 'utf8'); const matterResult = matter(fileContents); const stats = readingTime(matterResult.content); return { id, date: matterResult.data.date as string, title: matterResult.data.title as string, description: matterResult.data.description as string, readingTime: stats.text, wip: matterResult.data.wip ?? false, }; }); return allPostsData.sort((a, b) => (a.date < b.date ? 1 : -1)); } catch (error) { console.log("No 'posts' directory found, returning empty array."); return []; } } export function getAllPostIds() { try { const fileNames = fs.readdirSync(postsDirectory); return fileNames.map(fileName => ({ params: { slug: fileName.replace(/\.mdx$/, ''), }, })); } catch (error) { return []; } } export function getAdjacentPosts(slug: string): { prev: PostMeta | null; next: PostMeta | null } { const posts = getSortedPostsData(); // newest → oldest const index = posts.findIndex((p) => p.id === slug); if (index === -1) return { prev: null, next: null }; return { prev: posts[index + 1] ?? null, // older next: posts[index - 1] ?? null, // newer }; } export async function getPostData(id: string): Promise { const fullPath = path.join(postsDirectory, `${id}.mdx`); if (!fs.existsSync(fullPath)) notFound(); const fileContents = fs.readFileSync(fullPath, 'utf8'); const matterResult = matter(fileContents); const stats = readingTime(matterResult.content); return { id, content: matterResult.content, date: matterResult.data.date, title: matterResult.data.title, description: matterResult.data.description, readingTime: stats.text, headings: extractHeadings(matterResult.content), wip: matterResult.data.wip ?? false, }; }