import { mkdir, writeFile } from "node:fs/promises"; const blogs = [ { slug: "research", label: "Research", summary: "Notes from papers, algorithms, computational social choice, and mini-surveys of themes I am learning about.", }, { slug: "vibes", label: "Vibes", summary: "Loose experiments, AI conversations, and work-in-progress thoughts that do not need a formal frame.", }, { slug: "puzzles", label: "Puzzles", summary: "Problems, teaching prompts, contest-style curiosities, and small mathematical rabbit holes.", }, { slug: "reflections", label: "Reflections", summary: "Personal notes on teaching, academia, writing, attention, and the rhythms of intellectual work.", }, { slug: "poetry", label: "Poetry", summary: "Poems, tiny forms, and playful writing around mathematics, code, and ordinary life.", }, { slug: "reviews", label: "Reviews", summary: "Notes on tools, workflows, books, hardware, and objects that shape everyday work.", }, { slug: "art", label: "Art", summary: "Algorithmic sketches, generative art, visual experiments, and mathematical images.", }, ]; const rssHostSuffix = process.env.RSS_HOST_SUFFIX ?? "neeldhara.email"; const publicHostSuffix = process.env.PUBLIC_HOST_SUFFIX ?? "neeldhara.blog"; const outDir = new URL("../dist/", import.meta.url); function decodeXml(value) { return value .replace(/'/g, "'") .replace(/"/g, '"') .replace(/>/g, ">") .replace(/</g, "<") .replace(/&/g, "&"); } function escapeHtml(value) { return value .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function textFrom(block, tag) { const match = block.match(new RegExp(`<${tag}[^>]*>([\\s\\S]*?)`, "i")); return match ? decodeXml(match[1].trim()) : ""; } function parseItems(xml) { return [...xml.matchAll(/([\s\S]*?)<\/item>/gi)].slice(0, 2).map((match) => { const block = match[1]; const pubDate = new Date(textFrom(block, "pubDate")); return { title: textFrom(block, "title"), link: textFrom(block, "link"), date: Number.isNaN(pubDate.valueOf()) ? "" : pubDate.toISOString().slice(0, 10), }; }); } async function getPosts(slug) { const url = `https://${slug}.${rssHostSuffix}/rss.xml`; const response = await fetch(url); if (!response.ok) { throw new Error(`${url} returned ${response.status}`); } return parseItems(await response.text()); } function renderPost(post) { const date = post.date ? new Date(`${post.date}T00:00:00Z`) : null; const dateLabel = date ? date.toLocaleDateString("en", { month: "short", day: "numeric", timeZone: "UTC" }) : ""; return `
  • ${escapeHtml(dateLabel)}${escapeHtml(post.title)}
  • `; } function renderRssIcon(blog) { const href = `https://${blog.slug}.${publicHostSuffix}/rss.xml`; return ` `; } function renderBlog(blog, posts) { return `
    ${escapeHtml(blog.label)} ${renderRssIcon(blog)}

    ${escapeHtml(blog.summary)}

    `; } const entries = await Promise.all( blogs.map(async (blog) => [blog, await getPosts(blog.slug)]), ); const html = ` neeldhara.blog

    Neeldhara Misra

    seven small blogs

    A quiet index of notes on research, puzzles, writing, tools, art, and other recurring fascinations.

    ${entries.map(([blog, posts]) => renderBlog(blog, posts)).join("\n ")}
    `; await mkdir(outDir, { recursive: true }); await writeFile(new URL("index.html", outDir), html);