これは yuriacats アドベントカレンダー3日目の記事です.
remarkって何?
remarkとは高機能なMarkdownパーサーライブラリー群です。
特徴としてmdastというAST1(抽象構文木)を介していることが挙げられます。ASTを仲介させることでMarkdownとTexを混在させた形態などにも対応できる柔軟さを生み出し、それもあってかGatsbyのスターターの一つである「gatsby-starter-blog」でも採用されています。ESにのみ対応しておりますが、TypeScript対応やDeno対応などモダンな開発にも適応できるサポートがあります。いかにプラグインを活用することでできることを箇条書きにします。
- Githubの記法のMarkdown対応
- 目次の作成
- Markdownの文法チェック
- Katex(TexのJS移植版)
- その他のプラグイン
しかしながらかなり、使いこなすのが難しいライブラリーでもあると思います。公式で言っているように単にMarkdownをHTMLに変換する用途であればmicromarkなどの他のライブラリーを積極的に使っても良いかもしれません。
remarkの主要ライブラリー
ざっくりと入力と出力のからプラグインを割り出そうとすると以下のようになるかと思います。
出力\入力 | HTML | Markdown | MDAST |
---|---|---|---|
HTML | \ | micromark2 | rehype-stringify |
Markdown | micromark2 | \ | remarkStringify |
MDAST | rehype-parse | remak-parse | \ |
- HTMLのサポートをするため(rehype系プラグインを使うため)にremark-rehypeがある
- ASTを扱う基幹ライブラリーとしてunifiedがある。
実装編1
(参考リポジトリー:https://github.com/yuriacats/remark-howto )
- 目標 :テキストエリアに書かれた文字をそのままHTML要素に変換してHTMLを横に生成する。
yarn install remark unified remark-parse remark-rehype rehype-stringify
- Reactのチュートリアル等を参考にしながら、文字をそのまま返すような形にしてみよう。そうすると以下のコードになる。以下のコードをいじって今回はMarkdownをそのまま表示することにする。
import React, { useEffect, useState } from "react";
function App() {
const [testText, setTestText] = useState("")
const [resultText, setResultText] = useState("")
return (
<div>
<div className="App">
<input
value={testText}
onChange={(event) => setTestText(event.target.)}
/>
<p>{testText}</p>
</div>
</div>
);
}
export default App;
2. 今回は、Markdown→mdast→Stringに変換していきたいと思いますので使用するライブラリーである以下をimportします。大体途中まで書くとVSCodeが補完してくれるので、それでうまく書いていきましょう。
- unified
- remark-parse
- remark-rehype
- rehype-stringify
import React, { useEffect, useState, Fragment } from "react";
import { unified } from "unified";
import remarkParse from "remark-parse/lib";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify/lib";
function App() {
const [testText, setTestText] = useState("")
const [resultText, setResultText] = useState(Fragment)
return (
<div>
<p>HTMLを入力すると下にMarkdown変換したものを表示するよ!</p>
<textarea
rows={10}
onChange={(event) => setTestText(event.target.value)}
/>
<p>{testText}</p>
</div>
);
}
export default App;
- 今回は書いたものをリアルタイムでHTMLの例として出力したい
- testTextが変化することをトリガーに関数を走らせましょう。
- Dom動作を皮切りに変更させる場合
useEffect(関数,[監視する変数])
を利用すると良い。 - Markdown → mdast → HTML という順番なので、
remark-parse→remark-rehype→rehype-stringify
で変換するとよさそう。 - 実際にremark-parseのチュートリアルには、
remark-parse→remark-rehype→rehype-stringify
でチェーンが作られている。 - 帰ってくるオブジェクトはPromiseなので.then内で変数を変更させて結果をDomに反映させてみた。
結果的には以下のようになるかと思います。(github)
import React, { useEffect, useState, Fragment } from "react";
import { unified } from "unified";
import remarkParse from "remark-parse/lib";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify/lib";
function App() {
const [testText, setTestText] = useState("")
const [resultText, setResultText] = useState(Fragment)
useEffect(() => {
const result = unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeStringify)
.process(testText)
.then((res) => setResultText(res.value))
}, [testText])
return (
<div>
<p>HTMLを入力すると下にMarkdown変換したものを表示するよ!</p>
<textarea
rows={10}
onChange={(event) => setTestText(event.target.value)}
/>
<p>
{resultText}
</p>
</div>
);
}
export default App;