# 機能設計書 78-Tapableフックシステム

## 概要

本ドキュメントは、webpackの拡張可能なプラグインシステムの基盤となるtapableライブラリによるフックシステムについて、そのフック種別・プラグイン登録メカニズム・イベント発火パターンを詳細に記述した機能設計書である。

### 本機能の処理概要

TapableフックシステムはSyncHook / SyncBailHook / SyncWaterfallHook / AsyncSeriesHook / AsyncParallelHook等の多種のフックを提供し、webpackのコンパイルライフサイクル全体にわたるプラグインの登録・実行を管理する機能である。

**業務上の目的・背景**：webpackはプラグインアーキテクチャを採用しており、コアの機能の多くがプラグインとして実装されている。tapableはこのプラグインシステムの基盤ライブラリであり、同期/非同期、直列/並列、ウォーターフォール/ベイルアウト等の多様な実行パターンを提供する。これにより、webpackの各コンパイルフェーズに対して外部プラグインがフレキシブルに介入できる。

**機能の利用シーン**：Compiler、Compilation、NormalModuleFactory、ContextModuleFactory等のすべてのwebpackクラスがtapableフックを公開し、プラグインがtap/tapAsync/tapPromiseメソッドでハンドラを登録する。

**主要な処理内容**：
1. フッククラスの提供（SyncHook、SyncBailHook、SyncWaterfallHook、SyncLoopHook、AsyncSeriesHook、AsyncSeriesBailHook、AsyncSeriesWaterfallHook、AsyncSeriesLoopHook、AsyncParallelHook、AsyncParallelBailHook）
2. プラグイン登録（tap/tapAsync/tapPromise）とインターセプト（intercept）
3. フック発火（call/callAsync/promise）
4. HookMapによる名前付きフックの動的生成・管理
5. MultiHookによる複数フックの一括操作

**関連システム・外部連携**：tapableはnpmパッケージとしてwebpackの外部依存関係として管理される。webpack内の全主要クラスがtapableフックを使用する。

**権限による制御**：本機能に権限による制御はない。

## 関連画面

本機能に直接関連する画面はない。プラグインシステムの内部基盤機能である。

## 機能種別

イベントシステム・プラグインアーキテクチャ基盤

## 入力仕様

### 入力パラメータ

フック生成:

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| args | string[] | Yes | フック引数の名前リスト | - |
| name | string | No | フック名 | - |

プラグイン登録（tap）:

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| options | string \| TapOptions | Yes | プラグイン名またはオプション | - |
| fn | Function | Yes | コールバック関数 | - |

### 入力データソース

プラグインのapplyメソッドから呼び出される

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| (call結果) | any | SyncHook: undefined、SyncBailHook: 最初の非undefined値、SyncWaterfallHook: 最終変換値 |
| (callAsync結果) | void | コールバック経由で結果を返却 |
| (promise結果) | Promise | Promise経由で結果を返却 |

### 出力先

呼び出し元に返却される。

## 処理フロー

### 処理シーケンス

```
1. フックの定義（クラスのコンストラクタで定義）
   └─ new SyncHook(["arg1", "arg2"])
2. プラグインの登録
   ├─ hook.tap("PluginName", (arg1, arg2) => { ... })
   ├─ hook.tapAsync("PluginName", (arg1, arg2, callback) => { ... })
   └─ hook.tapPromise("PluginName", (arg1, arg2) => { return promise; })
3. フックの発火
   ├─ hook.call(arg1, arg2)  [同期]
   ├─ hook.callAsync(arg1, arg2, callback)  [非同期コールバック]
   └─ hook.promise(arg1, arg2)  [非同期Promise]
4. 登録されたプラグインの実行
   └─ 実行パターンはフック種別による
```

### フローチャート

