2026-05-30 02:00:08 +00:00
|
|
|
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
|
|
|
import path from "node:path";
|
|
|
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
|
|
|
|
|
|
const sourceDir = path.join("src", "styles");
|
2026-05-31 11:26:11 +00:00
|
|
|
const orderPath = path.join(sourceDir, "order.json");
|
2026-05-30 02:00:08 +00:00
|
|
|
const outputPath = "styles.css";
|
|
|
|
|
|
|
|
|
|
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
2026-06-19 23:18:19 +00:00
|
|
|
if (process.argv.length > 2) {
|
|
|
|
|
console.error("Usage: node scripts/build-styles.mjs");
|
|
|
|
|
process.exit(1);
|
2026-05-30 02:00:08 +00:00
|
|
|
}
|
2026-06-19 23:18:19 +00:00
|
|
|
await buildStyles();
|
2026-05-30 02:00:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function buildStyles() {
|
2026-06-19 23:18:19 +00:00
|
|
|
await checkStyleOrder();
|
2026-05-30 02:00:08 +00:00
|
|
|
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
|
|
|
await writeFile(outputPath, await renderStyles());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function renderStyles() {
|
|
|
|
|
const parts = [];
|
|
|
|
|
|
2026-05-31 11:26:11 +00:00
|
|
|
const sourceFiles = await readStyleOrder();
|
2026-05-30 02:00:08 +00:00
|
|
|
for (const file of sourceFiles) {
|
|
|
|
|
const content = await readFile(path.join(sourceDir, file), "utf8");
|
|
|
|
|
parts.push(content.trimEnd());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return `${parts.join("\n\n")}\n`;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-31 11:26:11 +00:00
|
|
|
async function readStyleOrder() {
|
|
|
|
|
const value = JSON.parse(await readFile(orderPath, "utf8"));
|
2026-05-30 02:00:08 +00:00
|
|
|
if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) {
|
2026-05-31 11:26:11 +00:00
|
|
|
throw new Error(`${orderPath} must be a JSON array of CSS file names.`);
|
2026-05-30 02:00:08 +00:00
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-31 11:26:11 +00:00
|
|
|
async function checkStyleOrder() {
|
|
|
|
|
const sourceFiles = await readStyleOrder();
|
2026-05-30 02:00:08 +00:00
|
|
|
const listed = new Set(sourceFiles);
|
|
|
|
|
const duplicates = sourceFiles.filter((file, index) => sourceFiles.indexOf(file) !== index);
|
|
|
|
|
const actual = (await readdir(sourceDir)).filter((file) => file.endsWith(".css")).sort((left, right) => left.localeCompare(right));
|
|
|
|
|
|
|
|
|
|
const missing = sourceFiles.filter((file) => !actual.includes(file));
|
|
|
|
|
const unlisted = actual.filter((file) => !listed.has(file));
|
|
|
|
|
|
|
|
|
|
if (duplicates.length === 0 && missing.length === 0 && unlisted.length === 0) return;
|
|
|
|
|
|
2026-05-31 11:26:11 +00:00
|
|
|
if (duplicates.length > 0) console.error(`Duplicate entries in ${orderPath}: ${[...new Set(duplicates)].join(", ")}`);
|
2026-05-30 02:00:08 +00:00
|
|
|
if (missing.length > 0) console.error(`Listed CSS files missing from ${sourceDir}: ${missing.join(", ")}`);
|
2026-05-31 11:26:11 +00:00
|
|
|
if (unlisted.length > 0) console.error(`CSS files missing from ${orderPath}: ${unlisted.join(", ")}`);
|
2026-05-30 02:00:08 +00:00
|
|
|
process.exit(1);
|
|
|
|
|
}
|