# 機能設計書 57-DefinePlugin

## 概要

本ドキュメントは、webpackにおけるDefinePlugin機能の設計仕様を記述する。コンパイル時に指定された定数定義をソースコード内の識別子やtypeof式に置換し、環境変数の注入や条件付きコンパイル（デッドコード除去と連携）を実現する機能である。

### 本機能の処理概要

**業務上の目的・背景**：フロントエンド開発において、開発環境と本番環境で異なる動作をさせたい場合（例: `process.env.NODE_ENV`による分岐）、コンパイル時に定数を置換することで、ミニファイア（Terser等）がデッドコードとして不要な分岐を除去できるようにする。DefinePluginはこの定数置換を担い、バンドルサイズの最適化とビルド時の環境変数注入を実現する中核プラグインである。

**機能の利用シーン**：`process.env.NODE_ENV`の置換、APIエンドポイントの環境別切り替え、フィーチャーフラグの注入、`typeof window`の静的評価（Node.js/ブラウザ判定）等に利用される。

**主要な処理内容**：
1. 定義マップ（key→value）を受け取り、JavaScriptパーサのフックにタップする
2. ソースコード内のアクセス式（`process.env.NODE_ENV`等）を検出し、定数値に置換する
3. typeof式（`typeof window`等）を検出し、型文字列に置換する
4. ネストされたオブジェクト定義をサポートし、プロパティアクセスチェーンを再帰的に処理する
5. RuntimeValue機能により、コンパイル実行時に動的に値を決定可能にする
6. キャッシュバージョン管理により、値の変更時にモジュールの再ビルドをトリガーする

**関連システム・外部連携**：ConstDependencyを生成してソースコードの該当箇所を定数で置換する。BasicEvaluatedExpressionと連携してコンパイル時式評価を行う。DotenvPlugin（No.60）はDefinePluginに委譲して環境変数を注入する。

**権限による制御**：特になし。webpack設定のpluginsで設定する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能はUIを持たない内部処理であり、直接関連する画面は存在しない |

## 機能種別

コード変換処理 / コンパイル時定数注入

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| definitions | Record<string, CodeValue> | Yes | キー→値のマッピング。キーはドット区切りの識別子パス | - |
| CodeValue | string \| number \| boolean \| object \| RegExp \| RuntimeValue | - | 置換される値 | - |

### 入力データソース

- webpack設定のplugins配列: `new DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') })`
- RuntimeValue: `DefinePlugin.runtimeValue(fn, options)` で動的値を定義可能

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ConstDependency | Dependency | ソースコード上の該当箇所を定数値で置換する依存オブジェクト |

### 出力先

モジュールのソースコード内の該当式が定数に置換される（ASTレベルの置換）

## 処理フロー

### 処理シーケンス

```
1. DefinePlugin.apply() → compiler.hooks.compilation にタップ
   └─ ConstDependency.Templateを登録
2. definitionsフックで定義マップをマージ
   └─ 他プラグイン（DotenvPlugin等）の定義も統合
3. normalModuleFactory.hooks.parser にタップ
   └─ JavaScript AUTO/DYNAMIC/ESM の全モジュールタイプに対応
4. parser.hooks.program でビルド情報にvalueDependenciesを設定
   └─ キャッシュバージョン管理
5. walkDefinitions で定義を再帰的に処理
   a. オブジェクト型: 子プロパティを再帰処理 + applyObjectDefine
   b. プリミティブ型: applyDefineKey + applyDefine
6. applyDefine でパーサフックを登録
   a. expression フック: 識別子アクセスを定数に置換
   b. typeof フック: typeof式の評価結果を置換
   c. evaluateTypeof フック: コンパイル時typeof評価
   d. evaluateIdentifier フック: コンパイル時式評価
   e. evaluateDefinedIdentifier フック: 定義済み判定
```

### フローチャート

