// Build iammiti99: render src/template.html with values from config.yaml, // decorate `repo:`-flagged links with live GitHub stars + last-commit age. // Output: _site/index.html plus copied static assets from src/. // // Runs locally and in GitHub Actions. Uses Node 20+ built-in fetch. import { readFile, writeFile, mkdir, cp } from "node:fs/promises"; import { existsSync } from "node:fs"; import yaml from "js-yaml"; const SITE_DIR = "_site"; const TEMPLATE_PATH = "src/template.html"; const CONFIG_PATH = "config.yaml"; const TOKEN = process.env.GITHUB_TOKEN; const config = yaml.load(await readFile(CONFIG_PATH, "utf8")); const template = await readFile(TEMPLATE_PATH, "utf8"); if (!config?.site || !config?.profile) { throw new Error("config.yaml: `site` and `profile` are required."); } // --- GitHub data fetcher --------------------------------------------------- async function fetchRepo(slug) { const headers = { "User-Agent": "iammiti99-build", Accept: "application/vnd.github+json" }; if (TOKEN) headers.Authorization = `Bearer ${TOKEN}`; try { const r = await fetch(`https://api.github.com/repos/${slug}`, { headers }); if (!r.ok) { console.warn(`[warn] GitHub API ${slug} → ${r.status}`); return null; } const j = await r.json(); return { stars: j.stargazers_count, pushed: j.pushed_at }; } catch (e) { console.warn(`[warn] GitHub API ${slug} threw: ${e.message}`); return null; } } const repoSlugs = (config.links ?? []).filter((l) => l.repo).map((l) => l.repo); const repoData = Object.fromEntries( await Promise.all(repoSlugs.map(async (s) => [s, await fetchRepo(s)])), ); // --- Helpers --------------------------------------------------------------- const daysSince = (iso) => Math.floor((Date.now() - new Date(iso).getTime()) / 86_400_000); // Minimal HTML escape for config-derived text. Single-user content but cheap insurance. const esc = (s) => String(s ?? "") .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); // --- Renderers ------------------------------------------------------------- function renderLink(l) { const data = l.repo ? repoData[l.repo] : null; const meta = data ? `` : ""; const target = esc(l.target ?? "_self"); const rel = target === "_blank" ? ' rel="noopener"' : ""; return `