# 機能設計書 35-pretty-format

## 概要

本ドキュメントは、pretty-formatパッケージの機能設計を記載する。pretty-formatは、任意のJavaScript値を人間が読みやすい文字列形式に整形する機能を提供する。Jestのスナップショットテスト、アサーション失敗時の出力、デバッグ出力などで使用される。

### 本機能の処理概要

pretty-formatは、JavaScript/TypeScriptの任意の値を、構造が分かりやすい文字列表現に変換するライブラリである。

**業務上の目的・背景**：テスト実行時にアサーションが失敗した場合や、スナップショットテストで差分が発生した場合に、値の内容を開発者が理解しやすい形式で表示する必要がある。標準の`JSON.stringify`では関数やSymbol、循環参照などを適切に表示できないため、より高機能な整形機能が必要とされた。

**機能の利用シーン**：
- スナップショットテストでの値のシリアライズ
- expect/toEqualなどのアサーション失敗時のエラーメッセージ
- jest-diffでの差分比較用の値変換
- デバッグ時のオブジェクト構造の確認

**主要な処理内容**：
1. format関数: 任意の値を文字列に整形
2. プリミティブ値の整形（boolean, number, string, null, undefined, symbol, bigint）
3. オブジェクト値の整形（Array, Object, Map, Set, Date, RegExp, Error等）
4. 循環参照の検出と表示（[Circular]）
5. プラグインによる拡張（React, DOM, Immutable等）
6. 構文ハイライト（ansi-stylesによる色付け）

**関連システム・外部連携**：
- @jest/schemas: オプションのスキーマ定義
- ansi-styles: 構文ハイライト用のANSIカラーコード
- react-is: Reactコンポーネントの判定に使用

**権限による制御**：特になし

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 12 | Snapshot Testingページ | 補助機能 | スナップショットの人間可読フォーマット処理 |

## 機能種別

データ変換 / フォーマット / ユーティリティ

## 入力仕様

### 入力パラメータ

#### format関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| val | unknown | Yes | 整形対象の任意の値 | - |
| options | OptionsReceived | No | 整形オプション | validateOptionsで検証 |

#### オプション（OptionsReceived）

| パラメータ名 | 型 | 必須 | 説明 | デフォルト値 |
|-------------|-----|-----|------|-------------|
| callToJSON | boolean | No | toJSONメソッドを呼び出すか | true |
| compareKeys | CompareKeys | No | オブジェクトキーのソート関数 | undefined |
| escapeRegex | boolean | No | 正規表現をエスケープするか | false |
| escapeString | boolean | No | 文字列をエスケープするか | true |
| highlight | boolean | No | 構文ハイライトを有効にするか | false |
| indent | number | No | インデント幅（スペース数） | 2 |
| maxDepth | number | No | 最大ネスト深度 | Infinity |
| maxWidth | number | No | 配列等の最大表示幅 | Infinity |
| min | boolean | No | 最小化モード（改行なし） | false |
| plugins | Plugins | No | カスタムプラグインの配列 | [] |
| printBasicPrototype | boolean | No | 基本プロトタイプ名を表示 | true |
| printFunctionName | boolean | No | 関数名を表示するか | true |
| theme | Theme | No | 構文ハイライトのテーマ | DEFAULT_THEME |

### 入力データソース

- jest-snapshot: スナップショット保存時の値
- expect: アサーション失敗時の値
- jest-diff: 差分比較時の値

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | string | 整形された文字列表現 |

### 出力先

関数の戻り値として返却

## 処理フロー

### 処理シーケンス

```
1. format関数の呼び出し
   └─ オプションの検証（validateOptions）
2. プラグインのマッチング
   └─ 各プラグインのtest()でマッチするものを検索
   └─ マッチすればプラグインで整形
3. 基本値の整形（printBasicValue）
   └─ boolean, undefined, null
   └─ number, bigint, string
   └─ function, symbol
   └─ Promise, WeakMap, WeakSet
   └─ Date, Error, RegExp
4. 複雑な値の整形（printComplexValue）
   └─ 循環参照のチェック
   └─ toJSONメソッドの呼び出し
   └─ Arguments, Array, TypedArray
   └─ Map, Set
   └─ 一般オブジェクト
```

### フローチャート

