import fs from "node:fs/promises"; import path from "node:path"; import fg from "fast-glob"; import matter from "gray-matter"; import { marked } from "marked"; const ROOT = process.cwd(); const SRC = path.join(ROOT, "src"); const ARTICLES_GLOB = "articles/**/index.md"; marked.setOptions({ breaks: true, gfm: true }); const logo = ` `; const shellHead = ({ title, desc, image }) => ` ${title} ${image ? `` : ""} `; const nav = `
${logo}

apophenia.news

for pattern seekers

`; const footer = ` `; const fmtDate = (date) => new Date(date).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" }); const escapeHtml = (s = "") => s.replace(/[&<>"']/g, (ch) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[ch])); const fixInternalLinks = (html) => html .replace(/href="\.\//g, 'href="/') .replace(/href="([^"]+)"(?=[^>]*>)/g, (_, href) => href.startsWith("http") || href.startsWith("#") || href.endsWith("/") || href.endsWith(".md") ? `href="${href}"` : `href="${href}/"` ); const renderHome = (articles) => ` ${shellHead({ title: "apophenia.news — The news outlet for pattern seekers", desc: "Signals, anomalies, civilization trajectories, and deep pattern analysis.", image: "https://direct-img.link/constellation+data+points+minimal+white+background" })} ${nav}

Pattern Intelligence Journalism

The news outlet for pattern seekers

High-agency analysis at the intersection of AGI, consciousness, geopolitics, and first-contact logic.

Latest Articles

${articles.length} published
${articles .map( (a) => ` ` ) .join("")}
${footer} `; const renderArticle = (article) => ` ${shellHead({ title: `${article.title} — apophenia.news`, desc: article.description, image: article.header_image })} ${nav}
${escapeHtml(article.title)}

${fmtDate(article.date)} • ${escapeHtml(article.author || "Apophenia")}

${escapeHtml(article.title)}

${escapeHtml(article.description || "")}

${(article.tags || []).map((t) => `#${escapeHtml(t)}`).join("")}

${article.html}
Back to Home
${footer} `; const ensureCleanGenerated = async () => { await fs.mkdir(SRC, { recursive: true }); const children = await fs.readdir(SRC, { withFileTypes: true }); const keep = new Set(["assets"]); await Promise.all( children .filter((d) => d.isDirectory() && !keep.has(d.name)) .map((d) => fs.rm(path.join(SRC, d.name), { recursive: true, force: true })) ); }; const run = async () => { const files = await fg(ARTICLES_GLOB, { cwd: ROOT, absolute: true }); const articles = []; for (const file of files) { const md = await fs.readFile(file, "utf8"); const { data, content } = matter(md); const html = fixInternalLinks(marked.parse(content)); if (!data.slug) continue; articles.push({ ...data, html }); } articles.sort((a, b) => +new Date(b.date) - +new Date(a.date)); await ensureCleanGenerated(); await fs.writeFile(path.join(SRC, "index.html"), renderHome(articles), "utf8"); for (const article of articles) { const dir = path.join(SRC, article.slug); await fs.mkdir(dir, { recursive: true }); await fs.writeFile(path.join(dir, "index.html"), renderArticle(article), "utf8"); } console.log(`Generated ${articles.length} article pages + home.`); }; run().catch((err) => { console.error(err); process.exit(1); });