# 帳票設計書 1-YAMLファイル出力

## 概要

本ドキュメントは、Decap CMSにおけるコンテンツデータのYAML形式ファイル出力機能の設計を記述する。yamlFormatterモジュールのtoFileメソッドにより、JavaScriptオブジェクトをYAML文字列にシリアライズしてファイルとして出力する仕組みを定義する。

### 本帳票の処理概要

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

**業務上の目的・背景**：Decap CMSはGitベースのヘッドレスCMSであり、コンテンツデータをGitリポジトリ上のファイルとして管理する。YAMLはJekyll、Hugo等の静的サイトジェネレータで広く採用されるデータフォーマットであり、人間が読み書きしやすく、コンテンツの構造化データ記述に適している。本機能はCMSで編集されたコンテンツをYAML形式でGitリポジトリにコミットするために必要である。

**帳票の利用シーン**：コンテンツ編集者がCMSのエディタ画面でエントリを作成・編集し、保存操作を行う際に、バックエンドへのコミットデータとしてYAMLファイルが生成される。コレクション設定でformat: ymlまたはformat: yamlが指定されている場合、あるいはファイル拡張子が.ymlの場合に本機能が使用される。

**主要な出力内容**：
1. エントリのメタデータフィールド（タイトル、日付、タグ等）をYAMLキー・値ペアとして出力
2. コメント付与（各キーに対するコメントを`commentBefore`として付加）
3. キーのソート順制御（sortedKeysパラメータによる指定順序でのキー配置）

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

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

## 帳票種別

データファイル出力（YAML形式テキストファイル）

## 利用画面

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

## 出力形式

### 基本仕様

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

### PDF固有設定

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

### Excel固有設定

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

## 帳票レイアウト

### レイアウト概要

YAML形式のテキストファイルとして出力される。キー・値ペアが改行区切りで並び、ネストされた構造はインデントで表現される。

```
┌─────────────────────────────────────┐
│ # コメント（commentBefore）          │
│ key1: value1                        │
│ key2: value2                        │
│ nested:                             │
│   subkey1: subvalue1                │
│ list:                               │
│   - item1                           │
│   - item2                           │
└─────────────────────────────────────┘
```

### ヘッダー部

N/A（YAMLファイルにはヘッダー部の概念はない）

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | キー名 | コレクション設定のfieldsで定義されたフィールド名 | collection.fields[].name | YAML key | N/A |
| 2 | 値 | 各フィールドに入力されたデータ | entry.data[fieldName] | YAMLの型に応じた表現 | N/A |
| 3 | コメント | フィールドに関連付けられたコメント | comments[key] | `# コメント文` として出力 | N/A |

### フッター部

N/A

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| エントリデータ | エディタで入力された全フィールドデータ | Yes |
| ソートキー指定 | sortedKeysパラメータで指定されたキー順序 | No |
| コメント指定 | commentsパラメータで指定されたキー別コメント | No |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | sortedKeysで指定された順序 | 指定順（sortKeys関数による比較） |
| 2 | 未指定キー | 元の順序を維持（安定ソート） |

### 改ページ条件

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

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

### 参照テーブル一覧

N/A（Decap CMSはデータベースを使用せず、Gitリポジトリ上のファイルをデータストアとして利用する）

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

N/A

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| タイムスタンプ文字列化 | `(value as Date).toISOString()` | N/A | timestampTagカスタムタグによりDate型をISO 8601形式に変換 |
| キーソート順序 | `sortKeys(sortedKeys, item => item.key?.toString())` | N/A | sortedKeys配列のインデックスに基づく比較関数 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[Save操作] --> B[resolveFormat でフォーマッタ決定]
    B --> C[yamlFormatter.toFile 呼び出し]
    C --> D[yaml.createNode でYAMLノード生成]
    D --> E[addComments でコメント付与]
    E --> F[items.sort でキーソート]
    F --> G[yaml.Document 生成]
    G --> H[doc.toString で文字列化]
    H --> I[バックエンドAPIでGitコミット]
    I --> J[終了]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| シリアライズエラー | yaml.createNodeが不正なデータを受け取った場合 | YAMLライブラリのエラーメッセージ | 入力データの型・構造を確認 |
| バックエンドエラー | Gitコミットに失敗した場合 | バックエンド固有のエラーメッセージ | ネットワーク接続・認証情報を確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 1エントリあたり数十〜数百フィールド |
| 目標出力時間 | 数ミリ秒（yaml.Document.toString処理） |
| 同時出力数上限 | 1件（エディタ操作は逐次処理） |

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

