# 機能設計書 1-コンパイラ（Compiler）

## 概要

本ドキュメントは、webpackのコアバンドリング機能であるCompilerクラスの機能設計を記述する。Compilerはwebpackビルドプロセス全体のライフサイクルを管理する中核的なオーケストレーターである。

### 本機能の処理概要

Compilerは、webpackの設定オブジェクトを受け取り、コンパイルの開始から成果物の出力までの一連のプロセスを制御する。ファイル監視によるインクリメンタルリビルド、プラグインシステムとの統合、キャッシュ管理、アセット出力など、ビルドパイプライン全体を統括する。

**業務上の目的・背景**：モダンなJavaScriptアプリケーション開発では、多数のモジュールファイルを一つまたは複数のバンドルにまとめる必要がある。Compilerは、この変換プロセス全体を一貫して管理し、開発時のウォッチモードによる自動リビルドや、本番ビルドでの最適化を含むビルドライフサイクルを提供することで、開発者の生産性を向上させる。

**機能の利用シーン**：webpack APIを直接呼び出す場合（`webpack(config)` または `webpack(config, callback)`）にCompilerインスタンスが生成される。CLIから実行する場合も内部でCompilerが生成される。開発サーバー（webpack-dev-server）はCompilerのwatchモードを利用する。

**主要な処理内容**：
1. webpack設定の受け取りとCompilerインスタンスの初期化（フックシステム、ファイルシステム、キャッシュ等）
2. `run()` メソッドによる単発コンパイルの実行
3. `watch()` メソッドによるファイル監視とインクリメンタルリビルド
4. `compile()` メソッドによるCompilationインスタンスの生成とモジュールグラフ構築の起動
5. `emitAssets()` メソッドによるバンドル成果物のファイルシステムへの書き出し
6. tapableフックを通じたプラグインへのイベント通知とライフサイクル制御

**関連システム・外部連携**：tapableライブラリ（フックシステム）、enhanced-resolve（モジュールパス解決）、neo-async（非同期処理制御）、webpack-sources（ソースコード抽象化）と連携する。ファイルシステムはInputFileSystem / OutputFileSystem / IntermediateFileSystem / WatchFileSystemの抽象インターフェースを通じて操作する。

**権限による制御**：特になし。webpack設定オブジェクトの内容に基づいて動作が決定される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | Compilerは画面に直接関連しない基盤機能。全画面のビルドプロセスを統括する |

## 機能種別

データ連携 / ライフサイクル管理 / オーケストレーション

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| context | string | Yes | コンパイルのルートディレクトリパス | 絶対パスであること |
| options | WebpackOptions | Yes | webpack設定オブジェクト（正規化済み） | JSON Schemaによるバリデーション済み |

### 入力データソース

- webpack設定ファイル（webpack.config.js等）を正規化した設定オブジェクト
- ファイルシステム上のソースコード群
- recordsInputPathが指定されている場合はレコードファイル

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Stats | Stats | コンパイル結果の統計情報オブジェクト |
| assets | Map<string, Source> | 生成されたアセットファイル群（バンドル、ソースマップ等） |
| records | Records | モジュール/チャンクIDの永続化レコード |

### 出力先

- outputPath（設定値）で指定されたディレクトリにバンドルファイルを出力
- recordsOutputPathが指定されている場合はレコードファイルを出力
- コールバック関数にStatsオブジェクトを返却

## 処理フロー

### 処理シーケンス

```
1. run() 呼び出し
   └─ running状態チェック（二重実行防止）
2. beforeRun フック呼び出し
   └─ InputFileSystemのパージ等の準備処理
3. run フック呼び出し
   └─ プラグインによる前処理
4. readRecords() 実行
   └─ 過去のレコードファイルの読み込み
5. compile(onCompiled) 実行
   ├─ beforeCompile フック呼び出し
   ├─ NormalModuleFactory / ContextModuleFactory の生成
   ├─ compile フック呼び出し
   ├─ Compilation インスタンスの生成
   ├─ thisCompilation / compilation フック呼び出し
   ├─ make フック呼び出し（エントリポイントからモジュールグラフ構築）
   ├─ finishMake フック呼び出し
   ├─ compilation.finish() 実行
   └─ compilation.seal() 実行（チャンクグラフ構築・最適化・コード生成）
6. shouldEmit フックで出力判定
   └─ falseの場合はアセット出力をスキップ
7. emitAssets() 実行
   ├─ emit フック呼び出し
   ├─ 各アセットをファイルシステムに書き出し
   ├─ 大文字小文字の衝突検出
   ├─ イミュータブルファイルのスキップ最適化
   └─ afterEmit フック呼び出し
8. emitRecords() 実行
   └─ レコードファイルの書き出し
9. done フック呼び出し
   └─ Statsオブジェクトをプラグインに通知
10. コールバック呼び出し
    └─ Stats オブジェクトを返却
```

