2024年3月24日

AstroのmarkdocでMermaidを導入する

読了時間の目安: 5分


こんにちは、asatoです。

このブログはAstro & Markdocを使っています。ブログ内でMermaidを利用したく、試行錯誤したので、最終的にどう実現したのかまとめておきます。

実際のコードはGitHubで公開しています。

GitHub - at946/at-blog

Table of Contents

用語

Astro

Astro

Astroはコンテンツ駆動のウェブサイトを作成するためのウェブフレームワークです。ブログなどに適しています。詳しくは公式サイトをご覧ください。

Astroを選ぶ理由 | Docs

Markdoc

Markdoc | A powerful, flexible, Markdown-based authoring framework

MarkdocはMarkdownベースのオーサリングフレームワークです。Markdownを自分好みに拡張できるイメージです。Markdownの記法の挙動をカスタマイズしたり、新たなタグを作成したりできます。

AstroでのMarkdocの利用方法は公式で用意されており、簡単に使い始めることができます。このブログの内容はこの手順ですでにmarkdocが導入されている前提です。

@astrojs/markdoc | Docs

Mermaid

Mermaid | Diagramming and charting tool

MermaidはダイアグラムやチャートをMarkdownのような書き心地で描くためのツールです。

やりたいこと - ブログ内でMermaidを使いたい

ブログ内で次のようにMermaidのコードを書くことでダイアグラムが描写されるようにしたい。

```mermaid
flowchart LR
Start --> Stop
```

課題 - Mermaidはクライアントで動作するのでAstroでは工夫が必要

Astroはサイトの高速化のため、SSG(Static Site Generation)が採用されています。一方、Mermaidはフロントで動作するツールなので、少し工夫が必要です。

Server Side Support · Issue #3650 · mermaid-js/mermaid · GitHub

解決方針 - Astroアイランド

Astroでは、一部のコンポーネントを動的に、またはクライアントサイドで動作させるための手段として、Astroアイランドを用意しています。

Astroアイランド | Docs

今回は、Astroアイランドを利用してReactコンポーネントでMermaidをレンダリングさせてみます。

実装

Markdocでcode fenceをカスタマイズ

今回はcode fence(```で囲まれたやつ)の言語指定でmermaidを定義したときにMermaidの図が描写されるようにしたいです。そのため、まずはcode fenceをカスタマイズする準備をします。

ディレクトリ構造は以下です(関連部分を抽出)。

Terminal window
- node_modules/
- src/
- components/
- mdoc/
- Fence.astro
- content/
- blog/
- sample.mdoc
- utils/
- mdoc/
- schema/
- fence.mdoc.ts
- mdoc.config.ts
- markdoc.config.mjs
markdoc.config.mjs
import { defineMarkdocConfig } from '@astrojs/markdoc/config'
import { config as markdocConfig } from './src/utils/mdoc/mdoc.config'
export default defineMarkdocConfig(markdocConfig)
src/utils/mdoc/mdoc.config.ts
import { fence } from './schema/fence.mdoc';
export const config = {
nodes: {
fence,
},
};
src/utils/mdoc/schema/fence.mdoc.ts
import { component } from '@astrojs/markdoc/config';
export const fence = {
render: component('./src/components/mdoc/Fence.astro'),
attributes: {
content: {
type: String,
required: true,
},
language: {
type: String,
},
},
};
src/components/mdoc/Fence.astro
---
import { Code } from 'astro-expressive-code/components';
interface Props {
content: string;
language: string;
}
const { content, language } = Astro.props as Props;
---
<Code code={content} language={language} />

code fenceのハイライトにはExpressive Codeを使っています。インストール手順などは公式サイトを参照してください。

Installing Expressive Code | Expressive Code

これでcode fenceをカスタマイズする準備が整いました。

language = mermaidの時はクライアントでMermaidを描写する

Astroアイランドを使います。UIフレームワークはReactを選択します。ReactをAstroに導入する手順は公式サイトを参照してください。

@astrojs/react | Docs

では、Mermaidをinstallします。

Terminal window
npm i mermaid

Mermaidを描写するReactコンポーネントを作成します。

src/components/mdoc/Mermaid.tsx
import mermaid, { type MermaidConfig } from 'mermaid';
import { useEffect } from 'react';
interface Props {
content: string;
}
const config: MermaidConfig = {
startOnLoad: true,
};
const Mermaid = ({ content }: Props) => {
mermaid.initialize(config);
useEffect(() => {
mermaid.contentLoaded();
}, []);
return (
<div>
<pre className='mermaid'>{content}</pre>
</div>
);
};
export default Mermaid;

Mermaidはデフォルトでclassmermaidが与えられているpreまたはdivタグ内を解析し、図を描写してくれます。useEffect内でmermaid.contentLoaded()を呼ぶことで描写をトリガーしています。

では、Fence.astroを編集してMermaidの描写を実現します。

src/components/mdoc/Fence.astro
---
import { Code } from 'astro-expressive-code/components';
import Mermaid from './Mermaid';
interface Props {
content: string;
language: string;
}
const { content, language } = Astro.props as Props;
---
<Code code={content} language={language} />
{ language === 'mermaid' ? (
<Mermaid content={content} client:only={React} />
) : (
<Code code={content} language={language} />
)}

client:only={React} を指定しクライアントでのみ描写されるようにしています。

テンプレートディレクティブの概要 | Docs

動作確認

ブログ内でmermaidのコードを書いて、正しく表示されるか確かめます。

src/content/blog/sample.mdoc
```mermaid
flowchart LR
Start --> Something --> End
```

表示が確認できました。

まとめ

このブログでは、クライアントでしか動作しないMermaidをAstro + Markdocの環境に導入する方法をまとめました。これでMermaidのいい感じなダイアグラムを手に入れました。

このブログが誰かの役に立ちますように。

気に入っていただけたら、サポートもお待ちしております!
  • 名前:asato
  • 仕事:スクラムマスター
  • 好き:家族、温泉、旅行、謎解き
  • 苦手:はじめまして、あんこ、うなぎ