t28.dev

Astro 製のサイトで Markdown 内に書いた Mermaid のダイアグラムを描画する

2024/2/3
Tech

Astro 製のブログ (というか、ここ) で Mermaid のダイアグラムを描画したくなりました。

```mermaid
flowchart LR
  Mermaid --> SVG
```

👆って書くと

Mermaid
SVG

👆ってなりたい。

JavaScript を消したい

せっかく Astro を使っているのでビルド成果物に Mermaid の JavaScript を含めたくない。 つまりビルドタイムで Mermaid のコードブロックを SVG に変換して HTML を出力したい。

実現手段の検討

Astro の組み込み機能

公式ドキュメント内には言及なしIssue で状況を確認してみると、Mermaid のシンタックスハイライトに関する issue はあった (Markdown mermaid is not supported #4433) けれど、Mermaid のコードをもとにダイアグラムを描画をする議論はなかった。

remark/rehype のプラグイン

Astro の markdown 機能は remark (と rehype) で構築されていてプラグインもサポートしているので、プラグインで Mermaid を描画出来ないか調べてみる。

temando/remark-mermaid

remcohaszing/remark-mermaidjs

southball/unist-remark-plugins - remark-mermaid

結論: mermaid-cli を動かす plugin を自分で作る

多少気になることがあっても自分で実装しないのが一番幸せな気もするけれど、 Mermaid を実行する remark plugin を作って astro.config.ts で読み込むようにしました。TatsuyaYamamoto/t28.dev#64

  • (1) Markdown 内の Mermaid のコードブロックを見つけて
  • (2) Puppeteer の Browser インスタンスを作って
  • (3) mermaid-cli が公開している renderMermaid() で Mermaid の描画を行って
  • (4) コードブロックを SVG要素を描画する html node で上書きする
export const mermaidRemarkPlugin: RemarkPlugin = () => {
  return async (root) => {
    // (1)
    const mermaidCodeBlocks: MermaidCodeBlock[] = [];

    // 超省略

    // (2)
    const browser = await puppeteer.launch({
      headless: true,
    });

    await Promise.all(
      mermaidCodeBlocks.map(async ({ code, index, parent }, blockIndex) => {
        // (3)
        const { data: svgBuffer } = await renderMermaidCli(
          browser,
          code.value,
          "svg",
        );

        // 超省略

        // (4)
        parent.children[index] = svgNode;
      }),
    );

    // 超省略
  };
};

mindmap自分でコードを書きたい学習独自の解決方法プログラミングスキル趣味楽しいブログ記事のネタチャレンジ新しい問題への対処ライブラリのコードリーディング既存ライブラリの調査ライブラリの強み開発速度の向上コミュニティのサポートライブラリの弱み制限された柔軟性依存関係が増えるデメリットの認識時間と労力の増大バグのリスクメンテナンスの責任再発明の罠