コンテンツにスキップ

Markdown プラグイン

本文書では,Markdown テキストを minitype のブロック列に変換する組込みプラグインを説明します.

本プラグインは,以下の関数を提供します.

関数説明
mdタグ付きテンプレートを用いて Markdown を記述する.
createMdカスタムマッパを持つ md タグを生成する.
mdStringMarkdown 文字列を変換する.
mdFileMarkdown ファイルを変換する.
mdInlineStringMarkdown インライン記法を含む文字列をインライン要素に変換する.

以下の Markdown 構文に対応します. その他の構文はスキップされます.

  • 見出し(#,##,###,####)
  • 段落
  • コードブロック(```lang ```)
  • リスト(- list,1. list)
    最大 3 階層まで対応しています.
  • 強調(*italic* / _italic_)
    em コマンドに変換されます.
  • 太字(**bold**,__bold__)
    b コマンドに変換されます.
  • コード(`code`)
    c コマンドに変換されます.
  • リンク([text](url))
  • 画像(![alt](src "title"))
    image ブロックおよび caption ブロックに変換されます.
  • テーブル(GFM テーブル記法)
  • 水平線(---,***,___)
    デフォルトでは何も出力しません.カスタムマッパを用いて処理できます.
  • 脚注([^label] 参照,[^label]: ... 定義)
  • コンテナブロック(:::name ... :::)
    minitype 独自の拡張構文です.

md はタグ付きテンプレートとして使用します.

export const md: MarkdownTag;

createMd は後述するカスタムマッパを受け取って,md と同じ型のタグ付きテンプレートを返します.

export const createMd: <
T extends Record<string, unknown> = Record<string, unknown>,
>(
mappers?: MarkdownMapping,
) => MarkdownTag<T>;

戻り値の型 MarkdownTag および MarkdownResult は以下の通りです.

type MarkdownTag<T extends Record<string, unknown> = Record<string, unknown>> =
(
strings: TemplateStringsArray,
...values: MarkdownInterpolation[]
) => () => Promise<MarkdownResult<T>>;
interface MarkdownResult<
T extends Record<string, unknown> = Record<string, unknown>,
> {
blocks: (Block | BlockExtender)[];
frontmatter: T;
}

ブロック,インラインの埋め込み

Section titled “ブロック,インラインの埋め込み”

テンプレートリテラルに Block,Block[],BlockExtender,InlineOrExtender を埋め込むことができます. これらは型に基づいて自動判別されます.

import { md, kern } from "@minitype/minitype";
import type { Box } from "@minitype/minitype";
// Block:ブロックとして自動判別
const myBox: Box = ;
// BlockExtender:ブロックとして自動判別
const intro = md`はじめに ...`;
// string,Command,Kerning 等のインライン値:インラインとして自動判別
const year = "2025";
export const body: Body = [
md`${myBox}
${intro}
本文中に${kern(0.5)}カーニングを挿入した例(${year}年).
`,
];

値は以下の通りに判別されます.

値の種類判別
Block(type フィールドで識別)ブロック
Block[]ブロック
BlockExtender(type: "blockExtender" で識別)ブロック
stringインライン
Command / Break / Kerning / InlineGraphicインライン
InlineExtender(type: "inlineExtender" で識別)インライン

タグ付きテンプレートではなく既存の文字列およびファイルを変換する場合,それぞれ mdString および mdFile を使用します. いずれも第 2 引数にカスタムマッパを渡すことができます. なお,値の補間はできません.

export const mdString: <
T extends Record<string, unknown> = Record<string, unknown>,
>(
content: string,
mappers?: MarkdownMapping,
) => MarkdownResult<T>;
export const mdFile: <
T extends Record<string, unknown> = Record<string, unknown>,
>(
filePath: string,
mapping?: MarkdownMapping,
) => Promise<MarkdownResult<T>>;

Markdown のインライン記法を含む文字列をインライン要素の配列に変換します. ブロック要素には対応していません.

import { mdInlineString, p } from "@minitype/minitype";
const inlines = mdInlineString("**太字**と `コード` を含むテキスト.");
export const body: Body = [p([inlines])];

Markdown の先頭に --- で囲まれた YAML ブロックを記述すると,frontmatter フィールドとして取得できます.

import { md } from "@minitype/minitype";
import type { MarkdownResult } from "@minitype/minitype";
interface Meta {
title: string;
}
const result: MarkdownResult<Meta> = await md<Meta>`
---
title: サンプル文書
---
本文のテキスト.
`();
// "サンプル文書"
console.log(result.frontmatter.title);

カスタムマッパを使用すると,各ブロック種別の変換処理をカスタマイズできます. 未指定のキーにはデフォルトのマッパが使用されます.

import { createMd } from "@minitype/minitype";
import { h2, h3, listing } from "...";
const md = createMd({
h2: (inlines) => h2(inlines),
code: (code, lang) => listing(lang ?? "", code),
});
export const body: Body = [
md`
## カスタム見出し
\`\`\`ts
const x = 1;
\`\`\`
`,
];

createMd が受け取る MarkdownMapping の各フィールドは以下の通りです.

interface MarkdownMapping {
h1?: HeadingMapper;
h2?: HeadingMapper;
h3?: HeadingMapper;
h4?: HeadingMapper;
paragraph?: ParagraphMapper;
code?: CodeMapper;
list?: ListMapper;
footnote?: FootnoteMapper;
image?: ImageMapper;
hr?: HrMapper;
table?: TableMapper;
containers?: Record<string, ContainerMapper>;
yaml?: Record<string, YamlMapper<any>>;
em?: (
inlines: InlineOrExtender[],
delimiter: EmDelimiter,
) => InlineOrExtender | InlineOrExtender[];
strong?: (
inlines: InlineOrExtender[],
delimiter: StrongDelimiter,
) => InlineOrExtender | InlineOrExtender[];
codespan?: (text: string) => InlineOrExtender | InlineOrExtender[];
link?: (
href: string,
inlines: InlineOrExtender[],
) => InlineOrExtender | InlineOrExtender[];
}
type HeadingMapper = (inlines: InlineOrExtender[]) => Block | Block[];
type ParagraphMapper = (inlines: InlineOrExtender[]) => Block | Block[];
type CodeMapper = (code: string, lang?: string) => Block | Block[];
type ListMapper = (items: ListItem[]) => Block[];
type FootnoteMapper = (
label: string,
inlines: InlineOrExtender[],
) => Block | Block[];
type ImageMapper = (
src: string,
alt: string,
title: string | null,
) => Block | Block[];
type HrMapper = (symbol: HrSymbol) => Block | Block[];
type TableMapper = (
header: InlineOrExtender[][],
rows: InlineOrExtender[][][],
align: ("center" | "left" | "right" | null)[],
) => Block | Block[];
type ContainerMapper = (blocks: Block[], args: string[]) => Block | Block[];
type YamlMapper<T = unknown> = (result: YamlParseResult<T>) => Block | Block[];
type EmDelimiter = "*" | "_";
type StrongDelimiter = "**" | "__";

list マッパに渡される ListItem は,ネストを含むリスト全体をフラットに展開したものです.

interface ListItem {
level: ListLevel;
listType: ListType;
inlines: InlineOrExtender[];
}

Markdown から変換された ListItem[] の例を以下に示します.

- A
- A-1
- A-2
- B
[
{ level: 1, listType: "unordered", inlines: ["A"] },
{ level: 2, listType: "unordered", inlines: ["A-1"] },
{ level: 2, listType: "unordered", inlines: ["A-2"] },
{ level: 1, listType: "unordered", inlines: ["B"] },
];