# 機能設計書 80-Node.js互換

## 概要

本ドキュメントは、webpackのNodeStuffPlugin / NodeTargetPlugin によるNode.jsグローバル変数（__dirname、__filename、global等）のポリフィル・Node.js組み込みモジュールの外部化機能について、その処理内容・設定オプション・ターゲット環境対応を詳細に記述した機能設計書である。

### 本機能の処理概要

Node.js互換機能は、Node.js固有のグローバル変数やモジュールのポリフィル・外部化を提供し、ブラウザ環境やユニバーサル環境でのNode.jsコードの実行互換性を確保する機能である。

**業務上の目的・背景**：webpackはブラウザ環境向けのバンドルも生成するが、Node.jsで書かれたモジュールが__dirname、__filename、global等のNode.js固有のグローバル変数を使用している場合がある。これらの変数はブラウザ環境には存在しないため、適切なポリフィルまたは代替値を提供する必要がある。また、Node.jsターゲットの場合、組み込みモジュール（fs、path等）をバンドルに含めず外部モジュールとして扱う必要がある。

**機能の利用シーン**：ブラウザ向けバンドルでNode.jsコードを使用する場合の__dirname/__filenameの置換。SSR（サーバーサイドレンダリング）ビルドでのNode.js組み込みモジュールの外部化。ユニバーサルビルドでのimport.meta.dirname/import.meta.filename対応。

**主要な処理内容**：
1. NodeStuffPlugin: __dirname/__filename/globalのポリフィル（mock/warn-mock/node-module/eval-only/true/false）
2. NodeStuffPlugin: import.meta.dirname/import.meta.filenameの対応
3. NodeTargetPlugin: Node.js組み込みモジュール（67種+node:プレフィックス）の外部化
4. NodeTargetPlugin: require使用時のcreateRequire互換性対応

**関連システム・外部連携**：ExternalsPlugin。enhanced-resolveリゾルバ。各JavaScriptパーサーフック。

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

## 関連画面

本機能に直接関連する画面はない。

## 機能種別

ポリフィル・外部モジュール化処理

## 入力仕様

### 入力パラメータ

NodeStuffPlugin:

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| options.global | boolean \| "warn" | Yes | globalのポリフィル設定 | false=無効、true=ポリフィル、"warn"=警告付きポリフィル |
| options.__dirname | boolean \| "mock" \| "warn-mock" \| "node-module" \| "eval-only" | Yes | __dirnameのポリフィル設定 | - |
| options.__filename | boolean \| "mock" \| "warn-mock" \| "node-module" \| "eval-only" | Yes | __filenameのポリフィル設定 | - |

NodeTargetPlugin:

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| type | ExternalsType | No | 外部モジュールの種別 | デフォルト: "node-commonjs" |

### 入力データソース

webpack設定のnode.global、node.__dirname、node.__filename。externalsPresets.nodeの値。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ConstDependency | Dependency | __dirname/__filenameの定数値置換 |
| CachedConstDependency | Dependency | キャッシュ対応の定数値置換 |
| ExternalModuleDependency | Dependency | node:urlモジュールのfileURLToPath使用によるパス変換 |
| ExternalsPlugin設定 | - | Node.js組み込みモジュールの外部化 |

### 出力先

パーサーの依存関係として各モジュールに付加される。

## 処理フロー

### 処理シーケンス

```
NodeStuffPlugin:
1. compilationフックで初期化
   ├─ dependencyTemplatesの登録
   └─ normalModuleFactory.hooks.parserで各パーサーにハンドラ登録
2. global処理
   ├─ false: 何もしない
   ├─ true: globalThisまたはRuntimeGlobals.globalに置換
   └─ "warn": 置換＋NodeStuffInWebError警告
3. __dirname処理
   ├─ false: 何もしない（ESMの場合import.meta.dirnameを保持）
   ├─ "mock": "/"に置換
   ├─ "warn-mock": "/"に置換＋警告
   ├─ "node-module": import.meta.urlからfileURLToPathで変換
   ├─ "eval-only": ESM出力時はimport.meta.url、非ESMは__dirname保持
   └─ true: コンパイラcontextからの相対パスに置換
4. __filename処理（__dirnameと同様のパターン）
5. import.meta.dirname/import.meta.filename処理
   └─ ESMモジュールの場合のみ、__dirname/__filenameと同等の処理

NodeTargetPlugin:
1. ExternalsPluginの適用
   ├─ 67種のNode.js組み込みモジュール名を外部化
   ├─ /^node:/パターンによるnode:プレフィックス対応
   └─ "pnpapi"（Yarn PnP用）の外部化
2. 依存カテゴリに基づく外部化タイプ判定
   └─ commonjsカテゴリ → "node-commonjs"（module出力時のcreateRequire互換）
```

