Astro ブログで Mermaid 図を使えるようにする
ブログ記事にアーキテクチャ図を入れたくなった。テキストのフロー図でもいいけど、Mermaid で書けると見やすい。Astro で Mermaid を使えるようにした。
やりたいこと
Markdown のコードブロックに mermaid と書くだけで図がレンダリングされる状態にしたい。
```mermaid
flowchart TD
A --> B --> C
```
試した選択肢と結果
- rehype-mermaid — ビルド時に Playwright でブラウザを起動してレンダリング。Cloudflare Pages のビルド環境に Playwright のバイナリがなく使えなかった。
pre-mermaidstrategy でもmermaid-isomorphicがplaywrightを import しようとして失敗する - astro-mermaid — Astro integration。静的ビルドで
mermaidモジュールの解決に失敗した
結局、追加パッケージなしで実現できた。
セットアップ
1. Shiki から mermaid を除外
Astro 5.5 で追加された excludeLangs を設定する。これがないとシンタックスハイライターが mermaid コードブロックを普通のコードとして処理してしまう。
// astro.config.mjs
export default defineConfig({
markdown: {
syntaxHighlight: {
type: 'shiki',
excludeLangs: ['mermaid'],
},
},
});
これで mermaid コードブロックが <pre><code class="language-mermaid"> として出力される。
2. mermaid.js をクライアントサイドで読み込み
Layout の </body> の前に追加。mermaid.js は <pre class="mermaid"> を期待するので、Shiki が出力した <code class="language-mermaid"> を変換してから初期化する。
<script is:inline type="module">
var mermaidBlocks = document.querySelectorAll('code.language-mermaid');
if (mermaidBlocks.length > 0) {
mermaidBlocks.forEach(function(code) {
var pre = code.parentElement;
pre.className = 'mermaid';
pre.textContent = code.textContent;
});
var { default: mermaid } = await import(
'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs'
);
mermaid.initialize({
startOnLoad: true,
theme: document.documentElement.dataset.theme === 'dark'
? 'dark' : 'default',
});
}
</script>
mermaid を使っていないページでは querySelectorAll が空なので CDN への読み込みは発生しない。
ハマったところ
Cloudflare Pages で rehype-mermaid が動かない
rehype-mermaid は内部で mermaid-isomorphic → playwright に依存している。pre-mermaid strategy でも import 時点で playwright パッケージを要求するため、Cloudflare Pages では使えない。結局 rehype プラグインは不要だった。
Shiki の出力形式
excludeLangs で除外しても、出力は <pre class="mermaid"> ではなく <pre><code class="language-mermaid"> になる。mermaid.js はルート要素に class="mermaid" を期待するため、クライアントサイドでの変換が必要。
結果
flowchart LR
A["Markdown"] --> B["code.language-mermaid"]
B -->|クライアントJS| C["pre.mermaid"]
C -->|mermaid.js| D["SVG 図"]
追加パッケージなし、Astro の設定1行 + Layout にスクリプト追加だけで動く。