# 帳票設計書 10-Stats モジュール依存理由レポート

## 概要

本ドキュメントは、各モジュールが依存される理由（依存元モジュール、依存タイプ、位置情報等）を出力する「Stats モジュール依存理由レポート」の設計仕様を定義する。

### 本帳票の処理概要

Stats モジュール依存理由レポートは、各モジュールが他のモジュールから依存される理由（moduleReason）の一覧を出力する帳票である。`SIMPLE_EXTRACTORS.moduleReason` により、ModuleGraphConnectionオブジェクトから依存元モジュール、依存タイプ、依存の活性状態、ユーザーリクエスト、位置情報等を抽出する。

**業務上の目的・背景**：モジュール間の依存関係を詳細に分析し、不要な依存の検出、依存チェーンの最適化、Tree-shaking効果の検証を行うために必要である。なぜあるモジュールがバンドルに含まれるのかを逆引きで調査する際の基礎データとなる。

**帳票の利用シーン**：バンドル分析時のモジュール依存調査、Tree-shakingのデバッグ、循環依存の特定、不要モジュールの排除判断に利用される。

**主要な出力内容**：
1. 依存元モジュール識別子（moduleIdentifier）
2. 依存元モジュール名（module / moduleName）
3. 解決済み依存元モジュール（resolvedModuleIdentifier / resolvedModule）
4. 依存タイプ（type: import / require / harmony side effect evaluation等）
5. 依存の活性状態（active）
6. 説明（explanation）
7. ユーザーリクエスト（userRequest）
8. 依存位置（loc）
9. 依存元モジュールID（moduleId）
10. 解決済みモジュールID（resolvedModuleId）

**帳票の出力タイミング**：`Stats.toJson()` または `Stats.toString()` 呼び出し時に、各モジュールのreasons配列として生成される。

**帳票の利用者**：フロントエンド開発者、バンドル分析ツール。

## 帳票種別

依存理由一覧出力

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | CLI | `webpack` | ビルド完了時にreasons有効なら出力 |
| - | Node.js API | プログラム内 | `stats.toJson({ reasons: true })` |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | JSON（toJson時）/ テキスト（toString時） |
| 用紙サイズ | N/A |
| 向き | N/A |
| ファイル名 | N/A |
| 出力方法 | StatsModule.reasonsフィールド（各モジュールのサブ項目） |
| 文字コード | UTF-8 |

## 帳票レイアウト

### レイアウト概要

StatsModuleReason型の配列として、各モジュールの`reasons`フィールド内に出力される。

