# 機能設計書 70-パフォーマンスヒント

## 概要

本ドキュメントは、webpackのパフォーマンスヒント機能の設計を記述する。`SizeLimitsPlugin` を中心に、アセットサイズ・エントリポイントサイズの閾値超過を警告する機能を提供する。

### 本機能の処理概要

パフォーマンスヒント機能は、ビルド後のアセットサイズおよびエントリポイントの合計サイズが設定された閾値を超過した場合に、警告またはエラーとして報告する機能を提供する。3種類の警告（AssetsOverSizeLimitWarning、EntrypointsOverSizeLimitWarning、NoAsyncChunksWarning）を通じて、Webパフォーマンスに影響を与える可能性のある問題を開発者に通知する。

**業務上の目的・背景**：フロントエンドアプリケーションのバンドルサイズは、ページロード時間やユーザ体験に直接影響する。大きなアセットや肥大化したエントリポイントは、特にモバイル環境や低速ネットワーク環境でのパフォーマンス低下を引き起こす。パフォーマンスヒント機能は、ビルド時にこれらの問題を自動検出し、コード分割やレイジーロードの導入を促す。

**機能の利用シーン**：本番ビルド時のサイズ監視、CI/CDパイプラインでのバンドルサイズチェック（hintsを"error"に設定してビルド失敗化）、新機能追加時のバンドルサイズ回帰防止に利用される。

**主要な処理内容**：
1. `afterEmit` フックで全アセットのサイズを検査し、閾値超過のアセットを収集する
2. 各エントリポイントの関連ファイルの合計サイズを算出し、閾値超過のエントリポイントを収集する
3. 閾値超過が検出された場合、対応する警告を生成する
4. 非同期チャンクが存在しない場合、コード分割の推奨警告（NoAsyncChunksWarning）を追加する
5. `hints` 設定に応じて、警告（"warning"）またはエラー（"error"）として報告する
6. `assetFilter` 関数でソースマップ等の開発用アセットをフィルタリングする

**関連システム・外部連携**：Compilation（アセット情報）、Entrypoint（エントリポイント情報）と連携する。

**権限による制御**：権限による制御は存在しない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能に直接関連する画面はない（ビルド結果として警告/エラーを報告） |

## 機能種別

バリデーション / 計算処理（サイズ検査・閾値チェック）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| hints | `false \| "warning" \| "error"` | No | ヒントの出力モード | falseで無効化 |
| maxAssetSize | `number` | No | アセットサイズの閾値（バイト単位） | デフォルト: 250000 (250KB) |
| maxEntrypointSize | `number` | No | エントリポイントサイズの閾値（バイト単位） | デフォルト: 250000 (250KB) |
| assetFilter | `(name: string, source: Source, info: AssetInfo) => boolean` | No | 検査対象アセットのフィルタ関数 | デフォルト: 開発用アセット除外 |

### 入力データソース

- webpack設定ファイルの `performance` セクション
- compilation.getAssets()（全アセット一覧）
- compilation.entrypoints（全エントリポイント一覧）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| AssetsOverSizeLimitWarning | `WebpackError` | 閾値を超過したアセットの一覧と推奨サイズ |
| EntrypointsOverSizeLimitWarning | `WebpackError` | 閾値を超過したエントリポイントの一覧と関連ファイル |
| NoAsyncChunksWarning | `WebpackError` | 非同期チャンクが存在しない場合のコード分割推奨 |

### 出力先

`hints` 設定に応じて `compilation.warnings`（"warning"）または `compilation.errors`（"error"）に追加される。

## 処理フロー

### 処理シーケンス

