WordPress でブログを運用していたとき、記事中のソースコードのシンタックスハイライトは Code Syntax Block というプラグインを利用していました。このプラグインは、Prism を WordPress のブロックエディタで利用するためのラッパーのようなものです。表示できる言語も豊富で、テーマカラーにお気に入りの one dark もあったので使っていました。
エディタ側の書き味が非常に良いので、Next.js に以降したからと言ってこの書き味は捨てたくありません。そのまま利用できないものか中身を探ってみました。JavaScript ライブラリのラッパーなので、おそらく PHP で HTML を描画した後に何かを実行しているはずなので、そのまま利用できるはず、という予測です。
Next.js を導入する前の WordPress の HTML ソースを覗いてみると、Prism に関連しそうなソースコードを3個所発見することができました。
<link rel='stylesheet' id='mkaz-code-syntax-prism-css-css' href='https://wp.katsushi-ougi.com/wp-content/plugins/code-syntax-block/assets/prism-onedark.css?ver=1652679827' type='text/css' media='all' />
<script type='text/javascript' id='mkaz-code-syntax-prism-js-js-extra'>
/* <![CDATA[ */
var prism_settings = {"pluginUrl":"https:\/\/wp.katsushi-ougi.com\/wp-content\/plugins\/code-syntax-block\/"};
/* ]]> */
</script>
<script type='text/javascript' src='https://wp.katsushi-ougi.com/wp-content/plugins/code-syntax-block/assets/prism/prism.js?ver=1652679827' id='mkaz-code-syntax-prism-js-js'></script>
どうやらこれらを Next.js に貼り付けるだけで上手くいきそうです。まずは _document.tsx の Head タグに CSS を貼ります。
import Document, { Html, Head, Main, NextScript } from "next/document";
class MyDocument extends Document {
render() {
return (
<Html>
<Head>
<link
href="https://fonts.googleapis.com/css2?family=Roboto+Condensed%3Awght%40400%3B700&display=swap&ver=v1.0.0"
rel="stylesheet"
/>
<link
rel="stylesheet"
id="mkaz-code-syntax-prism-css-css"
href="https://wp.katsushi-ougi.com/wp-content/plugins/code-syntax-block/assets/prism-onedark.css"
type="text/css"
media="all"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
次に Layout コンポーネントに グローバルスクリプトを追加します。
import Script from "next/script";
const Layout: FC<LayoutProps> = ({ children }) => {
...
return (
<>
<div className="flex h-[100%] flex-col content-between">
...
<GlobalFooter />
<Script
id="mkaz-code-syntax-prism-js-js-extra"
dangerouslySetInnerHTML={{
__html: `
var prism_settings = {"pluginUrl":"https:\/\/wp.katsushi-ougi.com\/wp-content\/plugins\/code-syntax-block\/"};
`,
}}
/>
</div>
</>
);
};
export default Layout;
外部スクリプトは、Script コンポーネントを利用して、dangerouslySetInnerHTML の中に書くのがポイントですね。
prism.js もここでロードしたら? と思うと思われますが、そうしてしまうと、ページ訪問時にしかコードシンタックスが効きません!
おそらく、prism.js は js がロードされたタイミングでしか初期化を実行していないのでしょう。window オブジェクトに何かインスタンスが生えてないかな?とか調べてたのですが、もっと簡単な方法で解決しました。
ページ表示時に、prism.js をロードして、ページ離脱時に prism.js を削除しました。
該当ページのスクリプトに以下のように追記しました。
useEffect(() => {
const s = document.querySelector("#prism");
if (s) {
document.head.removeChild(s);
}
importScript("/prism.js", "prism");
}, [router.asPath]);
if (!post) return null;
importScript メソッドの中身です。
const importScript = (url: string, id: string) => {
return new Promise((resolve, reject) => {
let s = document.createElement("script");
s.id = id;
s.onload = () => {
resolve("success");
};
s.onerror = () => {
reject();
};
s.src = url;
document.head.append(s);
});
};
export { importScript };
動的にスクリプトファイルをロードする、というのは React 以前もよく使っていたので、知識が役立ちました。