```mermaid
flowchart TD
    A[フック定義] --> B[プラグイン登録]
    B --> C{tap/tapAsync/tapPromise}
    C -->|tap| D[同期ハンドラ登録]
    C -->|tapAsync| E[非同期コールバックハンドラ登録]
    C -->|tapPromise| F[非同期Promiseハンドラ登録]
    D --> G[フック発火]
    E --> G
    F --> G
    G --> H{フック種別}
    H -->|SyncHook| I[全ハンドラを順次実行]
    H -->|SyncBailHook| J[非undefined返却で停止]
    H -->|SyncWaterfallHook| K[前の結果を次に渡す]
    H -->|AsyncSeriesHook| L[直列非同期実行]
    H -->|AsyncParallelHook| M[並列非同期実行]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-78-01 | SyncHook | 登録された全ハンドラを順次実行。戻り値は無視 | call時 |
| BR-78-02 | SyncBailHook | ハンドラが非undefined値を返した時点で実行停止 | call時 |
| BR-78-03 | SyncWaterfallHook | 各ハンドラの戻り値を次のハンドラの第1引数として渡す | call時 |
| BR-78-04 | SyncLoopHook | ハンドラがundefinedを返すまでループ実行 | call時 |
| BR-78-05 | AsyncSeriesHook | 各ハンドラを直列に非同期実行 | callAsync/promise時 |
| BR-78-06 | AsyncParallelHook | 全ハンドラを並列に非同期実行 | callAsync/promise時 |
| BR-78-07 | インターセプト | register/call/tap/loopのインターセプタを設定可能 | intercept時 |
| BR-78-08 | before/stage順序制御 | tapオプションのbefore/stageでハンドラの実行順序を制御 | tap時 |
| BR-78-09 | HookMap | 名前付きフックの遅延生成と管理 | for(name).tap()時 |

### 計算ロジック

特になし。

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

本機能にデータベース操作はない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Error | SyncHookでtapAsync/tapPromiseを使用 | tapを使用する |
| - | Error | フック発火時にハンドラがエラーをスロー | ハンドラのエラーハンドリングを修正 |

### リトライ仕様

リトライは行わない。

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

トランザクション管理は不要。

## パフォーマンス要件

tapableはコード生成による最適化を行い、フック発火時のオーバーヘッドを最小化している。大量のプラグインが登録された場合でも効率的に動作する。

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

プラグインは任意のコードを実行できるため、信頼できないプラグインの使用には注意が必要。

## 備考

- tapableはwebpackから独立したnpmパッケージとして管理されている
- webpack内では主にCompiler.hooksとCompilation.hooksが最も多くのフックを公開している
- フック実行時のコード生成により、静的に最適化されたコールチェーンが構築される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | tapableパッケージ | `node_modules/tapable/` | フッククラスの定義。SyncHook、AsyncSeriesHook等 |

**読解のコツ**: tapableはwebpack外部の依存パッケージである。webpack内でのフック使用パターンを理解するにはCompiler.jsやCompilation.jsのフック定義を参照するのが効果的。

#### Step 2: webpackでのフック使用例

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Compiler.js | `lib/Compiler.js` | this.hooksの定義。environment、afterEnvironment、entryOption、afterPlugins等 |
| 2-2 | Compilation.js | `lib/Compilation.js` | this.hooksの定義。buildModule、succeedModule、seal、optimizeModules等 |

#### Step 3: プラグインからのフック利用

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | WebpackOptionsApply.js | `lib/WebpackOptionsApply.js` | プラグインがcompiler.hooks.xxx.tapでハンドラを登録する典型的なパターン |

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

```
tapable (外部ライブラリ)
    |
    +-- SyncHook / SyncBailHook / SyncWaterfallHook / SyncLoopHook
    +-- AsyncSeriesHook / AsyncSeriesBailHook / AsyncSeriesWaterfallHook
    +-- AsyncParallelHook / AsyncParallelBailHook
    +-- HookMap / MultiHook
    |
    +-- webpack Compiler.hooks
    |       +-- environment (SyncHook)
    |       +-- afterEnvironment (SyncHook)
    |       +-- entryOption (SyncBailHook)
    |       +-- afterPlugins (SyncHook)
    |       +-- thisCompilation (SyncHook)
    |       +-- compilation (SyncHook)
    |       +-- make (AsyncParallelHook)
    |       +-- ...
    |
    +-- webpack Compilation.hooks
    |       +-- buildModule (SyncHook)
    |       +-- succeedModule (SyncHook)
    |       +-- seal (SyncHook)
    |       +-- optimizeModules (SyncBailHook)
    |       +-- processAssets (AsyncSeriesHook)
    |       +-- ...
    |
    +-- Plugin.apply(compiler)
            +-- compiler.hooks.xxx.tap("PluginName", handler)
            +-- compiler.hooks.xxx.tapAsync("PluginName", handler)
            +-- compiler.hooks.xxx.tapPromise("PluginName", handler)
```

### データフロー図

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

Plugin.apply() ------> hook.tap("name", fn) --------> ハンドラ登録
                                                           |
コンパイルフェーズ -----> hook.call(args) -----------> 全ハンドラ実行
                        hook.callAsync(args, cb) ----> 非同期実行
                        hook.promise(args) ----------> Promise実行
                                                           |
                                                           v
                                                      結果返却
                                                   (Bail: 最初の値)
                                                   (Waterfall: 変換値)
                                                   (Series: 順次完了)
                                                   (Parallel: 全完了)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| tapable | `node_modules/tapable/` | 外部依存 | フックシステムライブラリ |
| Compiler.js | `lib/Compiler.js` | ソース | Compilerレベルのフック定義 |
| Compilation.js | `lib/Compilation.js` | ソース | Compilationレベルのフック定義 |
| NormalModuleFactory.js | `lib/NormalModuleFactory.js` | ソース | モジュールファクトリレベルのフック定義 |
| ContextModuleFactory.js | `lib/ContextModuleFactory.js` | ソース | コンテキストモジュールファクトリレベルのフック定義 |
| ResolverFactory.js | `lib/ResolverFactory.js` | ソース | リゾルバレベルのフック定義 |
| JavascriptParser.js | `lib/javascript/JavascriptParser.js` | ソース | パーサーレベルのフック定義 |
