# 機能設計書 17-@jest/snapshot-utils

## 概要

本ドキュメントは、Jestテスティングフレームワークにおける`@jest/snapshot-utils`パッケージの機能設計を記述する。このパッケージは、スナップショットテストで使用される低レベルユーティリティ関数群を提供する。

### 本機能の処理概要

**業務上の目的・背景**：`jest-snapshot`パッケージが依存するスナップショットファイルの読み書き、キー変換、バージョン管理などの基盤機能を分離して提供する。これにより、スナップショット機能の保守性と再利用性が向上する。

**機能の利用シーン**：
- スナップショットファイルの読み込みと解析
- スナップショットファイルへの書き込み
- テスト名とスナップショットキーの相互変換
- スナップショットファイルのバージョン検証

**主要な処理内容**：
1. `getSnapshotData()` - スナップショットファイルの読み込みとパース
2. `saveSnapshotFile()` - スナップショットファイルへの書き込み
3. `testNameToKey()` - テスト名からスナップショットキーへの変換
4. `keyToTestName()` - スナップショットキーからテスト名への変換
5. `escapeBacktickString()` - バッククォート文字列のエスケープ
6. `ensureDirectoryExists()` - ディレクトリの確保
7. `normalizeNewlines()` - 改行コードの正規化

**関連システム・外部連携**：`jest-snapshot`パッケージが主な利用者。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 4 | Snapshot Testingページ | 参照画面 | スナップショットファイル形式の説明 |

## 機能種別

ユーティリティ関数 / ファイル操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| snapshotPath | string | Yes | スナップショットファイルパス | 有効なファイルパス |
| update | SnapshotUpdateState | Yes (getSnapshotData) | 更新モード | 'all'\|'new'\|'none' |
| snapshotData | SnapshotData | Yes (saveSnapshotFile) | 保存データ | オブジェクト |
| testName | string | Yes (testNameToKey) | テスト名 | 文字列 |
| count | number | Yes (testNameToKey) | カウンター | 正の整数 |

### 入力データソース

スナップショットファイル、テストランナーからの引数

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| data | SnapshotData | パース済みスナップショットデータ |
| dirty | boolean | ファイル更新が必要か |
| key | string | スナップショットキー |

### 出力先

スナップショットファイル、呼び出し元関数

## 処理フロー

### 処理シーケンス

```
getSnapshotData():
1. ファイル存在確認
2. ファイル読み込み
3. Function constructorでパース
4. ヘッダーバリデーション
5. {data, dirty}を返却

saveSnapshotFile():
1. キーのソート
2. スナップショット文字列の構築
3. ディレクトリ作成
4. ファイル書き込み
```

### フローチャート

```mermaid
flowchart TD
    A[getSnapshotData呼び出し] --> B{ファイル存在?}
    B -->|No| C[空データ返却]
    B -->|Yes| D[ファイル読み込み]
    D --> E[Function constructorでパース]
    E --> F[ヘッダーバリデーション]
    F --> G{バージョン有効?}
    G -->|No| H{updateモード?}
    H -->|none| I[エラースロー]
    H -->|all/new| J[dirty=true設定]
    G -->|Yes| K[dirty=false]
    J --> L[{data, dirty}返却]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | スナップショットバージョン | 現在バージョンは'1' | 常時 |
| BR-02 | ヘッダー形式 | `// Jest Snapshot v1, https://...` | ファイル先頭 |
| BR-03 | キー形式 | `テスト名 カウンター` | 常時 |
| BR-04 | 改行正規化 | CR/CRLFをLFに変換 | ファイル保存時 |
| BR-05 | 自然順ソート | キーはnaturalCompareでソート | ファイル保存時 |

### 計算ロジック

testNameToKey: `${normalizeTestNameForKey(testName)} ${count}`
keyToTestName: 末尾の` ${count}`を除去してdenormalize

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| N/A | Error | 古いスナップショットバージョン | スナップショット更新 |
| N/A | Error | 新しいスナップショットバージョン | Jest更新 |
| N/A | Error | キーが数字で終わらない | 正しいキー形式を使用 |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

Function constructorによるパースは高速だが、大量のスナップショットではメモリ使用量に注意。

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

Function constructorを使用するため、スナップショットファイルの信頼性が重要。

## 備考

- スナップショットファイルはJavaScriptとして実行可能な形式
- natural-compareライブラリでキーをソート

---

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

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

### 推奨読解順序

#### Step 1: 型定義を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/jest-snapshot-utils/src/types.ts` | SnapshotData型 |

**主要処理フロー**:
- SnapshotData: `{ [key: string]: string }` の形式

#### Step 2: コアユーティリティを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | utils.ts | `packages/jest-snapshot-utils/src/utils.ts` | 全ユーティリティ関数 |

**主要処理フロー**:
- **15-16行目**: SNAPSHOT_VERSION = '1' 定数
- **17行目**: SNAPSHOT_GUIDE_LINK 定数
- **24-25行目**: writeSnapshotVersion - ヘッダー生成
- **27-92行目**: validateSnapshotHeader - ヘッダー検証
- **94-106行目**: normalizeTestNameForKey - 改行文字の正規化
- **108-121行目**: denormalizeTestNameFromKey - 改行文字の復元
- **122-123行目**: testNameToKey - キー生成
- **125-131行目**: keyToTestName - テスト名抽出
- **133-165行目**: getSnapshotData - ファイル読み込みとパース
- **167-168行目**: escapeBacktickString - バッククォートエスケープ
- **173-177行目**: ensureDirectoryExists - ディレクトリ作成
- **179-180行目**: normalizeNewlines - 改行正規化
- **182-200行目**: saveSnapshotFile - ファイル書き込み

**読解のコツ**: `new Function('exports', snapshotContents)` でスナップショットをパースしている点に注目。

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

```
getSnapshotData (utils.ts:133)
    │
    ├─ fs.existsSync
    │
    ├─ fs.readFileSync
    │
    ├─ new Function('exports', contents) - パース
    │
    └─ validateSnapshotHeader (utils.ts:27)
           │
           ├─ SNAPSHOT_HEADER_REGEXP.exec()
           │
           └─ バージョン比較

saveSnapshotFile (utils.ts:182)
    │
    ├─ Object.keys().sort(naturalCompare)
    │
    ├─ printBacktickString
    │      └─ escapeBacktickString
    │
    ├─ normalizeNewlines
    │
    ├─ ensureDirectoryExists (utils.ts:173)
    │      └─ fs.mkdirSync
    │
    └─ fs.writeFileSync

testNameToKey (utils.ts:122)
    │
    └─ normalizeTestNameForKey (utils.ts:94)

keyToTestName (utils.ts:125)
    │
    └─ denormalizeTestNameFromKey (utils.ts:108)
```

### データフロー図

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

スナップショットファイル ──▶ getSnapshotData() ─────────▶ {data, dirty}
                              │
                              ├── fs.readFileSync
                              │
                              ├── new Function('exports', ...)
                              │
                              └── validateSnapshotHeader

SnapshotData ─────────────▶ saveSnapshotFile() ─────────▶ .snapファイル
                              │
                              ├── キーソート
                              │
                              ├── バッククォートエスケープ
                              │
                              └── fs.writeFileSync
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/jest-snapshot-utils/src/index.ts` | ソース | エクスポート定義 |
| utils.ts | `packages/jest-snapshot-utils/src/utils.ts` | ソース | 全ユーティリティ関数実装 |
| types.ts | `packages/jest-snapshot-utils/src/types.ts` | ソース | 型定義 |
