import sharp from 'sharp'; import { readdir, rename, unlink } from 'node:fs/promises'; import { join, resolve } from 'node:path'; const iconDirectory = resolve('./static'); const iconFiles = (await readdir(iconDirectory)) .filter((file) => file.toLocaleLowerCase().endsWith('.png')) .sort((left, right) => left.localeCompare(right)); for (const file of iconFiles) { const source = join(iconDirectory, file); const temporary = join(iconDirectory, `.${file}.trimmed`); const before = await sharp(source).metadata(); try { const result = await sharp(source) .trim({ background: { r: 0, g: 0, b: 0, alpha: 0 } }) .extend({ top: 2, right: 2, bottom: 2, left: 2, background: { r: 0, g: 0, b: 0, alpha: 0 }, }) .png({ compressionLevel: 9, adaptiveFiltering: true }) .toFile(temporary); if (!result.width || !result.height || result.channels !== 4) { throw new Error(`${file} did not produce a valid transparent RGBA icon.`); } await rename(temporary, source); console.log( `${file}: ${before.width}x${before.height} -> ${result.width}x${result.height}`, ); } catch (error) { await unlink(temporary).catch(() => {}); throw error; } } console.log( `Trimmed ${iconFiles.length} static PNG icons with a 2px transparent safety margin.`, );