```
1. afterEmitフックでトリガー
   └─ ビルド出力完了後に検査を実行
2. 全アセットのサイズ検査
   └─ assetFilterでフィルタ後、各アセットのサイズをmaxAssetSizeと比較
   └─ 超過アセットをassetsOverSizeLimitに収集
   └─ 超過アセットのSourceをisOverSizeLimitSetに追加
3. 全エントリポイントの合計サイズ検査
   └─ エントリポイントに関連するファイルのサイズを合計
   └─ 合計がmaxEntrypointSizeを超過した場合、entrypointsOverLimitに収集
   └─ 超過エントリポイントをisOverSizeLimitSetに追加
4. 警告の生成
   └─ assetsOverSizeLimit: AssetsOverSizeLimitWarning
   └─ entrypointsOverLimit: EntrypointsOverSizeLimitWarning
5. 非同期チャンク存在チェック
   └─ 警告があり非同期チャンクが存在しない場合、NoAsyncChunksWarningを追加
6. 警告/エラーとしてコンパイルに追加
   └─ hints === "error": compilation.errors.push()
   └─ hints === "warning": compilation.warnings.push()
```

### フローチャート

```mermaid
flowchart TD
    A[afterEmit フック] --> B[全アセットサイズ検査]
    B --> C{閾値超過アセットあり?}
    C -->|Yes| D[AssetsOverSizeLimitWarning生成]
    C -->|No| E[次へ]
    D --> E
    E --> F[全エントリポイントサイズ検査]
    F --> G{閾値超過エントリあり?}
    G -->|Yes| H[EntrypointsOverSizeLimitWarning生成]
    G -->|No| I[次へ]
    H --> I
    I --> J{警告があり非同期チャンクなし?}
    J -->|Yes| K[NoAsyncChunksWarning生成]
    J -->|No| L[次へ]
    K --> L
    L --> M{hints設定}
    M -->|error| N[compilation.errors に追加]
    M -->|warning| O[compilation.warnings に追加]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | デフォルトフィルタ | 開発用アセット（info.development === true）はサイズ検査対象外 | assetFilter未指定時 |
| BR-02 | アセットサイズ | 個々のアセットファイルのサイズがmaxAssetSizeを超過した場合に警告 | 全アセットに適用 |
| BR-03 | エントリポイントサイズ | エントリポイントに含まれる全ファイルの合計サイズがmaxEntrypointSizeを超過した場合に警告 | 全エントリポイントに適用 |
| BR-04 | 非同期チャンク推奨 | サイズ超過警告があり、かつ非同期チャンクが一つも存在しない場合にコード分割を推奨する | 警告あり + 非同期チャンクなし |
| BR-05 | isOverSizeLimit | 超過したSource/Entrypoint/ChunkGroupはWeakSetに記録され、外部からisOverSizeLimit()で照会可能 | 閾値超過時 |
| BR-06 | サイズ取得 | asset.info.sizeが利用可能であればそれを使用し、なければsource.size()を使用する | アセットサイズ取得時 |

### 計算ロジック

エントリポイントサイズ算出:
```
getEntrypointSize(entrypoint) = sum(
  for each file in entrypoint.getFiles():
    if assetFilter(asset) and asset.source:
      asset.info.size || asset.source.size()
)
```

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

データベース操作は存在しない。

## エラー処理

### エラーケース一覧

特別なエラー処理は存在しない。サイズ超過自体が検出すべきイベントであり、警告/エラーとして適切に報告される。

### リトライ仕様

リトライ仕様は存在しない。

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

トランザクション仕様は存在しない。

## パフォーマンス要件

- `afterEmit` フックで全アセットを走査するため、大量のアセットがある場合はオーバーヘッドが発生する可能性があるが、サイズ比較のみのため通常は問題にならない

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

特にセキュリティに関する考慮事項は存在しない。

## 備考

- webpackの `performance` 設定オプションに対応し、`mode: "production"` 時にデフォルトで有効化される
- デフォルトの閾値（250KB）はWeb Vitalsのパフォーマンス指標に基づいた推奨値
- `SizeLimitsPlugin.isOverSizeLimit()` 静的メソッドにより、統計情報出力時にサイズ超過アセットをハイライト表示できる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SizeLimitsPlugin.js | `lib/performance/SizeLimitsPlugin.js` | `AssetDetails` 型（22-25行目）: `{ name, size }` |
| 1-2 | SizeLimitsPlugin.js | `lib/performance/SizeLimitsPlugin.js` | `EntrypointDetails` 型（27-32行目）: `{ name, size, files }` |
| 1-3 | SizeLimitsPlugin.js | `lib/performance/SizeLimitsPlugin.js` | `isOverSizeLimitSet`（35行目）: WeakSet |

**読解のコツ**: 警告クラス（AssetsOverSizeLimitWarning等）はWebpackErrorを継承したシンプルなクラスで、フォーマットされたメッセージを保持する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SizeLimitsPlugin.js | `lib/performance/SizeLimitsPlugin.js` | `apply()` メソッド（72-183行目）がエントリーポイント |

**主要処理フロー**:
1. **78行目**: `compiler.hooks.afterEmit` にタップ
2. **86-99行目**: `getEntrypointSize()` - エントリポイントサイズ算出関数
3. **102-116行目**: アセットサイズ検査ループ
4. **129-140行目**: エントリポイントサイズ検査ループ
5. **142-179行目**: 警告生成とcompilation.warnings/errorsへの追加

#### Step 3: 警告クラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AssetsOverSizeLimitWarning.js | `lib/performance/AssetsOverSizeLimitWarning.js` | アセット超過警告メッセージのフォーマット |
| 3-2 | EntrypointsOverSizeLimitWarning.js | `lib/performance/EntrypointsOverSizeLimitWarning.js` | エントリポイント超過警告メッセージのフォーマット |
| 3-3 | NoAsyncChunksWarning.js | `lib/performance/NoAsyncChunksWarning.js` | コード分割推奨メッセージ |

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

```
SizeLimitsPlugin.apply(compiler)
    |
    +-- compiler.hooks.afterEmit.tap()
            |
            +-- [全アセット走査]
            |       +-- assetFilter(name, source, info)
            |       +-- size > maxAssetSize → assetsOverSizeLimit に追加
            |       +-- isOverSizeLimitSet.add(source)
            |
            +-- [全エントリポイント走査]
            |       +-- getEntrypointSize(entry)
            |       +-- size > maxEntrypointSize → entrypointsOverLimit に追加
            |       +-- isOverSizeLimitSet.add(entry)
            |
            +-- [警告生成]
            |       +-- new AssetsOverSizeLimitWarning(assetsOverSizeLimit, assetSizeLimit)
            |       +-- new EntrypointsOverSizeLimitWarning(entrypointsOverLimit, entrypointSizeLimit)
            |       +-- new NoAsyncChunksWarning() [非同期チャンクなし時]
            |
            +-- [compilation.warnings/errors に追加]
