generate-pwa-icons.mjs 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142
  1. import sharp from 'sharp';
  2. import { mkdir, readFile, writeFile } from 'node:fs/promises';
  3. import { join } from 'node:path';
  4. const svgPath = 'src/lib/assets/favicon.svg';
  5. const outDir = 'static';
  6. const svg = await readFile(svgPath);
  7. await mkdir(outDir, { recursive: true });
  8. await writeFile(join(outDir, 'favicon.svg'), svg);
  9. const sizes = [16, 32, 48, 72, 96, 128, 144, 152, 180, 192, 256, 384, 512];
  10. for (const size of sizes) {
  11. await sharp(svg).resize(size, size).png().toFile(join(outDir, `icon-${size}x${size}.png`));
  12. }
  13. await sharp(svg).resize(180, 180).png().toFile(join(outDir, 'apple-touch-icon.png'));
  14. await sharp(svg).resize(32, 32).png().toFile(join(outDir, 'favicon.png'));
  15. await sharp(svg).resize(150, 150).png().toFile(join(outDir, 'mstile-150x150.png'));
  16. async function maskable(size, name) {
  17. const pad = Math.round(size * 0.1);
  18. const inner = size - pad * 2;
  19. const icon = await sharp(svg).resize(inner, inner).png().toBuffer();
  20. await sharp({
  21. create: {
  22. width: size,
  23. height: size,
  24. channels: 4,
  25. background: { r: 18, g: 18, b: 18, alpha: 1 }
  26. }
  27. })
  28. .composite([{ input: icon, left: pad, top: pad }])
  29. .png()
  30. .toFile(join(outDir, name));
  31. }
  32. await maskable(192, 'maskable-icon-192x192.png');
  33. await maskable(512, 'maskable-icon-512x512.png');
  34. console.log('PWA icons written to static/');
  35. console.log('Note: favicon.ico is not regenerated here; keep the existing multi-size .ico.');