### フローチャート

```mermaid
flowchart TD
    A[NodeStuffPlugin.apply] --> B[compilationフック]
    B --> C[パーサーハンドラ登録]
    C --> D{モジュールタイプ}
    D -->|auto| E[__dirname + __filename + import.meta]
    D -->|dynamic| F[__dirname + __filename]
    D -->|esm| G[import.meta.dirname + import.meta.filename]

    E --> H{__dirname設定}
    H -->|false| I[何もしない]
    H -->|mock| J["/"]
    H -->|warn-mock| K["/" + 警告]
    H -->|node-module| L[fileURLToPath変換]
    H -->|eval-only| M[環境依存分岐]
    H -->|true| N[相対パス]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-80-01 | globalポリフィル | environment.globalThisがサポートされている場合は"globalThis"に、そうでなければRuntimeGlobals.globalに置換 | node.global !== false |
| BR-80-02 | __dirname mock | __dirnameを"/"に置換 | node.__dirname === "mock" |
| BR-80-03 | __dirname true | __dirnameをコンパイラcontextからの相対パスに置換 | node.__dirname === true |
| BR-80-04 | __dirname node-module | import.meta.urlからfileURLToPathで実行時にパスを変換 | node.__dirname === "node-module" |
| BR-80-05 | __dirname eval-only | ESM出力時はimport.meta.urlから変換、非ESMは__dirnameをそのまま保持 | node.__dirname === "eval-only" |
| BR-80-06 | warn-mock警告 | NodeStuffInWebError警告を追加（"__dirname/filename is a Node.js feature..."） | "warn-mock"または"warn"指定時 |
| BR-80-07 | ESM import.meta対応 | ESMモジュールではimport.meta.dirname/filenameを__dirname/__filenameと同様に処理 | JAVASCRIPT_MODULE_TYPE_ESM |
| BR-80-08 | autoモジュール両対応 | JAVASCRIPT_MODULE_TYPE_AUTOでは__dirname/__filenameとimport.meta両方を処理 | JAVASCRIPT_MODULE_TYPE_AUTO |
| BR-80-09 | Node.js組み込み外部化 | 67種の組み込みモジュール名とnode:プレフィックスを外部化 | NodeTargetPlugin適用時 |
| BR-80-10 | commonjsカテゴリのcreateRequire互換 | module出力でcommonjsカテゴリの依存は"node-commonjs"型で外部化 | module出力 + requireパターン |
| BR-80-11 | data:URL除外 | parserOptions.node === falseの場合はimport.meta.dirname/filenameをそのまま保持 | ESMパーサーでparserOptions.node=false |
| BR-80-12 | importMetaDirnameAndFilename環境 | 環境がimport.meta.dirname/filenameをネイティブサポートする場合はそのまま使用 | environment.importMetaDirnameAndFilenameがtrue |

### 計算ロジック

__dirname (true): `relative(compiler.inputFileSystem, compiler.context, module.context)`
__filename (true): `relative(compiler.inputFileSystem, compiler.context, module.resource)`

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | NodeStuffInWebError（警告） | global/warn-mock指定でブラウザ環境での使用検出 | node.global/node.__dirname/node.__filenameの設定を見直す |

### リトライ仕様

リトライは不要。

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

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

## パフォーマンス要件

パーサーフックによる処理であり、対象の式（__dirname、__filename、global）が出現するモジュールのみで処理が発生する。CachedConstDependencyにより同一モジュール内の重複処理を回避している。

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

__dirname/__filenameにファイルシステムの絶対パスが含まれる可能性がある。ブラウザ向けバンドルに含める場合は情報漏洩に注意。mock設定を使用することでパス情報の漏洩を防止できる。

## 備考

- NodeStuffPluginはWebpackOptionsApplyで常時適用される（設定値がfalseの場合は各処理が無効化される）
- NodeTargetPluginはexternalsPresets.node=true時に適用される
- webpack 5ではNode.jsポリフィルがデフォルトで提供されなくなった（BREAKING CHANGE）
- Yarn PnP環境のpnpapiモジュールもNodeTargetPluginで外部化される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | NodeOptions型定義 | `declarations/WebpackOptions.d.ts` | NodeOptionsの型定義（global、__dirname、__filename） |

**読解のコツ**: NodeOptionsの各プロパティは複数の値を取りうる（boolean、"mock"、"warn-mock"、"node-module"、"eval-only"）。各値の意味はNodeStuffPlugin内のswitch文で実装されている。

#### Step 2: NodeStuffPlugin

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | NodeStuffPlugin.js | `lib/NodeStuffPlugin.js` | 全体。global/dirname/filenameの各ハンドラ |

**主要処理フロー**:
1. **50-53行目**: applyメソッド。compilationフックの登録
2. **70-113行目**: globalHandler関数。globalThis/RuntimeGlobals.global置換
3. **125-149行目**: setModuleConstant/setCachedModuleConstant関数
4. **239-244行目**: setUrlModuleConstant関数（node:urlのfileURLToPath使用）
5. **352-516行目**: dirnameAndFilenameHandler関数。各__dirname/__filename設定値のswitch処理
6. **524-565行目**: handler関数。モジュールタイプ別の処理振り分け
7. **567-581行目**: normalModuleFactory.hooks.parser登録（auto/dynamic/esm別）

#### Step 3: NodeTargetPlugin

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | NodeTargetPlugin.js | `lib/node/NodeTargetPlugin.js` | Node.js組み込みモジュールの外部化 |

**主要処理フロー**:
- **13-73行目**: builtins配列（67種の組み込みモジュール名 + /^node:/パターン + "pnpapi"）
- **75-82行目**: constructor。ExternalsType設定
- **89-98行目**: apply。ExternalsPluginの適用。commonjsカテゴリ判定

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

```
WebpackOptionsApply.process()
    |
    +-- new NodeStuffPlugin({global, __dirname, __filename}).apply(compiler)
    |       |
    |       +-- compiler.hooks.compilation.tap()
    |               |
    |               +-- normalModuleFactory.hooks.parser.for("javascript/auto").tap()
    |               |       +-- globalHandler(parser, options)
    |               |       +-- dirnameAndFilenameHandler(parser, options, {__dirname, __filename})
    |               |       +-- dirnameAndFilenameHandler(parser, options, {import.meta.dirname, import.meta.filename})
    |               |
    |               +-- normalModuleFactory.hooks.parser.for("javascript/dynamic").tap()
    |               |       +-- globalHandler + __dirname/__filename のみ
    |               |
    |               +-- normalModuleFactory.hooks.parser.for("javascript/esm").tap()
    |                       +-- import.meta.dirname/import.meta.filename のみ
    |
    +-- new NodeTargetPlugin(type).apply(compiler) [externalsPresets.node時]
            |
            +-- new ExternalsPlugin(typeCallback, builtins).apply(compiler)
                    +-- 67種の組み込みモジュール外部化
