# 帳票設計書 2-TOMLファイル出力

## 概要

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

### 本帳票の処理概要

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

**業務上の目的・背景**：TOMLはHugo等の静的サイトジェネレータで設定ファイルやコンテンツメタデータの記述に利用されるフォーマットである。YAMLと比較してインデントに依存しない明確な構文を持ち、設定ファイルとしての可読性が高い。本機能はTOML形式でのコンテンツ管理が求められるプロジェクトにおいて、CMSで編集されたデータをGitリポジトリにTOMLファイルとしてコミットするために必要である。

**帳票の利用シーン**：コレクション設定でformat: tomlが指定されている場合、またはファイル拡張子が.tomlの場合に、エントリの保存時に本フォーマッタが使用される。Hugoベースのサイトで設定データやメタデータをTOML形式で管理する場合に利用される。

**主要な出力内容**：
1. エントリのメタデータフィールドをTOMLキー・値ペアとして出力
2. 整数値の小数点なし表現（outputReplacer関数による整数のtoString変換）
3. Dayjs日付オブジェクトの指定フォーマットでの文字列変換
4. AssetProxyオブジェクトのパス文字列変換

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

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

## 帳票種別

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

## 利用画面

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

## 出力形式

### 基本仕様

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

### PDF固有設定

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

### Excel固有設定

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

## 帳票レイアウト

### レイアウト概要

TOML形式のテキストファイルとして出力される。キー・値ペアが改行区切りで並び、テーブル（セクション）は`[section]`記法で表現される。

```
┌─────────────────────────────────────┐
│ key1 = "value1"                     │
│ key2 = 42                           │
│ date = 2024-01-01T00:00:00Z         │
│                                     │
│ [section]                           │
│ subkey1 = "subvalue1"               │
└─────────────────────────────────────┘
```

### ヘッダー部

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

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | キー名 | コレクション設定のfieldsで定義されたフィールド名 | collection.fields[].name | TOML key | N/A |
| 2 | 値 | 各フィールドに入力されたデータ | entry.data[fieldName] | TOMLの型に応じた表現 | N/A |

### フッター部

N/A

## 出力条件

### 抽出条件

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

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | sortedKeysで指定された順序 | 指定順（sortKeys関数による比較） |

### 改ページ条件

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

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

### 参照テーブル一覧

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

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

N/A

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| 整数値変換 | `value.toString()` | N/A | Number.isInteger(value)がtrueの場合、小数点なし表現にするためtoString()で文字列化 |
| Dayjs日付フォーマット | `value.format(value._f)` | N/A | Dayjsオブジェクトの場合、元の書式文字列（_f）でフォーマット |
| AssetProxyパス変換 | `${value.path}` | N/A | AssetProxyインスタンスの場合、pathプロパティを文字列として出力 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[Save操作] --> B[resolveFormat でフォーマッタ決定]
    B --> C[tomlFormatter.toFile 呼び出し]
    C --> D[tomlify.toToml でTOML文字列生成]
    D --> E{outputReplacer による値変換}
    E -->|Dayjs| F[日付フォーマット変換]
    E -->|AssetProxy| G[パス文字列変換]
    E -->|Integer| H[toString変換]
    E -->|その他| I[デフォルト処理]
    F --> J[TOML文字列出力]
    G --> J
    H --> J
    I --> J
    J --> K[バックエンドAPIでGitコミット]
    K --> L[終了]
