HonoXでsatoriを使ってOGイメージもSSGする
- meta
- framework
このサイトをHonoXに書き換えたのですが、そういえばOGイメージがないことに気づきました。
どうやって画像を生成するか
今回はOGイメージもSSGしてしまうことにしました。記事数が増えてきて時間がかかるようになってきたら、ダイナミックレンダリングするように変えるかもしれません。
いったんSSGですませるのは、HonoXからCloudflare PagesのAdvanced ModeをSSGと両立していい感じにデプロイする方法がまだいい感じではないからです。何とかしようとは思っています。
satoriとresvgを使ってHonoXでビルドしたい
OGイメージを動的に生成する道具としては @vercel/og
が有名どころです。これは内部的に satori
と Resvg
を使っています。
Open Graph (OG) Image GenerationのLimitationsの項目に記載があるように、Node.jsや他のランタイムで動作させたい場合はsatoriを直接使うことが勧められています。
素朴に導入してみると盛大にエラーが出ますので先行事例をあたると、HonoXと同様にViteに乗っているAstroの事例として、satoriを使ったAstroのOGP画像生成メモ | Marginaliaがありました。
いろいろ細かい調査を省いて、 ssr.external
に satori
@resvg/resvg-js
を追加すると、SSGのコマンドが完走できるようになりました。
satori
は単に依存にCommonJSが含まれていました。HonoXでは ssr.noExternal
がtrueなので、SSRビルドでも明示的に指定する必要があります。
@resvg/resvg-js
はネイティブモジュールがViteの事前ビルドに失敗します。上記記事に書かれているのと同じ問題でした。
バイナリをHonoのSSGヘルパーで出力すると壊れる問題があった(Hono v4.0.8で修正済み)
開発用サーバーだと画像が表示できるのですが、SSRした画像が開けない問題がありました。Resvgを通さずにSVGファイルとして出力すると問題がなかったので当初はResvgを疑ったのですが、開発用サーバーで動くことを思うと説明がつきません。
PNGファイルとして書き出していたので、PNGファイルのヘッダーを念のため確認してみると、各所に書かれている 89 50 4E 47 0D 0A 1A 0A
とは異なっていました。
雑に壊れているPNGの冒頭のバイト列で検索してみると、node.js - Unexpected buffer result when fetching PNG image - Stack Overflowのやり取りを見つけました。バイナリを誤ってUTF-8として解釈してしまっているという話のようです。
なるほど、 ef bf bd
はUTF-8のコードポイントU+FFFD
(replacement character)にあたります。UTF-8として解釈したときにうまくいかないバイト列があるときに現れます。こうなってくると、SSGの処理に問題がありそうです。
hono/ssg
のヘルパーに露骨にバイナリをUTF-8扱いして文字列化する処理が含まれていたので、honojs/hono #2275を出して修正しました。
ついでにBudouXで改行位置をいい感じにする
いい感じの折り返し制御のために、BudouX を使いました。説明は他所に譲ります。これも ssr.external
に追加する必要がありました。
実装はBudouXとSatoriを使ってタイトルが分かち書きされたOGP画像を出力する。 - return $lock;が参考になりました。
まとめ
HonoXでOGイメージをSSGするためにsatoriとresvgを使って実現しました。Viteの上でビルドするためのHonoX独特の設定が必要なのと、HonoのSSGヘルパーに問題があったので修正しました。皆さんもお試しください。