takahira

blog

ハイライターをshikiからhighlight.jsに切り替えた

published at

このサイトのコードハイライターにはshikiを採用していましたが、このたびhighlight.jsに切り替えました。

shikiはVS Codeの開発に携わっていたエンジニアの方が開発したコードハイライターで、優れた精度でコードをハイライトしてくれる点が気に入っていたのですが、Next.jsのSSGやISRで使用するとビルド時にこけるということが度々起きていていました。

これはshikiのコードハイライトの設定Jsonファイルの読み込み方に起因するもので、その辺の詳しい原因については過去の調査結果をまとめた以下の記事に詳しいです。

Next.jsにshikiを導入すると、プレビューモード やISRでエラーが発生する原因を調査する プレビューモード の不具合を修正

これまではビルド時にハイライターの設定ファイルがバンドル対象に含まれるように色々と小細工をしていたのですが、その辺をうまく調整してもshikiがアップデートされるたびにビルドで再びエラーが起こるということが続いていて、いい加減調整して使うことに疲れてしまったので、思い切って別のハイライターに乗り換えることにしました。

乗り換え先として選んだのはhighlight.jsです。

shikiからhighlight.jsへの乗り換えも、既にunifiedで作っていたtranspiler関数のrehype-shikirehype-highlightに代えるだけ。非常に楽です。shikiではvercelでのデプロイ時に、ハイライターのテーマの設定jsonファイルがビルド時のバンドル対象に含まれるようfs.readdirSync()で読み込んでおく、という小細工をしていましたが、highlight.jsはハイライトテーマのスタイルはcssファイルから読み込んでいるので、そういった小細工も不要です。

before

import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import rehypeShiki from '@leafac/rehype-shiki';
import { getHighlighter } from 'shiki';
import fs from 'fs';

// FIXME: Allows shiki to access production theme/language files.
const path = process.cwd();
fs.readdirSync(`${path}/node_modules/shiki/languages`);
fs.readdirSync(`${path}/node_modules/shiki/themes`);
fs.readdirSync(`${path}/node_modules/vscode-textmate`);

export const markdownToHtml = async (markdown: string) =>
  unified()
    .use(remarkParse)
    .use(remarkRehype)
    .use(rehypeShiki, {
      highlighter: await getHighlighter({
        theme: 'slack-dark',
      }),
    })
    .use(rehypeStringify)
    .processSync(markdown);

after

import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import rehypeHighlight from 'rehype-highlight';

export const markdownToHtml = async (markdown: string) =>
  unified()
    .use(remarkParse)
    .use(remarkRehype)
    .use(rehypeHighlight)
    .use(rehypeStringify)
    .processSync(markdown);

あとは、src/pages/_app.tsxに使いたいハイライトテーマのcssをimportしてやれば完了です。楽だ!

import React from 'react';
import { AppProps } from 'next/app';
import '../styles/globals.scss';
import 'highlight.js/styles/nord.css';

const App: React.FC<AppProps> = ({ Component, pageProps }) => (
  <Component {...pageProps} />
);

export default App;

コードの見た目的にもスッキリしました。ハイライトの精度もshikiとはほぼ差はない感じです。 もっと早く乗り換えておけばよかったです。

portrait

takahira

デザインと関数型プログラミングが好きです。