mermaid.jsのグラフをビルド時に生成する

前回、このブログにmermaid.jsで描いたグラフを埋め込めるようにしたんだけど、リロードすると表示されたりされなかったり不安定な挙動だった。

pie title 一日
  "睡眠" : 8
  "労働" : 9
  "通勤" : 1.5
  "朝の支度" : 1.5
  "その他" : 4

そこで、クライアントサイドでmermaid.jsを実行するのではなく、gatsbyがページを生成するタイミングでpuppeteer上でmermaid.jsを実行し、レンダリングされたSVGをページに埋め込むようにした。これで、リロードするたびに表示されたりされなかったりすることはない。

実装

雰囲気だとだいたいこんな感じの実装をしている。

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(`file://${path.resolve(__dirname, "index.html")}`);
await page.addScriptTag({ path: require.resolve("mermaid/dist/mermaid.min.js") });

const result = await page.evaluate(() => {
  const text = "graph LR\n  a --> b";
  return new Promise((resolve, reject) => {
    try {
      mermaid.initialize({ startOnLoad: true });
      mermaid.mermaidAPI.render("diagram", text, svg => {
        resolve(svg)
      });
    } catch (e) {
      reject(e);
    }
  })
});

await browser.close();
  • レンダリング用のHTMLを用意して、そこにmermaid.jsを埋め込む。
  • 引数に渡した関数がPromiseを返すときevaluate()は解決を待ってくれるので、mermaid.jsのrenderが終わったタイミングでresolveを呼び出すようにしている。
  • evaluate()は解決した結果を返すので、ここではレンダリング結果を受け取ることができている。