import { readFile, readdir } from 'node:fs/promises' import { extname, join, basename } from 'node:path' import matter from 'gray-matter' import { pageFrontmatterSchema, postFrontmatterSchema, projectFrontmatterSchema, type PageFrontmatter, type PostFrontmatter, type ProjectFrontmatter, } from './schemas' export type DiscoveredFile = { type: 'post' | 'project' | 'page' filePath: string slug: string body: string frontmatter: T isMdx: boolean } async function listMarkdown(dir: string): Promise { const out: string[] = [] const entries = await readdir(dir, { withFileTypes: true }) for (const e of entries) { if (e.isFile() && (extname(e.name) === '.md' || extname(e.name) === '.mdx')) { out.push(join(dir, e.name)) } } return out } function slugFromFile(filePath: string): string { return basename(filePath).replace(/\.(md|mdx)$/, '') } async function parseOne(filePath: string, schema: { parse: (raw: unknown) => T }) { const raw = await readFile(filePath, 'utf8') const { data, content } = matter(raw) const frontmatter = schema.parse(data) return { filePath, slug: slugFromFile(filePath), body: content, frontmatter, isMdx: extname(filePath) === '.mdx', } } export async function discoverAll(sourceRoot: string) { const blogDir = join(sourceRoot, 'src/content/blog') const projectsDir = join(sourceRoot, 'src/content/projects') const pagesDir = join(sourceRoot, 'src/content/pages') const [blogFiles, projectFiles, pageFiles] = await Promise.all([ listMarkdown(blogDir), listMarkdown(projectsDir), listMarkdown(pagesDir), ]) const posts: DiscoveredFile[] = [] for (const f of blogFiles) { try { posts.push({ type: 'post', ...(await parseOne(f, postFrontmatterSchema)) }) } catch (err) { console.warn(`[discover] skipping ${f}: ${(err as Error).message}`) } } const projects: DiscoveredFile[] = [] for (const f of projectFiles) { try { projects.push({ type: 'project', ...(await parseOne(f, projectFrontmatterSchema)) }) } catch (err) { console.warn(`[discover] skipping ${f}: ${(err as Error).message}`) } } const pages: DiscoveredFile[] = [] for (const f of pageFiles) { try { pages.push({ type: 'page', ...(await parseOne(f, pageFrontmatterSchema)) }) } catch (err) { console.warn(`[discover] skipping ${f}: ${(err as Error).message}`) } } return { posts, projects, pages } }