### フローチャート

```mermaid
flowchart TD
    A[run呼び出し] --> B{running状態?}
    B -->|Yes| C[ConcurrentCompilationError]
    B -->|No| D[running = true]
    D --> E[beforeRun フック]
    E --> F[run フック]
    F --> G[readRecords]
    G --> H[compile]
    H --> I[Compilation生成]
    I --> J[make フック - モジュールグラフ構築]
    J --> K[compilation.finish]
    K --> L[compilation.seal]
    L --> M{shouldEmit?}
    M -->|No| N[done フック - Stats返却]
    M -->|Yes| O[emitAssets]
    O --> P{needAdditionalPass?}
    P -->|Yes| Q[additionalPass フック] --> H
    P -->|No| R[emitRecords]
    R --> N
    N --> S[コールバック実行]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 二重実行防止 | runningフラグがtrueの場合、ConcurrentCompilationErrorをスローする | run() / watch() 呼び出し時 |
| BR-02 | アセット出力判定 | shouldEmitフックがfalseを返した場合、アセット出力をスキップする | compile完了後 |
| BR-03 | 追加パス | needAdditionalPassフックがtrueを返した場合、再度compileを実行する | emitAssets完了後 |
| BR-04 | イミュータブルファイルスキップ | immutableフラグがtrueのアセットは、既存ファイルが存在する場合書き込みをスキップする | emitAssets内 |
| BR-05 | 大文字小文字衝突検出 | 同一パスで大文字小文字のみ異なるアセットが存在する場合エラーとする | emitAssets内 |

### 計算ロジック

特になし。

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

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

データベースは使用しない。ファイルシステムへの読み書きが主な外部操作である。

| 操作 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| readRecords | recordsInputPath | READ | 過去のビルドレコードをJSONファイルから読み込み |
| emitRecords | recordsOutputPath | WRITE | ビルドレコードをJSONファイルに書き出し |
| emitAssets | outputPath | WRITE | バンドルファイル群をファイルシステムに書き出し |

### テーブル別操作詳細

該当なし（RDBMSは使用しない）。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ConcurrentCompilationError | 実行エラー | running状態でrun()/watch()が呼ばれた場合 | 前のコンパイルの完了を待つ |
| WebpackError (case-insensitive) | アセット出力エラー | 大文字小文字のみ異なるファイルパスが検出された場合 | 出力ファイル名の衝突を解消する |
| ファイルシステムエラー | I/Oエラー | outputFileSystemへの書き込みに失敗した場合 | ディスク容量・権限を確認する |

### リトライ仕様

リトライ機構は実装されていない。エラー発生時はfailedフックが呼ばれ、コールバックにエラーが渡される。

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

ファイルシステム操作にはトランザクション機構はない。アセット出力は個別ファイル単位で行われ、並列度15で制限される（asyncLib.forEachLimitによる）。出力中にエラーが発生した場合、既に書き出されたファイルはロールバックされない。

## パフォーマンス要件

- ファイル出力時、既存ファイルとの内容比較を行い、変更がなければ書き込みをスキップする（mtime保持、watcher不要通知の抑制）
- イミュータブルファイル（contenthash含むファイル名等）は存在確認のみでスキップ可能
- SizeOnlySourceによるメモリ最適化（出力後のSourceオブジェクトをサイズ情報のみに置換しGC対象化）
- キャッシュ機構（beginIdle / endIdle）によるインクリメンタルビルドの高速化

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

- ファイルシステム操作はOutputFileSystem抽象を通じて行われるため、環境依存の制約が適用される
- recordsファイルにはモジュール/チャンクIDが含まれるが、ソースコード自体は含まれない

## 備考

- Compilerはwebpackのメインファサードである `webpack()` 関数から生成される
- 子コンパイラ（runAsChild）のサポートにより、プラグインが独立したコンパイルを実行可能
- watchモードはWatchingクラスに委譲し、ファイル変更検出とリビルドトリガーを管理する

---

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

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

### 推奨読解順序

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

Compilerクラスのプロパティとフック定義を理解することが最初のステップである。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Compiler.js | `lib/Compiler.js` | コンストラクタ（163-346行目）でフック定義とプロパティ初期化を理解する |
| 1-2 | Watching.js | `lib/Watching.js` | watchモードの状態管理構造（28-82行目）を理解する |

**読解のコツ**: tapableライブラリのフック型（SyncHook, AsyncSeriesHook, AsyncParallelHook, SyncBailHook）の違いを理解することが重要。SyncBailHookは戻り値によって後続処理をスキップ可能で、shouldEmitフックなどで使用される。

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

Compilerの主要メソッドであるrun()とwatch()を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Compiler.js | `lib/Compiler.js` | run()メソッド（496-632行目）の実行フローを理解する |
| 2-2 | Compiler.js | `lib/Compiler.js` | watch()メソッド（480-490行目）からWatchingへの委譲を理解する |

**主要処理フロー**:
1. **496-500行目**: running状態チェックと二重実行防止
2. **606-620行目**: run関数内でbeforeRun -> run -> readRecords -> compileの順にフック呼び出し
3. **532-604行目**: onCompiledコールバックでshouldEmit判定、emitAssets、emitRecords、doneフックの順に処理

#### Step 3: コンパイル処理を理解する

compile()メソッドの内部処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Compiler.js | `lib/Compiler.js` | compile()メソッド（末尾付近）でCompilation生成とmakeフック呼び出しを理解する |

#### Step 4: アセット出力処理を理解する

emitAssets()の詳細な出力ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Compiler.js | `lib/Compiler.js` | emitAssets()メソッド（699-1045行目）でファイル書き出しロジックを理解する |

**主要処理フロー**:
- **716-719行目**: asyncLib.forEachLimitで並列度15のアセット出力
- **768-803行目**: 大文字小文字の衝突検出（checkSimilarFile）
- **917-948行目**: 既存ファイルとの内容比較によるスキップ最適化（processExistingFile）
- **840-868行目**: 実際のファイル書き込み処理（doWrite）

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

```
webpack() [エントリーポイント]
    │
    ├─ Compiler.constructor(context, options)
    │      ├─ フック初期化（tapable）
    │      ├─ ResolverFactory生成
    │      └─ Cache生成
    │
    ├─ Compiler.run(callback)
    │      ├─ hooks.beforeRun.callAsync()
    │      ├─ hooks.run.callAsync()
    │      ├─ readRecords()
    │      └─ compile(onCompiled)
    │             ├─ hooks.beforeCompile.callAsync()
    │             ├─ NormalModuleFactory生成
    │             ├─ ContextModuleFactory生成
    │             ├─ new Compilation(this, params)
    │             ├─ hooks.thisCompilation.call()
    │             ├─ hooks.compilation.call()
    │             ├─ hooks.make.callAsync()
    │             ├─ hooks.finishMake.callAsync()
    │             ├─ compilation.finish()
    │             └─ compilation.seal()
    │
    ├─ Compiler.emitAssets(compilation, callback)
    │      ├─ hooks.emit.callAsync()
    │      ├─ asyncLib.forEachLimit(assets, 15, ...)
    │      │      ├─ checkSimilarFile()
    │      │      ├─ processExistingFile() / processMissingFile()
    │      │      └─ doWrite()
    │      └─ hooks.afterEmit.callAsync()
    │
    └─ Compiler.watch(watchOptions, handler)
           └─ new Watching(this, watchOptions, handler)
