# 機能設計書 11-チャンクグループ管理

## 概要

本ドキュメントは、webpackのチャンクグループ管理機能について、その設計と実装の詳細を記述する。ChunkGroupクラスおよびEntrypointクラスを中心に、関連するチャンクのグループ化、親子関係の管理、並列読み込みの制御について解説する。

### 本機能の処理概要

チャンクグループ管理は、webpackのバンドル処理において生成される複数のチャンク（出力ファイル）を論理的にグループ化し、それらの間の依存関係や読み込み順序を制御する機能である。

**業務上の目的・背景**：Webアプリケーションでは、単一の巨大なバンドルファイルではなく、複数のチャンクに分割して出力することが一般的である。チャンクグループは、ページやルートごとに必要なチャンクの集合を管理し、ブラウザが正しい順序でチャンクを読み込めるようにする。これにより、初期ロード時間の最適化と、コード分割による効率的なキャッシュ戦略が可能となる。

**機能の利用シーン**：マルチページアプリケーションにおける各ページのエントリポイント管理、動的インポート（`import()`）によるオンデマンドチャンク読み込み、SplitChunksPluginによる共通モジュール分割時のチャンクグルーピングなど、あらゆるコード分割シナリオで利用される。

**主要な処理内容**：
1. チャンクの論理グループへの追加・削除・置換の管理
2. チャンクグループ間の親子関係（`_parents`, `_children`）の構築と維持
3. 非同期エントリポイント（`_asyncEntrypoints`）の管理
4. モジュールの前順序（preOrder）および後順序（postOrder）インデックスの管理
5. オリジン情報（どのモジュールのどの位置からこのグループが参照されたか）の記録
6. Entrypointクラスによるランタイムチャンクの管理とdependOn機構の提供

**関連システム・外部連携**：ChunkGraph、buildChunkGraph、Compilation、各種最適化プラグイン（SplitChunksPlugin、RemoveParentModulesPlugin等）と密接に連携する。

**権限による制御**：特になし。コンパイル内部で自動的に管理される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | Page A (many-pages) | 補助機能 | 分割されたチャンクの並列読み込み制御 |
| 2 | Page B (many-pages) | 補助機能 | 分割されたチャンクの並列読み込み制御 |
| 3 | Page C (many-pages) | 補助機能 | 分割されたチャンクの並列読み込み制御 |
| 4 | Page D (many-pages) | 補助機能 | 分割されたチャンクの並列読み込み制御 |
| 8 | Page A (hybrid-routing) | 遷移先機能 | ページ遷移時のチャンク依存関係管理 |
| 9 | Page B (hybrid-routing) | 遷移先機能 | ページ遷移時のチャンク依存関係管理 |

## 機能種別

データ構造管理 / グラフ構築

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| options | `string \| ChunkGroupOptions` | No | チャンクグループ名またはオプションオブジェクト | 文字列の場合は`{name: options}`に変換 |
| options.name | `string \| undefined` | No | チャンクグループの名前 | - |
| options.preloadOrder | `number` | No | プリロードの優先順序 | - |
| options.prefetchOrder | `number` | No | プリフェッチの優先順序 | - |
| options.fetchPriority | `"low" \| "high" \| "auto"` | No | フェッチ優先度 | - |

### 入力データソース

- Compilation.addChunkInGroupによるチャンクグループの生成
- buildChunkGraphによるモジュールグラフからのチャンクグループ構築
- EntryPluginによるエントリポイントの登録

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| chunks | `Chunk[]` | このグループに属するチャンクの配列 |
| origins | `OriginRecord[]` | このグループの生成元情報 |
| _children | `SortableSet<ChunkGroup>` | 子チャンクグループの集合 |
| _parents | `SortableSet<ChunkGroup>` | 親チャンクグループの集合 |
| id | `string` | チャンクIDの連結による一意識別子 |

### 出力先

ChunkGraph、Compilationオブジェクト内のチャンクグループコレクション

## 処理フロー

### 処理シーケンス

```
1. ChunkGroupインスタンスの生成
   └─ オプションの正規化、デバッグID割り当て、内部データ構造の初期化
2. チャンクの追加（pushChunk / unshiftChunk / insertChunk）
   └─ 重複チェック後、チャンク配列に追加
3. 親子関係の構築（addChild / addParent）
   └─ SortableSetによるソート済み管理
4. オリジン情報の記録（addOrigin）
   └─ モジュール、ロケーション、リクエスト名を記録
5. モジュール順序インデックスの設定
   └─ preOrder / postOrder のインデックスをモジュールごとに記録
6. チャンクグループの削除時（remove）
   └─ 親子関係の再接続、チャンクからのグループ参照解除
```

