# 帳票設計書 14-LibManifestPlugin DLLマニフェスト

## 概要

本ドキュメントは、DLLバンドルのマニフェスト情報をJSON形式で出力する「LibManifestPlugin DLLマニフェスト」の設計仕様を定義するものである。

### 本帳票の処理概要

LibManifestPlugin DLLマニフェストは、webpackのDLLPlugin機能で生成されたDLLバンドルのモジュール情報をJSON形式のマニフェストファイルとして出力する帳票である。各モジュールのID、ビルドメタデータ、エクスポート情報を記録し、DllReferencePluginが参照するための対応表を提供する。これにより、共有ライブラリの事前ビルドと本体ビルドの分離が可能となる。

**業務上の目的・背景**：大規模プロジェクトにおいて、変更頻度の低いライブラリ群を事前にDLLバンドルとしてビルドし、本体のビルド時間を短縮するために本帳票が必要である。マニフェストファイルにより、DLLバンドル内のモジュールIDとエクスポート情報を本体ビルドに伝達できる。

**帳票の利用シーン**：DLLPlugin使用時のライブラリ事前ビルド、マイクロフロントエンドアーキテクチャでの共有モジュール管理など。

**主要な出力内容**：
1. name - DLLの外部公開名（output.library の値）
2. type - DLLバンドルの外部タイプ（output.libraryTarget の値）
3. content - モジュール識別子とモジュールデータ（ID、ビルドメタ、エクスポート）の対応マップ

**帳票の出力タイミング**：webpackビルドの `emit` フック（stage: 110）で実行される。ビルド成果物の出力段階で、中間ファイルシステムにマニフェストファイルが書き出される。

**帳票の利用者**：フロントエンド開発者、ビルドエンジニア。

## 帳票種別

マニフェスト（DLLモジュールマッピング情報）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | webpack設定 | `plugins: [new DllPlugin({ path, name })]` | ビルド実行時に自動出力 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | JSON |
| 用紙サイズ | N/A（データ出力） |
| 向き | N/A |
| ファイル名 | options.path で指定（パステンプレート使用可能） |
| 出力方法 | 中間ファイルシステム（intermediateFileSystem）への直接書き込み |
| 文字コード | UTF-8 |

### JSON固有設定

| 項目 | 内容 |
|-----|------|
| インデント | options.format が true の場合は2スペース、false の場合はなし |
| エンコーディング | Buffer.from(content, "utf8") で UTF-8 バッファに変換して書き込み |

## 帳票レイアウト

### レイアウト概要

DLLマニフェストは以下の構造のJSONオブジェクトとして出力される。

