# 帳票設計書 16-EvalSourceMapDevToolPlugin ソースマップ

## 概要

本ドキュメントは、eval形式のソースマップを生成する「EvalSourceMapDevToolPlugin ソースマップ」の設計仕様を定義するものである。

### 本帳票の処理概要

EvalSourceMapDevToolPlugin ソースマップは、webpackビルドで生成されたJavaScriptモジュールに対して、eval()ラッパー内にインラインソースマップを埋め込む形式で出力する帳票である。各モジュールが個別のeval()呼び出しでラップされ、Data URI形式のソースマップがモジュールコードの末尾に追加される。これにより、ブラウザDevToolsで個別のモジュールファイルとしてデバッグが可能となる。

**業務上の目的・背景**：開発環境において高速なソースマップ生成とモジュール単位のデバッグを両立するために本帳票が必要である。ファイルベースのソースマップと比較して再ビルド時の生成速度が高速であり、開発サーバーでのHMR（Hot Module Replacement）との親和性が高い。

**帳票の利用シーン**：開発環境でのデバッグ、HMR使用時のモジュール単位デバッグ。本番環境での使用は推奨されない。

**主要な出力内容**：
1. eval()ラップされたモジュールコード - 各モジュールがeval()で囲まれたコード
2. インラインソースマップ - Data URI（base64エンコード）形式でモジュール末尾に埋め込み
3. sourceURL コメント - webpack-internal:// 形式のモジュール識別URL
4. 警告コメント - 出力ファイル先頭にeval-source-map使用の警告メッセージ

**帳票の出力タイミング**：webpackビルドの compilation フック内で、`JavascriptModulesPlugin` の `renderModuleContent` フックで各モジュールの描画時に実行される。

**帳票の利用者**：フロントエンド開発者。

## 帳票種別

ソースマップ（開発環境向けインラインデバッグ支援）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | webpack設定 | `devtool: 'eval-source-map'` | ビルド実行時に自動出力 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | JavaScript（eval()ラッパー内にインラインJSON） |
| 用紙サイズ | N/A |
| 向き | N/A |
| ファイル名 | バンドルファイル内に埋め込み（独立ファイルなし） |
| 出力方法 | モジュールコードとしてバンドルに組み込み |
| 文字コード | UTF-8 |

## 帳票レイアウト

### レイアウト概要

各モジュールが以下の形式でeval()内にラップされて出力される。

```javascript
eval("{モジュールコード}
//# sourceURL=[module]
//# sourceMappingURL=data:application/json;charset=utf-8;base64,{base64エンコードされたソースマップ}
//# sourceURL=webpack-internal:///{moduleId}
");
```

出力ファイル先頭には警告コメントが追加される：
```javascript
/*
 * ATTENTION: An "eval-source-map" devtool has been used.
 * This devtool is neither made for production nor for readable output files.
 * ...
 */
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | 警告コメント | eval-source-map使用の注意メッセージ | devtoolWarning（静的テキスト） | JSブロックコメント |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | モジュールコード | eval()内のモジュールソースコード | source.sourceAndMap() / source.source() | JavaScript | - |
| 2 | ソースマップ | Data URI形式のインラインソースマップ | sourceMap をJSON化してbase64エンコード | base64文字列 | - |
| 3 | sourceURL | モジュール識別URL | `webpack-internal:///${moduleId}` | URLコメント | - |
| 4 | sources | ソースファイル名一覧 | ModuleFilenameHelpers.createFilename() | 文字列配列 | - |
| 5 | file | ソースマップ対象ファイル名 | moduleId（数値の場合は .js 付加） | 文字列 | - |

### フッター部

該当なし。

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| NormalModule | 対象モジュールが NormalModule であること（ConcatenatedModule の場合は rootModule が NormalModule） | Yes |
| リソースマッチ | matchModule(module.resource) が true を返すこと | Yes |
| ソースマップ存在 | source.sourceAndMap() または source.map() が有効なソースマップを返すこと | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| - | モジュールの描画順に従う | - |

### 改ページ条件

N/A