```mermaid
flowchart TD
    A[format開始] --> B[オプション検証]
    B --> C{プラグインマッチ?}
    C -->|Yes| D[printPlugin]
    C -->|No| E[printBasicValue]
    E --> F{基本値?}
    F -->|Yes| G[基本値の文字列を返却]
    F -->|No| H[printComplexValue]
    H --> I{循環参照?}
    I -->|Yes| J[return '[Circular]']
    I -->|No| K{maxDepth超過?}
    K -->|Yes| L[return '[ConstructorName]']
    K -->|No| M{toJSON存在?}
    M -->|Yes| N[toJSON呼び出して再帰]
    M -->|No| O[値の種類に応じて整形]
    D --> P[結果を返却]
    G --> P
    J --> P
    L --> P
    N --> P
    O --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 循環参照検出 | 既に整形済みのオブジェクトを再度整形しようとした場合[Circular]を表示 | オブジェクト値 |
| BR-02 | 最大深度制限 | maxDepthを超えた場合はコンストラクタ名のみ表示 | depth > maxDepth |
| BR-03 | toJSON優先 | callToJSON=trueの場合、toJSONメソッドの結果を使用 | toJSONメソッド存在時 |
| BR-04 | -0表示 | Object.is(val, -0)の場合、"-0"と表示 | 数値-0 |
| BR-05 | 無効なDate | Number.isNaN(+val)の場合、"Date { NaN }"と表示 | 無効なDateオブジェクト |
| BR-06 | minとindent排他 | min=trueの場合、indentは0以外指定不可 | オプション検証時 |

### 計算ロジック

**インデント生成**：
```typescript
function createIndent(indent: number): string {
  return Array.from({length: indent + 1}).join(' ');
}
```

**プラグイン検索**：
```typescript
function findPlugin(plugins: Plugins, val: unknown) {
  for (const plugin of plugins) {
    if (plugin.test(val)) {
      return plugin;
    }
  }
  return null;
}
```

## データベース操作仕様

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Error | 未知のオプションが指定された | オプション名を確認 |
| - | Error | minとindentが同時に指定された | どちらかのみ指定 |
| - | Error | themeがnull | themeをオブジェクトで指定 |
| - | TypeError | themeがオブジェクト以外 | themeをオブジェクトで指定 |
| - | TypeError | プラグインがstring以外を返却 | プラグインの実装を修正 |
| - | PrettyFormatPluginError | プラグイン実行時のエラー | プラグインの実装を確認 |

### リトライ仕様

該当なし

## トランザクション仕様

該当なし

## パフォーマンス要件

- 深いネスト構造でも適切な時間で処理を完了すること（maxDepthで制限可能）
- 大きな配列でも適切な時間で処理を完了すること（maxWidthで制限可能）

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

- センシティブな情報を含むオブジェクトを整形する場合、出力にその情報が含まれることに注意
- toJSONメソッドが実行されるため、副作用のあるtoJSONを持つオブジェクトには注意

## 備考

- 組み込みプラグイン: AsymmetricMatcher, DOMCollection, DOMElement, Immutable, ReactElement, ReactTestComponent
- プラグインはtest()でマッチング、serialize()またはprint()で整形を行う

---

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

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

### 推奨読解順序

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

まず、型定義から理解を始める。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/pretty-format/src/types.ts` | Options、Config、Plugin等の型定義 |

