# 機能設計書 12-SplitChunksPlugin

## 概要

本ドキュメントは、webpackのSplitChunksPluginによる共通モジュールの自動分割およびキャッシュグループによる高度なコード分割機能について、その設計と実装の詳細を記述する。

### 本機能の処理概要

SplitChunksPluginは、複数のチャンク間で共有されるモジュールを自動的に検出し、独立した共通チャンクとして分割する最適化プラグインである。キャッシュグループの仕組みにより、node_modulesからのベンダーライブラリ分離や、特定条件に基づくカスタムチャンク分割を柔軟に設定できる。

**業務上の目的・背景**：Webアプリケーションにおいて、複数のページやルートで共通に使用されるライブラリやユーティリティを個別にバンドルすると、ブラウザキャッシュの効率が低下し、全体のダウンロードサイズが増大する。SplitChunksPluginは共通モジュールを自動的に検出して分離し、ブラウザキャッシュの再利用を最大化しつつ、初回ロードと再訪問時の両方でパフォーマンスを最適化する。

**機能の利用シーン**：マルチページアプリケーションでの共通ベンダーライブラリの分離（React, lodash等）、SPA内の動的チャンク間での共通コード共有、明示的なvendorチャンク定義、チャンクサイズの制御と最適化。

**主要な処理内容**：
1. モジュールがどのチャンクで使用されているかを分析し、キャッシュグループ条件に照合
2. minChunks、minSize、maxSize等の閾値に基づくフィルタリング
3. 決定的グルーピングアルゴリズムによるモジュールのグループ化
4. 既存チャンクの再利用判定（reuseExistingChunk）
5. 新規チャンクの生成とモジュールの移動
6. maxAsyncRequests / maxInitialRequestsによるリクエスト数制限

**関連システム・外部連携**：ChunkGraph、ModuleGraph、Chunk、ChunkGroup、deterministicGroupingユーティリティと連携する。

**権限による制御**：特になし。optimization.splitChunks設定により制御される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | Page A (many-pages) | 主機能 | 共通モジュール(m1,m2等)を他ページと共有するための自動チャンク分割 |
| 2 | Page B (many-pages) | 主機能 | 共通モジュール(m1,m2等)を他ページと共有するための自動チャンク分割 |
| 3 | Page C (many-pages) | 主機能 | 共通モジュール(m1,m2等)を他ページと共有するための自動チャンク分割 |
| 4 | Page D (many-pages) | 主機能 | 共通モジュール(m6,m7,m8等)を他ページと共有するための自動チャンク分割 |
| 5 | Page E (many-pages) | 主機能 | 共通モジュール(m6,m7,m8)を他ページと共有するための自動チャンク分割 |
| 6 | Page F (many-pages) | 主機能 | 共通モジュール(m6,m7,m8,s2,s3)を他ページと共有するための自動チャンク分割 |
| 7 | Page G (many-pages) | 主機能 | 共通モジュール(m6)を他ページと共有するための自動チャンク分割 |
| 12 | Page A (multiple-entry-points) | 主機能 | commons.jsチャンクとしてcommon.jsをPageBと共有分割 |
| 13 | Page B (multiple-entry-points) | 主機能 | commons.jsチャンクとしてcommon.jsをPageAと共有分割 |
| 14 | Page A (two-explicit-vendor-chunks) | 主機能 | 明示的なvendorチャンク(vendor1,vendor2)の分離 |
| 15 | Page B (two-explicit-vendor-chunks) | 主機能 | 明示的なvendorチャンクの分離 |
| 16 | Page C (two-explicit-vendor-chunks) | 主機能 | 明示的なvendorチャンクの分離 |

## 機能種別

最適化処理 / チャンク分割

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| chunks | `"all" \| "async" \| "initial" \| function` | No | 対象チャンクのフィルタ | デフォルト: "all" |
| minSize | `number \| Record<string, number>` | No | 分割対象の最小サイズ | デフォルト: 型別指定可能 |
| maxSize | `number \| Record<string, number>` | No | チャンクの最大サイズ | 超過時にさらに分割 |
| minChunks | `number` | No | 分割に必要な最小共有チャンク数 | デフォルト: 1 |
| maxAsyncRequests | `number` | No | オンデマンド時の最大並列リクエスト数 | - |
| maxInitialRequests | `number` | No | 初期読み込み時の最大並列リクエスト数 | - |
| cacheGroups | `object` | No | キャッシュグループの設定 | test/priority/enforce等 |
| cacheGroups[].test | `RegExp \| string \| function` | No | モジュールマッチング条件 | - |
| cacheGroups[].priority | `number` | No | キャッシュグループの優先度 | 高い値が優先 |
| cacheGroups[].enforce | `boolean` | No | minSize/minChunks等の制約を無視して強制分割 | - |
| cacheGroups[].reuseExistingChunk | `boolean` | No | 既存チャンクの再利用 | - |
| cacheGroups[].usedExports | `boolean` | No | usedExportsを考慮した分割 | - |

