# 帳票設計書 6-JSON Frontmatter Markdownファイル出力

## 概要

本ドキュメントは、Decap CMSにおけるJSON Frontmatter付きMarkdownファイル出力機能の設計を記述する。FrontmatterFormatterクラスのtoFileメソッドにより、メタデータ部分を`{}`区切りのJSON形式で、本文をMarkdownとして結合出力する仕組みを定義する。

### 本帳票の処理概要

本帳票は、CMSのコンテンツエディタで編集されたエントリデータを、JSON Frontmatter付きMarkdownファイルとして出力する機能を定義する。

**業務上の目的・背景**：JSON Frontmatter付きMarkdownは、Hugoで利用可能なFrontmatter形式の1つである。JSON形式はプログラムによる自動生成や解析に適しており、ツールチェーンとの統合が容易である。また、JSON Schemaによるバリデーションとの親和性が高い。JSONの構文が厳密であるため、メタデータの型安全性が他の形式より高い。

**帳票の利用シーン**：コレクション設定でformat: json-frontmatterが指定されている場合に使用される。JSON形式のメタデータ管理を好むプロジェクトや、自動化ツールとの連携が必要な場合に利用される。

**主要な出力内容**：
1. `{}`区切りのJSON Frontmatter部分（メタデータフィールド）- 先頭/末尾の`{}`はデリミタとして機能するため、JSON文字列からは除去される
2. Markdown本文（bodyフィールド）

**帳票の出力タイミング**：エディタ画面で「Save」ボタンを押下した際、resolveFormatで決定されたフォーマッタのtoFileメソッドが呼び出され、JSON Frontmatter付きMarkdown文字列が生成される。

**帳票の利用者**：コンテンツ編集者（CMS利用者）、Hugo静的サイトジェネレータ

## 帳票種別

データファイル出力（JSON Frontmatter付きMarkdownファイル）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | エディタ画面 | /collections/{collection}/entries/{slug} | Saveボタン押下 |
| - | 新規エントリ作成画面 | /collections/{collection}/new | Saveボタン押下 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | Markdown (.md) |
| 用紙サイズ | N/A（テキストファイル） |
| 向き | N/A |
| ファイル名 | コレクション設定のslugテンプレートに基づく |
| 出力方法 | Gitリポジトリへのコミット（バックエンドAPI経由） |
| 文字コード | UTF-8 |

### PDF固有設定

N/A（テキストファイル出力のため該当なし）

### Excel固有設定

N/A（テキストファイル出力のため該当なし）

## 帳票レイアウト

### レイアウト概要

JSON Frontmatter部分とMarkdown本文部分が`{}`デリミタで区切られたファイルとして出力される。JSON文字列の先頭`{`と末尾`}`はデリミタとして機能するため、stringify時にトリムされる。

```
┌─────────────────────────────────────┐
│ {                                   │
│   "title": "記事タイトル",           │
│   "date": "2024-01-01",             │
│   "tags": ["tag1", "tag2"]          │
│ }                                   │
├─────────────────────────────────────┤
│                                     │
│ # 見出し                            │
│                                     │
│ 本文のMarkdownコンテンツ             │
│                                     │
└─────────────────────────────────────┘
```

### ヘッダー部（Frontmatter）

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | 開始デリミタ | Frontmatter開始マーカー | 固定値 `{` | { |
| 2 | メタデータ | body以外の全フィールドデータ（先頭/末尾の{}なし） | entry.data（bodyを除く） | JSON形式（{}除去） |
| 3 | 終了デリミタ | Frontmatter終了マーカー | 固定値 `}` | } |

### 明細部（本文）

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | 本文 | Markdownコンテンツ | entry.data.body | Markdown記法 | N/A |

### フッター部

N/A

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| エントリデータ | エディタで入力された全フィールドデータ | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | JSON.stringifyのデフォルト順序 | プロパティ定義順 |

### 改ページ条件

N/A（テキストファイルのため該当なし）

## データベース参照仕様

### 参照テーブル一覧

N/A（Gitリポジトリベース）

### テーブル別参照項目詳細

N/A

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| body分離 | `const { body = '', ...meta } = data` | N/A | FrontmatterFormatter.toFileで分離 |
| JSON文字列{}除去 | `JSONoutput.slice(1, -1)` | N/A | parsers.json.stringifyで先頭`{`と末尾`}`をトリム |
| JSON文字列{}補完 | `'{' + JSONinput + '}'` | N/A | parsers.json.parseで先頭に`{`がない場合に補完 |
| 末尾改行トリム | `body.slice(-1) !== '\n'`条件で末尾改行除去 | N/A | gray-matterの末尾改行付加への対策 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[Save操作] --> B[resolveFormat でフォーマッタ決定]
    B --> C[FrontmatterFormatter.toFile 呼び出し]
    C --> D[bodyとmetaを分離]
    D --> E[format = JSON, delimiters = {/}]
    E --> F[matter.stringify でFrontmatter生成]
    F --> G[parsers.json.stringify呼び出し]
    G --> H[jsonFormatter.toFile呼び出し]
    H --> I[先頭/末尾の{}をトリム]
    I --> J[末尾改行トリム処理]
    J --> K[バックエンドAPIでGitコミット]
    K --> L[終了]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| JSONシリアライズエラー | 循環参照を含むオブジェクトの場合 | TypeError: Converting circular structure to JSON | 入力データの循環参照を除去 |
