// Build-time loader for the auto-generated mod catalogue. tooling/build-modlist.py // writes src/data/mods.json (gitignored, generated) from the distribution's full // client modset; it MAY NOT EXIST on a fresh checkout, so we read it at runtime // with fs (a static `import` would hard-fail the build when absent). Fall back to // the committed .example.json (an empty catalogue); if even that is unreadable, // expose an empty catalogue so the page simply renders no mods and the stat tile // keeps its default. Same pattern as loadLauncherManifest in site.ts. // @ts-ignore — node builtins (no @types/node in this Astro SSG; same reason // `process`/`import.meta.url` reads work at build time, as in site.ts). import { readFileSync } from "node:fs"; export type Mod = { id: string; name: string; version: string; authors: string; description: string; logo: string | null; }; export type ModCatalogue = { schemaVersion: number; count: number; mods: Mod[]; }; export function loadMods(): ModCatalogue { const empty: ModCatalogue = { schemaVersion: 1, count: 0, mods: [] }; // Resolve relative to this module first (works when import.meta.url points at // the source dir), then fall back to a cwd-relative path (Astro runs the build // from the landing root, so src/data/* resolves there even when the module has // been bundled to a different location). Prefer the generated mods.json, then // the committed empty mods.example.json. // @ts-ignore — `process` is untyped here (no @types/node), same as site.ts. const cwd: string = typeof process !== "undefined" ? process.cwd() : "."; const candidates: Array = []; for (const name of ["mods.json", "mods.example.json"]) { try { candidates.push(new URL(name, import.meta.url)); } catch { // import.meta.url unavailable — skip the module-relative candidate. } candidates.push(`${cwd}/src/data/${name}`); } for (const c of candidates) { try { const parsed = JSON.parse(readFileSync(c, "utf8")) as ModCatalogue; if (parsed && Array.isArray(parsed.mods)) return parsed; } catch { // try the next candidate (file missing/unreadable → fall back) } } return empty; } export const MODS = loadMods();