- YAML出力データはGitリポジトリに保存されるため、リポジトリのアクセス制御に依存する
- 機密情報をコンテンツフィールドに含めないよう運用で制御する必要がある
- timestampTagカスタムタグにより、意図しないYAMLタグ解釈を防止している

## 備考

- yamlライブラリ（npmパッケージ`yaml`）を使用
- fromFileメソッドでは、末尾の`---`を除去してからパースする処理が含まれる
- addComments関数はネストされたオブジェクトに対しても再帰的にコメントを付与する
- ドット区切りのキーパス（例: `parent.child`）でネストされたフィールドへのコメント指定が可能

---

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

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

### 推奨読解順序

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

まず、YAML出力に関わるデータ型とインターフェースを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | yaml.ts | `packages/decap-cms-core/src/formats/yaml.ts` | toFileメソッドの引数（data: object, sortedKeys: string[], comments: Record<string, string>）を確認し、入力データの構造を理解する |
| 1-2 | helpers.ts | `packages/decap-cms-core/src/formats/helpers.ts` | sortKeys関数の比較ロジック（sortedKeys配列のインデックス比較）を理解する |

**読解のコツ**: yaml.tsはTypeScript型注釈（YAMLMap, YAMLSeq, Pair, Node等）を`yaml`パッケージから参照している。これらはyamlライブラリ固有の型であり、YAMLドキュメントのAST（抽象構文木）表現である。

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

処理の起点となるフォーマット解決の仕組みを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | formats.ts | `packages/decap-cms-core/src/formats/formats.ts` | resolveFormat関数がコレクション設定とファイル拡張子からyamlFormatterを選択する流れを確認する |

**主要処理フロー**:
1. **66行目**: `resolveFormat`関数 - コレクションのformat指定を確認
2. **74-77行目**: format指定がある場合、`formatByName`でフォーマッタを取得
3. **43-44行目**: `formatByName`内で`yml`/`yaml`キーに対してyamlFormatterを返却

#### Step 3: YAML出力処理を理解する

実際のYAML文字列生成処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | yaml.ts | `packages/decap-cms-core/src/formats/yaml.ts` | toFileメソッド（47-57行目）の処理フローを理解する |

**主要処理フロー**:
- **48行目**: `yaml.createNode(data)` でJSオブジェクトをYAML ASTノードに変換
- **50行目**: `addComments(contents.items, comments)` でコメントを付与
- **52行目**: `contents.items.sort(sortKeys(...))` でキーをソート
- **53-54行目**: `new yaml.Document()` でドキュメントオブジェクトを生成し、contentsを設定
- **56行目**: `doc.toString()` でYAML文字列に変換して返却

#### Step 4: コメント付与処理を理解する

addComments関数の再帰的なコメント付与処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | yaml.ts | `packages/decap-cms-core/src/formats/yaml.ts` | addComments関数（7-21行目）の再帰処理を確認する |

**主要処理フロー**:
- **8行目**: items配列を反復処理
- **10行目**: キー名を取得し、プレフィックス付きのキーパスを構築
- **12-14行目**: commentsオブジェクトにキーが存在すれば、改行を` \n `に変換してcommentBeforeに設定
- **16-18行目**: 値がitems配列を持つ場合（ネストされたオブジェクト）、再帰呼び出し

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

```
resolveFormat (formats.ts:66)
    |
    +-- formatByName (formats.ts:42)
            |
            +-- yamlFormatter.toFile (yaml.ts:47)
                    |
                    +-- yaml.createNode (yaml library)
                    +-- addComments (yaml.ts:7)
                    |       +-- addComments (再帰呼び出し)
                    +-- sortKeys (helpers.ts:1)
                    +-- yaml.Document.toString (yaml library)
```

### データフロー図

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

entry.data (Object) -----> yaml.createNode -----> YAML ASTノード
                                                        |
sortedKeys (string[]) ---> sortKeys -----------> ソート済みASTノード
                                                        |
comments (Record) -------> addComments ---------> コメント付きASTノード
                                                        |
                           yaml.Document -------> YAML文字列 (.yml)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| yaml.ts | `packages/decap-cms-core/src/formats/yaml.ts` | ソース | YAMLフォーマッタ本体（fromFile/toFile） |
| helpers.ts | `packages/decap-cms-core/src/formats/helpers.ts` | ソース | sortKeysユーティリティ関数 |
| formats.ts | `packages/decap-cms-core/src/formats/formats.ts` | ソース | フォーマット解決・フォーマッタ選択ロジック |
| registry.js | `packages/decap-cms-core/src/lib/registry.js` | ソース | カスタムフォーマット登録・取得（getCustomFormatsFormatters） |
