import { unified } from 'unified'; import remarkParse from 'remark-parse'; import remarkFrontmatter from 'remark-frontmatter'; import remarkGfm from 'remark-gfm'; import remarkDirective from 'remark-directive'; import yaml from 'js-yaml'; import type { Root } from 'mdast'; /** * Markdownパーサー * unified/remarkエコシステムを使用してMarkdownをASTにパースする */ export class MarkdownParser { private readonly processor; constructor() { this.processor = unified() .use(remarkParse) .use(remarkFrontmatter, ['yaml']) .use(remarkGfm) .use(remarkDirective); } /** * MarkdownコンテンツをASTにパースする */ parse(content: string): Root { return this.processor.parse(content); } /** * Markdownコンテンツからfrontmatterを抽出する */ extractFrontmatter(content: string): Record { const tree = this.parse(content); for (const node of tree.children) { if (node.type === 'yaml') { try { const frontmatter = yaml.load((node as { value: string }).value); if (frontmatter && typeof frontmatter === 'object') { return frontmatter as Record; } } catch { // 無効なYAMLの場合は空オブジェクトを返す return {}; } } } return {}; } }