# 機能設計書 63-ProgressPlugin

## 概要

本ドキュメントは、webpackの組込みプラグインである `ProgressPlugin` の機能設計を記述する。`ProgressPlugin` は、コンパイル進捗をパーセンテージ付きで報告するプラグインである。

### 本機能の処理概要

ProgressPluginは、webpackのコンパイルプロセス全体を通じて進捗状況をリアルタイムでパーセンテージ付きで報告する機能を提供する。ビルド開始からモジュール処理、最適化、アセット生成、出力までの各フェーズの進行度をモニタリングし、カスタムハンドラ関数またはデフォルトのロガーを通じて報告する。

**業務上の目的・背景**：大規模プロジェクトでは、webpackのビルドに数十秒から数分かかることがある。ProgressPluginは、ビルドの進捗を可視化することで、開発者がビルドの完了を待つ際のフィードバックを提供し、ビルドプロセスのどのフェーズに時間がかかっているかを把握可能にする。

**機能の利用シーン**：CI/CDパイプラインでのビルドログ出力、開発時のwebpackターミナル出力、ビルドパフォーマンスのボトルネック特定、MultiCompiler環境での複数コンパイルの進捗統合に利用される。

**主要な処理内容**：
1. コンパイラフック（beforeRun, run, compile, make, seal等）にインターセプトを設定し、各フェーズの進捗を報告する
2. モジュール処理の進捗をFactorizeQueue、AddModuleQueue、ProcessDependenciesQueueのイベントから算出する
3. パーセンテージ計算に、モジュール数・依存数・エントリ数の中央値（median3）を使用する
4. Sealingフェーズの35以上のフックを均等に0.70-0.95の範囲に割り当てる
5. MultiCompiler対応で、各コンパイラの進捗を平均化して報告する
6. プロファイルモードで、各フェーズの所要時間をロガー出力する
7. 前回のモジュール数・依存数をキャッシュに保存し、次回ビルドの進捗予測に使用する

**関連システム・外部連携**：Compiler、MultiCompiler、Compilationの各種フックシステム、AsyncQueueのhooks、キャッシュシステムと連携する。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能に直接関連する画面はない（ターミナル/CLIに出力） |

## 機能種別

計算処理 / データ連携（進捗レポーティング）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| handler | `HandlerFn` | No | カスタム進捗ハンドラ関数 | 関数型 |
| profile | `boolean` | No | プロファイルモード（各フェーズの所要時間を記録） | デフォルト: false |
| modulesCount | `number` | No | 予測モジュール数 | デフォルト: 5000 |
| dependenciesCount | `number` | No | 予測依存数 | デフォルト: 10000 |
| modules | `boolean` | No | モジュール数の表示 | デフォルト: true |
| dependencies | `boolean` | No | 依存数の表示 | デフォルト: true |
| activeModules | `boolean` | No | アクティブモジュールの表示 | デフォルト: false |
| entries | `boolean` | No | エントリ数の表示 | デフォルト: true |
| percentBy | `string` | No | パーセンテージ算出基準（"entries"/"dependencies"/"modules"） | 未指定時はmedian3 |

### 入力データソース

- webpack設定ファイルのpluginsセクション
- コンパイラの各種フックイベント
- AsyncQueueのadded/resultイベント
- キャッシュから復元された前回ビルドのカウントデータ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| percentage | `number` | 進捗パーセンテージ（0.0〜1.0） |
| message | `string` | 現在の処理段階を示すメッセージ |
| args | `string[]` | 追加の詳細情報（モジュール数、アクティブモジュール名等） |

### 出力先

カスタムハンドラ関数、またはデフォルトのInfrastructureLogger（`webpack.Progress`）。

## 処理フロー

### 処理シーケンス

```
1. apply()でCompiler/MultiCompilerを判別
   └─ MultiCompilerの場合は各子Compilerに個別のProgressPluginを適用
2. コンパイラフックにインターセプトを設定
   └─ beforeRun(0.01), run(0.02), watchRun(0.03), compile(0.07)等
3. compilation フックでキュー監視を設定
   └─ factorizeQueue.hooks.added/result, addModuleQueue.hooks.added等
4. ビルドフェーズ（0.1〜0.65）
   └─ モジュール数/依存数/エントリ数のmedian3で進捗を算出
5. シーリングフェーズ（0.70〜0.95）
   └─ 35以上のフックに均等にパーセンテージを割り当て
6. エミットフェーズ（0.95〜0.98）
   └─ emit, afterEmitフックに対応
7. 完了フェーズ（0.99〜1.0）
   └─ done, cache操作フックに対応
8. キャッシュにモジュール数・依存数を保存
   └─ afterCompileフックでcache.storePromise()
```

