滝行記録

滝行記録アップデート情報(2021年6月)

What

滝行記録のアップデート情報です。

react-markdownを6.0にアップデート

滝行記録のmarkdown記事ファイルをreactコンポーネントとしてレンダリングするreact-markdownが6.0にメジャーバージョンアップし、プラグイン設定方法も変更されたので、バージョン6.0に追随する更新を行いました。

現在コードハイライトに使用しているreact-syntax-highlighterをreact-markdownに組み込む部分の記述方法が変更されたので、ここここを参考(というかほぼそのまま)に修正します。

で、そのまま修正するとtypeerrorが出るので、この辺を参考に型を追加してあげます。

// バージョンアップ前 import ReactMarkdown from "react-markdown" import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' import { vscDarkPlus } from "react-syntax-highlighter/dist/cjs/styles/prism" // (略) const renderers = { code: ({language, value}) => { return <SyntaxHighlighter style={vscDarkPlus} language={language} children={value} /> } } // (略) <ReactMarkdown className="post-body" renderers={renderers}> {params.content} </ReactMarkdown>
// バージョンアップ後 import ReactMarkdown from "react-markdown" import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' import { vscDarkPlus } from "react-syntax-highlighter/dist/cjs/styles/prism" // (略) type CodeProps = Parameters<CodeComponent>[0] const components = { code({node, className, ...props}: CodeProps) { const match = /language-(\w+)/.exec(className || '') return match ? <SyntaxHighlighter style={vscDarkPlus} language={match[1]} PreTag="div" {...props} /> : <code className={className} {...props} /> } } // (略) <ReactMarkdown components={components} className="post-body" > {params.content} </ReactMarkdown>

修正後に問題なくコードハイライトされていることを確認し、完了です。

ダイアグラム描画の導入(6/12追記)

現在このブログにはmarkdownのコードハイライト機能と画像表示機能がありますが、試験的にダイアグラム描画機能を導入することにしました。具体的にはZenn.devでも採用実績のあるmermaidを使ってみることにします。

「使ってみることにします」とか言ってはみたものの、かなり回り道をしました。react-markdownにプラグインとして組み込むのが一番楽そうなremark-mermaidは最終更新が2018年1月とかなり放置されていたので回避。次にmermaid記法でダイアグラムをreactコンポーネントとして出力できるreact-mermaidを見てみると最終更新が6年前。おそらく後継であろうreact-mermaid2も更新が17ヶ月前で、プルリクエストもイシューも全部放置されているので今後のアップデートは望めなさそうです。グエー。

仕方がないので自分で書くことにします。Zenn.devのzenn-markdown-htmlでmermaidの組み込み方を確認しようとするも、使用しているmarkdownパーサがreact-markdownではなくmarkdown-itだったので断念。

結局はここを参考にし、前段で書いたcomponents部分に手を入れて組み込むことにしました。

// document.tsx <Head> // (略) <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script> // (略) </Head>
// [slug].tsx const components = { code({node, className, children, ...props}: CodeProps) { if (className === 'language-mermaid'){ return <Mermaid graphDefinition={node.children[0].value}/> } else { const match = /language-(\w+)/.exec(className || '') return match ? <SyntaxHighlighter style={vscDarkPlus} language={match[1]} PreTag="div" children={children} {...props} /> : <code className={className} {...props} /> } } }
// Mermaid.tsx import { useState, useEffect } from 'react'; let currentId = 0; const uuid = () => `mermaid-${(currentId++).toString()}`; declare global { interface Window { mermaid: { mermaidAPI: { render( id: string, txt: string, cb?: ( svgCode: string, bindFunctions: (element: Element) => void ) => void, container?: Element ): string; } } } } const Mermaid = ({ graphDefinition }) => { const [html, setHtml] = useState(''); useEffect(() => { if (graphDefinition) { try { window.mermaid.mermaidAPI.render(uuid(), graphDefinition, svgCode => setHtml(svgCode) ); } catch (e) { setHtml(''); console.error(e); } } }, [graphDefinition]); return graphDefinition ? <div dangerouslySetInnerHTML={{__html: html}} /> : null; } export default Mermaid;

これで記事markdownをパースする際、言語名をmermaidと指定したコードブロックはclass="mermaid"となりhtmlヘッダーで読み込んだmermaidによってダイアグラムとして描画され、それ以外のコードブロックはSyntaxHighlighterによってコードハイライトされます。

なおdangerouslySetInnerHTMLを使用していますが、外部から値を受け取っていないため、危険はないと判断して使用しています。

mermaidで描画されたダイアグラムのサンプルを以下に載せておきます。

next-remote-watch導入(6/15追記)

Next.jsはnext devコマンドで開発サーバが立ち上がり、開発サーバにはソース変更を監視してプレビューをホットリロードしてくれる機能があります。

しかしその監視対象はJS/TSのソースに限られており、当ブログのようにリポジトリにmarkdownファイルを含めてgetStaticProps()でそれを取得するような仕組みであった場合は、markdownファイルの変更は監視されていないために手動でリロードをしないとプレビューが更新されません。ダイアグラムを導入したのもあって、できればプレビューは勝手に更新されてほしいものです。

そこで開発サーバの監視対象を増やせるnext-remote-watchを導入することにしました。

インストールして./content/postsを指定するだけなのであっさり終了。注意点としてnext-remote-watchのREADMEにも書いてありますが、Next.jsの非公開機能を使っているのでNext.jsのバージョンアップ時に予告なく使用できなくなる可能性があります。そのためひとまずpackage.jsonのNext.jsバージョンを固定しておき、バージョンアップ時は動作確認をする運用にしておきます。