UNPKG

@wxt-dev/auto-icons

Version:

WXT module for automatically generating extension icons in different sizes

90 lines (87 loc) 3.62 kB
import 'wxt'; import { defineWxtModule } from 'wxt/modules'; import { resolve, relative } from 'node:path'; import defu from 'defu'; import sharp from 'sharp'; import { exists, ensureDir } from 'fs-extra'; const index = defineWxtModule({ name: "@wxt-dev/auto-icons", configKey: "autoIcons", async setup(wxt, options) { const parsedOptions = defu( options, { enabled: true, baseIconPath: resolve(wxt.config.srcDir, "assets/icon.png"), developmentIndicator: "grayscale", sizes: [128, 48, 32, 16] } ); if (options?.grayscaleOnDevelopment !== void 0) { wxt.logger.warn( '`[auto-icons]` "grayscaleOnDevelopment" is deprecated. Use "developmentIndicator" instead.' ); if (options?.developmentIndicator === void 0) { parsedOptions.developmentIndicator = options.grayscaleOnDevelopment ? "grayscale" : false; } } const resolvedPath = resolve(wxt.config.srcDir, parsedOptions.baseIconPath); if (!parsedOptions.enabled) return wxt.logger.warn(`\`[auto-icons]\` ${this.name} disabled`); if (!await exists(resolvedPath)) { return wxt.logger.warn( `\`[auto-icons]\` Skipping icon generation, no base icon found at ${relative(process.cwd(), resolvedPath)}` ); } wxt.hooks.hook("build:manifestGenerated", async (wxt2, manifest) => { if (manifest.icons) return wxt2.logger.warn( "`[auto-icons]` icons property found in manifest, overwriting with auto-generated icons" ); manifest.icons = Object.fromEntries( parsedOptions.sizes.map((size) => [size, `icons/${size}.png`]) ); }); wxt.hooks.hook("build:done", async (wxt2, output) => { const outputFolder = wxt2.config.outDir; for (const size of parsedOptions.sizes) { const resizedImage = sharp(resolvedPath).resize(size).png(); if (wxt2.config.mode === "development") { if (parsedOptions.developmentIndicator === "grayscale") { resizedImage.grayscale(); } else if (parsedOptions.developmentIndicator === "overlay") { const buildDevOverlay = (size2) => { const rectHeight = Math.round(size2 * 0.5); const fontSize = Math.round(size2 * 0.35); return Buffer.from(`<?xml version="1.0" encoding="UTF-8"?> <svg width="${size2}" height="${size2}" viewBox="0 0 ${size2} ${size2}" xmlns="http://www.w3.org/2000/svg"> <rect x="0" y="${size2 - rectHeight}" width="${size2}" height="${rectHeight}" fill="#ffff00" /> <text x="${size2 / 2}" y="${size2 - rectHeight / 2}" font-family="Arial, Helvetica, sans-serif" font-size="${fontSize}" font-weight="bold" fill="black" text-anchor="middle" dominant-baseline="middle">DEV</text> </svg>`); }; const overlayBuffer = await sharp(buildDevOverlay(size)).png().toBuffer(); resizedImage.composite([ { input: overlayBuffer, left: 0, top: 0 } ]); } } ensureDir(resolve(outputFolder, "icons")); await resizedImage.toFile(resolve(outputFolder, `icons/${size}.png`)); output.publicAssets.push({ type: "asset", fileName: `icons/${size}.png` }); } }); wxt.hooks.hook("prepare:publicPaths", (wxt2, paths) => { for (const size of parsedOptions.sizes) { paths.push(`icons/${size}.png`); } }); } }); export { index as default };