@wxt-dev/auto-icons
Version:
WXT module for automatically generating extension icons in different sizes
90 lines (87 loc) • 3.62 kB
JavaScript
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 };