import sharp from 'sharp'; import { mkdir, readFile, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; const svgPath = 'src/lib/assets/favicon.svg'; const outDir = 'static'; const svg = await readFile(svgPath); await mkdir(outDir, { recursive: true }); await writeFile(join(outDir, 'favicon.svg'), svg); const sizes = [16, 32, 48, 72, 96, 128, 144, 152, 180, 192, 256, 384, 512]; for (const size of sizes) { await sharp(svg).resize(size, size).png().toFile(join(outDir, `icon-${size}x${size}.png`)); } await sharp(svg).resize(180, 180).png().toFile(join(outDir, 'apple-touch-icon.png')); await sharp(svg).resize(32, 32).png().toFile(join(outDir, 'favicon.png')); await sharp(svg).resize(150, 150).png().toFile(join(outDir, 'mstile-150x150.png')); async function maskable(size, name) { const pad = Math.round(size * 0.1); const inner = size - pad * 2; const icon = await sharp(svg).resize(inner, inner).png().toBuffer(); await sharp({ create: { width: size, height: size, channels: 4, background: { r: 18, g: 18, b: 18, alpha: 1 } } }) .composite([{ input: icon, left: pad, top: pad }]) .png() .toFile(join(outDir, name)); } await maskable(192, 'maskable-icon-192x192.png'); await maskable(512, 'maskable-icon-512x512.png'); console.log('PWA icons written to static/'); console.log('Note: favicon.ico is not regenerated here; keep the existing multi-size .ico.');