# 帳票設計書 28-ProgressPlugin 進捗レポート

## 概要

本ドキュメントは、webpackのビルドプロセスの進捗状況をリアルタイムで出力するProgressPluginの設計仕様を定義する。lib/ProgressPlugin.jsに基づく。

### 本帳票の処理概要

ProgressPluginは、webpackのビルドプロセスにおける各フェーズの進捗をリアルタイムでコンソールに出力するプラグインである。ビルドの開始（0%）から完了（100%）まで、setup、building、sealing、emitting、doneの各フェーズのパーセンテージとメッセージを継続的に出力する。進捗計算は、エントリー数・モジュール数・依存関係数の中央値（median3）に基づいて行われる。

**業務上の目的・背景**：大規模プロジェクトのビルドは長時間を要するため、ビルドが正常に進行しているかどうかをリアルタイムで確認する必要がある。ProgressPluginはビルドの各フェーズの進行状況を可視化することで、ビルドのハングアップ検出、ボトルネック特定、所要時間の見積もりを支援する。プロファイルモード有効時には、各フェーズの処理時間も計測・報告される。

**帳票の利用シーン**：開発環境でのビルド進捗モニタリング、CI/CDパイプラインでのビルド進行状況の可視化、ビルドパフォーマンスのプロファイリング、大規模プロジェクトでのビルドハングアップ検出。

**主要な出力内容**：
1. 進捗パーセンテージ（0%〜100%）
2. 現在のフェーズ名（setup/building/sealing/emitting/done）
3. 処理中の詳細情報（モジュール数、依存関係数、エントリー数、アクティブモジュール名）
4. プロファイル情報（profile=true時：各フェーズの処理時間をログレベル別に出力）
5. ローダーインポート状況

**帳票の出力タイミング**：ビルドプロセスの実行中にリアルタイムで継続的に出力される。各コンパイラフック・コンパイレーションフックの実行時にhandler関数が呼び出される。

**帳票の利用者**：フロントエンド開発者、ビルドエンジニア、CI/CDシステム

## 帳票種別

進捗レポート（リアルタイム出力）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | CLI | webpack --progress | コマンドライン引数指定 |
| - | webpack設定ファイル | webpack.config.js | plugins: [new webpack.ProgressPlugin()] |
| - | Node.js API | - | new ProgressPlugin(options) |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | テキスト（コンソール出力） |
| 用紙サイズ | 該当なし |
| 向き | 該当なし |
| ファイル名 | 該当なし（標準出力） |
| 出力方法 | コンソールステータスライン（リアルタイム更新） |
| 文字コード | UTF-8 |

## 帳票レイアウト

### レイアウト概要

進捗レポートはリアルタイムに更新されるステータスラインとして出力される。各フェーズの進捗パーセンテージとメッセージが表示される。

```
┌──────────────────────────────────────────────┐
│ XX% building  N/M entries  N/M dependencies  │
│              N/M modules  N active           │
│              [active module name]            │
├──────────────────────────────────────────────┤
│ XX% sealing  [phase name]  [plugin name]    │
├──────────────────────────────────────────────┤
│ XX% emitting  emit                          │
├──────────────────────────────────────────────┤
│ XX% done  plugins                           │
└──────────────────────────────────────────────┘
```

### ヘッダー部

該当なし（リアルタイム出力のためヘッダーは存在しない）

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | percentage | 進捗パーセンテージ | 内部計算 | XX% | - |
| 2 | phase | 現在のフェーズ | コンパイラフック | 文字列（setup/building/sealing/emitting/done） | - |
| 3 | detail | 詳細情報 | 内部カウンター | 文字列 | - |
| 4 | entries | エントリー進捗 | doneEntries/entriesCount | N/M entries | - |
| 5 | dependencies | 依存関係進捗 | doneDependencies/dependenciesCount | N/M dependencies | - |
| 6 | modules | モジュール進捗 | doneModules/modulesCount | N/M modules | - |
| 7 | activeModules | アクティブモジュール数 | activeModules.size | N active | - |
| 8 | activeModule | 処理中モジュール名 | lastActiveModule | ファイルパス | - |

### フッター部

該当なし

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| ProgressPlugin有効化 | plugins配列にProgressPluginを追加 | Yes |
| --progress フラグ | CLIで--progressを指定 | Yes（CLI使用時） |

### ソート順

該当なし（時系列で出力）

### 改ページ条件

該当なし（リアルタイムステータスライン）

## データベース参照仕様

### 参照テーブル一覧

