sendou.ink/scripts/check-homemade-badges.ts
Kalle e27260f88b Bye bye .png
... well mostly. Some places will still use it like PWA splash screens.
Otherwise browser support for .avif strong enough now to do this
now.
2026-03-29 16:48:47 +03:00

75 lines
2.1 KiB
TypeScript
Raw Blame History

/** biome-ignore-all lint/suspicious/noConsole: Biome v2 migration */
import fs from "node:fs";
import path from "node:path";
import { z } from "zod";
import badgesJson from "../app/features/badges/homemade.json" with {
type: "json",
};
const schema = z.record(
z.string().regex(/^[a-z0-9-_]+$/),
z.object({
displayName: z.string().min(1).max(50),
authorDiscordId: z.string().regex(/^\d{17,19}$/),
}),
);
const parsedBadges = schema.safeParse(badgesJson);
if (!parsedBadges.success) {
console.error(
"Invalid homemade.json format",
JSON.stringify(parsedBadges.error),
);
process.exit(1);
}
const badges = parsedBadges.data;
// check keys in alphabetical order
let lastKey = "";
for (const key of Object.keys(badges)) {
if (key.localeCompare(lastKey) < 0) {
console.error(`Invalid key order in homemade.json: ${lastKey} > ${key}`);
process.exit(1);
}
lastKey = key;
}
// check for duplicate displayName values and encoding issues
const displayNames = new Map<string, string>();
for (const [key, badge] of Object.entries(badges)) {
const existingKey = displayNames.get(badge.displayName);
if (existingKey) {
console.error(
`Duplicate displayName "${badge.displayName}" found in keys: ${existingKey} and ${key}`,
);
process.exit(1);
}
displayNames.set(badge.displayName, key);
// check for Unicode replacement characters (encoding issues)
if (badge.displayName.includes("\uFFFD")) {
console.error(
`Invalid encoding in displayName for badge "${key}": contains replacement character (<28>). This usually means the file was saved with incorrect encoding.`,
);
process.exit(1);
}
}
// check each key has the 3 matching files in the right location
const badgesLocation = path.join("public", "static-assets", "badges");
for (const fileName of Object.keys(badges)) {
for (const ext of ["avif", "gif"]) {
const filePath = path.join(badgesLocation, `${fileName}.${ext}`);
if (!fs.existsSync(filePath)) {
console.error(
`Missing file for badge ${fileName}: ${filePath} does not exist`,
);
process.exit(1);
}
}
}
console.info("Homemade badges check passed");