| パースエラー | JSON構文が不正な場合 | SyntaxError: Unexpected token | JSON構文を確認 |
| バックエンドエラー | Gitコミットに失敗した場合 | バックエンド固有のエラーメッセージ | ネットワーク接続・認証情報を確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 1エントリ（メタデータ + 本文） |
| 目標出力時間 | 数ミリ秒 |
| 同時出力数上限 | 1件（エディタ操作は逐次処理） |

## セキュリティ考慮事項

- JSON Frontmatter付きMarkdownファイルはGitリポジトリに保存されるため、リポジトリのアクセス制御に依存する
- JSON.parse/stringifyはJavaScript標準APIであり、任意コード実行のリスクはない

## 備考

- YAML/TOML Frontmatterと同じFrontmatterFormatterクラスを使用し、Languageパラメータのみが異なる
- JSON Frontmatter固有の処理として、parsers.json.stringifyで先頭/末尾の`{}`をトリムする処理がある（gray-matterが`{`と`}`をデリミタとして使用するため）
- parsers.json.parseでも同様に、先頭に`{`がない場合に`{}`で補完する処理がある
- sortedKeys、commentsパラメータは非対応（parsers.json.stringifyに該当オプションなし）

---

## コードリーディングガイド

本帳票を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

JSON Frontmatter固有の{}トリム処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | frontmatter.ts | `packages/decap-cms-core/src/formats/frontmatter.ts` | parsers.jsonオブジェクト（26-43行目）のparse/stringifyメソッドを確認。特に{}のトリム/補完処理を理解 |
| 1-2 | frontmatter.ts | `packages/decap-cms-core/src/formats/frontmatter.ts` | JSON形式のデリミタが`['{', '}']`（配列）であることを確認（83行目） |

**読解のコツ**: JSON Frontmatterでは、gray-matterが`{`と`}`をデリミタとして認識するため、JSON文字列そのものから`{}`を除去する必要がある。parsers.json.stringifyの36-39行目とparsers.json.parseの29-31行目がこの処理を担当する。

#### Step 2: エントリーポイントを理解する

FrontmatterFormatterのJSONモードでのインスタンス化を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | frontmatter.ts | `packages/decap-cms-core/src/formats/frontmatter.ts` | frontmatterJSON関数（148-150行目）でLanguages.JSON指定のFrontmatterFormatterを生成 |
| 2-2 | formats.ts | `packages/decap-cms-core/src/formats/formats.ts` | formatByNameで`json-frontmatter`キーに対してfrontmatterJSON()を呼び出す（49行目） |

**主要処理フロー**:
1. **148行目**: `frontmatterJSON(customDelimiter)` 呼び出し
2. **149行目**: `new FrontmatterFormatter(Languages.JSON, customDelimiter)` でインスタンス生成
3. **83行目**: getFormatOpts内でJSONのデリミタ`['{', '}']`が設定される

#### Step 3: stringify/parseの{}トリム処理を理解する

JSON Frontmatter固有の処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | frontmatter.ts | `packages/decap-cms-core/src/formats/frontmatter.ts` | parsers.json.stringify（35-42行目）で先頭/末尾の`{}`をトリムする処理を確認 |
| 3-2 | frontmatter.ts | `packages/decap-cms-core/src/formats/frontmatter.ts` | parsers.json.parse（27-34行目）で先頭に`{`がない場合に補完する処理を確認 |

**主要処理フロー**:
- **35行目**: `parsers.json.stringify` が呼び出される
- **36行目**: `jsonFormatter.toFile(metadata).trim()` でJSON文字列を生成しトリム
- **38行目**: 先頭が`{`かつ末尾が`}`の場合
- **39行目**: `JSONoutput.slice(1, -1)` で先頭/末尾の`{}`を除去
- **27行目**: `parsers.json.parse` が呼び出される
- **29行目**: 先頭が`{`でない場合
- **30行目**: `'{' + JSONinput + '}'` で`{}`を補完

### プログラム呼び出し階層図

```
resolveFormat (formats.ts:66)
    |
    +-- formatByName (formats.ts:42)
            |
            +-- frontmatterJSON (frontmatter.ts:148)
                    |
                    +-- new FrontmatterFormatter('json', customDelimiter)
                            |
                            +-- toFile (frontmatter.ts:112)
                                    |
                                    +-- matter.stringify (gray-matter library)
                                    |       +-- parsers.json.stringify
                                    |               +-- jsonFormatter.toFile (json.ts:6)
                                    |               +-- {}トリム処理 (slice(1,-1))
                                    +-- 末尾改行トリム処理
```

### データフロー図

```
[入力]                          [処理]                          [出力]

entry.data ─────────────┐
  |                     |
  +-- body (string) ----+-> matter.stringify --------> {/}区切りの
  |                     |     |                        JSON Frontmatter
  +-- meta (残り) ------+     +-- parsers.json         付きMarkdown (.md)
                              |     +-- jsonFormatter
                              |     +-- {}トリム
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| frontmatter.ts | `packages/decap-cms-core/src/formats/frontmatter.ts` | ソース | FrontmatterFormatterクラス、parsers.jsonオブジェクト（{}トリム処理含む） |
| json.ts | `packages/decap-cms-core/src/formats/json.ts` | ソース | JSONフォーマッタ（parsers.json.stringify経由で呼び出し） |
| formats.ts | `packages/decap-cms-core/src/formats/formats.ts` | ソース | フォーマット解決・フォーマッタ選択ロジック |