### 入力データソース

- webpack設定のoptimization.splitChunksオプション
- CompilationのchunkGraph・moduleGraphからのモジュール・チャンク情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 新規チャンク | `Chunk` | 分割により生成された新しいチャンク |
| 更新されたチャンクグループ | `ChunkGroup` | 新チャンクを含むグループ |
| 移動されたモジュール | `Module` | 共通チャンクに移動されたモジュール |

### 出力先

Compilationのchunksコレクション、ChunkGraphのモジュール-チャンクマッピング

## 処理フロー

### 処理シーケンス

```
1. 設定の正規化
   └─ chunks, minSize, maxSize, cacheGroups等のオプションを正規化
2. モジュールのキャッシュグループ照合
   └─ 各モジュールに対してgetCacheGroupsを実行し、マッチするグループを特定
3. チャンク情報の収集
   └─ 各キャッシュグループごとに、モジュール・チャンク・サイズ情報を集計
4. エントリのフィルタリング
   └─ minSize, minChunks, maxAsyncRequests, maxInitialRequests等の制約でフィルタ
5. エントリの優先度ソート
   └─ priority, チャンク数, サイズ削減量, モジュール識別子で比較ソート
6. チャンク生成・モジュール移動
   └─ 最も効果的なエントリから順に新チャンク生成とモジュール移動
7. maxSizeによる追加分割
   └─ maxSizeを超えるチャンクをさらに分割
```

### フローチャート

```mermaid
flowchart TD
    A[optimizeChunks hook] --> B[設定の正規化]
    B --> C[全モジュールをスキャン]
    C --> D{キャッシュグループに<br>マッチ?}
    D -->|Yes| E[ChunksInfoItemに追加]
    D -->|No| C
    E --> F[minSize/minChunks<br>フィルタリング]
    F --> G[優先度ソート]
    G --> H{処理対象あり?}
    H -->|Yes| I[新チャンク生成]
    I --> J[モジュール移動]
    J --> K[maxRequests制約チェック]
    K --> G
    H -->|No| L[maxSize分割]
    L --> M[完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | チャンクフィルタ | "all"=全チャンク、"async"=非同期のみ、"initial"=初期のみ | chunksオプション |
| BR-02 | 最小共有チャンク数 | minChunks個以上のチャンクで共有されるモジュールのみ分割対象 | モジュール照合時 |
| BR-03 | 優先度による選択 | 同じモジュールが複数のキャッシュグループにマッチする場合、priority値が高いグループが優先 | キャッシュグループ照合時 |
| BR-04 | 既存チャンク再利用 | reuseExistingChunkがtrueの場合、条件に合致する既存チャンクを再利用 | チャンク生成時 |
| BR-05 | 強制分割 | enforceがtrueの場合、minSize/minChunks等の制約を無視して分割 | キャッシュグループに設定時 |
| BR-06 | リクエスト数制限 | maxAsyncRequests/maxInitialRequestsを超える分割は行わない | チャンク生成時 |

### 計算ロジック

- **サイズ比較**: `checkMinSize(sizes, minSize)`により、各ソースタイプごとにサイズが閾値以上かを確認
- **エントリ比較**: `compareEntries(a, b)`で (1)priority (2)チャンク数 (3)サイズ削減量 (4)キャッシュグループインデックス (5)モジュール数 (6)モジュール識別子 の順で比較
- **ハッシュ付きファイル名**: `hashFilename(name, outputOptions)`でチャンク名が長すぎる場合に8文字のハッシュダイジェストに変換

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | MinMaxSizeWarning | minSizeとmaxSizeが矛盾する設定の場合 | 警告として報告 |
| - | WebpackError | 不正なキャッシュグループ設定 | コンパイルエラーとして報告 |

### リトライ仕様

該当なし

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

該当なし（最適化フェーズでの一括処理）

## パフォーマンス要件

- deterministicGroupingアルゴリズムにより効率的なモジュールグルーピングを実現
- WeakMapによるモジュールキーのキャッシュ（getKeyCache）で重複計算を回避
- STAGE_ADVANCEDフェーズで実行（他の基本最適化の後）

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

該当なし

## 備考

- デフォルトではchunks: "all"で全てのチャンクが対象
- node_modules内のモジュールは自動的にvendorsキャッシュグループにマッチするデフォルト設定がある（WebpackOptionsApplyで設定）
- usedExportsオプションにより、実際に使用されているエクスポートのみを考慮した分割が可能

---

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

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

### 推奨読解順序

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

SplitChunksPluginで使用される型定義とデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SplitChunksPlugin.js | `lib/optimize/SplitChunksPlugin.js` | 62-177行目: CacheGroupSource, CacheGroup, FallbackCacheGroup, SplitChunksOptions, ChunksInfoItem等の型定義 |

**読解のコツ**: まずChunksInfoItem（168-177行目）を理解することが重要。これは分割候補の情報を保持する中心的なデータ構造で、modules（対象モジュール集合）、cacheGroup（所属キャッシュグループ）、chunks（共有元チャンク集合）、sizes（サイズ情報）を持つ。

#### Step 2: 設定の正規化を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SplitChunksPlugin.js | `lib/optimize/SplitChunksPlugin.js` | 304-314行目: normalizeSizes - 数値またはオブジェクトのサイズ指定を正規化 |
| 2-2 | SplitChunksPlugin.js | `lib/optimize/SplitChunksPlugin.js` | 287-297行目: INITIAL_CHUNK_FILTER, ASYNC_CHUNK_FILTER, ALL_CHUNK_FILTER |
| 2-3 | SplitChunksPlugin.js | `lib/optimize/SplitChunksPlugin.js` | 658-699行目: コンストラクタでのオプション正規化 |

**主要処理フロー**:
1. **287-297行目**: チャンクフィルタ関数の定義（initial/async/all）
2. **304-314行目**: サイズ値の正規化（数値→ソースタイプ別オブジェクト）
3. **614-654行目**: createCacheGroupSourceでキャッシュグループ設定を内部形式に変換

#### Step 3: メインの最適化ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SplitChunksPlugin.js | `lib/optimize/SplitChunksPlugin.js` | apply()メソッド内のoptimizeChunksフック |

**主要処理フロー**:
- **257-281行目**: compareEntries関数 - 分割候補のソート順序（priority → チャンク数 → サイズ削減量 → キャッシュグループインデックス → モジュール数 → モジュール識別子）
- **367-392行目**: checkMinSize / checkMinSizeReduction - サイズ制約の検証

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | SplitChunksPlugin.js | `lib/optimize/SplitChunksPlugin.js` | 545-560行目: checkTest - テスト条件によるモジュールフィルタリング |
| 4-2 | SplitChunksPlugin.js | `lib/optimize/SplitChunksPlugin.js` | 569-583行目: checkModuleType - モジュールタイプによるフィルタリング |
| 4-3 | SplitChunksPlugin.js | `lib/optimize/SplitChunksPlugin.js` | 592-606行目: checkModuleLayer - レイヤーによるフィルタリング |

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

```
Compilation.hooks.optimizeChunks (STAGE_ADVANCED)
    |
    +-- SplitChunksPlugin.apply()
            |
            +-- getCacheGroups(module, context)
            |       +-- checkTest(test, module, context)
            |       +-- checkModuleType(type, module)
            |       +-- checkModuleLayer(layer, module)
            |
            +-- compareEntries(a, b)
            |
            +-- normalizeSizes(value, defaultSizeTypes)
            +-- checkMinSize(sizes, minSize)
            +-- checkMinSizeReduction(sizes, minSizeReduction, chunkCount)
            |
            +-- deterministicGrouping(options)  [maxSize分割用]
            |
            +-- chunkGraph.connectChunkAndModule()
            +-- chunkGraph.disconnectChunkAndModule()
            +-- compilation.addChunk()