## データベース参照仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| chunkGraph | モジュールIDの取得 | chunkGraph.getModuleId(module) |
| compilation | ソースマップ解決 | compilation.findModule(source) |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| ソースマップData URI | `data:application/json;charset=utf-8;base64,${Buffer.from(JSON.stringify(sourceMap), "utf8").toString("base64")}` | なし | base64エンコード |
| file | typeof moduleId === "number" ? `${moduleId}.js` : moduleId | なし | 数値IDの場合は .js 付加 |
| footer | sourceMapComment + sourceURL | なし | モジュール末尾に追加 |
| debugId | generateDebugId(content, sourceMap.file) | なし | debugIds オプション有効時のみ |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[renderModuleContent フック] --> B{キャッシュヒット?}
    B -->|Yes| C[キャッシュから返却]
    B -->|No| D{NormalModule or ConcatenatedModule?}
    D -->|その他| E[元のソースをそのまま返却]
    D -->|Yes| F{matchModule?}
    F -->|No| E
    F -->|Yes| G[source.sourceAndMap / source.map]
    G --> H{ソースマップ存在?}
    H -->|No| E
    H -->|Yes| I[ソースマップ clone]
    I --> J[ソースパス解決 → モジュール参照]
    J --> K[ModuleFilenameHelpers.createFilename]
    K --> L[重複名解決 replaceDuplicates]
    L --> M{ignoreList オプション?}
    M -->|Yes| N[ignoreList 生成]
    M -->|No| O[noSources / sourceRoot 設定]
    N --> O
    O --> P[debugId 生成（オプション）]
    P --> Q[footer 生成 sourceMapComment + sourceURL]
    Q --> R{trustedTypes 有効?}
    R -->|Yes| S[createScript でラップ]
    R -->|No| T[JSON.stringify でラップ]
    S --> U[eval() ラップした RawSource 返却]
    T --> U
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| 特になし | - | - | ソースマップが取得できない場合は元のソースがそのまま返却される |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | モジュール数に依存（数十〜数千件） |
| 目標出力時間 | モジュール単位で高速生成（WeakMapキャッシュ使用） |
| 同時出力数上限 | N/A |

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

- eval() を使用するため、CSP（Content Security Policy）の `unsafe-eval` が必要となる。
- `trustedTypes` オプションが有効な場合、`RuntimeGlobals.createScript` でTrusted Types APIを使用して安全なスクリプト生成が行われる。
- ソースマップにはオリジナルのソースコードが含まれる（`noSources` で除外可能）。
- 本番環境での使用は推奨されない（出力ファイル先頭の警告コメント参照）。

## 備考

- eval形式はファイルベースのソースマップと比較して再ビルドが高速だが、本番環境・可読性の高い出力には不向き。
- `inlineInRuntimeBailout` フックで "the eval-source-map devtool is used." を返し、ランタイムのインライン化を抑制する。
- `chunkHash` フックで PLUGIN_NAME と "2" をハッシュに追加し、ソースマップ設定変更時にキャッシュを無効化する。
- WeakMap による結果キャッシュにより、同一ソースに対する再計算を防止する。

---

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

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

### 推奨読解順序

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

eval形式ソースマップの出力構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | EvalSourceMapDevToolPlugin.js | `lib/EvalSourceMapDevToolPlugin.js` | devtoolWarning の内容（31-39行目）。出力ファイル先頭に追加される警告テキスト |
| 1-2 | EvalSourceMapDevToolPlugin.js | `lib/EvalSourceMapDevToolPlugin.js` | sourceMapComment のデフォルト値（59-61行目）。`//# sourceURL=[module]\n//# sourceMappingURL=[url]` |

**読解のコツ**: eval形式では各モジュールが個別にeval()でラップされるため、ブラウザDevToolsでモジュール単位のファイルとして表示される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | EvalSourceMapDevToolPlugin.js | `lib/EvalSourceMapDevToolPlugin.js` | apply()メソッド（77-249行目）。renderModuleContent フックへの登録 |

**主要処理フロー**:
1. **79行目**: `compiler.hooks.compilation.tap` で compilation フックに登録
2. **80行目**: `JavascriptModulesPlugin.getCompilationHooks(compilation)` でJSモジュールフック取得
3. **86-88行目**: `hooks.renderModuleContent.tap` で各モジュール描画時に介入

