import { existsSync, mkdirSync, writeFileSync } from 'node:fs' import path, { join } from 'node:path' import sharp from 'sharp' const __dirname = path.dirname(new URL(import.meta.url).pathname) // 创建圆角遮罩 function createRoundedCornersMask(size: number, cornerRadius: number) { const r = cornerRadius return ` ` } // 为图片添加圆角 async function addRoundedCorners( imageBuffer: Buffer, size: number, ): Promise { // 计算圆角半径,约为尺寸的 12% const cornerRadius = Math.round(size * 0.12) const maskSvg = createRoundedCornersMask(size, cornerRadius) const maskBuffer = Buffer.from(maskSvg) return sharp(imageBuffer) .composite([ { input: maskBuffer, blend: 'dest-in', }, ]) .png() .toBuffer() } // 生成不同尺寸的 favicon export async function generateFavicons() { const logoPath = join(__dirname, '../logo.jpg') const outputDir = join(process.cwd(), 'public') // 检查 logo 文件是否存在 if (!existsSync(logoPath)) { throw new Error('Logo file not found: logo.jpg') } if (!existsSync(outputDir)) { mkdirSync(outputDir, { recursive: true }) } const sizes = [ { size: 16, name: 'favicon-16x16.png' }, { size: 32, name: 'favicon-32x32.png' }, { size: 48, name: 'favicon-48x48.png' }, { size: 180, name: 'apple-touch-icon.png' }, { size: 192, name: 'android-chrome-192x192.png' }, { size: 512, name: 'android-chrome-512x512.png' }, ] try { // 读取原始 logo 图片 const logoBuffer = await sharp(logoPath).jpeg({ quality: 100 }).toBuffer() // 生成各种尺寸的 PNG 文件 for (const { size, name } of sizes) { const resizedBuffer = await sharp(logoBuffer) .resize(size, size, { fit: 'cover', position: 'center', }) .png({ quality: 100, compressionLevel: 6, }) .toBuffer() // 添加圆角效果 const roundedBuffer = await addRoundedCorners(resizedBuffer, size) const outputPath = join(outputDir, name) writeFileSync(outputPath, roundedBuffer) console.info(`✅ Generated favicon: ${name} (${size}x${size})`) } // 生成主 favicon.ico(使用 32x32) const faviconResizedBuffer = await sharp(logoBuffer) .resize(32, 32, { fit: 'cover', position: 'center', }) .png({ quality: 100, compressionLevel: 6, }) .toBuffer() // 为 favicon.ico 添加圆角 const faviconBuffer = await addRoundedCorners(faviconResizedBuffer, 32) const faviconPath = join(outputDir, 'favicon.ico') writeFileSync(faviconPath, faviconBuffer) console.info(`✅ Generated main favicon: favicon.ico`) // PWA manifest 由 vite-plugin-pwa 生成,这里不再生成重复的文件 console.info( `🎨 All favicons generated successfully from logo.jpg with rounded corners!`, ) } catch (error) { console.error('❌ Error generating favicons:', error) throw error } } // 如果直接运行此脚本 if (import.meta.url === `file://${process.argv[1]}`) { generateFavicons().catch(console.error) }