2022/12/02
はじめに
Next.js を用いて本ブログを構築している。
Prismを用いてコードブロックのシンタックスハイライトをしているのだが、Mermaid 記法をチェックしていてもそれが反映されないようであった。
事象の確認
生成された HTML を見ると、Mermaid コードブロックも他のコードブロック同様、token ...
クラスが割り当てられていた。どうやらシンタックスハイライトは、Mermaid 記法をグラフ表示するのではなく Mermaid 記法のシンタックスをハイライトしてくれるもののようだ。
対策の検討
ここで記した流れで MDX がシリアライズされる内、コードブロックへの Prism による変換は rehype で行っているため、remark プラグインでmermaid.js
の求める形に MDX を変換してしまえばよい。
例えば、以下のコードブロックを
```mermaid
flowchart LR
MDX/Markdown ==> Remark ==> Rehype
```
以下のように変換する。
<div class="mermaid">flowchart LR MDX/Markdown ==> Remark ==> Rehype</div>
MDX→HTML へのフローは以下のようになる。
flowchart LR
MDX ==> mermaid記法をdivに書き換え ==> ... ==> HTML
パッケージ開発
ここにパッケージを作った。
リポジトリはこちら
パッケージ利用
pages/post/[slug].tsx
import * as fs from 'fs';
import * as path from 'path';
import mermaid from 'mermaid';
import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote';
import rehypePrism from '@mapbox/rehype-prism';
import matter from 'gray-matter';
import { serialize } from 'next-mdx-remote/serialize';
import remarkMermaid from '@ysuzuki19/remark-mermaid';
export const getStaticProps: GetStaticProps = async ({ params: { slug } }) => {
const filepath = path.join('..', 'posts', `${slug}.mdx`);
const file = fs.readFileSync(filepath);
const { content } = matter(file);
const { compiledSource } = await serialize(content, {
mdxOptions: {
remarkPlugins: [remarkMermaid],
rehypePlugins: [rehypePrism],
},
});
const source = { compiledSource };
return {
props: {
source,
},
};
};
interface PostPageProps {
source: MDXRemoteSerializeResult<Record<string, unknown>>;
}
const PostPage: NextPage<PostPageProps> = ({ source }) => {
React.useEffect(() => {
mermaid.init()
}, [])
return <MDXRemote {...source} />;
};
注意点
getStaticProps()
で SVG を生成しているわけではないので、mermaid.init()
が必要。
おわりに
mermaid 記法を導入したことによって、ブログに載せるアーキテクチャやフローチャートを画像を使うことなくコードで表現できるようになったのでかなり便利!
パッケージは、SSG 前提のためmermaid.init()
をフロントで書くようにしていたが、将来的にはgetStaticProps()
内で SVG を生成するようにしたい。