### フローチャート

```mermaid
flowchart TD
    A[ChunkGroup生成] --> B[オプション正規化]
    B --> C{チャンクの追加要求}
    C -->|pushChunk| D[末尾に追加]
    C -->|unshiftChunk| E[先頭に追加]
    C -->|insertChunk| F[指定位置に挿入]
    D --> G[親子関係の構築]
    E --> G
    F --> G
    G --> H[addParent/addChild]
    H --> I[モジュール順序インデックス設定]
    I --> J[オリジン情報記録]
    J --> K{削除要求?}
    K -->|Yes| L[親子関係の再接続]
    L --> M[チャンク参照解除]
    K -->|No| N[完了]
    M --> N
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | チャンク重複防止 | 同一チャンクを同じグループに二重追加しない | pushChunk/unshiftChunk/insertChunk時 |
| BR-02 | 親子関係の双方向性 | 親への追加は子への参照設定も伴う | addChild/addParent時 |
| BR-03 | 削除時の再接続 | グループ削除時、子は親の子として再接続される | remove()実行時 |
| BR-04 | オプションマージ戦略 | Order系オプションはMath.maxでマージ | addOptions時 |
| BR-05 | Entrypointの初期チャンク判定 | EntrypointのisInitial()はコンストラクタのinitialフラグに依存 | isInitial()呼び出し時 |

### 計算ロジック

- **チャンクグループID**: `Array.from(this.chunks, x => x.id).join("+")`により、所属チャンクのIDを"+"で結合して生成
- **ソート順序**: `sortById`関数によりチャンクグループのIDで辞書順にソート

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

該当なし（インメモリデータ構造のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Error | insertChunkで`before`チャンクが見つからない | "before chunk not found"エラーをスロー |
| - | Error | addOptionsでマージ戦略が未定義のキー | "No option merge strategy"エラーをスロー |
| - | checkConstraints | 親子関係の整合性が崩れた場合 | 子に親がない、または親に子がないケースを検出 |

### リトライ仕様

該当なし

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

該当なし（インメモリ操作のみ）

## パフォーマンス要件

- SortableSetによるソート済みセット管理で、重複検出と順序保証をO(log n)で実現
- bigintベースのビットマスクによるモジュール管理（RemoveParentModulesPlugin連携時）

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

該当なし

## 備考

- ChunkGroupはベースクラスであり、Entrypointはその特殊化（初期読み込み用チャンクグループ）
- `getModuleIndex` / `getModuleIndex2`は非推奨となり、`getModulePreOrderIndex` / `getModulePostOrderIndex`に改名済み
- debugIdは5000から開始する内部カウンタで割り当て

---

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

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

### 推奨読解順序

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

ChunkGroupクラスの内部データ構造を把握することが最初のステップである。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ChunkGroup.js | `lib/ChunkGroup.js` | コアとなるデータ構造（_children, _parents, chunks, origins等） |
| 1-2 | SortableSet.js | `lib/util/SortableSet.js` | 親子関係管理に使われるソート済みセットの実装 |

**読解のコツ**: ChunkGroupのコンストラクタ（74-113行目）を読むことで、全てのインスタンスプロパティを把握できる。`SortableSet`は`Set`を拡張しソート機能とキャッシュ機能を追加したもの。

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

Entrypointクラスはエントリポイント固有の拡張を提供する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Entrypoint.js | `lib/Entrypoint.js` | ChunkGroupを継承し、runtimeChunk・entrypointChunk・dependOn機能を追加 |

**主要処理フロー**:
1. **28-44行目**: コンストラクタでinitialフラグ、_runtimeChunk、_entrypointChunk、_dependOnを初期化
2. **58-72行目**: setRuntimeChunk/getRuntimeChunkによるランタイムチャンクの管理。getRuntimeChunkは親Entrypointへの再帰的探索を行う
3. **107-109行目**: addDependOnによるエントリポイント間の依存関係設定

#### Step 3: チャンク管理操作を理解する

ChunkGroupのチャンク追加・削除・置換操作を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ChunkGroup.js | `lib/ChunkGroup.js` | pushChunk(226-233行目)、unshiftChunk(187-197行目)、insertChunk(205-219行目)、replaceChunk(240-256行目)、removeChunk(262-269行目) |

**主要処理フロー**:
- **226-233行目**: pushChunkは重複チェック後に末尾に追加。既に存在する場合はfalseを返す
- **187-197行目**: unshiftChunkは先頭に追加。既に存在する場合は先頭に移動
- **240-256行目**: replaceChunkは旧チャンクを新チャンクに置換。新チャンクが既に存在する場合の位置調整ロジックあり

#### Step 4: 親子関係とグラフ構造を理解する

チャンクグループのツリー構造がどのように構築・維持されるかを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ChunkGroup.js | `lib/ChunkGroup.js` | addChild(282-286行目)、addParent(321-327行目)、remove(449-483行目) |
| 4-2 | buildChunkGraph.js | `lib/buildChunkGraph.js` | チャンクグループの親子関係がどのように構築されるか |

**主要処理フロー**:
- **449-483行目**: remove()メソッドは、自身を削除する際に親と子の直接接続を行い、グラフの連結性を保つ。まず親から自身を削除し、子を親の子に追加。次に子から自身を削除。最後にチャンクからグループ参照を解除

#### Step 5: ソートと比較を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | ChunkGroup.js | `lib/ChunkGroup.js` | compareTo(496-503行目)、getChildrenByOrders(510-543行目)、sortItems(485-487行目) |

**主要処理フロー**:
- **496-503行目**: compareToはまずチャンク数で比較し、同数の場合はチャンクの内容を反復比較
- **510-543行目**: getChildrenByOrdersは子グループをprefetch/preload等の種別ごとに分類し、order値でソートして返す

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

```
Compilation
    |
    +-- buildChunkGraph.js
    |       |
    |       +-- new ChunkGroup(options)
    |       +-- new Entrypoint(entryOptions)
    |       +-- chunkGroup.pushChunk(chunk)
    |       +-- chunkGroup.addChild(childGroup)
    |       +-- chunkGroup.addParent(parentGroup)
    |       +-- chunkGroup.addBlock(asyncBlock)
    |       +-- chunkGroup.addOrigin(module, loc, request)
    |
    +-- SplitChunksPlugin
    |       +-- chunkGroup.getChildren()
    |       +-- chunkGroup.chunks (読み取り)
    |
    +-- RemoveParentModulesPlugin
    |       +-- chunkGroup.parentsIterable
    |       +-- chunkGroup.childrenIterable
    |       +-- chunkGroup.chunks (読み取り)
    |
    +-- Entrypoint
            +-- setRuntimeChunk(chunk)
            +-- getRuntimeChunk()
            +-- setEntrypointChunk(chunk)
            +-- addDependOn(entrypoint)
