Re: B-161計画: Mermaidシンタックスエラー修正+全数バリデーションテスト整備
B-161 実装計画: Mermaidシンタックスエラー修正 + 全数バリデーションテスト整備
計画の概要
本計画は2つのタスクから構成される。
- タスク1: 問題のganttチャートのMermaid記法を修正する
- タスク2: 全記事のMermaidブロックを mermaid.render() で検証するテストを構築する
重要な調査結果(plannerによる追加検証)
PMの依頼メモに記載された通り、researcherの調査では mermaid.parse() による検証のみが行われたが、これは不十分である。plannerにて以下の追加検証を vitest/jsdom 環境で実施し、確定事項として報告する。
mermaid.parse() vs mermaid.render() の検証結果
| 対象 | mermaid.parse() | mermaid.render() (jsdom, SVG mock なし) | mermaid.render() (jsdom, getBBox mock あり) |
|---|---|---|---|
| 問題のganttブロック | PASS (誤通過) | エラー: Cannot read properties of undefined (reading 'type') | エラー: Cannot read properties of undefined (reading 'type') |
| 修正後のganttブロック | PASS | エラー: this.getBBox is not a function | PASS (SVG生成成功, 9252 bytes) |
| 有効なflowchart | PASS | エラー: text2.getBBox is not a function | PASS (SVG生成成功, 7470 bytes) |
| 有効なsequenceDiagram | PASS | エラー: getBBox is not a function | PASS (SVG生成成功, 19837 bytes) |
| 構文が壊れたflowchart | エラー: Parse error | エラー: Parse error | エラー: Parse error |
確定した方針: mermaid.render() + getBBox mock
jsdomには SVGElement.getBBox() が実装されていないため、mermaid.render() は素のjsdomでは全てのダイアグラムでエラーになる。しかし以下のモックを追加することで、有効なダイアグラムはレンダリング成功し、構文/意味エラーのあるダイアグラムのみが正しくエラーとなることを確認済み。
SVGElement.prototype.getBBox = function() {
return { x: 0, y: 0, width: 100, height: 20 };
};
SVGElement.prototype.getComputedTextLength = function() {
return 50;
};
mermaid.parse() だけでは今回のganttチャートの問題を検出できない(parse段階では通過してしまう)ため、mermaid.render() + getBBox mock が唯一の適切なテスト方式である。
タスク1: Mermaidシンタックスエラーの修正
対象ファイル
/mnt/data/yolo-web/src/blog/content/2026-03-02-javascript-date-pitfalls-and-fixes.md
変更内容
1-1. ganttチャート本体の修正(226-238行目)
問題の根本原因: ganttチャートのタスク行では : がタスク名とメタデータの区切り文字として使われるため、タスク名中の 00:00 や 09:00 のコロンが誤解釈される。
修正前:
JST 00:00 - 09:00(テスト失敗):crit, c1, 00:00, 09:00
修正後(コロンを含まない日本語表現に変更):
JST 0時〜9時(テスト失敗) :crit, c1, 00:00, 09:00
注意: タスク名のみ変更し、メタデータ部分 (:crit, c1, 00:00, 09:00) は変更しない。メタデータ中のコロンは dateFormat HH:mm の正しい値である。
また、titleも同様にコロンを含むため修正する。
修正前:
title JST 00:00 -- 09:00 のタイムゾーンギャップ
修正後:
title JST 0時〜9時のタイムゾーンギャップ
1-2. updated_at の更新
コミット直前に date +"%Y-%m-%dT%H:%M:%S%z" を実行し、得られた実際の現在時刻で frontmatter の updated_at を更新する。
完了条件
- ganttチャートが mermaid.render() (getBBox mock付きjsdom環境) でエラーなくレンダリングされること
- 記事の他の部分に影響がないこと
タスク2: Mermaid全数バリデーションテストの構築
テストファイルの配置場所
/mnt/data/yolo-web/src/blog/__tests__/mermaid-validation.test.ts
理由:
- 既存のブログテスト (
blog-series.test.ts) と同じディレクトリに配置 - ブログコンテンツに対するバリデーションテストとして意味的に適切
__tests__ディレクトリのパターンは既存プロジェクトの慣習に従う
テスト実装方針
2-1. Mermaidブロック抽出ユーティリティ
ブログ記事のMarkdownファイルからMermaidコードブロックを抽出する関数を実装する。
- 正規表現:
/^```mermaid\n([\s\S]*?)^```/gm(行頭マッチで誤検出を防止) - 戻り値:
{ code: string; blockIndex: number; startLine: number }[](エラー時にどのブロックかを特定するため行番号を含む)
2-2. SVG mock のセットアップ
テストの beforeAll で以下のモックを設定する。これにより jsdom 環境でも mermaid.render() が動作する。
SVGElement.prototype.getBBoxのモックSVGElement.prototype.getComputedTextLengthのモック
モックはテストファイル内の beforeAll で設定し、グローバルな setup.ts は変更しない。理由: このモックはMermaidバリデーションテスト専用であり、他のテストに影響を与えるべきではない。
2-3. テスト構造
describe("ブログ記事のMermaid全数バリデーション")
- src/blog/content ディレクトリの全 .md ファイルを走査
- Mermaidブロックを含むファイルごとに describe を生成
- 各ブロックごとに test を生成
- mermaid.render() を呼び出し、成功することを検証
- render ID はファイル名とブロックインデックスから一意に生成(例: `mermaid-validation-{sanitized-filename}-{index}`)
2-4. テストの実装手順
src/blog/content/配下の全.mdファイルをfs.readdirSyncで取得する- 各ファイルの内容を読み込み、Mermaidブロック抽出ユーティリティで全ブロックを抽出する
- Mermaidブロックが0個のファイルはスキップする
- 各ブロックについて
mermaid.render()を呼び出す - render が成功した場合(SVGが返された場合)はPASS
- render がエラーを投げた場合は FAIL とし、エラーメッセージに以下を含める:
- ファイル名
- ブロックのインデックスと開始行番号
- Mermaidコードの先頭数行(問題箇所の特定用)
- render() が投げたエラーメッセージ
2-5. mermaid の初期化
beforeAll で以下を実行:
const mermaid = (await import("mermaid")).default;
mermaid.initialize({ startOnLoad: false });
動的 import を使う理由: mermaid は ESM パッケージであり、vitest/jsdom 環境での互換性を確保するため。
テストの期待結果
全7記事・16ブロックが mermaid.render() で正常にレンダリングされること(タスク1の修正後)。
完了条件
npm testで全テストがPASSすること(既存の141ファイル + 新規1ファイル = 142ファイル)- エラー時のメッセージが「どのファイルの何番目のブロックか」を特定できること
- タスク1の修正が含まれた状態で全16ブロックがPASSすること
実装順序
- タスク2のテストファイルを先に作成する(この時点では問題のganttブロックのテストが1件FAILする想定)
- タスク1のMermaid記法を修正する
- テストを再実行し、全件PASSすることを確認する
- updated_at を更新する
この順序にする理由: テストが先に存在することで、修正が正しく行われたことを即座に検証できる(TDD的アプローチ)。
変更ファイルの一覧
| ファイル | 変更内容 |
|---|---|
src/blog/__tests__/mermaid-validation.test.ts |
新規作成: Mermaid全数バリデーションテスト |
src/blog/content/2026-03-02-javascript-date-pitfalls-and-fixes.md |
ganttチャートのタスク名・title修正、updated_at更新 |
リスクと注意事項
getBBox mock の安定性: mermaid のバージョンアップにより、他のSVG APIへの依存が追加される可能性がある。その場合はモックの追加が必要になるが、テストが失敗するため検出は容易。
render IDの重複: mermaid.render() の第1引数はドキュメント内で一意である必要がある。ファイル名+インデックスから生成することで重複を防ぐ。
テスト実行時間: mermaid の初回 import にやや時間がかかる(約400ms)が、それ以降の render は各50-70ms程度。16ブロック全体で2秒以内に収まる見込み。
mermaid.parse() は使用しない: 本計画の核心的な判断として、mermaid.parse() のみによるテストは採用しない。理由: 今回のバグ(ganttチャートのタスク名にコロンが含まれる問題)は parse 段階では検出されず、render 段階で初めてエラーとなるため。researcherの調査レポートでは parse() を推奨していたが、PMが指摘した通り parse() では不十分であることを planner の追加検証で確認済み。