この記事はNext.js版の内容です。現在はAstroで構築し直したため、情報が古い可能性があります。
当時のリポジトリは こちら にあるので参考にしてください。
先日、v2.0.0をリリースしたばかりであるが、間髪を入れずv2.1.0を公開する運びとなった。
ここまでスムーズに実装が進んだのは、ひとえに開発環境を整えたおかげである。
目に見えない部分であるがゆえに軽視されがちな開発環境。
しかし、その整備がもたらす効果と、ストレスなく作業が進むこの感覚を、コードに触れない人々にどうわかりやすく伝えるかが今後の課題だ。
さて、今回のリリースではタグページとタグクラウドを作成した。
これらはすべてSSG(静的サイト生成)でFrozenしてあるため、表示速度と安定性に優れる点が特徴である。
以下に、いくつかのポイントをまとめておく。
このブログの記事は Contentlayer(正確にはフォーク版の Contentlayer2)を使い、MDX形式で書かれている。
MDXは柔軟性が高く、拡張することでFrontmatterと呼ばれるメタデータを定義できるため、そこにtagsを仕込んでいる。
以下はその例。
title: "v2.1.0 リリースノート: タグクラウドの追加"
タグ一覧用のオブジェクト は別に定義してあるため、そのデータを使ってタグクラウドを生成していく。
ここで少し背景の話をしておこう。
Contentlayerでは、すべての記事データを1つのオブジェクトにまとめてくれる便利機能がある(たとえばpostであればallPostsというオブジェクトに)。
だが、その内部にはトランスパイルされたMDXデータが詰め込まれており、驚くほど重い。
これをそのまま利用すれば、ビルド時間が増大するし、Client Componentsで扱えば、Lighthouseのスコアは悪化する。
ゆえに、記事データを扱う際には細心の注意が必要だ。
話を戻すと、allPostsを生成する前にはバリデーションを行い、間違ったタグが紛れ込まないよう対策している(ソースはこちら)。
これにより、正確なデータを元にタグページを生成することが可能になる。
そして、記事追加時に以下のコード(抜粋)をnpmスクリプトで実行し、タグクラウド用のデータを生成してキャッシュしておく。
const generateTagCloudItems = async () => {
const tagCloudItems = (await Promise.all(
flatTags.map(async ({ title, slug, emoji }) => ({
emojiSvg: await generateEmojiSvg({ emoji, isColored: true }),
)) satisfies TagCloudItem[];
FILE_PATHS.TAG_COULD_ITEMS,
前述のgenerateEmojiSvg関数は、今回のタグクラウド生成の中核となる処理である。
絵文字をSVGに変換するという見た目に関わる大事な部分を担っているが、この処理が意外と重い。
そのため、頻繁に実行するのは避け、生成されたデータをキャッシュすることで効率化を図っている。
絵文字をSVG化する際は Satori というライブラリを使用している。
以前の記事 ではSatoriを使って同様に絵文字(Noto Emoji)をSVGに変換していたが、今回使用するのはカラーフォントなので、前回のように簡単にはいかなかった。
このプロセスの肝はloadAdditionalAsset関数である。
この関数を使い、必要なデータをAPI経由で取得し、それを元に絵文字をSVG化している。
具体的な実装は以下のコードを参照いただきたい。
今回はMicrosoftの Fluent Emoji という絵文字に変換しているが、他にも以下の絵文字に変換することも可能。
1つ注意しなければならないのは、絵文字のSVG化を支えるAPIのURLが変更された場合である。
このような事態が発生すると、当然ながら絵文字が正しく表示されなくなる。
その際は手動で修正する必要があるが、問題に気づくタイミングはおそらく「なんか絵文字が表示されない…」という状況になってからだろう。
これを未然に防ぐため、特定の部分にVisual Regression Testingを導入するのも一つの手かもしれない。
今まで面倒で避けていたが、実装してみると意外と便利なものだ。
これまで記事を書くときにタグを深く考えていなかったが、これを機にちゃんと分類することにした。
制約は創作の幅を広げると言うし、タグを活用すれば記事も書きやすくなりそうだ。