# 機能設計書 19-エクスポート名マングリング

## 概要

本ドキュメントは、webpackのMangleExportsPluginによるエクスポート名の短縮（マングリング）機能について、その設計と実装の詳細を記述する。

### 本機能の処理概要

エクスポート名マングリング機能は、モジュールのエクスポート名を短い識別子（a, b, c, ...）に置換することで、バンドルサイズを削減する最適化機能である。決定的（deterministic）モードと非決定的モードの2つの動作モードを提供する。

**業務上の目的・背景**：JavaScriptバンドルにおいて、エクスポート名（例: `calculateTotalPrice`, `getUserProfile`等）は文字列としてバンドル内に残る。これらの長い名前を短い識別子に置換することで、特に多数のエクスポートを持つモジュールでバンドルサイズを顕著に削減できる。minifier（Terser等）と組み合わせることでさらなる圧縮効果が得られる。

**機能の利用シーン**：プロダクションビルドでの自動最適化、ライブラリバンドルのサイズ削減、多数の名前付きエクスポートを持つモジュールの最適化。

**主要な処理内容**：
1. 全モジュールのエクスポート情報を取得
2. マングリング可能なエクスポートを選別（canMangle判定）
3. 決定的IDまたは連番IDによる短縮名の割り当て
4. usedNameの設定による名前の確定

**関連システム・外部連携**：ExportsInfo、Template（numberToIdentifier）、IdHelpers（assignDeterministicIds）と連携する。

**権限による制御**：optimization.mangleExports設定で制御（true/false/"deterministic"）。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能は画面機能マッピングに直接の関連画面なし。バンドルサイズ最適化として動作 |

## 機能種別

最適化処理 / 識別子短縮

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| optimization.mangleExports | `boolean \| "deterministic"` | No | マングリングの有効/無効・モード | デフォルト: productionで"deterministic" |

### 入力データソース

- ModuleGraphのExportsInfo（各モジュールのエクスポート情報）
- モジュールのbuildMeta（exportsType情報）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ExportInfo.usedName | `string` | マングリング後の短縮エクスポート名 |

### 出力先

ModuleGraphのExportsInfo内の各ExportInfo

## 処理フロー

### 処理シーケンス

```
1. optimizeCodeGenerationフックで全モジュールを処理
2. 各モジュールのExportsInfoを取得
3. canMangle判定
   └─ otherExportsInfoがUnused以外の場合はマングリング不可
   └─ canMangle=trueのエクスポートが存在するか確認
4. エクスポートの分類
   └─ 既にusedNameがあるもの → スキップ
   └─ canMangle=falseまたは1文字名 → 元の名前をそのまま使用
   └─ 決定的モードで2文字以下の名前 → 元の名前をそのまま使用
   └─ 上記以外 → マングリング対象
5. マングリング名の割り当て
   └─ 決定的モード: assignDeterministicIdsで安定的に割り当て
   └─ 非決定的モード: 使用/未使用の2グループに分け連番割り当て
6. ネストされたExportsInfoの再帰処理
   └─ OnlyPropertiesUsedまたはUnusedの場合は内部エクスポートもマングリング
```

### フローチャート

