MDXのコードハイライト補助関数を提供するパッケージ作ってみた
react-mdx-prism-lighter とは
GatsbyJS の mdx 用コードハイライト補助関数を提供するパッケージです。
以下の画像はMDXファイルで、コードブロックを使用した例です。
画像1の1行目の highlight={1-3,12}では、ハイライトしたい行を数字で選択しています。 複数行の場合は1-3のように書くことで1行目、2行目、3行目をハイライトすることが可能です。
また、コードブロックの中でも、一行をハイライトする//highlight、複数行ハイライトの//highlight-start、//highlight-endを使用することで簡単にハイライトを導入できます。
以下のコードブロックは画像1のプレビュー画面となります。
使い方
MDXでカスタムコンポーネントを使用するには、MDXProviderを用いてコンポーネントを定義します。 GatsbyJSではwrapRootElementを用いて、ルートエレメントをプロバイダーで囲むことができます。
カスタムなコードブロックを作成し、そこでこのパッケージの関数を使うには以下の手順が必要です。
- MDXProviderを用いたコンポーネントを作成
- そのコンポーネントの中で、カスタムコードブロックを定義
- そのコンポーネントをgatsby-ssr.js、gatsby-browser.jsの両方でインポートし、wrapRootElementでエクスポート
プロジェクトのルートにGatsby-Root.jsを作成する。
import React from "react"
import { MDXProvider } from "@mdx-js/react"
import CodeBlock from "./src/components/Code" //カスタムコンポーネント
const BasicComponents = {
pre: ({ children }) => {
const child = children.props
if (child.mdxType === "code") {
return (
<CodeBlock
codeString={child.children.trim()}
language={child.className.replace("language-", "")}
title={child.title}
highlight={child.highlight}
/>
)
} else return null
},
}
const Root = ({ element }) => {
return (
<MDXProvider components={{ ...BasicComponents }}>
{element}
</MDXProvider>
)
}
export default Root
カスタムコンポーネントを作成します。 例えば以下のような実装です。(tailwindcssを使用しています。)
import React, { useState } from "react"
import Highlight, { Prism } from "prism-react-renderer"
import nightOwl from "prism-react-renderer/themes/nightOwl"
import { CopyToClipboard } from "react-copy-to-clipboard"
import {
LinesToHighlight,
LinesNumberToHighlight,
} from "react-mdx-prism-lighter"
const HighlightClassName = " highlight-line" //ハイライトの行に適用されるクラス名
const CodeBlock = ({ codeString, language, title, highlight }) => {
const [value, setValues] = useState(["Copy", false])
const CopyText = () => { //コピー機能
setValues(["Copied", true])
setTimeout(() => {
setValues(["Copy", false])
}, 5000)
}
return (
<Highlight
theme={nightOwl}
Prism={Prism}
code={codeString}
language={language}>
{({ style, tokens, getLineProps, getTokenProps }) => {
return (
<div className="mb-8">
<div className="gatsby-title">
{title && (
<div className="py-3">
<p className="text-gray-900 text-sm px-3 select-none">
{title}
</p>
</div>
)}
</div>
<div className="relative" data-language={language}>
<pre className="group rounded-md" style={style}>
<div className="px-0 pt-4 pb-4 overflow-auto">
<CopyToClipboard
text={codeString.replace(
/\/\/(highlight\s|(highlight-start)|(highlight-end))/g,
""
)}
onCopy={CopyText}>
<button
className={`mx-4 focus:outline-none focus absolute top-2.5 right-0`}
disabled={value[1]}>
<p className="rounded-lg text-xs text-white text-opacity-60 bg-coolgray-700 bg-opacity-70 py-1 px-2.5 transition-all group-hover:opacity-100 opacity-0">
{value[0]}
</p>
</button>
</CopyToClipboard>
<div className="code_font text-sm sm:text-sm text-blue-900 tracking-normal px-6 float-left block min-w-full ">
{tokens.map((line, index) => {
const lineProps = getLineProps({ line, key: index })
if (LinesToHighlight(line)) {
lineProps.className += HighlightClassName
}
if (LinesNumberToHighlight(highlight, index)) {
lineProps.className += HighlightClassName
}
return (
<div {...lineProps}>
{line.map((token, key) => {
const tokenProps = getTokenProps({
token,
key: key,
})
return <span {...tokenProps} />
})}
</div>
)
})}
</div>
</div>
</pre>
</div>
</div>
)
}}
</Highlight>
)
}
export default CodeBlock
上の実装ではreact-copy-to-clipboardでコピー機能を、ハイライトそのものはprism-react-rendererを使用しています。 ハイライトしている箇所で,react-mdx-prism-lighterが提供する関数を使用しています。
最後にgatsby-ssr.jsとgatsby-browser.jsで./Gatsby-Rootをインポートし、wrapRootElementとしてエクスポートします。
import Root from "./Gatsby-Root"
export const wrapRootElement = Root
react-mdx-prism-lighterの関数
提供される関数は2つです。
LinesNumberToHighlight
指定した行番号をハイライトしてくれます。1行から複数行対応しています。
LinesToHighlight
コード上で簡単にハイライトしたい行を指定できるものです。これも1行から複数行対応しています
元々自分が使うようとして作ったのですが、もしよかったら皆さんも使ってみてくださいね〜
↓ npm
Discussion