# 機能設計書 20-コンテンツハッシュ

## 概要

本ドキュメントは、webpackのRealContentHashPluginによる出力ファイルの内容に基づくハッシュ生成機能について、その設計と実装の詳細を記述する。

### 本機能の処理概要

コンテンツハッシュ機能は、出力アセットの実際の内容に基づいてハッシュ値を生成し、ファイル名に埋め込む機能である。コンパイル時のハッシュ（コンテンツが確定する前に計算されるプレースホルダーハッシュ）を、アセット内容が確定した後の「実コンテンツハッシュ」に置換する。

**業務上の目的・背景**：Webアプリケーションのキャッシュバスティング（キャッシュ無効化）戦略において、ファイル名にコンテンツハッシュを含めることが重要である。ファイル内容が変更されればハッシュが変わり、ブラウザは新しいバージョンを取得する。内容が変更されなければハッシュは同一のままで、キャッシュが継続的に利用される。RealContentHashPluginは、アセット間の相互参照（あるアセットが別のアセットのハッシュを含む）を考慮した正確なコンテンツハッシュを計算する。

**機能の利用シーン**：プロダクションビルドでのファイル名ハッシュ（`[contenthash]`プレースホルダー）、長期キャッシュ戦略の実装、CDN配信でのキャッシュバスティング。

**主要な処理内容**：
1. 全アセットのコンテンツとハッシュ情報を収集
2. アセット間のハッシュ参照関係（あるアセットが別アセットのハッシュを含む）を解析
3. 依存関係の順序でハッシュを再計算
4. アセット名とソース内のプレースホルダーハッシュを実コンテンツハッシュに置換

**関連システム・外部連携**：Compilation.processAssets（PROCESS_ASSETS_STAGE_OPTIMIZE_HASH）、createHash、webpack-sources（CachedSource, RawSource）と連携する。

**権限による制御**：output.hashFunction、output.hashDigest設定で制御。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能は画面機能マッピングに直接の関連画面なし。出力ファイル名のハッシュ生成として動作 |

## 機能種別

最適化処理 / ハッシュ生成

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| hashFunction | `HashFunction` | Yes | ハッシュアルゴリズム | "md4", "xxhash64"等 |
| hashDigest | `HashDigest` | Yes | ハッシュのダイジェスト形式 | "hex", "base64"等 |

### 入力データソース

- Compilationの全アセット（compilation.getAssets()）
- アセットのAssetInfo（contenthashフィールド）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 更新されたアセット | `Source` | 内部のハッシュプレースホルダーが実ハッシュに置換されたソース |
| 更新されたアセット名 | `string` | ファイル名のハッシュが実ハッシュに置換された名前 |
| 更新されたAssetInfo | `AssetInfo` | contenthashが実ハッシュに更新された情報 |

### 出力先

Compilationのアセットコレクション

## 処理フロー

### 処理シーケンス

```
1. 全アセットの情報収集
   └─ ソース、AssetInfo、コンテンツを取得
   └─ contenthashからhashToAssetsマップを構築
2. ハッシュ参照関係の解析
   └─ 各アセットのコンテンツ内でhashToAssetsのハッシュを正規表現で検索
   └─ ownHashes（自身のハッシュ）とreferencedHashes（参照先のハッシュ）を分離
3. 依存関係の解決
   └─ ハッシュ間の依存関係をDAGとして構築
   └─ トポロジカルソートで処理順序を決定
4. 実コンテンツハッシュの計算
   └─ 依存先のハッシュが確定してから自身のハッシュを計算
   └─ updateHashフックでカスタムハッシュ計算を許可
5. ハッシュの置換
   └─ アセットのソースとファイル名内のプレースホルダーハッシュを実ハッシュに置換
```

### フローチャート

