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());` と変えるだけでいい。


便利。・・・便利か? 私はほしかったが需要があるかはちょっと不明


この記事が気に入ったらサポートをしてみませんか?