# 機能設計書 61-EnvironmentPlugin

## 概要

本ドキュメントは、webpackの組込みプラグインである `EnvironmentPlugin` の機能設計を記述する。`EnvironmentPlugin` は、`process.env` および `import.meta.env` 経由で環境変数をバンドルに注入するプラグインである。

### 本機能の処理概要

EnvironmentPluginは、Node.jsの `process.env` から環境変数を読み取り、webpackのDefinePluginを内部的に利用してバンドルコード内の `process.env.KEY` および `import.meta.env.KEY` を対応する値に置換する機能を提供する。

**業務上の目的・背景**：フロントエンドアプリケーションでは、APIエンドポイントのURL、機能フラグ、バージョン情報などの環境依存の設定値をビルド時に注入する必要がある。EnvironmentPluginは、DefinePluginの設定を簡略化し、環境変数をシンプルに注入するインターフェースを提供することで、開発者の設定負担を軽減する。

**機能の利用シーン**：本番環境・ステージング環境・開発環境ごとに異なるAPI URLやログレベルを設定する場合、CI/CDパイプラインで環境変数を注入する場合、機能フラグによる条件付きコンパイルを行う場合に利用される。

**主要な処理内容**：
1. コンストラクタで環境変数キーの配列またはデフォルト値付きオブジェクトを受け取り正規化する
2. `thisCompilation` フックで各キーについて `process.env` から値を取得し、未定義の場合はデフォルト値を使用する
3. 値が未定義（デフォルト値もなし）の場合は `EnvVariableNotDefinedError` としてコンパイルエラーを追加する
4. 各キーについて `process.env.KEY` と `import.meta.env.KEY` の両方の定義を `DefinePlugin` に委譲する

**関連システム・外部連携**：DefinePluginに内部的に依存し、実際のコード置換処理はDefinePluginが担当する。Node.jsの `process.env` からランタイム環境変数を読み取る。

**権限による制御**：権限による制御は存在しない。ビルド時のNode.jsプロセスの環境変数にアクセスできることが前提となる。

## 関連画面

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

## 機能種別

データ連携 / コード変換（コンパイル時の定数注入）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| keys | `string[]` または `Record<string, any>` | Yes | 環境変数キーの配列、またはデフォルト値を持つオブジェクト | 配列の場合は文字列の配列、オブジェクトの場合はキーが環境変数名 |

### 入力データソース

- Node.jsランタイムの `process.env` オブジェクト
- コンストラクタに渡されるデフォルト値オブジェクト

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| definitions | `Record<string, CodeValue>` | DefinePluginに渡される定義マップ。`process.env.KEY` と `import.meta.env.KEY` の両方のキーが含まれる |
| errors | `WebpackError[]` | 未定義の環境変数に対するエラー（`EnvVariableNotDefinedError`） |

### 出力先

DefinePluginを通じてバンドルコード内に定数として埋め込まれる。未定義変数のエラーはコンパイルエラーとして報告される。

## 処理フロー

### 処理シーケンス

```
1. コンストラクタでの入力正規化
   └─ 引数の形式（配列/オブジェクト/可変長引数）に応じてkeysとdefaultValuesを設定
2. apply()でDefinePluginインスタンスを生成
   └─ 空の定義オブジェクトでDefinePluginを作成
3. thisCompilationフックにタップ
   └─ 各コンパイルの開始時に定義を動的に生成
4. 各キーについて値を解決
   └─ process.env[key] が定義されていればその値、なければdefaultValues[key]を使用
5. 未定義値のエラー処理
   └─ 値がundefinedの場合、EnvVariableNotDefinedErrorをcompilation.errorsに追加
6. 定義マップの構築
   └─ process.env.KEY と import.meta.env.KEY の両方にJSON.stringify済みの値を設定
7. DefinePluginに定義を設定し適用
   └─ definePlugin.definitionsを更新し、DefinePlugin.apply()を呼び出す
```

### フローチャート