データベースは参照しない。コンパイラ・コンパイレーションの内部状態から直接データを取得する。

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| Compiler hooks | フェーズ進行状況 | compiler.hooks.* |
| Compilation hooks | ビルド詳細進捗 | compilation.hooks.* |
| AsyncQueue hooks | モジュール・依存関係カウント | factorizeQueue/addModuleQueue |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| percentByModules | doneModules / max(lastModulesCount \|\| modulesCount \|\| 1, modulesCount) | なし | 0〜1の範囲 |
| percentByEntries | doneEntries / max(lastEntriesCount \|\| dependenciesCount \|\| 1, entriesCount) | なし | 0〜1の範囲 |
| percentByDependencies | doneDependencies / max(lastDependenciesCount \|\| 1, dependenciesCount) | なし | 0〜1の範囲 |
| percentageFactor | median3(percentByModules, percentByEntries, percentByDependencies) | なし | percentByに基づく選択もあり |
| percentage(building) | 0.1 + percentageFactor * 0.55 | Math.floor(p*100) | 10%〜65%の範囲 |
| median3(a,b,c) | a + b + c - max(a,b,c) - min(a,b,c) | なし | 3値の中央値 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[ProgressPlugin.apply] --> B{CompilerまたはMultiCompiler?}
    B -->|Compiler| C[_applyOnCompiler]
    B -->|MultiCompiler| D[_applyOnMultiCompiler]
    C --> E[コンパイラフックにインターセプト登録]
    E --> F[cache.endIdle: 0%]
    F --> G[beforeRun/run: 1-2%]
    G --> H[watchRun: 3%]
    H --> I[normalModuleFactory/contextModuleFactory: 4-5%]
    I --> J[beforeCompile/compile: 6-7%]
    J --> K[thisCompilation/compilation: 8-9%]
    K --> L[make: 10% - building開始]
    L --> M[building: 10-65%]
    M --> N[finishMake: 69%]
    N --> O[sealing: 70-95%]
    O --> P[emit: 95%]
    P --> Q[afterEmit: 98%]
    Q --> R[done: 99%]
    R --> S[beginIdle/shutdown: 100%]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| ハンドラー未設定 | handler が undefined の場合 | - | createDefaultHandlerで自動生成される |
| キャッシュ取得失敗 | cache.getPromise()が失敗 | - | エラーは無視される（行421-423） |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | フックごとに1回の出力 |
| 目標出力時間 | リアルタイム（500msスロットリング） |
| 同時出力数上限 | 1（ビルドごとに1インスタンス） |

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

activeModulesオプション有効時にはモジュールのファイルパスが出力されるため、外部に公開される環境では注意が必要。contextifyによりパスは短縮されるが、ディレクトリ構造の情報は含まれる。

## 備考

- デフォルトオプション（行700-708）：profile: false, modulesCount: 5000, dependenciesCount: 10000, modules: true, dependencies: true, activeModules: false, entries: true
- MultiCompiler対応（行201-213）：各コンパイラの進捗を平均化して全体の進捗を算出する
- スロットリング（行242-244）：500msの間隔でupdateを制限し、過剰な出力を防ぐ
- SKIPPED_QUEUE_CONTEXTS（行131）：`import-module`と`load-module`コンテキストのキュー処理はスキップカウントに計上される
- プロファイルモード（行62-129）：有効時は各処理フェーズの経過時間をログレベル別に出力（10秒超: error、1秒超: warn、10ms超: info、5ms超: log、5ms以下: debug）
- ビルドフェーズの進捗配分：setup(0-9%)、building(10-65%)、finishMake(69%)、sealing(70-95%)、emitting(95-98%)、done(99%)、cache(99-100%)
- キャッシュを利用してビルド間でモジュール数・依存関係数を記憶し、次回ビルドの進捗計算精度を向上させる（行405-448）

---

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

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

### 推奨読解順序

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

ProgressPluginが管理する内部状態とカウンターを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行32-36: CountsData型定義（modulesCount, dependenciesCount） |
| 1-2 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行700-708: defaultOptions - デフォルト設定値 |
| 1-3 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行55: HandlerFn型定義（percentage, msg, ...args） |

**読解のコツ**: ProgressPluginは内部でmodulesCount, dependenciesCount, entriesCountの3つのカウンターを管理し、完了数（done*）との比率で進捗を算出する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行157-176: コンストラクタ - オプションの解析と保存 |
| 2-2 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行182-194: apply()メソッド - handler取得とCompiler/MultiCompiler分岐 |

**主要処理フロー**:
1. **行157-164**: コンストラクタでオプションを解析（関数の場合はhandlerに変換）
2. **行183-188**: handlerが未指定の場合はcreateDefaultHandlerで生成
3. **行189-193**: MultiCompilerかCompilerかで処理を分岐

#### Step 3: 進捗計算ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行246-315: update()関数 - 進捗パーセンテージの計算ロジック |
| 3-2 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行53: median3()関数 - 3値の中央値計算 |
| 3-3 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行261-277: percentByの選択ロジック（entries/dependencies/modules/median3） |

