# 機能設計書 53-ハッシュベースID生成

## 概要

本ドキュメントは、webpackにおけるハッシュベースID生成機能（HashedModuleIdsPlugin）の設計仕様を記述する。モジュールの識別子からハッシュダイジェストを生成し、短い固定長の文字列IDを割り当てることで、ビルド間のキャッシュを安定化させる機能である。

### 本機能の処理概要

**業務上の目的・背景**：webpackの以前のバージョン（v4以前）では、モジュールIDに連番が使用されていたため、モジュールの追加・削除により全IDが変動し、キャッシュが無効化される問題があった。HashedModuleIdsPluginはモジュールの相対パスからハッシュを生成し、短い文字列IDを割り当てることで、コード変更に影響されないモジュールのIDを安定させ、長期キャッシュの効率を向上させる。現在はDeterministicModuleIdsPluginが推奨されるが、互換性のために維持されている。

**機能の利用シーン**：webpack 4からの移行プロジェクトや、文字列ベースのハッシュIDを好む場面で利用される。`optimization.moduleIds: 'hashed'`として設定される（非推奨）。DEFAULTS.HASH_FUNCTIONで定義されたハッシュアルゴリズムを使用する。

**主要な処理内容**：
1. コンパイル済みモジュールの一覧を取得し、使用済みIDを収集する
2. モジュールをPreOrderIndexまたは識別子でソートする
3. 各モジュールの相対パス（識別子）からハッシュダイジェストを生成する
4. ハッシュをbase64エンコードし、指定桁数（デフォルト4文字）で切り出す
5. 衝突時はダイジェスト長を1文字ずつ延長して解決する

**関連システム・外部連携**：createHash（lib/util/createHash.js）を利用してハッシュ計算を行う。

**権限による制御**：特になし。webpack設定のoptimization.moduleIds により有効化・無効化を制御する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能はUIを持たない内部最適化処理であり、直接関連する画面は存在しない |

## 機能種別

計算処理 / 最適化処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| context | string | No | モジュール識別子を相対化するベースディレクトリ | 未指定時はcompiler.contextを使用 |
| hashFunction | string | No | ハッシュアルゴリズム（デフォルト: DEFAULTS.HASH_FUNCTION） | JSON Schemaバリデーション |
| hashDigest | string | No | ハッシュダイジェストのエンコーディング（デフォルト: "base64"） | JSON Schemaバリデーション |
| hashDigestLength | number | No | ハッシュダイジェストの初期長（デフォルト: 4） | JSON Schemaバリデーション |

### 入力データソース

- compilation.modules: コンパイル対象の全モジュール一覧
- compilation.usedModuleIds: 既に予約済みのモジュールID

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| moduleId | string | 各モジュールに割り当てられたハッシュベースの文字列ID（例: "a3b4"） |

### 出力先

ChunkGraphにsetModuleId()で設定

## 処理フロー

### 処理シーケンス

```
1. compilation.hooks.moduleIds フック発火
   └─ コンパイル最適化フェーズで呼び出される
2. 使用済みID一覧を収集（getUsedModuleIdsAndModules）
   └─ 既にIDが設定されているモジュールのIDをSetに格納
3. モジュールをPreOrderIndexまたは識別子でソート
   └─ compareModulesByPreOrderIndexOrIdentifier
4. 各モジュールの識別子からハッシュダイジェスト生成
   └─ createHash(hashFunction).update(ident).digest(hashDigest)
5. ダイジェストをhashDigestLength文字で切り出し
   └─ 衝突時はlen++で1文字ずつ延長
6. IDをChunkGraphに設定
   └─ chunkGraph.setModuleId(module, moduleId)
```

### フローチャート

