import { readdir } from "node:fs/promises"; import { YAML } from "bun"; import metadata from "./metadata.json"; import templateRaw from "./templates/post.html" with { type: "text" }; import homeRaw from "./templates/home.html" with { type: "text" }; const year = new Date().getFullYear().toString(); const applyVars = (raw: unknown) => (raw as string) .replaceAll("[[SITE]]", metadata.title) .replaceAll("[[NAME]]", metadata.name) .replaceAll("[[TIMEZONE]]", metadata.timezone) .replaceAll("[[YEAR]]", year); const template = applyVars(templateRaw); const home = applyVars(homeRaw); const posts = await readdir("./posts"); const postLinks: { title: string; date: Date; link: string; }[] = []; const defaultFrontmatter = { title: "Untitled", desc: "", date: "0000-00-00", // YYYY-MM-DD draft: true, }; function fetchFrontmatter(raw?: string) { try { if (!raw) throw new Error("No frontmatter found."); const parsed = { ...defaultFrontmatter, ...(YAML.parse(raw) as typeof defaultFrontmatter) }; return { ...parsed, date: new Date(parsed.date) }; } catch { console.warn("Failed to parse frontmatter. Using default values."); return { ...defaultFrontmatter, date: new Date(NaN) }; } } await Promise.all(posts.map(async (post) => { const raw = (await Bun.file(`./posts/${post}`).text()).split("^---"); const content = raw.at(-1) || "_No content_"; const frontmatter = fetchFrontmatter(raw.at(0)); const html = Bun.markdown.html(content, { autolinks: true, headings: true, underline: true, tagFilter: true, }); const render = template .replaceAll("[[TITLE]]", `${metadata.title} - ${frontmatter.title}`) .replaceAll("[[POST_TITLE]]", frontmatter.title) .replaceAll("[[DATE]]", frontmatter.date.toDateString()) .replaceAll("[[REVISIONS]]", `${metadata.revisions}/${post}`) .replaceAll("[[DESC]]", frontmatter.desc) .replace("[[CONTENT]]", html); await Bun.write(`./dist/posts/${post.replace(".md", ".html")}`, render); if (!frontmatter.draft) postLinks.push({ title: frontmatter.title, date: frontmatter.date, link: `/posts/${post.replace(".md", ".html")}`, }); console.log(`Built page for ${frontmatter.draft ? 'draft' : 'post'}: ${frontmatter.title}`); // TODO: table of contents generation })); const parsedPosts = postLinks .sort((a, b) => b.date.getTime() - a.date.getTime()) .map((post) => `
  • ${post.title} - ${post.date.toDateString()}
  • `) .join("\n"); const render = home .replace("[[ABOUT]]", Bun.markdown.html(metadata.description)) .replace("[[MAIN]]", metadata.main) .replace("[[POSTS]]", parsedPosts); await Bun.write("./dist/index.html", render);