```mermaid
flowchart TD
    A[optimizeCodeGeneration hook] --> B[全モジュールスキャン]
    B --> C[ExportsInfo取得]
    C --> D{canMangle?}
    D -->|No| B
    D -->|Yes| E[エクスポート分類]
    E --> F{hasUsedName?}
    F -->|Yes| G[スキップ]
    F -->|No| H{canMangle=true<br>& 名前条件OK?}
    H -->|No| I[元の名前をsetUsedName]
    H -->|Yes| J[マングリング対象リストに追加]
    I --> K{決定的モード?}
    J --> K
    K -->|Yes| L[assignDeterministicIds]
    K -->|No| M[used/unusedソート後連番]
    L --> N[numberToIdentifier]
    M --> N
    N --> O[setUsedName]
    O --> P{ネストExportsInfo?}
    P -->|Yes| Q[再帰処理]
    P -->|No| B
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 1文字名スキップ | 1文字で`[a-z0-9_$]`にマッチするエクスポート名はマングリングしない | 全モード |
| BR-02 | 2文字名スキップ | 決定的モードでは2文字の特定パターンの名前もスキップ | 決定的モード |
| BR-03 | 非提供エクスポート回避 | 非namespaceの場合、provided!==trueのエクスポートはマングリングしない | avoidMangleNonProvided |
| BR-04 | otherExportsInfo制約 | otherExportsInfo.getUsed()がUnused以外ならマングリング不可 | canMangle判定時 |
| BR-05 | cacheUnaffected非互換 | moduleMemCaches使用時はエラー（グローバル効果のため） | プラグイン適用時 |

### 計算ロジック

- **numberToIdentifier(id)**: 数値を識別子文字列に変換。0→"a", 1→"b", ..., 25→"z", 26→"A", ...
- **usedNames Set**: 既に使用中の名前を管理し、重複を防止
- **決定的モード**: assignDeterministicIdsにより、名前のハッシュに基づく安定した割り当て。ビルド間でのIDの安定性を保証
- **非決定的モード**: 使用中(Used)のエクスポートを先にソートし、未使用のエクスポートを後にソート。連番で割り当て

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Error | cacheUnaffectedとの併用 | "optimization.mangleExports can't be used with cacheUnaffected"エラー |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- optimizeCodeGenerationフェーズで全モジュールを一括処理
- NUMBER_OF_IDENTIFIER_START_CHARS / NUMBER_OF_IDENTIFIER_CONTINUATION_CHARSの定数を利用した効率的なID生成

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

- マングリングによりデバッグが困難になるため、developmentモードではデフォルト無効

## 備考

- 決定的モードはlong-term cachingに有利（モジュール追加・削除時のID変動が最小）
- namespaceエクスポート（exportsType: "namespace"）の場合、非提供エクスポートもマングリング対象
- 再帰的にネストされたExportsInfoもマングリング処理される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | MangleExportsPlugin.js | `lib/optimize/MangleExportsPlugin.js` | 8-15行目: UsageState, numberToIdentifier, assignDeterministicIds等のインポート |

**読解のコツ**: ExportInfoの`canMangle`, `hasUsedName()`, `setUsedName()`, `getUsed()`の4つのメソッド/プロパティがマングリングの中核。

#### Step 2: canMangle判定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | MangleExportsPlugin.js | `lib/optimize/MangleExportsPlugin.js` | 31-42行目: canMangle関数 |

**主要処理フロー**:
- **32行目**: otherExportsInfoのgetUsed(undefined)がUnused以外→マングリング不可
- **36-41行目**: 全エクスポートを走査し、canMangle===trueのものが一つでもあればtrue

#### Step 3: マングリングロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | MangleExportsPlugin.js | `lib/optimize/MangleExportsPlugin.js` | 53-155行目: mangleExportsInfo関数 |

**主要処理フロー**:
1. **56-57行目**: usedNames Set（使用済み名前）とmangleableExports配列を初期化
2. **64-72行目**: avoidMangleNonProvided判定（namespace以外 or 提供エクスポートが存在する場合にtrue）
3. **73-107行目**: エクスポートの分類ループ - スキップ条件の段階的チェック
4. **78-80行目**: canMangle!==true → 元の名前を使用
5. **80行目**: 1文字名のチェック（`/^[a-z0-9_$]/i.test(name)`）
6. **82-84行目**: 決定的モードでの2文字名チェック
7. **86行目**: 非提供エクスポートの回避
8. **94-106行目**: ネストされたExportsInfoの再帰処理
9. **108-128行目**: 決定的モード - assignDeterministicIdsで安定的に名前割り当て
10. **129-154行目**: 非決定的モード - used/unusedに分けてソート後、連番割り当て

#### Step 4: プラグインの登録を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | MangleExportsPlugin.js | `lib/optimize/MangleExportsPlugin.js` | 159-192行目: MangleExportsPluginクラス |

**主要処理フロー**:
- **163-165行目**: コンストラクタでdeterministicフラグを保持
- **175行目**: compilation.hooks.optimizeCodeGenerationに登録
- **183-187行目**: 各モジュールのisNamespace判定とexportsInfo取得
- **187行目**: mangleExportsInfo(deterministic, exportsInfo, isNamespace)を呼び出し

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

```
Compilation.hooks.optimizeCodeGeneration
    |
    +-- MangleExportsPlugin
            |
            +-- moduleGraph.getExportsInfo(module)
            |
            +-- mangleExportsInfo(deterministic, exportsInfo, isNamespace)
                    |
                    +-- canMangle(exportsInfo)
                    |       +-- exportsInfo.otherExportsInfo.getUsed(undefined)
                    |       +-- exportInfo.canMangle
                    |
                    +-- exportInfo.hasUsedName()
                    +-- exportInfo.setUsedName(name)
                    |
                    +-- [決定的モード]
                    |       +-- assignDeterministicIds(mangleableExports, ...)
                    |       +-- numberToIdentifier(id)
                    |
                    +-- [非決定的モード]
                    |       +-- comparator (名前の数値比較でソート)
                    |       +-- numberToIdentifier(i++)
                    |
                    +-- mangleExportsInfo(...) [再帰: ネストExportsInfo]
```

### データフロー図

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

モジュール群 ------------> MangleExportsPlugin            +--> ExportInfo.usedName
                               |                          |      (短縮名: "a","b","c",...)
ExportsInfo ------------> canMangle判定                   |
                               |                          |
                               v                          |
                        エクスポート分類                   |
                               |                          |
                               v                          |
                        名前割り当て                       |
                        (deterministic or sequential)     |
                               |                          |
                               v                          |
                        setUsedName ----------------------+
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| MangleExportsPlugin.js | `lib/optimize/MangleExportsPlugin.js` | ソース | メインプラグイン（全195行） |
| ExportsInfo.js | `lib/ExportsInfo.js` | ソース | エクスポート情報データモデル |
| Template.js | `lib/Template.js` | ソース | numberToIdentifier関数 |
| IdHelpers.js | `lib/ids/IdHelpers.js` | ソース | assignDeterministicIds関数 |
| comparators.js | `lib/util/comparators.js` | ユーティリティ | 比較関数群 |
