Rehype プラグインで Prism.js によるシンタックスハイライトを自動適用する
コードのシンタックスハイライトというと highlight.js をよく使っていたが、同じくらい人気のライブラリとして Prism.js というモノもあるらしい。
通常、Prism.js を単体で使う時は、CSS と JS を読み込んだ上で、次のようにコーディングすれば、シンタックスハイライトが有効になる。
<pre><code class="language-css">p { color: red }</code></pre>
pre
と code
をネストして、code
要素の方に language-【言語名】
と書くだけ。
今回はこの Prism.js を Rehype プラグインとして利用できる、rehype-prism
を紹介する。
目次
rehype-prism をインストールする
rehype-prism は、HTML を出力する前に Prism.js によるシンタックスハイライトを効かせるための Rehype プラグイン。元のデータは Markdown でも、HTML でも使える。
以降の例では、Markdown から HTML に変換する際に Prism.js を適用させる例で説明する。
# Markdown を HTML へと変換するために必要な最低限の Unified・Remark・Rehype 関連パッケージ
$ npm install --save-dev unified remark-parse remark-rehype rehype-stringify
# Prism.js を適用する Rehype プラグイン
$ npm install --save-dev @mapbox/rehype-prism
サンプルコード
Markdown の方には、次のようにコードブロックを書く。
# サンプルのため、バッククォートを全角で記述しています
実際は半角のバッククォートで記述してください。
```css
p { color: red }
```
↑コードブロック。
そして次のようにパースする。
const fs = require('fs').promises;
const unified = require('unified');
const remarkParse = require('remark-parse');
const remarkRehype = require('remark-rehype');
const rehypePrism = require('@mapbox/rehype-prism');
const rehypeStringify = require('rehype-stringify');
(async () => {
const inputMarkdown = await fs.readFile('./example.md', 'utf-8');
const processor = unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypePrism, {
ignoreMissing: true // 存在しない言語名を書いていた時に無視する
})
.use(rehypeStringify);
const result = await processor.process(inputMarkdown);
const outputHtml = result.contents;
await fs.writeFile('./result.html', outputHtml, 'utf-8');
})();
すると次のような HTML が生成される。
<h1>サンプルのため、バッククォートを全角で記述しています</h1>
<p>実際は半角のバッククォートで記述してください。</p>
<pre class="language-css"><code class="language-css"><span class="token selector">p</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token color">red</span> <span class="token punctuation">}</span>
</code></pre>
<p>↑コードブロック。</p>
少々分かりにくいが、pre
および code
要素に language-css
クラスが付与され、キーワードごとに span.token
が付与されている。
この CSS クラスに応じてカラーリングするには、Prism.js 公式サイトで配布されている CSS ファイルを取得・適用する必要がある。別途 rehype-document
などを使って HTML 化する際に外部 CSS ファイルを読み込む link
要素を付け足しておくと良いだろう。
なお、自分は以下にある Monokai テーマを利用していたりする。
言語名にエイリアスを当てたい
コレは自分の癖なのだが、はてなブログでは sh
という言語名で、シェルスクリプトがシンタックスハイライトされる。しかし Prism.js には sh
という言語名の定義がなく、shell
もしくは bash
と書く必要がある。
Prism.js 本体には、言語名のエイリアスを割り当てられる機能があるのだが、@mapbox/rehype-prism
パッケージが Prism.js 本体を隠蔽しているため、割り当てられない。
現状なんとかするには、かなり荒業だが、node_modules/
配下のスクリプトを直接書き換えてしまうことで、強引にエイリアスを追加することはできる。
本稿執筆時点の最新版である、@mapbox/rehype-prism@0.5.0
を前提とする。
./node_modules/@mapbox/rehype-prism/index.js
// 前略…
const refractor = require('refractor');
module.exports = (options) => {
// …中略…
function visitor(node, index, parent) {
// …中略…
let result;
try {
parent.properties.className = (parent.properties.className || []).concat(
'language-' + lang
);
// ↑既存の行
// ココにエイリアスを追加するコードを挿入する
// 以下で、「sh」と打てば「bash」相当のシンタックスハイライトが有効になる
refractor.alias('bash', 'sh');
// ↓既存の行
result = refractor.highlight(nodeToString(node), lang);
} catch (err) {
// 以下略…
}
};
こんな感じで、@mapbox/rehype-prism
のメインファイルに
refractor.alias('bash', 'sh');
refractor.alias('【Prism.js が持つ言語名】', '【エイリアス名】');
と書く
このように行を追加すれば良い。
このぐらいなら Fork して作れそうだな…。