React コンポーネントを PNG へ変換する Bun スクリプトを作った
個人的に需要があった Bun スクリプトを書いてみた
// biome-ignore lint/style/useNodejsImportProtocol: <explanation>
import { parseArgs } from "util";
import { renderLogoSVG } from "@/app/logo.svg/logo";
import sharp from "sharp";
const DEFAULT_CONFIG = {
DEFAULT_INPUT: "./public/logo.svg",
DEFAULT_OUTPUT: "./public/logo.webp",
DEFAULT_QUALITY: 80,
};
const parseArguments = () => {
const {
values: { input, output, quality, ...optionalArguments },
} = parseArgs({
args: Bun.argv,
options: {
input: {
type: "string",
short: "i",
default: DEFAULT_CONFIG.DEFAULT_INPUT,
},
output: {
type: "string",
short: "o",
default: DEFAULT_CONFIG.DEFAULT_OUTPUT,
},
quality: {
type: "string",
short: "q",
default: DEFAULT_CONFIG.DEFAULT_QUALITY.toString(),
},
debug: {
type: "boolean",
default: false,
},
},
strict: true,
allowPositionals: true,
});
if (!(input && output && quality)) {
throw new Error("No logo path provided.");
}
return {
input,
output,
quality: Number.parseInt(quality, 10),
...optionalArguments,
};
};
const confirmOverwrite = async (filePath: string): Promise<boolean> => {
console.log(`🆙 File already exists: ${filePath}`, 33); // Yellow
process.stdout.write("Are you sure you want to overwrite? (y/n): ");
for await (const line of console) {
if (line.toLowerCase().trim() === "y") {
return true;
}
return false;
}
return false;
};
const convertToWebP = async (string: string, quality: number) => {
const { data: buffer, info } = await sharp(Buffer.from(string))
.webp({ quality })
.toBuffer({ resolveWithObject: true });
return { buffer, info };
};
const main = async () => {
const { input: inputPath, output: outputPath, quality } = parseArguments();
const svgString = await renderLogoSVG();
const file = Bun.file(outputPath, { type: "text/xml" });
if ((await file.exists()) && !(await confirmOverwrite(outputPath))) {
console.log("Logo file not written.");
return;
}
const { buffer, info } = await convertToWebP(svgString, quality);
Bun.write(file, buffer);
console.log(`✅ Logo file written to ${outputPath}.`);
console.log(info);
};
main();
例えば、以下のような next/satori を使ってSVGのプロダクトロゴを生成する処理を作ると、HTML/CSSでPNGが作れるため、馴れている人にとってはこっちのほうが素早い場合もあるだろう
import satori from "satori";
export const renderLogoSVG = async () => {
const fontData = await fetch("http://localhost:8888/fonts/Inter-Black.ttf", {
cache: "force-cache",
}).then((res) => res.arrayBuffer());
const svgString = await satori(
<div
style={{
color: "hsl(0, 0%, 10%)",
fontWeight: 900,
width: "100%",
height: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
borderWidth: 1,
borderStyle: "solid",
borderColor: "hsl(0, 0%, 90%)",
}}
>
<div
style={{
fontSize: 300,
}}
>
HoN
</div>
</div>,
{
width: 1200,
height: 1200,
fonts: [
{
name: "Roboto",
data: fontData,
weight: 900,
style: "normal",
},
],
},
);
return svgString;
};
「そんな都合良く next/satori のコードなんかねえよ!」という場合は、
`const svgString = await renderLogoSVG();` の部分を
→ `const svgString = renderToString(SvgIcon());` と変えるだけでいい。
便利。・・・便利か? 私はほしかったが需要があるかはちょっと不明
この記事が気に入ったらサポートをしてみませんか?