# 機能設計書 66-ContextReplacementPlugin

## 概要

本ドキュメントは、webpackの組込みプラグインである `ContextReplacementPlugin` の機能設計を記述する。`ContextReplacementPlugin` は、コンテキストモジュールの解決結果を置換するプラグインである。

### 本機能の処理概要

ContextReplacementPluginは、require式やimport式で動的に生成されるコンテキストモジュール（ディレクトリベースの動的インポート）の解決結果を制御する機能を提供する。ContextModuleFactoryの `beforeResolve` と `afterResolve` フックにタップし、コンテキストのリクエストパス、再帰フラグ、正規表現フィルタ、依存関係マップを置換できる。

**業務上の目的・背景**：動的なrequire式（例：`require('./locale/' + lang)`）はwebpackがコンテキストモジュールとして処理するが、不要なファイルがバンドルに含まれたり、クリティカル依存警告が発生したりする問題がある。ContextReplacementPluginはこれらの問題を解決し、コンテキストモジュールの範囲を適切に制御する。

**機能の利用シーン**：momentjsのロケール制限（必要なロケールのみバンドル）、動的importの対象範囲制限、クリティカル依存警告の抑制、カスタムコンテキストマップによるモジュール解決の制御に利用される。

**主要な処理内容**：
1. コンストラクタで柔軟なパラメータパターンを解析し、内部状態を構築する
2. ContextModuleFactoryの `beforeResolve` でリクエストパス、再帰フラグ、正規表現フィルタを置換する
3. ContextModuleFactoryの `afterResolve` でリソースパス、再帰フラグ、正規表現フィルタを置換する
4. コールバック関数が指定されている場合、resolveDataを引数として呼び出す
5. コンテキストマップが指定されている場合、`resolveDependencies` を置換して独自の依存関係解決を提供する
6. コールバック関数を指定しない場合、全ての依存関係の `critical` フラグを `false` に設定する

**関連システム・外部連携**：ContextModuleFactory、ContextElementDependency、InputFileSystemと連携する。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能に直接関連する画面はない |

## 機能種別

データ連携 / コード変換（コンテキストモジュール置換）

## 入力仕様

### 入力パラメータ

コンストラクタは複数のパラメータパターンをサポートする:

| パターン | パラメータ | 説明 |
|---------|-----------|------|
| 1 | `(resourceRegExp, callback)` | 正規表現+コールバック関数 |
| 2 | `(resourceRegExp, newContentResource, contextMap)` | 正規表現+新パス+コンテキストマップ |
| 3 | `(resourceRegExp, newContentResource, callback)` | 正規表現+新パス+コールバック |
| 4 | `(resourceRegExp, newContentResource, newContentRecursive, newContentRegExp)` | 正規表現+新パス+再帰フラグ+新正規表現 |
| 5 | `(resourceRegExp, newContentRecursive, newContentRegExp)` | 正規表現+再帰フラグ+新正規表現 |
| 6 | `(resourceRegExp, newContentRegExp)` | 正規表現+新正規表現 |

### 入力データソース

- webpack設定ファイルのpluginsセクション
- ContextModuleFactoryの解決データ（request, resource, recursive, regExp, dependencies）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result.request | `string` | 置換されたリクエストパス |
| result.resource | `string \| string[]` | 置換されたリソースパス |
| result.recursive | `boolean` | 再帰フラグ |
| result.regExp | `RegExp` | フィルタ用正規表現 |
| result.resolveDependencies | `Function` | 依存関係解決関数 |

### 出力先

ContextModuleFactoryの解決データが直接変更される（in-place mutation）。

## 処理フロー

### 処理シーケンス

```
1. コンストラクタで入力パターンを解析
   └─ 引数の型・数に応じて内部プロパティを設定
2. apply()でContextModuleFactoryフックにタップ
   └─ cmf.hooks.beforeResolve と afterResolve に登録
3. beforeResolveフックでコンテキストを検査
   └─ resourceRegExpがrequestにマッチした場合、各プロパティを置換
4. afterResolveフックで解決済みコンテキストを検査
   └─ resourceRegExpがresourceにマッチした場合、各プロパティを置換
5. コールバック関数による動的置換
   └─ 指定されている場合、resolveDataを引数として呼び出し
6. コンテキストマップによる依存解決置換
   └─ resolveDependenciesを独自の解決関数に置換
7. クリティカル依存警告の抑制
   └─ コールバック未指定時、全依存のcriticalをfalseに設定
```

### フローチャート