**主要処理フロー**:
1. **10-16行目**: Colors型（構文ハイライトの色定義）
2. **27-33行目**: Options型（オプションの完全型）
3. **35-41行目**: PrettyFormatOptions型（ユーザー入力オプション）
4. **45-60行目**: Config型（内部設定）
5. **62-69行目**: Printer型（整形関数の型）
6. **73-83行目**: NewPlugin型（新形式プラグイン）
7. **91-100行目**: OldPlugin型（旧形式プラグイン）

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/pretty-format/src/index.ts` | format関数とエクスポート |

**主要処理フロー**:
1. **517-539行目**: format関数（メインエントリーポイント）
   - **518-519行目**: オプションの検証
   - **520-525行目**: プラグインのマッチング
   - **528-535行目**: 基本値の整形
   - **538行目**: 複雑な値の整形
2. **408-422行目**: DEFAULT_OPTIONS（デフォルトオプション）
3. **541-548行目**: 組み込みプラグインのエクスポート

#### Step 3: 基本値の整形を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.ts | `packages/pretty-format/src/index.ts` | printBasicValue関数 |

**主要処理フロー**:
1. **122-198行目**: printBasicValue関数
   - **128-136行目**: boolean, undefined, nullの処理
   - **140-157行目**: number, bigint, string, function, symbolの処理
   - **161-194行目**: Promise, WeakMap, WeakSet, Date, Error, RegExpの処理

#### Step 4: 複雑な値の整形を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.ts | `packages/pretty-format/src/index.ts` | printComplexValue関数 |

**主要処理フロー**:
1. **204-299行目**: printComplexValue関数
   - **212-216行目**: 循環参照のチェック
   - **218行目**: 深度制限のチェック
   - **221-229行目**: toJSONの呼び出し
   - **231-243行目**: Argumentsの整形
   - **244-254行目**: 配列・TypedArrayの整形
   - **255-279行目**: Map、Setの整形
   - **283-298行目**: 一般オブジェクトの整形

#### Step 5: コレクション整形を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | collections.ts | `packages/pretty-format/src/collections.ts` | コレクション整形関数 |

**主要処理フロー**:
1. **35-94行目**: printIteratorEntries（Map等のエントリー整形）
2. **101-141行目**: printIteratorValues（Set等の値整形）
3. **148-195行目**: printListItems（配列等の要素整形）
4. **202-236行目**: printObjectProperties（オブジェクトプロパティ整形）

#### Step 6: プラグイン処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | index.ts | `packages/pretty-format/src/index.ts` | プラグイン関連関数 |

**主要処理フロー**:
1. **301-303行目**: isNewPlugin（プラグイン形式の判定）
2. **305-344行目**: printPlugin（プラグインの実行）
3. **346-358行目**: findPlugin（マッチするプラグインの検索）

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

```
format (index.ts)
    │
    ├─ validateOptions
    │
    ├─ findPlugin
    │      └─ plugin.test()
    │
    ├─ printPlugin (プラグインマッチ時)
    │      ├─ plugin.serialize() [NewPlugin]
    │      └─ plugin.print() [OldPlugin]
    │
    ├─ printBasicValue
    │      ├─ printNumber
    │      ├─ printBigInt
    │      ├─ printFunction
    │      ├─ printSymbol
    │      └─ printError
    │
    └─ printComplexValue
           │
           ├─ printer (再帰) [toJSON呼び出し時]
           │
           ├─ printListItems (collections.ts)
           │      └─ printer (再帰)
           │
           ├─ printIteratorEntries (collections.ts)
           │      └─ printer (再帰)
           │
           ├─ printIteratorValues (collections.ts)
           │      └─ printer (再帰)
           │
           └─ printObjectProperties (collections.ts)
                  └─ printer (再帰)
```

### データフロー図

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

任意のJavaScript値 ─────────▶ format()
                                  │
                                  ├─ プラグインチェック
                                  │      └─ マッチ → printPlugin
                                  │
                                  ├─ 基本値チェック
                                  │      └─ 基本値 → printBasicValue
                                  │
                                  └─ 複雑な値
                                         └─ printComplexValue
                                                │
                                                ├─ [Circular] (循環参照)
                                                ├─ [Array] (深度超過)
                                                └─ 再帰的に整形
                                                       │
                                                       ▼
                                              整形された文字列 ──▶ 出力

例：
{name: "test", count: 42}
         │
         ▼
Object {
  "count": 42,
  "name": "test",
}
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/pretty-format/src/index.ts` | ソース | メインのformat関数と整形ロジック |
| types.ts | `packages/pretty-format/src/types.ts` | ソース | 型定義 |
| collections.ts | `packages/pretty-format/src/collections.ts` | ソース | コレクション整形ヘルパー |
| AsymmetricMatcher.ts | `packages/pretty-format/src/plugins/AsymmetricMatcher.ts` | ソース | expect.any()等のプラグイン |
| DOMCollection.ts | `packages/pretty-format/src/plugins/DOMCollection.ts` | ソース | DOM要素コレクションプラグイン |
| DOMElement.ts | `packages/pretty-format/src/plugins/DOMElement.ts` | ソース | DOM要素プラグイン |
| Immutable.ts | `packages/pretty-format/src/plugins/Immutable.ts` | ソース | Immutable.jsプラグイン |
| ReactElement.ts | `packages/pretty-format/src/plugins/ReactElement.ts` | ソース | React要素プラグイン |
| ReactTestComponent.ts | `packages/pretty-format/src/plugins/ReactTestComponent.ts` | ソース | ReactTestRendererプラグイン |
| package.json | `packages/pretty-format/package.json` | 設定 | パッケージ定義 |