```mermaid
flowchart TD
    A[EnvironmentPlugin.apply] --> B[DefinePlugin インスタンス生成]
    B --> C[thisCompilation フックにタップ]
    C --> D[各キーをイテレーション]
    D --> E{process.env にキーが存在?}
    E -->|Yes| F[process.env の値を使用]
    E -->|No| G{デフォルト値が存在?}
    G -->|Yes| H[デフォルト値を使用]
    G -->|No| I[EnvVariableNotDefinedError を追加]
    F --> J[JSON.stringify で値を変換]
    H --> J
    I --> K[値を 'undefined' 文字列に設定]
    J --> L[process.env.KEY と import.meta.env.KEY に定義を追加]
    K --> L
    L --> M{次のキーがある?}
    M -->|Yes| D
    M -->|No| N[definePlugin.definitions を更新]
    N --> O[DefinePlugin.apply を呼び出し]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 環境変数優先 | process.envの値がデフォルト値より優先される | process.env[key]がundefinedでない場合 |
| BR-02 | 未定義エラー | デフォルト値なしでprocess.envにもない場合はコンパイルエラー | 値がundefinedの場合 |
| BR-03 | 値のシリアライズ | 環境変数値はJSON.stringifyで文字列化される | undefinedでない全ての値 |
| BR-04 | デュアル注入 | process.env.KEY と import.meta.env.KEY の両方に注入される | 全てのキーに対して |

### 計算ロジック

値の解決ロジック:
```
value = process.env[key] !== undefined ? process.env[key] : defaultValues[key]
defValue = value === undefined ? "undefined" : JSON.stringify(value)
```

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

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

データベース操作は存在しない。本プラグインはコンパイル時のコード変換のみを行う。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| EnvVariableNotDefinedError | コンパイルエラー | 環境変数がprocess.envに定義されておらず、デフォルト値も指定されていない場合 | デフォルト値付きオブジェクトを渡すか、環境変数を設定する |

### リトライ仕様

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

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

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

## パフォーマンス要件

コンパイル開始時に一度だけ環境変数の読み取りと定義マップの構築が行われるため、パフォーマンスへの影響は極めて小さい。

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

- 環境変数の値がバンドルに平文で埋め込まれるため、シークレットキーやパスワードなどの機密情報を環境変数として注入しないよう注意が必要
- バンドルファイルはクライアントサイドに配信されるため、注入される値は公開されても問題ないものに限定すべき

## 備考

- EnvironmentPluginはDefinePluginのショートカット（便利ラッパー）として位置づけられる
- DefinePluginとの違いは、process.envからの自動読み取りとデフォルト値のサポートにある
- ESModuleの `import.meta.env.KEY` にも自動的に注入される（webpack 5.x以降）

---

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

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

### 推奨読解順序

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

EnvironmentPluginが扱うデータ構造はシンプルで、環境変数キーの配列とデフォルト値のマップが中心である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | EnvironmentPlugin.js | `lib/EnvironmentPlugin.js` | `keys: string[]` と `defaultValues: Record<string, any>` の2つのインスタンスプロパティ |

**読解のコツ**: コンストラクタの3つのパターン（配列、オブジェクト、可変長引数）を理解することが重要。

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

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

**主要処理フロー**:
1. **42行目**: 空のDefinePluginインスタンスを生成
2. **44行目**: `compiler.hooks.thisCompilation` にタップし、コンパイルごとに定義を構築
3. **47-67行目**: 各キーについてprocess.envから値を取得し、定義マップを構築
4. **68行目**: definePlugin.definitionsを更新
5. **70行目**: DefinePlugin.apply(compiler)を呼び出し

#### Step 3: 値解決ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | EnvironmentPlugin.js | `lib/EnvironmentPlugin.js` | 48-66行目の値解決ロジック |

**主要処理フロー**:
- **48-51行目**: process.envから値を取得、なければdefaultValuesから取得
- **53-61行目**: 値が未定義の場合のエラー処理
- **63-66行目**: 定義値の構築（JSON.stringifyまたは"undefined"文字列）

#### Step 4: DefinePluginの連携を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | DefinePlugin.js | `lib/DefinePlugin.js` | DefinePluginがどのように定義をコードに適用するかを理解 |

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

```
EnvironmentPlugin.apply(compiler)
    |
    +-- new DefinePlugin({})
    |
    +-- compiler.hooks.thisCompilation.tap()
    |       |
    |       +-- process.env[key] の値取得
    |       +-- JSON.stringify(value) で値変換
    |       +-- definitions['process.env.KEY'] = defValue
    |       +-- definitions['import.meta.env.KEY'] = defValue
    |       +-- definePlugin.definitions = definitions
    |
    +-- definePlugin.apply(compiler)
            |
            +-- DefinePlugin の標準処理（コード内の定数置換）
```

### データフロー図

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

process.env ────────┐
                    ├──▶ EnvironmentPlugin.apply() ──▶ DefinePlugin ──▶ バンドルコード内の
defaultValues ──────┘       (値解決・定義構築)          (コード置換)     定数が環境変数値に置換
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| EnvironmentPlugin.js | `lib/EnvironmentPlugin.js` | ソース | 本機能のメインプラグイン。環境変数の読み取りとDefinePluginへの委譲 |
| DefinePlugin.js | `lib/DefinePlugin.js` | ソース | 実際のコード置換処理を行うプラグイン |
| WebpackError.js | `lib/WebpackError.js` | ソース | 未定義環境変数のエラー生成に使用 |