```mermaid
flowchart TD
    A[moduleIdsフック発火] --> B[使用済みID収集]
    B --> C[モジュールをPreOrderIndexでソート]
    C --> D[各モジュールの相対パス取得]
    D --> E[ハッシュダイジェスト生成]
    E --> F[hashDigestLength文字で切り出し]
    F --> G{IDが使用済み?}
    G -->|Yes| H[ダイジェスト長を1文字延長]
    H --> G
    G -->|No| I[IDをChunkGraphに設定]
    I --> J[usedIdsに追加]
    J --> K{次のモジュール?}
    K -->|Yes| D
    K -->|No| L[完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-53-01 | ハッシュ衝突解決 | ダイジェスト長をインクリメントして衝突を解決する | usedIds.has(hashId.slice(0, len))がtrueの場合 |
| BR-53-02 | 処理順序固定 | PreOrderIndexまたは識別子による安定ソートで処理順序を固定する | 常時 |
| BR-53-03 | JSON Schemaバリデーション | オプションはHashedModuleIdsPlugin.jsonスキーマで検証される | プラグインコンストラクタ |
| BR-53-04 | デフォルトハッシュ関数 | DEFAULTS.HASH_FUNCTIONが使用される | hashFunctionオプション未指定時 |

### 計算ロジック

ID算出: `createHash(hashFunction).update(getFullModuleName(module, context, root)).digest(hashDigest).slice(0, hashDigestLength)`。衝突時は`hashDigestLength`を1ずつインクリメントし、usedIdsに存在しないIDが見つかるまで繰り返す。

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

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | 本機能はデータベースを使用しない |

### テーブル別操作詳細

該当なし（インメモリ処理のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | - | 本機能は明示的なエラーをスローしない | 衝突は自動延長で解決される |

### リトライ仕様

該当なし。衝突はダイジェスト長の延長で自動解決される。

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

該当なし（同期的なインメモリ処理）

## パフォーマンス要件

- createHashによるハッシュ計算は暗号ハッシュを使用するため、numberHashより計算コストが高い
- DeterministicModuleIdsPluginに比べてパフォーマンス面で劣る場合がある

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

特になし。ハッシュはモジュール識別用であり、セキュリティ目的ではない。

## 備考

- 本プラグインはwebpack 4時代の互換プラグインであり、新規プロジェクトではDeterministicModuleIdsPluginの使用が推奨される
- チャンク向けの対応プラグインは存在しない（モジュールIDのみ）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | HashedModuleIdsPlugin.js | `lib/ids/HashedModuleIdsPlugin.js` | プラグインのオプション型定義とデフォルト値を確認する |

**読解のコツ**: webpack 4との互換性を意識した設計であり、DeterministicModuleIdsPluginとの比較で理解すると良い。

- **8行目**: `DEFAULTS`からHASH_FUNCTIONを取得（configモジュールの定数）
- **22-29行目**: JSON Schemaバリデーションの設定
- **37-48行目**: コンストラクタ - デフォルト値の適用（hashFunction, hashDigest: "base64", hashDigestLength: 4）

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | HashedModuleIdsPlugin.js | `lib/ids/HashedModuleIdsPlugin.js` | apply()メソッドの全体フロー |

**主要処理フロー**:
1. **56行目**: `compilation.hooks.moduleIds`にタップ
2. **63行目**: `getUsedModuleIdsAndModules`で既存ID・未割り当てモジュールを取得
3. **64-66行目**: `compareModulesByPreOrderIndexOrIdentifier`でモジュールをソート
4. **67-79行目**: 各モジュールに対してハッシュIDを算出
   - **68行目**: `getFullModuleName`で相対パスを取得
   - **69行目**: `createHash(hashFunction)`でハッシュオブジェクト生成
   - **70行目**: `hash.update(ident)`でモジュール識別子をハッシュに入力
   - **71行目**: `hash.digest(hashDigest)`でダイジェスト生成
   - **73-75行目**: usedIdsとの衝突チェック、衝突時はlen++
   - **77行目**: `chunkGraph.setModuleId(module, moduleId)`でID設定

#### Step 3: ヘルパー関数の確認

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | IdHelpers.js | `lib/ids/IdHelpers.js` | getFullModuleName, getUsedModuleIdsAndModulesの実装 |

**主要処理フロー**:
- **119-120行目**: `getFullModuleName` - makePathsRelativeでコンテキスト相対パスを返す
- **232-261行目**: `getUsedModuleIdsAndModules` - 使用済みIDと未割り当てモジュールの取得

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

```
Compiler
    │
    └─ compilation.hooks.moduleIds
           └─ HashedModuleIdsPlugin.apply()
                  ├─ IdHelpers.getUsedModuleIdsAndModules()
                  │      └─ chunkGraph.getModuleId()
                  ├─ comparators.compareModulesByPreOrderIndexOrIdentifier()
                  ├─ IdHelpers.getFullModuleName()
                  │      └─ identifier.makePathsRelative()
                  └─ createHash()
                         ├─ hash.update(ident)
                         └─ hash.digest(hashDigest)
```

### データフロー図

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

compilation.modules ───────▶ getUsedModuleIdsAndModules() ──▶ [usedIds, modules]
                                                                    │
compiler.context ──────────▶ getFullModuleName() ──────────▶ ident（相対パス）
                                                                    │
ident ─────────────────────▶ createHash().update().digest() ▶ ハッシュダイジェスト
                                                                    │
hashDigest.slice(0, len) ──▶ 衝突チェック ────────────────▶ moduleId（例: "a3b4"）
                                                                    │
                              chunkGraph.setModuleId() ─────▶ モジュールにID設定
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| HashedModuleIdsPlugin.js | `lib/ids/HashedModuleIdsPlugin.js` | ソース | ハッシュベースモジュールID割り当てプラグイン |
| IdHelpers.js | `lib/ids/IdHelpers.js` | ソース | ID生成ヘルパー関数群 |
| createHash.js | `lib/util/createHash.js` | ソース | ハッシュ関数抽象化 |
| comparators.js | `lib/util/comparators.js` | ソース | ソート用比較関数 |
| identifier.js | `lib/util/identifier.js` | ソース | パス相対化ユーティリティ |
| defaults.js | `lib/config/defaults.js` | ソース | DEFAULTS.HASH_FUNCTION定義元 |
| HashedModuleIdsPlugin.json | `schemas/plugins/ids/HashedModuleIdsPlugin.json` | スキーマ | オプションバリデーションスキーマ |
| HashedModuleIdsPlugin.check.js | `schemas/plugins/ids/HashedModuleIdsPlugin.check.js` | スキーマ | バリデーション関数 |