```mermaid
flowchart TD
    A[processAssets OPTIMIZE_HASH] --> B[全アセット情報収集]
    B --> C[hashToAssetsマップ構築]
    C --> D[ハッシュ正規表現生成]
    D --> E[各アセットのコンテンツを検索]
    E --> F[ownHashes / referencedHashes 分離]
    F --> G[依存関係DAG構築]
    G --> H[トポロジカルソート]
    H --> I{未処理ハッシュ<br>あり?}
    I -->|Yes| J[依存先ハッシュ確定済み?]
    J -->|Yes| K[createHashで<br>実ハッシュ計算]
    J -->|No| I
    K --> L[アセット名・ソースの<br>ハッシュ置換]
    L --> I
    I -->|No| M[完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 自己参照対応 | アセットが自身のハッシュを含む場合（ownHashes）、自身のハッシュを除外してコンテンツハッシュを計算 | ownHashes存在時 |
| BR-02 | 循環参照検出 | 依存関係に循環がある場合は適切にエラー報告 | DAG構築時 |
| BR-03 | キャッシュ活用 | 分析（analyse）と生成（generate）を別キャッシュとして管理 | リビルド時 |
| BR-04 | Bufferコンテンツ対応 | コンテンツがBufferの場合はハッシュ検索をスキップ | バイナリアセット |
| BR-05 | updateHashフック | SyncBailHookでカスタムハッシュ計算を許可 | フック登録時 |

### 計算ロジック

- **ハッシュ正規表現**: 全アセットのcontenthashをOR結合した正規表現で一括検索
- **コンテンツハッシュ計算**: `createHash(hashFunction).update(buffers).digest(hashDigest)` でアセットのコンテンツバッファからハッシュを生成
- **重複バッファ除去**: `mapAndDeduplicateBuffers`でBuffer.equalsによる比較で重複を排除

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | WebpackError | アセットがキャッシュから復元できない場合 | "Some kind of unexpected caching problem occurred"エラー |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- CachedSourceによるソースのキャッシュ化で重複計算を回避
- 分析・生成の2段階キャッシュ（cacheAnalyse, cacheGenerate）
- WeakMapによるCachedSourceのキャッシュマッピング
- Promise.allによる並列処理

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

該当なし

## 備考

- `[contenthash]`プレースホルダーはoutput.filenameやoutput.chunkFilenameで使用
- xxhash64がデフォルトのハッシュアルゴリズム（md4より高速）
- PROCESS_ASSETS_STAGE_OPTIMIZE_HASHは他の全てのアセット最適化後に実行される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | RealContentHashPlugin.js | `lib/optimize/RealContentHashPlugin.js` | 96-108行目: AssetInfoForRealContentHash型定義（name, info, source, newSource, content, ownHashes, referencedHashes, hashes等） |

**読解のコツ**: AssetInfoForRealContentHashが中心的なデータ構造。各アセットに対してownHashes（自身のハッシュ）とreferencedHashes（参照先のハッシュ）を分離して管理する。

#### Step 2: アセット情報の収集を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RealContentHashPlugin.js | `lib/optimize/RealContentHashPlugin.js` | 176-212行目: 全アセットの情報収集とhashToAssetsマップの構築 |

**主要処理フロー**:
1. **177行目**: compilation.getAssets()で全アセットを取得
2. **182-183行目**: toCachedSourceでソースをキャッシュ化
3. **184行目**: cachedSource.source()でコンテンツを取得
4. **187行目**: info.contenthashからハッシュセットを構築
5. **203-211行目**: hashToAssetsマップ（ハッシュ→アセットリスト）を構築

#### Step 3: ハッシュ参照関係の解析を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | RealContentHashPlugin.js | `lib/optimize/RealContentHashPlugin.js` | 213-248行目: 正規表現によるハッシュ検索とownHashes/referencedHashesの分離 |

**主要処理フロー**:
- **213-216行目**: hashToAssetsのキーからOR結合した正規表現を生成
- **217行目**: Promise.allで全アセットを並列処理
- **220-224行目**: Bufferコンテンツはスキップ
- **229-246行目**: cacheAnalyse経由で分析結果をキャッシュ。コンテンツ内のハッシュマッチを走査し、自身のハッシュ（ownHashes）と参照先のハッシュ（referencedHashes）に分類

#### Step 4: 依存関係解決とハッシュ再計算を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | RealContentHashPlugin.js | `lib/optimize/RealContentHashPlugin.js` | 249-299行目: getDependencies関数とhashInfo関数 |

**主要処理フロー**:
- **253-288行目**: getDependenciesでハッシュの依存関係を取得。キャッシュ不整合時はWebpackErrorを報告
- **293-299行目**: hashInfoでデバッグ用のハッシュ情報文字列を生成

#### Step 5: ヘルパー関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | RealContentHashPlugin.js | `lib/optimize/RealContentHashPlugin.js` | 36-91行目: addToList, mapAndDeduplicateBuffers, quoteMeta, toCachedSource |

**主要処理フロー**:
- **36-44行目**: addToListは配列または単一値をSetに追加
- **52-66行目**: mapAndDeduplicateBuffersでBuffer.equalsによる重複排除
- **73行目**: quoteMetaで正規表現のメタ文字をエスケープ
- **82-91行目**: toCachedSourceでSourceをCachedSourceに変換（WeakMapキャッシュ）

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

```
Compilation.hooks.processAssets (PROCESS_ASSETS_STAGE_OPTIMIZE_HASH)
    |
    +-- RealContentHashPlugin
            |
            +-- compilation.getAssets()
            +-- toCachedSource(source)
            |
            +-- [ハッシュ参照解析]
            |       +-- hashRegExp (全ハッシュのOR結合)
            |       +-- content.match(hashRegExp)
            |       +-- ownHashes / referencedHashes 分離
            |
            +-- [依存関係解決]
            |       +-- getDependencies(hash)
            |       +-- トポロジカルソート
            |
            +-- [ハッシュ再計算]
            |       +-- createHash(hashFunction)
            |       +-- mapAndDeduplicateBuffers()
            |       +-- hooks.updateHash.call()
            |
            +-- [ハッシュ置換]
                    +-- compilation.updateAsset(name, newSource, newInfo)
                    +-- compilation.renameAsset(name, newName)
```

### データフロー図

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

compilation.getAssets() ------> アセット情報収集              +--> hashToAssetsマップ
                                     |                        |
コンテンツ文字列 ------------> ハッシュ参照解析              +--> ownHashes / referencedHashes
                                     |                        |
AssetInfo.contenthash -------> 依存関係DAG構築              +--> 処理順序（トポロジカル）
                                     |                        |
hashFunction / hashDigest --> 実コンテンツハッシュ計算      +--> 新しいハッシュ値
                                     |                        |
                                ハッシュ置換               +--> 更新されたアセット名
                                     |                    +--> 更新されたソース
                                     v                    +--> 更新されたAssetInfo
                                compilation.updateAsset()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| RealContentHashPlugin.js | `lib/optimize/RealContentHashPlugin.js` | ソース | メインプラグイン |
| createHash.js | `lib/util/createHash.js` | ユーティリティ | ハッシュアルゴリズム抽象化 |
| Compilation.js | `lib/Compilation.js` | ソース | アセット管理・processAssetsフック |
| WebpackError.js | `lib/WebpackError.js` | ソース | エラークラス |
| comparators.js | `lib/util/comparators.js` | ユーティリティ | 比較関数 |