### フローチャート

```mermaid
flowchart TD
    A[apply] --> B{MultiCompiler?}
    B -->|Yes| C[各子Compilerに個別適用]
    B -->|No| D[_applyOnCompiler]
    D --> E[コンパイラフックにインターセプト設定]
    E --> F[compilationフックでキュー監視設定]
    F --> G[ビルドフェーズ 0.1-0.65]
    G --> H[モジュール/依存/エントリの進捗算出]
    H --> I[median3で統合パーセンテージ算出]
    I --> J[シーリングフェーズ 0.70-0.95]
    J --> K[エミットフェーズ 0.95-0.98]
    K --> L[完了フェーズ 0.99-1.0]
    L --> M[キャッシュにカウント保存]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | median3算出 | モジュール・依存・エントリの進捗の中央値を使用 | percentByが未指定の場合 |
| BR-02 | パーセンテージ範囲 | ビルド: 0.1-0.65、シーリング: 0.70-0.95、エミット: 0.95-0.98、完了: 0.99-1.0 | 全コンパイルに適用 |
| BR-03 | 更新スロットリング | 500ms以内の連続更新をスロットリングする | updateThrottled()呼び出し時 |
| BR-04 | MultiCompiler平均化 | 各子Compilerの進捗を平均化して報告する | MultiCompiler使用時 |
| BR-05 | カウントキャッシュ | 前回ビルドのモジュール数・依存数をキャッシュに保存する | afterCompileフック時 |
| BR-06 | 子コンパイル除外 | 子コンパイルは進捗報告の対象外 | compiler.isChild()がtrueの場合 |

### 計算ロジック

パーセンテージ算出:
```
percentByModules = doneModules / max(lastModulesCount || modulesCount || 1, modulesCount)
percentByEntries = doneEntries / max(lastEntriesCount || dependenciesCount || 1, entriesCount)
percentByDependencies = doneDependencies / max(lastDependenciesCount || 1, dependenciesCount)
percentageFactor = median3(percentByModules, percentByEntries, percentByDependencies)
percentage = 0.1 + percentageFactor * 0.55
```

median3関数:
```
median3(a, b, c) = a + b + c - max(a, b, c) - min(a, b, c)
```

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

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

データベース操作は存在しない。キャッシュシステムを通じた読み書きのみ。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ValidationError | 設定エラー | オプションがスキーマに適合しない場合 | 正しいオプション形式で指定する |

### リトライ仕様

キャッシュ読み込みでエラーが発生した場合、エラーは無視される（421-423行目）。

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

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

## パフォーマンス要件

- 更新スロットリング（500ms間隔）により、高頻度のハンドラ呼び出しを抑制している
- モジュール数が50未満の場合は毎回、それ以降は100件ごとに更新する
- プロファイルモード有効時は各フェーズの所要時間を計測し、10秒超でerror、1秒超でwarn、10ms超でinfo、5ms超でlog、それ以下でdebugレベルで出力する

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

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

## 備考

- `ProgressPlugin.getReporter(compiler)` 静的メソッドにより、他のプラグインから現在のProgressReporterにアクセスできる
- `createDefaultHandler` が静的メソッドとして公開されており、カスタムハンドラ作成の参考に使用できる
- SKIPPEDなキューコンテキスト（"import-module", "load-module"）は実際のモジュール・依存カウントから除外される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ProgressPlugin.js | `lib/ProgressPlugin.js` | `CountsData`型（34-36行目）: `modulesCount`と`dependenciesCount`のキャッシュ構造 |
| 1-2 | ProgressPlugin.js | `lib/ProgressPlugin.js` | `HandlerFn`型（55行目）: `(percentage, msg, ...args) => void` |
| 1-3 | ProgressPlugin.js | `lib/ProgressPlugin.js` | `defaultOptions`（700-708行目）: デフォルトオプション値 |

**読解のコツ**: コンストラクタ（157-176行目）でオプションの正規化が行われる。handlerは関数として直接渡すこともできる（158-161行目）。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ProgressPlugin.js | `lib/ProgressPlugin.js` | `apply()`メソッド（182-194行目）- Compiler/MultiCompilerの判別 |

**主要処理フロー**:
1. **183-188行目**: ハンドラの決定（カスタムまたはデフォルト）
2. **189-193行目**: MultiCompiler/Compilerの分岐

#### Step 3: MultiCompiler対応を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ProgressPlugin.js | `lib/ProgressPlugin.js` | `_applyOnMultiCompiler()`（201-213行目） |

**主要処理フロー**:
- **202-204行目**: 各コンパイラの状態配列を初期化
- **206-211行目**: 各コンパイラに個別のProgressPluginを適用し、進捗を平均化

#### Step 4: 単一Compiler対応の詳細を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ProgressPlugin.js | `lib/ProgressPlugin.js` | `_applyOnCompiler()`（220-697行目）- メインの進捗追跡ロジック |

**主要処理フロー**:
- **246-315行目**: `update()`関数 - パーセンテージ算出とハンドラ呼び出し
- **322-330行目**: `factorizeAdd()` - 依存解決キューへの追加を監視
- **344-350行目**: `moduleAdd()` - モジュール追加キューへの追加を監視
- **450-580行目**: compilationフックでのキュー監視とシーリングフェーズの進捗設定
- **510-549行目**: 35以上のシーリングフックの定義
- **581-697行目**: コンパイラレベルのフックインターセプト

#### Step 5: プロファイルモードを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | ProgressPlugin.js | `lib/ProgressPlugin.js` | `createDefaultHandler()`（62-129行目）- プロファイル付きデフォルトハンドラ |

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

```
ProgressPlugin.apply(compiler)
    |
    +-- [MultiCompiler] _applyOnMultiCompiler()
    |       |
    |       +-- 各子Compilerに new ProgressPlugin(aggregateHandler).apply()
    |
    +-- [Compiler] _applyOnCompiler()
            |
            +-- compiler.hooks.beforeCompile.tap() -- キャッシュからカウント復元
            +-- compiler.hooks.afterCompile.tapPromise() -- カウントをキャッシュに保存
            +-- compiler.hooks.compilation.tap()
            |       |
            |       +-- compilation.factorizeQueue.hooks.added/result -- 依存追跡
            |       +-- compilation.addModuleQueue.hooks.added -- モジュール追跡
            |       +-- compilation.processDependenciesQueue.hooks.result -- モジュール完了追跡
            |       +-- compilation.hooks.addEntry/succeedEntry/failedEntry -- エントリ追跡
            |       +-- compilation.hooks[sealingHooks].intercept() -- シーリング進捗
            |
            +-- compiler.hooks.make.intercept() -- ビルドフェーズ
            +-- compiler.hooks.emit/afterEmit.intercept() -- エミットフェーズ
            +-- compiler.hooks.done.intercept() -- 完了フェーズ
            +-- compiler.cache.hooks -- キャッシュ操作フェーズ
```

### データフロー図

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

キューイベント ─────────┐
(factorize/addModule/  |
 processDependencies)  ├──▶ update() ──▶ handler(percentage, msg, ...args)
                       |    (median3算出)
コンパイラフック ────────┘
(make/seal/emit/done)

キャッシュ ──────────────▶ lastModulesCount/lastDependenciesCount ──▶ 次回ビルドの進捗予測
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ProgressPlugin.js | `lib/ProgressPlugin.js` | ソース | 本機能のメインプラグイン（712行） |
| Compiler.js | `lib/Compiler.js` | ソース | コンパイラフックの提供元 |
| MultiCompiler.js | `lib/MultiCompiler.js` | ソース | MultiCompiler判定とフック提供 |
| NormalModule.js | `lib/NormalModule.js` | ソース | beforeLoadersフックの提供（ローダー読み込み進捗） |
| ProgressPlugin.json | `schemas/plugins/ProgressPlugin.json` | 設定 | オプションのバリデーションスキーマ |