```mermaid
flowchart TD
    A[DefinePlugin.apply] --> B[compilation.hooks.compilationにタップ]
    B --> C[definitionsフックで定義マージ]
    C --> D[parser.hooks.programでvalueDependencies設定]
    D --> E[walkDefinitionsで再帰処理]
    E --> F{定義値の型?}
    F -->|オブジェクト| G[子プロパティを再帰処理]
    G --> H[applyObjectDefine]
    F -->|プリミティブ/RuntimeValue| I[applyDefine]
    I --> J[expressionフック登録]
    I --> K[typeofフック登録]
    I --> L[evaluateTypeofフック登録]
    I --> M[evaluateIdentifierフック登録]
    J --> N[ConstDependency生成 → ソース置換]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-57-01 | typeof接頭辞 | キーが"typeof "で始まる場合、typeof式の評価結果として処理する | TYPEOF_OPERATOR_REGEXPにマッチ |
| BR-57-02 | RuntimeValue | RuntimeValueインスタンスの場合、パーサ実行時にfn()を呼び出して値を動的決定する | code instanceof RuntimeValue |
| BR-57-03 | ネストオブジェクト | オブジェクト型の定義値はプロパティアクセスチェーンとして再帰的に展開する | typeof code === 'object' |
| BR-57-04 | ASI安全性 | 置換コードがASI（自動セミコロン挿入）に影響する場合、適切にラッピングする | asiSafe判定 |
| BR-57-05 | BigIntサポート | BigInt値はランタイムがBigIntリテラルをサポートする場合`Nn`形式、そうでなければ`BigInt("N")`形式 | typeof code === 'bigint' |
| BR-57-06 | キャッシュバージョン | 定義値の変更はvalueCacheVersionsで追跡され、変更時にモジュールが再ビルドされる | toCacheVersion関数 |
| BR-57-07 | webpack requireとの共存 | 定義キーが__webpack_require__と衝突する場合はスキップする | WEBPACK_REQUIRE_FUNCTION_REGEXP |

### 計算ロジック

値のコード化: `toCode(code, parser, valueCacheVersions, key, runtimeTemplate, logger, asiSafe, objKeys)` で各値型をJavaScriptコード文字列に変換する。null→"null"、undefined→"undefined"、RegExp→toString()、function→toString()、object→stringifyObj()再帰。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | 本機能はデータベースを使用しない |

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

該当なし（インメモリ処理のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Warning | 定義値がオブジェクトで、展開キーと単一キーの定義が競合する場合 | WebpackError警告をcompilationに追加 |

### リトライ仕様

該当なし。

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

該当なし（コンパイル時の同期的な変換処理）

## パフォーマンス要件

- パーサフックレベルの処理であり、各モジュールの解析時に実行される
- 定義数が増えるとフック登録数が増加するが、通常の使用範囲では問題にならない

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

- APIキーやシークレットをDefinePluginで注入するとバンドルに平文で含まれるため、公開バンドルには使用しないこと
- process.env全体の注入は避け、必要な変数のみを個別に注入すること

## 備考

- productionモードでは`process.env.NODE_ENV: '"production"'`がデフォルトで設定される
- `DefinePlugin.runtimeValue(fn, fileDeps)`で動的値を定義でき、ファイル変更時の再ビルドもトリガーできる
- `SyncWaterfallHook`による`definitions`フックで、他プラグインが定義を追加・上書き可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | DefinePlugin.js | `lib/DefinePlugin.js` | RuntimeValueクラスとCodeValue型を理解する |

**読解のコツ**: DefinePluginは大規模なファイルであるが、RuntimeValue→toCode→applyDefine→walkDefinitionsの順に理解するのが効率的。

- **38-39行目**: CodeValuePrimitive型とCodeValue型の定義
- **42-48行目**: RuntimeValueOptionsの型定義（fileDependencies, contextDependencies等）
- **52-128行目**: RuntimeValueクラス - コンパイル時に動的値を決定する仕組み
- **79-118行目**: `exec()`メソッド - パーサ実行時にfn()を呼び出し、依存情報をbuildInfoに追加

#### Step 2: 値のコード変換ロジック

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DefinePlugin.js | `lib/DefinePlugin.js` | toCode関数とstringifyObj関数 |

**主要処理フロー**:
- **153-211行目**: `stringifyObj`関数 - オブジェクト/配列を再帰的にJavaScriptコード文字列に変換
- **225-287行目**: `toCode`関数 - 各値型をコード文字列に変換（null, undefined, -0, RuntimeValue, RegExp, function, object, bigint）
- **293-327行目**: `toCacheVersion`関数 - キャッシュバージョン用の文字列表現を生成

#### Step 3: プラグインのエントリーポイントと定義処理

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | DefinePlugin.js | `lib/DefinePlugin.js` | DefinePluginクラスのapply()メソッド |

**主要処理フロー**:
1. **348-362行目**: `getCompilationHooks` - SyncWaterfallHook("definitions")の管理
2. **368-371行目**: コンストラクタ - definitions引数を保持
3. **387-390行目**: `apply()` - compilation.hooks.compilationにタップ
4. **394-397行目**: definitionsフックで定義をマージ
5. **415-419行目**: mainHashを生成しキャッシュバージョン管理
6. **426-432行目**: handlerでparser.hooks.programにタップ
7. **476-495行目**: `walkDefinitions` - 定義を再帰的に処理

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

```
DefinePlugin.apply(compiler)
    │
    ├─ compiler.hooks.compilation
    │      ├─ hooks.definitions.tap() ──▶ 定義マージ
    │      ├─ ConstDependency.Template 登録
    │      └─ normalModuleFactory.hooks.parser
    │             └─ handler(parser)
    │                    ├─ parser.hooks.program ──▶ valueDependencies設定
    │                    └─ walkDefinitions()
    │                           ├─ applyDefineKey() ──▶ canRenameフック登録
    │                           ├─ applyDefine() ──▶ expression/typeof/evaluate フック登録
    │                           │      └─ toCode() ──▶ ConstDependency生成
    │                           └─ applyObjectDefine() ──▶ オブジェクト展開
    │
    └─ RuntimeValue.exec() ──▶ 動的値決定
```

### データフロー図

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

definitions ───────────────▶ walkDefinitions()                    ConstDependency
  { "process.env.NODE_ENV":     ├─ applyDefine()                    │
    '"production"' }            │    ├─ expressionフック             │
                                │    │    └─ toCode() ──────────▶ ソース置換
                                │    ├─ typeofフック               "production"
                                │    └─ evaluateフック
                                └─ applyObjectDefine()

RuntimeValue(fn, deps) ────▶ RuntimeValue.exec() ─────────────▶ 動的値
                                └─ fn({module, key, version})
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DefinePlugin.js | `lib/DefinePlugin.js` | ソース | コンパイル時定数定義・置換プラグイン本体 |
| ConstDependency.js | `lib/dependencies/ConstDependency.js` | ソース | 定数置換依存オブジェクト |
| BasicEvaluatedExpression.js | `lib/javascript/BasicEvaluatedExpression.js` | ソース | コンパイル時式評価 |
| JavascriptParser.js | `lib/javascript/JavascriptParser.js` | ソース | パーサフック（expression, typeof, evaluate等） |
| JavascriptParserHelpers.js | `lib/javascript/JavascriptParserHelpers.js` | ソース | evaluateToString, toConstantDependencyヘルパー |
| createHash.js | `lib/util/createHash.js` | ソース | キャッシュバージョンハッシュ生成 |
| DotenvPlugin.js | `lib/DotenvPlugin.js` | ソース | DefinePluginに委譲して環境変数を注入 |