```

## エラー処理

### エラーケース一覧

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

## パフォーマンス要件

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

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

- TOML出力データはGitリポジトリに保存されるため、リポジトリのアクセス制御に依存する
- outputReplacer関数はfalseを返すことでデフォルト変換を使用するが、undefinedを返すとキーが削除される点に注意

## 備考

- パース用に`@iarna/toml`（parse-string）、シリアライズ用に`tomlify-j0.4`を使用
- outputReplacer関数の戻り値仕様：文字列を返すとその値が使用される、falseを返すとデフォルト変換、undefinedを返すとキー削除
- YAML版と異なりコメント付与機能は非対応

---

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

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

### 推奨読解順序

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

TOML出力に関わるデータ型と依存関係を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | toml.ts | `packages/decap-cms-core/src/formats/toml.ts` | toFileメソッドの引数（data: object, sortedKeys: string[]）を確認。YAMLと異なりcommentsパラメータがない点に注目 |
| 1-2 | AssetProxy.ts | `packages/decap-cms-core/src/valueObjects/AssetProxy.ts` | AssetProxyクラスのpathプロパティを確認し、outputReplacerでの変換対象を理解 |
| 1-3 | helpers.ts | `packages/decap-cms-core/src/formats/helpers.ts` | sortKeys関数がtomlify.toTomlのsortオプションに渡される形式を理解 |

**読解のコツ**: toml.tsはパース用（@iarna/toml）とシリアライズ用（tomlify-j0.4）で異なるライブラリを使用している。outputReplacer関数はtomlifyの`replace`オプションとして渡され、各値の変換を制御する。

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

フォーマット解決からTOMLフォーマッタが選択される流れを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | formats.ts | `packages/decap-cms-core/src/formats/formats.ts` | resolveFormat関数でformat: toml指定時、または拡張子.toml時にtomlFormatterが返される流れを確認 |

**主要処理フロー**:
1. **66行目**: `resolveFormat`関数でコレクションのformat指定を確認
2. **46行目**: `formatByName`内で`toml`キーに対してtomlFormatterを返却
3. **35行目**: `extensionFormatters`で拡張子tomlに対してtomlFormatterをマッピング

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

toFileメソッドとoutputReplacer関数の処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | toml.ts | `packages/decap-cms-core/src/formats/toml.ts` | toFileメソッド（30-32行目）とoutputReplacer関数（8-23行目）の処理を理解する |

**主要処理フロー**:
- **30行目**: `toFile(data, sortedKeys)` が呼び出される
- **31行目**: `tomlify.toToml(data, { replace: outputReplacer, sort: sortKeys(sortedKeys) })` でTOML文字列を生成
- **9行目**: outputReplacerでDayjsオブジェクトを検出し、元のフォーマット文字列で変換
- **14-16行目**: AssetProxyインスタンスをpathプロパティの文字列に変換
- **17-19行目**: 整数値をtoString()で文字列化（tomlifyが.0を付加するのを防止）
- **22行目**: それ以外はfalseを返してデフォルト処理に委譲

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

```
resolveFormat (formats.ts:66)
    |
    +-- formatByName (formats.ts:42)
            |
            +-- tomlFormatter.toFile (toml.ts:30)
                    |
                    +-- tomlify.toToml (tomlify-j0.4 library)
                    |       +-- outputReplacer (toml.ts:8) [各値に対して呼び出し]
                    |       |       +-- dayjs.isDayjs (dayjs library)
                    |       |       +-- instanceof AssetProxy
                    |       |       +-- Number.isInteger
                    |       +-- sortKeys (helpers.ts:1) [ソートオプション]
                    |
                    +-- 結果: TOML文字列
```

### データフロー図

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

entry.data (Object) -----> tomlify.toToml -------> TOML文字列
                                |
sortedKeys (string[]) ---> sortKeys (sortオプション)
                                |
                           outputReplacer --------> 値変換
                             |  |  |
                  Dayjs日付  |  |  整数値
                  AssetProxy    |
                           TOML文字列 (.toml)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| toml.ts | `packages/decap-cms-core/src/formats/toml.ts` | ソース | TOMLフォーマッタ本体（fromFile/toFile） |
| helpers.ts | `packages/decap-cms-core/src/formats/helpers.ts` | ソース | sortKeysユーティリティ関数 |
| formats.ts | `packages/decap-cms-core/src/formats/formats.ts` | ソース | フォーマット解決・フォーマッタ選択ロジック |
| AssetProxy.ts | `packages/decap-cms-core/src/valueObjects/AssetProxy.ts` | ソース | AssetProxyクラス定義（outputReplacerの変換対象） |
| registry.js | `packages/decap-cms-core/src/lib/registry.js` | ソース | カスタムフォーマット登録・取得 |
