# 帳票設計書 13-ManifestPlugin マニフェスト

## 概要

本ドキュメントは、webpackビルドで生成されたアセットのマッピング情報をJSON形式（manifest.json）で出力する「ManifestPlugin マニフェスト」の設計仕様を定義するものである。

### 本帳票の処理概要

ManifestPlugin マニフェストは、webpackビルドで生成された全アセットのソースファイル名と出力ファイル名の対応関係をJSON形式で出力する帳票である。エントリーポイントごとのインポートチャンク情報や、個別アセットのファイルパス・ソースファイル名を一覧化し、サーバーサイドレンダリングやCDN配信時のアセット参照に活用される。

**業務上の目的・背景**：webpackのコンテンツハッシュ付きファイル名（例: `main.abc123.js`）をアプリケーション側で解決するために、ソース名とビルド後のファイルパスの対応表が必要である。マニフェストファイルにより、サーバーサイドテンプレートやランタイムからビルド成果物を正確に参照できるようになる。

**帳票の利用シーン**：本番デプロイ時のアセット参照解決、サーバーサイドレンダリング（SSR）でのスクリプトタグ生成、CDN配信時のファイルURL解決など。

**主要な出力内容**：
1. entrypoints - エントリーポイントごとのインポートファイル一覧と親エントリーポイント情報
2. assets - アセット名（ソースファイル名またはハッシュ除去名）と出力ファイルパスの対応マップ

**帳票の出力タイミング**：webpackビルドの `processAssets` フック（PROCESS_ASSETS_STAGE_SUMMARIZE ステージ）で実行される。ビルド成果物のサマリー段階で自動的に出力される。

**帳票の利用者**：フロントエンド開発者、バックエンド開発者、DevOpsエンジニア。

## 帳票種別

マニフェスト（アセットマッピング情報）

## 利用画面

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

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | JSON |
| 用紙サイズ | N/A（データ出力） |
| 向き | N/A |
| ファイル名 | デフォルト: `manifest.json`（options.filenameで変更可能） |
| 出力方法 | ビルド成果物としてemit |
| 文字コード | UTF-8 |

### JSON固有設定

| 項目 | 内容 |
|-----|------|
| インデント | 2スペース（JSON.stringify第3引数に2を指定） |
| シリアライズ | options.serialize で カスタマイズ可能（デフォルト: JSON.stringify） |

## 帳票レイアウト

### レイアウト概要

マニフェストファイルは ManifestObject 型のJSONオブジェクトとして出力される。