```
DLL Manifest
├── name: string (DLL名)
├── type: string (外部タイプ)
└── content: Record<string, ManifestModuleData>
    └── [モジュール識別子]
        ├── id: ModuleId
        ├── buildMeta?: BuildMeta
        └── exports?: ExportInfoName[]
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | name | DLLの外部公開名 | options.name（パステンプレート展開後） | 文字列 |
| 2 | type | DLLバンドルの外部タイプ | options.type | 文字列 |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | モジュール識別子（キー） | モジュールのライブラリ識別子 | module.libIdent({ context, associatedObjectForCache }) | 文字列 | - |
| 2 | id | モジュールID | chunkGraph.getModuleId(module) | 数値または文字列 | - |
| 3 | buildMeta | ビルドメタデータ | module.buildMeta | オブジェクト | - |
| 4 | exports | エクスポート情報 | exportsInfo.getProvidedExports() | 文字列配列またはundefined | - |

### フッター部

該当なし。

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| 初期チャンク | chunk.canBeInitial() が true であること | Yes |
| entryOnlyオプション | true（デフォルト）の場合、EntryDependency経由のモジュールのみを対象 | No |
| libIdent存在 | module.libIdent() が truthy な値を返すこと | Yes |
| ユニークパス | 各チャンクのマニフェスト出力パスが一意であること | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | compareModulesById(chunkGraph) | モジュールID順 |

### 改ページ条件

N/A（JSON出力のため改ページの概念なし）

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

### 参照テーブル一覧

本帳票はデータベースを参照せず、webpackの内部データ構造を参照する。

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| compilation.chunks | チャンク一覧 | Set<Chunk> |
| chunkGraph | モジュールとチャンクの対応関係 | compilation.chunkGraph |
| moduleGraph | モジュール間の依存関係とエクスポート情報 | compilation.moduleGraph |

### テーブル別参照項目詳細

#### ChunkGraph

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| getOrderedChunkModulesIterable(chunk, comparator) | content のイテレーション元 | チャンク内モジュール一覧 | compareModulesById でソート |
| getModuleId(module) | id | - | モジュールID |

#### ModuleGraph

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| getIncomingConnections(module) | entryOnly判定 | EntryDependency のチェック | c.dependency instanceof EntryDependency |
| getExportsInfo(module) | exports | getProvidedExports() | Array の場合のみ出力 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| name | compilation.getPath(options.name, { chunk, contentHashType: "javascript" }) | なし | パステンプレート展開 |
| targetPath | compilation.getPath(options.path, { chunk }) | なし | パステンプレート展開 |
| exports | Array.isArray(providedExports) ? providedExports : undefined | なし | 配列の場合のみ設定 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[emit フック] --> B[compilation.chunks イテレーション]
    B --> C{chunk.canBeInitial?}
    C -->|No| B
    C -->|Yes| D[targetPath 生成]
    D --> E{targetPath 重複チェック}
    E -->|重複| F[Error: each chunk must have a unique path]
    E -->|一意| G[name 生成]
    G --> H[チャンク内モジュールイテレーション]
    H --> I{entryOnly かつ EntryDependency でない?}
    I -->|Yes| H
    I -->|No| J[module.libIdent 取得]
    J --> K{libIdent 存在?}
    K -->|No| H
    K -->|Yes| L[ModuleId, buildMeta, exports 取得]
    L --> M[content に登録]
    M --> H
    H --> N[manifest オブジェクト生成]
    N --> O{format オプション?}
    O -->|Yes| P[JSON.stringify with indent 2]
    O -->|No| Q[JSON.stringify without indent]
    P --> R[Buffer 変換]
    Q --> R
    R --> S[mkdirp + writeFile]
    S --> B
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| パス重複 | 複数チャンクが同一パスに出力しようとした場合 | "each chunk must have a unique path" | options.path にチャンク固有のテンプレート変数を含める |
| ディレクトリ作成失敗 | mkdirp が失敗した場合 | ファイルシステムエラー | 出力先ディレクトリのパーミッションを確認 |
| ファイル書き込み失敗 | writeFile が失敗した場合 | ファイルシステムエラー | ディスク容量・パーミッションを確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | DLLバンドル内のモジュール数に依存（数十〜数百件） |
| 目標出力時間 | ビルド処理の一部として実行 |
| 同時出力数上限 | N/A |

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

マニフェストファイルにモジュールの内部識別子（ファイルパスベース）が含まれるため、ビルド環境のパス構造が漏洩する可能性がある。`context` オプションで相対パスの基準を指定可能。

## 備考

- LibManifestPlugin は DllPlugin の内部で使用されるプラグインである。直接使用するケースは稀で、通常は DllPlugin 経由で間接的に利用される。
- emit フックの stage が 110 に設定されており、通常の emit 処理より後に実行される。
- 非同期チャンク（`canBeInitial() === false`）はスキップされ、初期チャンクのみが対象となる。

---

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

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

### 推奨読解順序

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

DLLマニフェストの出力データ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | LibManifestPlugin.js | `lib/LibManifestPlugin.js` | ManifestModuleData 型定義（21-25行目）: id, buildMeta, exports |
| 1-2 | LibManifestPlugin.js | `lib/LibManifestPlugin.js` | LibManifestPluginOptions 型定義（28-35行目）: context, entryOnly, format, name, path, type |

**読解のコツ**: manifest オブジェクトは { name, type, content } の3プロパティで構成され、content が本質的なモジュールマッピング情報を保持する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | LibManifestPlugin.js | `lib/LibManifestPlugin.js` | apply()メソッド（52-141行目）。emit フックへの登録とチャンクイテレーション |

**主要処理フロー**:
1. **53-54行目**: `compiler.hooks.emit.tapAsync` で非同期フック登録（stage: 110）
2. **60-61行目**: `asyncLib.each` による全チャンクの並列処理
3. **63-66行目**: `canBeInitial()` による非同期チャンクの除外

#### Step 3: モジュールデータ収集

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | LibManifestPlugin.js | `lib/LibManifestPlugin.js` | モジュールイテレーションとデータ収集（83-113行目） |

**主要処理フロー**:
- **83-86行目**: `getOrderedChunkModulesIterable` でソート済みモジュール取得
- **87-95行目**: `entryOnly` オプションによる EntryDependency フィルタリング
- **96-99行目**: `module.libIdent()` でモジュール識別子取得
- **101-110行目**: ExportsInfo からエクスポート情報を取得し、ManifestModuleData を生成

#### Step 4: ファイル出力

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | LibManifestPlugin.js | `lib/LibManifestPlugin.js` | マニフェストのJSON化とファイル書き込み（114-136行目） |

**主要処理フロー**:
- **120-122行目**: format オプションによるJSON整形の分岐
- **123行目**: Buffer.from で UTF-8 バッファに変換
- **128-135行目**: mkdirp でディレクトリ作成後、writeFile で書き込み

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

```
LibManifestPlugin.apply(compiler)
    │
    └─ compiler.hooks.emit.tapAsync (stage: 110)
           │
           └─ asyncLib.each(compilation.chunks)
                  │
                  ├─ chunk.canBeInitial() チェック
                  │
                  ├─ compilation.getPath(options.path, { chunk })
                  │
                  ├─ usedPaths 重複チェック
                  │
                  ├─ compilation.getPath(options.name, { chunk })
                  │
                  ├─ chunkGraph.getOrderedChunkModulesIterable()
                  │      │
                  │      ├─ entryOnly フィルタリング
                  │      │      └─ moduleGraph.getIncomingConnections()
                  │      │             └─ EntryDependency チェック
                  │      │
                  │      ├─ module.libIdent() 取得
                  │      │
                  │      └─ ManifestModuleData 生成
                  │             ├─ chunkGraph.getModuleId(module)
                  │             ├─ module.buildMeta
                  │             └─ exportsInfo.getProvidedExports()
                  │
                  ├─ JSON.stringify(manifest)
                  │
                  └─ mkdirp + writeFile
```

### データフロー図

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

compilation.chunks       LibManifestPlugin                DLLマニフェスト JSON
chunkGraph               emit フック                      ├── name
moduleGraph              ──────────────────▶              ├── type
       │                  初期チャンクフィルタ              └── content
       │                  + モジュール収集                      └── [ident]: { id, buildMeta, exports }
       │                  + entryOnly フィルタ
       │                  + JSON シリアライズ
       │
intermediateFileSystem ◀─ mkdirp + writeFile
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| LibManifestPlugin.js | `lib/LibManifestPlugin.js` | ソース | DLLマニフェスト生成のメインロジック |
| DllPlugin.js | `lib/DllPlugin.js` | ソース | LibManifestPlugin を内部で使用する上位プラグイン |
| EntryDependency.js | `lib/dependencies/EntryDependency.js` | ソース | エントリーポイント依存の判定用 |
| Compilation.js | `lib/Compilation.js` | ソース | chunks, chunkGraph, moduleGraph の提供元 |