```

### データフロー図

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

webpack設定 ──────▶ Compiler.constructor() ──────▶ Compilerインスタンス
                          │
ソースファイル群 ──▶ Compiler.run()
                          │
                   ├─ compile()
                   │    ├─ Compilation生成 ─────▶ モジュールグラフ
                   │    ├─ make(エントリ処理)      チャンクグラフ
                   │    └─ seal(最適化・コード生成) ─▶ アセット群
                   │
                   ├─ emitAssets() ──────────────▶ バンドルファイル
                   │                                ソースマップ
                   └─ emitRecords() ─────────────▶ レコードファイル

                   done フック ───────────────────▶ Stats オブジェクト
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Compiler.js | `lib/Compiler.js` | ソース | Compilerクラス本体。ビルドライフサイクル全体を管理 |
| Compilation.js | `lib/Compilation.js` | ソース | 個別コンパイルインスタンス。Compilerから生成される |
| Watching.js | `lib/Watching.js` | ソース | ファイル監視とインクリメンタルリビルドの管理 |
| Stats.js | `lib/Stats.js` | ソース | コンパイル統計情報の管理 |
| NormalModuleFactory.js | `lib/NormalModuleFactory.js` | ソース | 通常モジュールの生成ファクトリ |
| ContextModuleFactory.js | `lib/ContextModuleFactory.js` | ソース | コンテキストモジュールの生成ファクトリ |
| ResolverFactory.js | `lib/ResolverFactory.js` | ソース | モジュールパス解決のリゾルバファクトリ |
| Cache.js | `lib/Cache.js` | ソース | キャッシュシステム |
| CacheFacade.js | `lib/CacheFacade.js` | ソース | キャッシュのファサード |
| WebpackError.js | `lib/WebpackError.js` | ソース | エラー基底クラス |
| ConcurrentCompilationError.js | `lib/ConcurrentCompilationError.js` | ソース | 二重実行エラー |
