import fs from "node:fs/promises"; import path from "node:path"; const ROOT = process.cwd(); const DIST_DIR = path.join(ROOT, "dist"); const DIST_INDEX_HTML = path.join(DIST_DIR, "index.html"); const SRC_CONFIG = path.join(ROOT, "src", "config.ts"); const DEFAULTS = { BLOG_NAME: "Hyperzine", BLOG_SITE_URL: "https://example.com", BLOG_DEFAULT_DESCRIPTION: "Hyperzine", MANIFEST_TX_ID: "" }; const asObject = (value) => (typeof value === "object" && value !== null ? value : null); const asString = (value) => (typeof value === "string" && value.trim().length > 0 ? value : ""); const htmlEscape = (value) => value .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """); const normalizeSlashPath = (input) => { const cleaned = input.trim().replace(/^\/+|\/+$/g, ""); return cleaned ? `/${cleaned}` : "/"; }; const toAbsoluteUrl = (siteUrl, inputPath) => new URL(normalizeSlashPath(inputPath), siteUrl).toString(); const parseConfig = async () => { let source = ""; try { source = await fs.readFile(SRC_CONFIG, "utf8"); } catch { return DEFAULTS; } const pick = (key) => { const match = source.match( new RegExp(`export\\s+const\\s+${key}\\s*=\\s*(?:"([^"]*)"|'([^']*)')`, "m") ); const value = match?.[1] || match?.[2]; return value ?? DEFAULTS[key]; }; return { BLOG_NAME: pick("BLOG_NAME"), BLOG_SITE_URL: pick("BLOG_SITE_URL"), BLOG_DEFAULT_DESCRIPTION: pick("BLOG_DEFAULT_DESCRIPTION"), MANIFEST_TX_ID: pick("MANIFEST_TX_ID") }; }; const parseFrontmatter = (input) => { const obj = asObject(input); if (!obj) return {}; const bannerValue = obj.banner; const bannerObj = asObject(bannerValue); const banner = asString(bannerValue) || asString(bannerObj?.txId) || asString(bannerObj?.id) || asString(bannerObj?.src) || asString(bannerObj?.url); return { title: asString(obj.title), desc: asString(obj.desc), description: asString(obj.description), excerpt: asString(obj.excerpt), slug: asString(obj.slug), banner, date: asString(obj.date), updated: asString(obj.updated) }; }; const parseManifest = (input) => { const root = asObject(input); const posts = Array.isArray(root?.posts) ? root.posts : []; return posts .map((entry) => { const post = asObject(entry); if (!post) return null; const frontmatter = parseFrontmatter(post.frontmatter); const bannerTxIdValue = post.bannerTxId; const bannerTxIdObject = asObject(bannerTxIdValue); const bannerTxId = asString(bannerTxIdValue) || asString(bannerTxIdObject?.txId) || asString(bannerTxIdObject?.id) || asString(bannerTxIdObject?.src) || asString(bannerTxIdObject?.url); const slug = asString(post.slug) || frontmatter.slug; const postTxId = asString(post.postTxId); if (!slug || !postTxId) return null; return { slug, title: asString(post.title) || frontmatter.title || "Untitled", description: asString(post.description) || frontmatter.desc || frontmatter.description || "", excerpt: asString(post.excerpt) || frontmatter.excerpt || "", bannerTxId: bannerTxId || frontmatter.banner || "", publishedAt: asString(post.publishedAt) || frontmatter.date || "", updated: asString(post.updated) || frontmatter.updated || "" }; }) .filter(Boolean) .sort((a, b) => { const aDate = a.publishedAt || ""; const bDate = b.publishedAt || ""; return aDate < bDate ? 1 : -1; }); }; const fetchManifestPosts = async (manifestTxId) => { if (!manifestTxId) return []; const url = `https://arweave.net/${manifestTxId}`; const response = await fetch(url, { cache: "no-store" }); if (!response.ok) throw new Error(`Manifest fetch failed (${response.status})`); const payload = await response.json(); return parseManifest(payload); }; const buildMetaTags = ({ title, description, canonicalUrl, siteName, imageUrl, type = "website", publishedTime, modifiedTime }) => { const tags = [ `