#### Step 3: モジュール単位のソースマップ生成

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | EvalSourceMapDevToolPlugin.js | `lib/EvalSourceMapDevToolPlugin.js` | モジュールタイプ判定とソースマップ取得（103-141行目） |
| 3-2 | EvalSourceMapDevToolPlugin.js | `lib/EvalSourceMapDevToolPlugin.js` | ソース名マッピングとevalラップ生成（147-225行目） |

**主要処理フロー**:
- **89-101行目**: WeakMapキャッシュチェック
- **103-122行目**: NormalModule / ConcatenatedModule の判定とmatchModule
- **131-138行目**: source.sourceAndMap() / source.map() でソースマップ取得
- **144行目**: ソースマップの浅いクローン（ミューテーション防止）
- **147-152行目**: ソースパスの絶対パス変換とモジュール参照解決
- **153-166行目**: ModuleFilenameHelpers.createFilename でファイル名テンプレート適用
- **167-173行目**: replaceDuplicates で重複名解決
- **197-201行目**: moduleId から file プロパティ設定
- **207-213行目**: ソースマップをbase64 Data URIに変換してfooter生成
- **215-224行目**: eval() ラップした RawSource を生成

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

```
EvalSourceMapDevToolPlugin.apply(compiler)
    │
    ├─ SourceMapDevToolModuleOptionsPlugin.apply(compilation)
    │
    ├─ hooks.renderModuleContent.tap
    │      │
    │      ├─ WeakMap キャッシュチェック
    │      │
    │      ├─ NormalModule / ConcatenatedModule 判定
    │      │
    │      ├─ matchModule(resource) フィルタ
    │      │
    │      ├─ source.sourceAndMap() / source.map()
    │      │
    │      ├─ ソースパス解決 → compilation.findModule()
    │      │
    │      ├─ ModuleFilenameHelpers.createFilename()
    │      │
    │      ├─ replaceDuplicates() 重複名解決
    │      │
    │      ├─ ignoreList / noSources / sourceRoot 設定
    │      │
    │      ├─ debugId 生成（オプション）
    │      │
    │      └─ eval() ラップ → RawSource 返却
    │
    ├─ hooks.inlineInRuntimeBailout.tap
    │
    ├─ hooks.render.tap (devtoolWarning 追加)
    │
    └─ hooks.chunkHash.tap (ハッシュ更新)
```

### データフロー図

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

Module Source              EvalSourceMapDevToolPlugin         eval() ラップされた
(sourceAndMap/map)         renderModuleContent フック          モジュールコード
       │                   ──────────────────────▶            + インラインソースマップ
       │                   ソースマップ取得                     + sourceURL コメント
       │                   + ソース名解決
       │                   + base64 エンコード
       │                   + eval() ラップ
       │
WeakMap cache ◀──────── キャッシュ保存/復元
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| EvalSourceMapDevToolPlugin.js | `lib/EvalSourceMapDevToolPlugin.js` | ソース | eval形式ソースマップ生成のメインロジック |
| SourceMapDevToolModuleOptionsPlugin.js | `lib/SourceMapDevToolModuleOptionsPlugin.js` | ソース | モジュールレベルのソースマップオプション設定 |
| ModuleFilenameHelpers.js | `lib/ModuleFilenameHelpers.js` | ソース | ファイル名テンプレート処理・マッチング・重複解決 |
| JavascriptModulesPlugin.js | `lib/javascript/JavascriptModulesPlugin.js` | ソース | JSモジュール描画フックの提供 |
| NormalModule.js | `lib/NormalModule.js` | ソース | 対象モジュール型の判定 |
| ConcatenatedModule.js | `lib/optimize/ConcatenatedModule.js` | ソース | Scope Hoisting されたモジュールの判定 |
| generateDebugId.js | `lib/util/generateDebugId.js` | ソース | debugId の生成 |
| RuntimeGlobals.js | `lib/RuntimeGlobals.js` | ソース | createScript ランタイムグローバル |