```

### データフロー図

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

optimization.splitChunks --> 設定の正規化                    +--> 新規共通チャンク
設定                              |                          |
                                  v                          |
ChunkGraph ------------> モジュール-チャンク分析 -------> SplitChunksPlugin
(modules/chunks)              |                          |    +--> 更新されたChunkGraph
                              v                          |
ModuleGraph ----------> キャッシュグループ照合            +--> 移動されたモジュール
                              |
                              v
                        フィルタリング・ソート
                              |
                              v
                        チャンク生成・モジュール移動
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SplitChunksPlugin.js | `lib/optimize/SplitChunksPlugin.js` | ソース | メインプラグイン実装 |
| MinMaxSizeWarning.js | `lib/optimize/MinMaxSizeWarning.js` | ソース | minSize/maxSize矛盾時の警告 |
| deterministicGrouping.js | `lib/util/deterministicGrouping.js` | ユーティリティ | 決定的グルーピングアルゴリズム |
| ChunkGraph.js | `lib/ChunkGraph.js` | ソース | チャンク-モジュール関連付け管理 |
| Chunk.js | `lib/Chunk.js` | ソース | チャンクの実体 |
| ChunkGroup.js | `lib/ChunkGroup.js` | ソース | チャンクグループ管理 |
| SortableSet.js | `lib/util/SortableSet.js` | ユーティリティ | ソート済みセット |
| SetHelpers.js | `lib/util/SetHelpers.js` | ユーティリティ | Set操作ヘルパー（isSubset等） |
| comparators.js | `lib/util/comparators.js` | ユーティリティ | 比較関数群 |
| createHash.js | `lib/util/createHash.js` | ユーティリティ | ハッシュ生成 |
| IdHelpers.js | `lib/ids/IdHelpers.js` | ユーティリティ | ID生成ヘルパー |
