Gatsby + microCMS なブログでシンタックスハイライトを行う


前回記事にしたサイトですが、あれから色々手直しをしています。

手始めに、microCMS のリッチエディタで作成したコンテンツをブログページで表示すると、コードブロックがそのままベタ書きになっていて見にくかったので、シンタックスハイライトをするようにしました1

参考にした方法

  • microCMS の公式ブログで記事になっていた方法を参考にしました:
    • こちらの記事では、 Nuxt.js の実装例が書かれていますので、 Gatsby に読み替えて実装します。
    • Nuxt.js は SSR であるため、高速化のため、サーバーサイドで処理することを想定して書かれていますが、 Gatsby は SSG なので、そのあたりは気にせずやります。

ほぼ結論

以下のような方法でブログ記事のコード部分にシンタックスハイライトを行います:

  • cheerio を用いて、該当のソースコード部分を抜き出す
  • 抜き出した部分にクラス hljs を付与し、 highlight.js が適用されるようにする

実装方法

microCMS で作成した記事の表示方法とコード箇所

  • microCMS のリッチエディタで作成された記事の内容は以下の GraphQL クエリで取り出せます:
query {
  allMicrocmsPost {
    nodes {
      content
    }
  }
}
  • GraphQL のクエリ結果は以下のような形です:
{
  "allMicrocmsPost": {
    "nodes": [
      {
        "content":"...<pre><code>$ firebase init\n ...</code></pre>"
      }
    ]
  }
}
  • クエリで取り出した content の内容を React の dangerouslySetInnerHTML に渡して表示しています。
return (
  <div dangerouslySetInnerHTML={{ __html: content }} />
)
  • content の中で、コードブロックは以下のような HTML で入っています:
<pre>
  <code>
    コードの中身
  </code>
</pre>
  • つまり、pre 要素内の code 要素を抽出してスタイルを適用すれば良いことになります。

要素を取り出してシンタックスハイライトを適用するための実装

  • 上記の microCMS 公式ブログの記事で紹介されている記事が以下のとおりです:
    • cheeriohighlight.js のライブラリを読み込みます
    • リッチエディタ部分から pre code な要素を取り出します
    • 取り出した部分が highlight.js の処理対象となるように、クラス hljs を付与します
    • 最終的には、クラスを付与して書き換えた記事部分を dangerouslySetInnerHTML に渡すことになります
import cheerio from 'cheerio';
import hljs from 'highlight.js'

// 略

const $ = cheerio.load(data.body);    // data.bodyはmicroCMSから返されるリッチエディタ部分
$('pre code').each((_, elm) => {
  const result = hljs.highlightAuto($(elm).text());
  $(elm).html(result.value);
  $(elm).addClass('hljs');
});

console.log($.html());    // ハイライト済みのHTML

Gatsby での実装方法

  • ↑を参考に進めます。
  • まずは、必要なライブラリをインストールします
$ yarn add cheerio
$ yarn add highlight.js
  • ブログ記事部分のテンプレートを以下のように書き換えます(シンタックスハイライトに関係ない箇所は省略しています):
import React from "react"
import { graphql, useStaticQuery } from "gatsby"
import cheerio from "cheerio"
import hljs from "highlight.js"
import "highlight.js/styles/solarized-dark.css"

const PostContent: React.FC= () => {
  const data = useStaticQuery(
    graphql`
      query {
        allMicrocmsPost(filter: {title: {eq: "hoge"}}) {
          nodes {
            content
          }
        }
      }
    `
  )

  const contentBody = cheerio.load(data.allMicrocmsPost.nodes.content)
  contentBody("pre code").each((_, elm) => {
    const result = hljs.highlightAuto(contentBody(elm).text())
    contentBody(elm).html(result.value)
    contentBody(elm).addClass("hljs")
  })

  return (
    <article>
      <div dangerouslySetInnerHTML={{ __html: contentBody.html() }} />
    </article>
  )
}

export default PostContent
  • これでビルドすると、コードブロック部分にシンタックスハイライトが行われています。

おまけ:その他の方法

  • microCMS のブログ記事のコンテンツ部分のデータ形式気を工夫することで、コードブロックに特定のクラス名を付与して、シンタックスハイライトを行う、という方法もあります。

  • たぶん、この方が自由度があって良い気がしますが、リッチエディタだけで済ませようと思うとこの記事で紹介した方法になるのではないかと思います。

  1. Markdown で書いた記事の部分は gatsby-remark-prismjs を利用すれば簡単に適用できたのですが、 microCMS で作成したものを表示している箇所ではこれは適用されませんでした。