**主要処理フロー**:
- **行249-257**: percentByModules/percentByEntries/percentByDependenciesを個別計算
- **行261-277**: percentByオプションに基づく選択（未指定時はmedian3）
- **行279**: `percentage = 0.1 + percentageFactor * 0.55` で10%〜65%の範囲にマッピング

#### Step 4: フックインターセプトを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行597-623: interceptHook()関数 - 汎用フックインターセプト |
| 4-2 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行510-579: sealingフェーズのフック一覧とインターセプト |
| 4-3 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行581-589: makeフックのインターセプト（building: 10%開始） |
| 4-4 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行624-696: 各コンパイラフックへのインターセプト登録 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行62-129: createDefaultHandler()関数 - プロファイル時の処理時間計測と出力 |
| 5-2 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行68-123: profile有効時の状態追跡と時間差分計算 |
| 5-3 | ProgressPlugin.js | `lib/ProgressPlugin.js` | 行93-105: 処理時間に応じたログレベル選択（error/warn/info/log/debug） |

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

```
ProgressPlugin.apply(compiler)
    |
    +-- createDefaultHandler(profile, logger)  [handler未指定時]
    |       |
    |       +-- defaultHandler(percentage, msg, ...args)
    |               +-- [profile時] 処理時間計測・ログ出力
    |               +-- logger.status() でステータスライン更新
    |
    +-- _applyOnCompiler(compiler, handler)
    |       |
    |       +-- update() 関数定義
    |       |       +-- percentByModules / percentByEntries / percentByDependencies 計算
    |       |       +-- median3() で中央値取得
    |       |       +-- handler(percentage, "building", ...items)
    |       |
    |       +-- factorizeAdd / factorizeDone  [依存関係カウント]
    |       +-- moduleAdd / moduleDone  [モジュールカウント]
    |       +-- entryAdd / entryDone  [エントリーカウント]
    |       +-- moduleBuild  [アクティブモジュール追跡]
    |       |
    |       +-- compiler.hooks.* インターセプト登録
    |       |       +-- cache.endIdle: 0%
    |       |       +-- beforeRun: 1%
    |       |       +-- run: 2%
    |       |       +-- watchRun: 3%
    |       |       +-- compile: 7%
    |       |       +-- make: 10%  -> building開始
    |       |       +-- finishMake: 69%
    |       |       +-- emit: 95%
    |       |       +-- done: 99%
    |       |
    |       +-- compilation.hooks.* インターセプト登録
    |               +-- sealing hooks: 70%〜95%
    |
    +-- _applyOnMultiCompiler(compiler, handler)  [MultiCompiler時]
            +-- 各子コンパイラに個別ProgressPlugin適用
            +-- 全体進捗 = sum(各コンパイラ進捗) / コンパイラ数
```

### データフロー図

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

Compiler hooks              ProgressPlugin._applyOnCompiler       コンソール
  .beforeRun          --->    interceptHook()                --->  ステータスライン
  .run                        |                                    "XX% phase detail"
  .compile                  handler(percentage, msg, args)
  .make                       |
  .emit               --->  createDefaultHandler()          --->  logger.status()
  .done                       |                                    "XX% msg args"
                            [profile時]
Compilation hooks             処理時間計測                    --->  logger.error/warn/
  .factorizeQueue    --->    diff = now - lastState.time            info/log/debug
  .addModuleQueue             |
  .processDependencies      update()
  .buildModule        --->    median3(modules, entries, deps)
  .addEntry                   percentage = 0.1 + factor*0.55
  .succeedEntry

Cache                       cache.getPromise()              --->  lastModulesCount
  .getPromise()       --->    前回ビルドのカウント取得              lastDependenciesCount
  .storePromise()     --->    今回ビルドのカウント保存
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ProgressPlugin.js | `lib/ProgressPlugin.js` | ソース | 進捗レポートのメインロジック |
| Compiler.js | `lib/Compiler.js` | ソース | コンパイラフック定義・getInfrastructureLogger |
| MultiCompiler.js | `lib/MultiCompiler.js` | ソース | マルチコンパイラ対応 |
| NormalModule.js | `lib/NormalModule.js` | ソース | ローダーインポート検出（beforeLoaders フック） |
| Logger.js | `lib/logging/Logger.js` | ソース | WebpackLoggerクラス（status/error/warn/info/log/debugメソッド） |
| Compilation.js | `lib/Compilation.js` | ソース | コンパイレーションフック（factorizeQueue, addModuleQueue等） |
| schemas/plugins/ProgressPlugin.json | `schemas/plugins/ProgressPlugin.json` | 設定 | オプションバリデーションスキーマ |