```

### データフロー図

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

compilation.getAssets() ──────▶ サイズ検査 ──▶ AssetsOverSizeLimitWarning
                                (閾値比較)

compilation.entrypoints ──────▶ 合計サイズ算出 ──▶ EntrypointsOverSizeLimitWarning
                                (閾値比較)

compilation.chunks ───────────▶ 非同期チャンク検査 ──▶ NoAsyncChunksWarning
                                (存在チェック)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SizeLimitsPlugin.js | `lib/performance/SizeLimitsPlugin.js` | ソース | 本機能のメインプラグイン（183行） |
| AssetsOverSizeLimitWarning.js | `lib/performance/AssetsOverSizeLimitWarning.js` | ソース | アセット超過警告（37行） |
| EntrypointsOverSizeLimitWarning.js | `lib/performance/EntrypointsOverSizeLimitWarning.js` | ソース | エントリポイント超過警告（41行） |
| NoAsyncChunksWarning.js | `lib/performance/NoAsyncChunksWarning.js` | ソース | コード分割推奨警告（22行） |
| SizeFormatHelpers.js | `lib/SizeFormatHelpers.js` | ソース | formatSize関数（サイズの人間可読フォーマット） |
| SetHelpers.js | `lib/util/SetHelpers.js` | ソース | find関数（Set内の要素検索） |