```
{
    "moduleIdentifier":         依存元モジュール識別子,
    "module":                   依存元モジュール名,
    "moduleName":               依存元モジュール名（moduleと同値）,
    "resolvedModuleIdentifier": 解決済み依存元識別子,
    "resolvedModule":           解決済み依存元名,
    "type":                     依存タイプ,
    "active":                   依存活性状態,
    "explanation":              説明,
    "userRequest":              ユーザーリクエスト文字列,
    "loc":                      依存位置,
    "moduleId":                 依存元モジュールID,
    "resolvedModuleId":         解決済みモジュールID
}
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | type | 依存タイプ | reason.dependency.type | 文字列 |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | moduleIdentifier | 依存元モジュール識別子 | reason.originModule.identifier() | 文字列（null可） | - |
| 2 | module | 依存元モジュール名 | reason.originModule.readableIdentifier() | 文字列（null可） | - |
| 3 | moduleName | 依存元モジュール名 | reason.originModule.readableIdentifier() | 文字列（moduleと同値） | - |
| 4 | resolvedModuleIdentifier | 解決済み依存元識別子 | reason.resolvedOriginModule.identifier() | 文字列（null可） | - |
| 5 | resolvedModule | 解決済み依存元名 | reason.resolvedOriginModule.readableIdentifier() | 文字列（null可） | - |
| 6 | active | 依存活性状態 | reason.isActive(runtime) | 真偽値 | - |
| 7 | explanation | 説明 | reason.explanation | 文字列（null可） | - |
| 8 | userRequest | ユーザーリクエスト | moduleDep.userRequest | 文字列（null可） | - |
| 9 | loc | 依存位置 | formatLocation(reason.dependency.loc) | 文字列（null可） | - |
| 10 | moduleId | 依存元モジュールID | chunkGraph.getModuleId(reason.originModule) | ID（null可） | - |
| 11 | resolvedModuleId | 解決済みモジュールID | chunkGraph.getModuleId(reason.resolvedOriginModule) | ID（null可） | - |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | filteredReasons | フィルタされた依存理由数 | spaceLimited() | 数値 |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| reasons | 依存理由出力の有効/無効 | No（デフォルト: false） |
| reasonsSpace | 依存理由表示のスペース制限 | No |
| ids | ID情報（moduleId, resolvedModuleId）出力 | No |
| orphanModules | 孤立モジュールの依存理由を含めるか | No |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | originModule | identifier昇順 |
| 2 | resolvedOriginModule | identifier昇順 |
| 3 | dependency.loc | 位置順 |
| 4 | dependency.type | 昇順 |

### 改ページ条件

N/A

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

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| ModuleGraph | 依存接続取得 | moduleGraph.getIncomingConnections(module) |
| ChunkGraph | モジュールID取得 | chunkGraph.getModuleId() |
| ModuleDependency | ユーザーリクエスト取得 | dep instanceof ModuleDependency |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| userRequest | dep instanceof ModuleDependency ? dep.userRequest : null | - | ModuleDependencyインスタンスの場合のみ |
| active | reason.isActive(runtime) | - | ランタイムに依存 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[module$visible.reasons] --> B[moduleGraph.getIncomingConnections取得]
    B --> C[factory.create module.reasons 呼び出し]
    C --> D[FILTER module.reasons !orphanModules フィルタ]
    D --> E[SORTERS module.reasons ソート]
    E --> F[moduleReason._ で基本情報抽出]
    F --> G[moduleReason.ids でID情報抽出]
    G --> H[spaceLimited でスペース制限]
    H --> I[StatsModuleReason配列返却]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| 依存元なし | reason.originModuleがnull | moduleIdentifier: null等 | 正常動作（エントリーポイント等） |
| 依存なし | reason.dependencyがnull | type: null, loc: 未設定 | 正常動作 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | モジュール当たり数件〜数十件 |
| 目標出力時間 | 全モジュールの理由数に比例 |
| 同時出力数上限 | 1 |

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

依存理由にはモジュールの識別子（ファイルパス）とユーザーリクエスト（importパス）が含まれる。ソースコードの構造情報が露出するため、公開環境での出力に注意。

## 備考

- `module` と `moduleName` は同値（readableIdentifier）で出力される
- `resolvedOriginModule` はOriginModuleとは異なり、ローダーチェーン解決後のモジュールを指す
- `!orphanModules` フィルタにより、孤立モジュールからの依存理由がデフォルトで除外される
- `userRequest` はModuleDependencyインスタンスの場合のみ設定され、それ以外はnull
- ソート順はoriginModule→resolvedOriginModule→dependency.loc→dependency.typeの複合ソート

---

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

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

### 推奨読解順序

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

StatsModuleReason型の定義を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | DefaultStatsFactoryPlugin.js | `lib/stats/DefaultStatsFactoryPlugin.js` | 行222-237: `KnownStatsModuleReason` 型定義。moduleIdentifier, module, moduleName, resolvedModuleIdentifier, resolvedModule, type, active, explanation, userRequest, loc, moduleId, resolvedModuleId |

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

module$visible.reasonsの呼び出し元を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DefaultStatsFactoryPlugin.js | `lib/stats/DefaultStatsFactoryPlugin.js` | 行1339-1356: `SIMPLE_EXTRACTORS.module$visible.reasons` - moduleGraph.getIncomingConnections → factory.create → spaceLimited |

**主要処理フロー**:
1. **行1341-1343**: `moduleGraph.getIncomingConnections(module)` で依存接続を取得
2. **行1344-1348**: `factory.create("module.reasons", [...connections], context)` で構造化
3. **行1349-1354**: `spaceLimited(groupsReasons, reasonsSpace)` でスペース制限

#### Step 3: moduleReason抽出処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | DefaultStatsFactoryPlugin.js | `lib/stats/DefaultStatsFactoryPlugin.js` | 行1464-1498: `SIMPLE_EXTRACTORS.moduleReason._` - originModule, resolvedOriginModule, dependency, active, explanation, userRequest, loc |
| 3-2 | DefaultStatsFactoryPlugin.js | `lib/stats/DefaultStatsFactoryPlugin.js` | 行1499-1507: `SIMPLE_EXTRACTORS.moduleReason.ids` - moduleId, resolvedModuleId |

**主要処理フロー**:
1. **行1466-1468**: dep取得、ModuleDependencyインスタンス判定
2. **行1470-1489**: statsModuleReasonオブジェクト構築（moduleIdentifier, module, type, active, explanation, userRequest）
3. **行1492-1497**: locの設定（dependency.locが存在する場合のみformatLocation）

#### Step 4: フィルタとソートを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | DefaultStatsFactoryPlugin.js | `lib/stats/DefaultStatsFactoryPlugin.js` | 行1668-1680: `FILTER["module.reasons"]["!orphanModules"]` - 孤立モジュールフィルタ |
| 4-2 | DefaultStatsFactoryPlugin.js | `lib/stats/DefaultStatsFactoryPlugin.js` | 行1732-1757: `SORTERS["module.reasons"]` - originModule→resolvedOriginModule→loc→typeの複合ソート |

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

```
SIMPLE_EXTRACTORS.module$visible.reasons()
    |
    +-- moduleGraph.getIncomingConnections(module)
    +-- factory.create("module.reasons", [...connections], context)
    |       |
    |       +-- FILTER["module.reasons"]["!orphanModules"]
    |       |       +-- chunkGraph.getNumberOfModuleChunks(originModule) === 0 → false
    |       |
    |       +-- SORTERS["module.reasons"]
    |       |       +-- compareSelect(originModule, compareModulesByIdentifier)
    |       |       +-- compareSelect(resolvedOriginModule, compareModulesByIdentifier)
    |       |       +-- compareSelect(dependency.loc, compareLocations)
    |       |       +-- compareSelect(dependency.type, compareIds)
    |       |
    |       +-- moduleReason._()
    |       |       +-- reason.originModule → moduleIdentifier, module, moduleName
    |       |       +-- reason.resolvedOriginModule → resolvedModuleIdentifier, resolvedModule
    |       |       +-- reason.dependency → type
    |       |       +-- reason.isActive(runtime) → active
    |       |       +-- reason.explanation
    |       |       +-- moduleDep.userRequest → userRequest
    |       |       +-- formatLocation(dependency.loc) → loc
    |       |
    |       +-- moduleReason.ids()
    |               +-- chunkGraph.getModuleId(originModule) → moduleId
    |               +-- chunkGraph.getModuleId(resolvedOriginModule) → resolvedModuleId
    |
    +-- spaceLimited(groupsReasons, reasonsSpace)
