0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

1人何かAdvent Calendar 2024

Day 22

Next.js (SSG) + MicroCMS のコードブロックにシンタックスハイライトをつける

Last updated at Posted at 2024-12-22

はじめに

現在ブログ投稿サイトを Next.js の SSG で作成しています。コンテンツは Micro CMS から取得しています。

この時にコードブロックにシンタックスハイライトをつける方法を残しておきたいと思います。

今回使うもの

  • Next.js (14.2.14)
  • cheerio (^1.0.0)
  • highlight.js (^11.11.0)

cherrio とは

HTMLパーサーです。

パーサーとはパースをするもののことで、パースは英語で parse で解析するという意味です。

コンピュータの世界でも同じ意味であり、このHTMLパーサーは HTML を解析して、プログラムで扱いやすいデータに変換してくれます。

以下のような特徴があるみたいです。

  • jQuery をベースに作られている
  • 解析が速い
  • 柔軟性がある(ブラウザ側でもサーバー側でも動く)

https://github.com/cheeriojs/cheerio?tab=readme-ov-file#features

❤ Proven syntax: Cheerio implements a subset of core jQuery. Cheerio removes all the DOM inconsistencies and browser cruft from the jQuery library, revealing its truly gorgeous API.

ϟ Blazingly fast: Cheerio works with a very simple, consistent DOM model. As a result parsing, manipulating, and rendering are incredibly efficient.

❁ Incredibly flexible: Cheerio wraps around parse5 for parsing HTML and can optionally use the forgiving htmlparser2. Cheerio can parse nearly any HTML or XML document. Cheerio works in both browser and server environments.

highlight.js とは

シンタックスハイライトしてくれる JavaScript ライブラリです。

シンタックスハイライトとというのはソースコード等が見やすくなるように意味別で色をつけることを意味します。

インストール

以下コマンドでインストールします

npm install cheerio highlight.js

HTML のロード

cheerio は前述した通り、ブラウザでもサーバー側でも動作する設計になっているので jQuery とは違って、HTML を明示的に読み込んであげる必要があります。

.....
import * as cheerio from 'cheerio';

export default async function StaticArticlePage({
  params: { articleId },
}: {
  params: { articleId: string };
}) {
  const article = await getArticle(articleId);
  const $ = cheerio.load(article.body); // ここで CMS から帰ってきた article.body (HTML) をロードする
  console.log('HTMLです', $.html()); // これで該当ページの HTML が取れていれば OK

  
  return (
    ........
  );
}

要素の抽出とハイライト

コードブロックは以下のような HTML となっています。

<pre>
	<code>
		......
	<code/>
</pre>

この pre内の code を抽出して、highlight.js を用いてハイライトします。

https://highlightjs.org/demo から各 theme がどのようなハイライトの仕方をするのか確認することができます。

import hljs from 'highlight.js';
import 'highlight.js/styles/vs2015.css'; // 使いたい theme を選んで指定します。

$('pre code').each((_, elem) => { // 各 pre code に対して以下コールバックを実行
	const result = hljs.highlightAuto($(elem).text());
	$(elem).html(result.value);
	$(elem).addClass('hljs');
})

これで、ハイライト自体は当たるようになります。

しかし、これでは highlight.js 側でどのような言語で書かれたコードなのかを推論して、ハイライトが当たることになります。

そのため、誤推論が発生し、意図したハイライトの当たり方にならない可能性があります。(実際に私もやってみたところ、一貫性のないハイライトの当たり方になっていました。)

以下のようにコードを変えてみます。

  $('pre code').each((_, elem) => {
    const className = $(elem).attr('class'); // pre 内の code タグの class 属性を取得する
    console.log('className:', className); // className: language-typescript という値が取れます
    const language = className?.replace('language-', ''); // language- を空文字にして言語だけを取り除く

    let result;
    if (language) {
      try {
        result = hljs.highlight($(elem).text(), { language });
      } catch (error) {
        result = hljs.highlightAuto($(elem).text());
      }
    } else {
      result = hljs.highlightAuto($(elem).text());
    }
    $(elem).html(result.value);
    $(elem).addClass('hljs');
  });

  article.body = $.html();

CMS 内でコードブロックに言語を指定することができるので、そこで指定してあげると code タグの classlanguage-{言語名} が付与されます。

この {言語名} を用いて、どの言語で書かれているかを highlight.js に教えてあげることで正確にハイライトしてくれるようになります。

catch ブロックや else ブロックはうまくいかなかったときに highlightAuto で適当な色をつけてあげるために入れています。

まとめ

言語の指定をしてあげないと一貫性のないハイライトの当たり方になってしまうところから抜け出すのにかなり時間がかかってしまいました。

同じような状態になってしまった人に少しでも役に立てば嬉しいです。

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?