ota2000
3 min read

Astro ブログで Mermaid 図を使えるようにする

ブログ記事にアーキテクチャ図を入れたくなった。テキストのフロー図でもいいけど、Mermaid で書けると見やすい。Astro で Mermaid を使えるようにした。

やりたいこと

Markdown のコードブロックに mermaid と書くだけで図がレンダリングされる状態にしたい。

```mermaid
flowchart TD
    A --> B --> C
```

試した選択肢と結果

  • rehype-mermaid — ビルド時に Playwright でブラウザを起動してレンダリング。Cloudflare Pages のビルド環境に Playwright のバイナリがなく使えなかった。pre-mermaid strategy でも mermaid-isomorphicplaywright を 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-isomorphicplaywright に依存している。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 にスクリプト追加だけで動く。