```

### データフロー図

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

moduleGraph                      -->  getIncomingConnections()       -->  ModuleGraphConnection[]
  ModuleGraphConnection[]        -->  FILTER !orphanModules          -->  フィルタ済みConnection[]
                                 -->  SORTERS module.reasons         -->  ソート済みConnection[]
  .originModule                  -->  moduleReason._()               -->  StatsModuleReason[]
  .resolvedOriginModule          -->    identifier() / readableId()  -->    .moduleIdentifier, .module
  .dependency                    -->    type / loc                   -->    .type, .loc
  .isActive(runtime)             -->    active判定                   -->    .active
  ModuleDependency.userRequest   -->    userRequest取得              -->    .userRequest
chunkGraph                       -->  moduleReason.ids()             -->    .moduleId, .resolvedModuleId
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DefaultStatsFactoryPlugin.js | `lib/stats/DefaultStatsFactoryPlugin.js` | ソース | moduleReason抽出処理、FILTER、SORTERS |
| StatsFactory.js | `lib/stats/StatsFactory.js` | ソース | データ変換パイプライン |
| Stats.js | `lib/Stats.js` | ソース | toJson() / toString() エントリーポイント |
| ModuleGraph.js | `lib/ModuleGraph.js` | ソース | getIncomingConnections()提供 |
| ModuleGraphConnection.js | `lib/ModuleGraphConnection.js` | ソース | 依存接続の表現 |
| ModuleDependency.js | `lib/dependencies/ModuleDependency.js` | ソース | userRequest提供 |
| ChunkGraph.js | `lib/ChunkGraph.js` | ソース | getModuleId() / getNumberOfModuleChunks() |
| formatLocation.js | `lib/formatLocation.js` | ソース | 依存位置のフォーマット |
