import { mkdir, readFile, writeFile } from "node:fs/promises"; import sharp from "sharp"; import { pwaConfig } from "../src/lib/pwa/config"; import { brandAssets } from "../src/lib/brand-assets"; // Rasterize the existing Figma favicon paths unchanged; maskable icons need a // separate opaque canvas with the entire mark inside the central safe circle. const source = await readFile(new URL("../public" + brandAssets.iconSvg, import.meta.url)); const outputs = [ { file: pwaConfig.icons.small, size: 192, mark: 192 }, { file: pwaConfig.icons.large, size: 512, mark: 512 }, { file: pwaConfig.icons.maskable, size: 512, mark: 288 }, { file: pwaConfig.icons.apple, size: 180, mark: 144 }, ]; const directory = new URL("../public/static/pwa/", import.meta.url); await mkdir(directory, { recursive: true }); for (const { file, size, mark } of outputs) { const image = await sharp(source, { density: 384 }).resize(mark, mark).png().toBuffer(); const bytes = await sharp({ create: { width: size, height: size, channels: 4, background: pwaConfig.themeColor, } }).composite([{ input: image, gravity: "centre" }]).png().toBuffer(); const path = new URL("../public" + file, import.meta.url); if (process.argv.includes("--check")) { const existing = await readFile(path); if (!existing.equals(bytes)) throw new Error(`${file} is stale. Run npm run assets:pwa.`); } else { await writeFile(path, bytes); } } console.log("PWA icons match the existing Figma favicon.");