import { promises as fs } from 'fs' import path from 'path' import wabtModule from 'wabt' import frontmatter from 'front-matter' export interface PostMeta { slug: string, date: Date, title: string, subtitle?: string, unlisted: boolean, filePath: string, } export interface Post extends Omit { body: string, script?: string, wasm?: Uint8Array, } type Attributes = { [key: string]: any } const POST_FILE_PATTERN = /^(?[0-9]{4}-[0-9]{2}-[0-9]{2}(-[0-9]{1,2})?)_(?[^\.]+)\.md$/ export async function getPostSlugs(): Promise { const postsDir = path.join(process.cwd(), 'posts') const postPaths = await fs.readdir(postsDir) return postPaths.map(getSlugFromFilePath).filter(slug => slug !== null) as string[] } export function getSlugFromFilePath(postPath: string): string | null { const regexResult = POST_FILE_PATTERN.exec(postPath) if (!regexResult?.groups) return null return regexResult.groups.slug } export async function loadPageMetada(slug: string): Promise { const postsDir = path.join(process.cwd(), 'posts') const postPaths = await fs.readdir(postsDir) const postMatch: RegExpExecArray | null | undefined = postPaths .map((postFile: string) => POST_FILE_PATTERN.exec(postFile)) .filter((regexResult: RegExpExecArray | null) => regexResult !== null) .find((regexResult: RegExpExecArray) => regexResult.groups?.slug === slug) if (!postMatch) return null; const fileName = `${postMatch.groups?.date}_${postMatch.groups?.slug}.md` const filePath = path.join(process.cwd(), 'posts', fileName) const fileContents = (await fs.readFile(filePath)).toString('utf8') const { attributes } = frontmatter(fileContents) const data: Attributes = attributes as Attributes if (!data.title) return null; const [year, month, day] = postMatch.groups?.date.split('-') || [] const date = new Date(Date.UTC(parseInt(year), parseInt(month) - 1, parseInt(day))) return { date, filePath, slug: postMatch.groups?.slug || '', title: data.title, subtitle: data.subtitle, unlisted: typeof data.unlisted === 'boolean' ? data.unlisted : false, } } export async function loadSinglePage(slug: string): Promise { const postMeta = await loadPageMetada(slug) if (!postMeta) return null const fileContents = (await fs.readFile(postMeta.filePath)).toString('utf8') const { attributes, body } = frontmatter(fileContents) const data: Attributes = attributes as Attributes if (data.script) { const scriptPath = path.join(process.cwd(), 'public/scripts', data.script) try { await fs.readFile(scriptPath) } catch { throw new Error(`Could not find script ${scriptPath} referenced in post ${slug}`) } finally { data.script = `/scripts/${data.script}` } } if (data.wasm) { const wabt = await wabtModule() const wasmPath = path.join(process.cwd(), 'public/scripts', data.wasm) const fileName = data.wasm.replace(/^.*\//, '') const wasmText = (await fs.readFile(wasmPath)) const wasmIntermed = wabt.parseWat(fileName, wasmText) const { buffer: wasmBinary } = wasmIntermed.toBinary({ write_debug_names: true }) data.wasm = wasmBinary } const { filePath, ...otherMeta } = postMeta return { ...otherMeta, script: data.script, wasm: data.wasm, body } }