```

### データフロー図

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

webpack config              NodeStuffPlugin
  node.__dirname --------> __dirname置換ロジック ---------> ConstDependency
  node.__filename -------> __filename置換ロジック -------> CachedConstDependency
  node.global -----------> global置換ロジック -----------> ExternalModuleDependency

webpack config              NodeTargetPlugin
  externalsPresets.node --> ExternalsPlugin -----------------> 外部モジュール化
                               builtins[67種]                  (require保持)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| NodeStuffPlugin.js | `lib/NodeStuffPlugin.js` | ソース | __dirname/__filename/globalポリフィル |
| NodeTargetPlugin.js | `lib/node/NodeTargetPlugin.js` | ソース | Node.js組み込みモジュール外部化 |
| NodeStuffInWebError.js | `lib/NodeStuffInWebError.js` | ソース | ブラウザ環境でのNode.js機能使用警告 |
| NodeEnvironmentPlugin.js | `lib/node/NodeEnvironmentPlugin.js` | ソース | Node.js環境のファイルシステム設定 |
| ExternalsPlugin.js | `lib/ExternalsPlugin.js` | ソース | 外部モジュール化プラグイン |
| ConstDependency.js | `lib/dependencies/ConstDependency.js` | ソース | 定数値置換依存 |
| CachedConstDependency.js | `lib/dependencies/CachedConstDependency.js` | ソース | キャッシュ対応定数値置換依存 |
| ExternalModuleDependency.js | `lib/dependencies/ExternalModuleDependency.js` | ソース | 外部モジュール依存（fileURLToPath用） |
| ImportMetaPlugin.js | `lib/dependencies/ImportMetaPlugin.js` | ソース | import.meta関連フック提供元 |