```mermaid
flowchart TD
    A[ContextModuleFactory] --> B[beforeResolve]
    B --> C{resourceRegExp.test request?}
    C -->|No| D[スキップ]
    C -->|Yes| E[request/recursive/regExp を置換]
    E --> F{newContentCallback?}
    F -->|Yes| G[コールバック呼び出し]
    F -->|No| H[dependencies.critical = false]

    A --> I[afterResolve]
    I --> J{resourceRegExp.test resource?}
    J -->|No| K[スキップ]
    J -->|Yes| L[resource/recursive/regExp を置換]
    L --> M{newContentCreateContextMap?}
    M -->|Yes| N[resolveDependencies を置換]
    M -->|No| O{newContentCallback?}
    O -->|Yes| P[コールバック呼び出し + 相対パス解決]
    O -->|No| Q[dependencies.critical = false]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 多様なコンストラクタパターン | 6種類のパラメータパターンをサポートする | コンストラクタ呼び出し時 |
| BR-02 | クリティカル警告抑制 | コールバック未指定時、依存のcriticalフラグをfalseに設定 | beforeResolve/afterResolveでコールバックがない場合 |
| BR-03 | 配列リソース対応 | afterResolveでresourceが配列の場合もsomeで検査する | resourceがstring[]の場合 |
| BR-04 | コールバック時の相対パス解決 | コールバックがリソースを相対パスに変更した場合、元リソースからのjoinで解決 | afterResolveでコールバック後にresourceが変更された場合 |
| BR-05 | コンテキストマップ | 指定されたマップに基づきContextElementDependencyを生成する | newContentCreateContextMapが指定されている場合 |

### 計算ロジック

特筆すべき計算ロジックは存在しない。

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

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

## エラー処理

### エラーケース一覧

コンテキストマップの解決関数でエラーが発生した場合、コールバックにエラーが渡される。

### リトライ仕様

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

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

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

## パフォーマンス要件

特に記載すべきパフォーマンス要件は存在しない。

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

- コンテキストの範囲を適切に制限することで、意図しないファイルがバンドルに含まれるリスクを軽減できる

## 備考

- ContextReplacementPluginの典型的なユースケースは `new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /ja|en/)` によるmomentjsロケール制限
- NormalModuleReplacementPlugin（No.65）が通常モジュールの置換であるのに対し、本プラグインはコンテキストモジュール（動的require/import）の置換を担当する

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ContextReplacementPlugin.js | `lib/ContextReplacementPlugin.js` | コンストラクタ（28-87行目）の6つのパラメータパターンの解析ロジック |

**読解のコツ**: コンストラクタは引数の型（string/function/boolean/RegExp/object）に基づいて分岐し、`newContentResource`, `newContentCallback`, `newContentRecursive`, `newContentRegExp`, `newContentCreateContextMap` のプロパティを設定する。

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

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

**主要処理フロー**:
1. **102行目**: `compiler.hooks.contextModuleFactory` にタップ
2. **103-124行目**: `beforeResolve` でコンテキストを置換
3. **125-199行目**: `afterResolve` で解決済みコンテキストを置換

#### Step 3: 依存関係解決の置換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ContextReplacementPlugin.js | `lib/ContextReplacementPlugin.js` | `createResolveDependenciesFromContextMap()` 関数（208-225行目） |

**主要処理フロー**:
- **212-222行目**: コンテキストマップのキーからContextElementDependencyを生成

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

```
ContextReplacementPlugin.apply(compiler)
    |
    +-- compiler.hooks.contextModuleFactory.tap()
            |
            +-- cmf.hooks.beforeResolve.tap()
            |       |
            |       +-- resourceRegExp.test(request)
            |       +-- [置換] request, recursive, regExp
            |       +-- [callback] newContentCallback(result)
            |       +-- [no callback] dependencies.critical = false
            |
            +-- cmf.hooks.afterResolve.tap()
                    |
                    +-- resourceRegExp.test(resource)
                    +-- [置換] resource, recursive, regExp
                    +-- [contextMap] createResolveDependenciesFromContextMap()
                    |       |
                    |       +-- new ContextElementDependency() for each entry
                    +-- [callback] newContentCallback(result) + 相対パス解決
                    +-- [no callback] dependencies.critical = false
```

### データフロー図

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

resolveData ──────────────▶ beforeResolve ──▶ 置換されたresolveData
(request, recursive,       (パターンマッチ+置換)
 regExp, dependencies)

afterResolveData ─────────▶ afterResolve ──▶ 置換されたafterResolveData
(resource, recursive,      (パターンマッチ+置換)
 regExp, resolveDependencies)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ContextReplacementPlugin.js | `lib/ContextReplacementPlugin.js` | ソース | 本機能のメインプラグイン（228行） |
| ContextElementDependency.js | `lib/dependencies/ContextElementDependency.js` | ソース | コンテキストマップから生成される依存関係 |
| ContextModuleFactory.js | `lib/ContextModuleFactory.js` | ソース | コンテキストモジュールの解決ファクトリ |
| fs.js | `lib/util/fs.js` | ソース | join関数の提供 |
