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

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つに分かれています

  1. Parse : 文字列を抽象構文木(AST)に変換する
  2. Transform : 抽象構文木(AST)に対して何か操作を行う
  3. Stringify : 抽象構文木(AST)を文字列に戻す

上記を踏まえて unified では Markdown から HTML への変換は以下のような流れで行われています

  1. unifiedはまず Markdown を Parse して mdast に変換します。この際に使用されるのが remark-parse プラグインです
  2. 次に remark-rehype プラグインを使用して mdast を HTML用のASTである hast に Transform します
  3. 最後に 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 の処理を行っていることがわかります

packages/markdown/remark/src/index.ts

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 への変換を行なっていることがわかりました

Recommend
こんな記事も読まれています!