```

### データフロー図

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

webpack設定のentry -----> EntryPlugin                        +--> Entrypoint (isInitial=true)
                               |                             |
モジュールグラフ --------> buildChunkGraph -------> ChunkGroup +---> chunks[]
                               |                             |
import()式 ------------> AsyncDependenciesBlock              +--> 親子関係グラフ
                               |                             |
SplitChunksPlugin -----> チャンク分割処理                     +--> 分割後の新ChunkGroup
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ChunkGroup.js | `lib/ChunkGroup.js` | ソース | チャンクグループの基底クラス |
| Entrypoint.js | `lib/Entrypoint.js` | ソース | エントリポイント特化のチャンクグループ |
| buildChunkGraph.js | `lib/buildChunkGraph.js` | ソース | モジュールグラフからチャンクグラフを構築 |
| ChunkGraph.js | `lib/ChunkGraph.js` | ソース | チャンクとモジュールの関連付け管理 |
| Chunk.js | `lib/Chunk.js` | ソース | 個々のチャンクの実体 |
| SortableSet.js | `lib/util/SortableSet.js` | ユーティリティ | ソート済みセットの実装 |
| comparators.js | `lib/util/comparators.js` | ユーティリティ | チャンク・モジュールの比較関数群 |
| GraphHelpers.js | `lib/GraphHelpers.js` | ユーティリティ | 親子関係接続のヘルパー関数 |
| AsyncDependenciesBlock.js | `lib/AsyncDependenciesBlock.js` | ソース | 非同期依存ブロック（動的インポート用） |
