MarkdownからHTMLへ変換する仕組みを調査

本記事では Astro における Markdown から HTML への変換の仕組みについてまとめました!
MarkdownからHTMLへの変換方法
unified について
unified はテキストをプログラムが理解可能な抽象構文木(AST)に変換して扱うためのインターフェースです
unified で使用するASTは unist として仕様が決められています
この unist を拡張して様々な形式を取り扱うようにした unified エコシステムのパッケージとして Remark と Rehype があります
Remark はマークダウンを unist を拡張した mdast と呼ばれる抽象構文木(AST)として扱うプラグインのエコシステムです
Rehype は HTML を unist を拡張した hast と呼ばれる抽象構文木(AST)として扱うプラグインのエコシステムです
各種 AST は相互変換することができ、この2つのパッケージのプラグインを使用することで Markdown から HTML への変換を実現することができます
処理の仕組み
Remark と Rehype の処理フローは以下の3つに分かれています
- Parse : 文字列を抽象構文木(AST)に変換する
- Transform : 抽象構文木(AST)に対して何か操作を行う
- Stringify : 抽象構文木(AST)を文字列に戻す
上記を踏まえて unified では Markdown から HTML への変換は以下のような流れで行われています
- unifiedはまず Markdown を Parse して mdast に変換します。この際に使用されるのが remark-parse プラグインです
- 次に remark-rehype プラグインを使用して mdast を HTML用のASTである hast に Transform します
- 最後に rehype-stringify プラグインを使用して hast を HTML に Stringify します
[画像を差し込む]
プログラムは以下のようになります
import unified from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
main()
async function main() {
const file = await unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeStringify)
.process('# Hello World')
console.log(String(file))
}
use で使用するプラグインを順番に設定し processSync メソッドで Markdown を受け取り処理を実行します
上記を実行すると HTML への変換が行われます
<h1>Hello World</h1>
AstroではどのようにMarkdownをHTMLに変換しているか
Astroとは
Astro については別の記事で紹介しているのであわせてご覧ください!
[別記事で紹介する]
AstroにおけるMarkdown→HTML変換プロセス
Astro のプロジェクトではデフォルトで Markdown から HTML への変換ができるようになっています
実際に Astro のソースコードを確認してみると packages/markdown/remark/src/index.ts ファイル内で、上記で説明した remarkParse → remarkRehype → rehypeStringify の処理を行っていることがわかります
import rehypeStringify from 'rehype-stringify';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import { unified } from 'unified';
/**
* Create a markdown preprocessor to render multiple markdown files
*/
export async function createMarkdownProcessor(
opts?: AstroMarkdownOptions,
): Promise<MarkdownProcessor> {
...
const parser = unified().use(remarkParse);
...
// Remark -> Rehype
parser.use(remarkRehype, {
allowDangerousHtml: true,
passThrough: [],
...remarkRehypeOptions,
});
...
// Stringify to HTML
parser.use(rehypeRaw).use(rehypeStringify, { allowDangerousHtml: true });
return {
async render(content, renderOpts) {
const result = await parser.process(vfile).catch((err) => {
// Ensure that the error message contains the input filename
// to make it easier for the user to fix the issue
err = prefixError(err, `Failed to parse Markdown file "${vfile.path}"`);
console.error(err);
throw err;
});
return {
code: String(result.value),
...
};
},
};
}
Astro では上記処理で Markdown から HTML への変換を行なっていることがわかりました