```
ManifestObject
├── entrypoints: Record<string, ManifestEntrypoint>  (entrypointsオプション有効時)
│   └── [エントリーポイント名]
│       ├── imports: string[]
│       └── parents?: string[]
└── assets: Record<string, ManifestItem>
    └── [アセット名]
        ├── file: string
        └── src?: string
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | entrypoints | エントリーポイント情報（オプション） | compilation.entrypoints | オブジェクト |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | imports | エントリーポイントのインポートファイル一覧 | chunk.files + extname() | 文字列配列 | - |
| 2 | parents | 親エントリーポイント名一覧 | entrypoint.getParents() | 文字列配列 | - |
| 3 | file | アセットの出力ファイルパス（prefix付き） | prefix + file | 文字列 | - |
| 4 | src | ソースファイル名 | asset.info.sourceFilename | 文字列 | - |

### フッター部

該当なし。

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| ManifestPlugin設定 | webpack設定のpluginsにManifestPluginが含まれていること | Yes |
| entrypointsオプション | trueの場合、エントリーポイント情報を出力（デフォルト: true） | No |
| filterオプション | ManifestItem を引数にとるフィルタ関数。falseを返すとアセットが除外される | No |
| generateオプション | ManifestObject を加工するカスタム関数 | No |
| HotUpdateChunk除外 | HotUpdateChunk のアセットは除外される | Yes（自動） |
| HMRアセット除外 | hotModuleReplacement フラグ付きアセットは除外される | Yes（自動） |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | compilation.entrypoints のイテレーション順 | 登録順 |
| 2 | compilation.chunks のイテレーション順 | 登録順 |

### 改ページ条件

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

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

### 参照テーブル一覧

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

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| compilation.entrypoints | エントリーポイント情報 | Map<string, Entrypoint> |
| compilation.chunks | チャンク情報 | Set<Chunk> |
| compilation.getAssets() | アセット情報 | Asset[] |

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

#### compilation.entrypoints

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| name（Mapキー） | entrypoints のキー | - | エントリーポイント名 |
| chunks | imports の生成元 | chunk.files をイテレーション | チャンク名 + 拡張子 |
| getParents() | parents | 親が存在する場合のみ | 親エントリーポイント |

#### compilation (Asset)

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| asset.name | assets のキー候補 | - | 重複チェック後に登録 |
| asset.info.sourceFilename | src, アセットキー | - | ソースファイル名 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| file | prefix + file | なし | prefix の `[publicpath]` は publicPath に置換 |
| アセット名 | usedName \|\| sourceFilename \|\| removeHash(file) | なし | 優先度順にフォールバック |
| extname | ファイル名の最後の `.` 以降の拡張子（gz/br/map の場合は2段階分） | なし | 圧縮・マップファイルの拡張子対応 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[processAssets フック] --> B[publicPath 取得]
    B --> C{entrypoints オプション有効?}
    C -->|Yes| D[compilation.entrypoints イテレーション]
    D --> E[各チャンクのファイルから imports 生成]
    E --> F[parents 取得]
    F --> G[manifest.entrypoints に設定]
    C -->|No| H[manifest.assets 生成開始]
    G --> H
    H --> I[compilation.chunks イテレーション]
    I --> J[auxiliaryFiles 処理]
    J --> K[chunk.files 処理]
    K --> L[compilation.getAssets イテレーション]
    L --> M{HMRアセット?}
    M -->|Yes| L
    M -->|No| N[handleFile でアセット登録]
    N --> O{filter 関数あり?}
    O -->|Yes| P{filter結果 true?}
    P -->|Yes| Q[assets に登録]
    P -->|No| L
    O -->|No| Q
    Q --> R{generate 関数あり?}
    R -->|Yes| S[manifest をカスタム加工]
    R -->|No| T[serialize でJSON化]
    S --> T
    T --> U[compilation.emitAsset で出力]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| オプションバリデーションエラー | 不正なオプションが指定された場合 | スキーマバリデーションエラー | 正しいオプションを指定する |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | アセット数に依存（数十〜数百件） |
| 目標出力時間 | ビルド処理の一部として実行（数ms以内） |
| 同時出力数上限 | N/A |

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

マニフェストファイルにはアセットのファイルパスとソースファイル名が含まれる。公開Webサーバーに配置する場合、プロジェクト構造の推測が可能となるため、アクセス制御を検討する必要がある。

## 備考

- デフォルトの `prefix` は `[publicpath]` で、publicPath の値に自動置換される。publicPath が `auto` の場合は `/` に置換される。
- `serialize` オプションでカスタムシリアライザを指定可能。デフォルトは `JSON.stringify(manifest, null, 2)`。
- 重複アセットは `added` Set で管理され、同一ファイルが2回登録されることはない。

---

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

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

### 推奨読解順序

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ManifestPlugin.js | `lib/ManifestPlugin.js` | ManifestObject, ManifestEntrypoint, ManifestItem の型定義（20-23行目のimport） |

**読解のコツ**: 型定義は `declarations/plugins/ManifestPlugin` に定義されている。ManifestObject は entrypoints と assets の2つのセクションで構成される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ManifestPlugin.js | `lib/ManifestPlugin.js` | コンストラクタ（58-69行目）でデフォルトオプション設定、apply()（76-239行目）でプラグイン登録 |

**主要処理フロー**:
1. **58-69行目**: コンストラクタでデフォルト値設定（filename: "manifest.json", prefix: "[publicpath]", entrypoints: true）
2. **77行目**: `compiler.hooks.thisCompilation.tap` でcompilationフックに登録
3. **78-81行目**: `processAssets` フック（PROCESS_ASSETS_STAGE_SUMMARIZE）に登録

#### Step 3: マニフェスト生成処理

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ManifestPlugin.js | `lib/ManifestPlugin.js` | エントリーポイント処理（125-155行目） |
| 3-2 | ManifestPlugin.js | `lib/ManifestPlugin.js` | アセット処理（158-225行目） |
| 3-3 | ManifestPlugin.js | `lib/ManifestPlugin.js` | マニフェスト出力（231-235行目） |

**主要処理フロー**:
- **84-87行目**: hashDigestLength と publicPath の取得
- **125-155行目**: entrypoints オプション有効時のエントリーポイント処理
- **168-201行目**: handleFile 関数によるアセット登録ロジック
- **203-215行目**: chunks イテレーション（HotUpdateChunk除外）
- **217-223行目**: 全アセットイテレーション（HMR除外）
- **231-235行目**: compilation.emitAsset によるマニフェストファイル出力

#### Step 4: ユーティリティ関数

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ManifestPlugin.js | `lib/ManifestPlugin.js` | extname関数（44-52行目）- 拡張子抽出（gz/br/map対応） |
| 4-2 | ManifestPlugin.js | `lib/ManifestPlugin.js` | removeHash関数（104-110行目）- ハッシュ除去 |

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

```
ManifestPlugin.apply(compiler)
    │
    └─ compiler.hooks.thisCompilation.tap
           │
           └─ compilation.hooks.processAssets.tap (STAGE_SUMMARIZE)
                  │
                  ├─ publicPath 取得
                  │
                  ├─ entrypoints 処理
                  │      ├─ compilation.entrypoints イテレーション
                  │      ├─ chunk.files → extname() → imports 生成
                  │      └─ entrypoint.getParents() → parents 生成
                  │
                  ├─ assets 処理
                  │      ├─ compilation.chunks イテレーション
                  │      │      ├─ auxiliaryFiles → handleFile()
                  │      │      └─ chunk.files → handleFile()
                  │      │
                  │      ├─ compilation.getAssets() イテレーション
                  │      │      └─ handleFile()
                  │      │
                  │      └─ handleFile()
                  │             ├─ 重複チェック (added Set)
                  │             ├─ アセット名決定 (sourceFilename || removeHash)
                  │             ├─ prefix 適用
                  │             └─ filter 適用
                  │
                  ├─ generate 関数適用（オプション）
                  │
                  └─ compilation.emitAsset(filename, serialized)
```

### データフロー図

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

compilation.entrypoints   ManifestPlugin                  manifest.json
compilation.chunks        processAssets フック             ├── entrypoints
compilation.getAssets()   ──────────────────▶             │   └── imports[], parents[]
       │                  エントリーポイント処理            └── assets
       │                  + アセット収集                        └── file, src
       │                  + フィルタリング
       │                  + prefix 適用
       │                  + serialize (JSON.stringify)
       │
publicPath ─────────────▶ prefix の [publicpath] 置換
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ManifestPlugin.js | `lib/ManifestPlugin.js` | ソース | マニフェスト生成のメインロジック |
| HotUpdateChunk.js | `lib/HotUpdateChunk.js` | ソース | HMR用チャンクの判定（除外対象） |
| Compilation.js | `lib/Compilation.js` | ソース | entrypoints, chunks, assets の提供元 |
| ManifestPlugin.json | `schemas/plugins/ManifestPlugin.json` | スキーマ | オプションバリデーション用スキーマ |
