# 帳票設計書 9-client-reference-manifest

## 概要

本ドキュメントは、Next.jsビルドプロセスにおいて生成される `client-reference-manifest` の設計を記述する。このマニフェストは、React Server Components（RSC）のクライアント参照情報を管理し、サーバーコンポーネントからクライアントコンポーネントを参照する際のモジュール解決情報を提供する。

### 本帳票の処理概要

client-reference-manifest は、`ClientReferenceManifestPlugin`（旧称 FlightManifestPlugin）によって生成されるJavaScriptファイルである。App Routerの各エントリポイントごとに生成され、クライアントコンポーネントのモジュールID・チャンク情報・SSRモジュールマッピング・CSS情報等を包含する。React Flightプロトコルがクライアントコンポーネントをシリアライズ・デシリアライズする際に使用する。

**業務上の目的・背景**：React Server Componentsアーキテクチャでは、サーバーコンポーネントがクライアントコンポーネントを参照する際、実際のモジュールコードではなく「参照」をシリアライズしてクライアントに送信する。クライアントはこの参照からモジュールを解決してレンダリングする。本マニフェストは、この参照解決に必要なモジュールID、チャンクURL、SSR用モジュールID等の情報を提供する。

**帳票の利用シーン**：App RouterのSSR処理時にクライアントコンポーネントの参照を解決する際、RSCペイロード生成時にクライアントコンポーネントのチャンク情報を埋め込む際、SSR時のモジュールID変換（クライアントID→SSR ID）に使用される。

**主要な出力内容**：
1. `clientModules` - クライアントコンポーネントのモジュールID・チャンク・非同期フラグ
2. `ssrModuleMapping` - クライアントモジュールIDからSSRモジュールIDへの変換マッピング
3. `edgeSSRModuleMapping` - Edge SSR用のモジュールIDマッピング
4. `rscModuleMapping` - RSC用のモジュールIDマッピング
5. `edgeRscModuleMapping` - Edge RSC用のモジュールIDマッピング
6. `entryCSSFiles` - エントリポイントごとのCSSファイル情報
7. `moduleLoading` - モジュールロード設定（prefix、crossOrigin）

**帳票の出力タイミング**：`next build` コマンド実行時のWebpackコンパイルプロセス中、`PROCESS_ASSETS_STAGE_ANALYSE` ステージでClientReferenceManifestPluginが実行された際に出力される。各App Routerエントリポイントに対して個別のマニフェストが生成される。

**帳票の利用者**：React Flightランタイム（RSCシリアライゼーション）、Next.jsサーバーランタイム（SSR時のモジュール解決）、Edgeランタイム。

## 帳票種別

ビルドマニフェスト（JavaScript形式のRSCクライアント参照マッピングファイル）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| N/A | CLIコマンド | `next build` | ビルドコマンド実行 |
| N/A | 開発サーバー | `next dev` | 開発サーバー起動・ファイル変更時 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | JavaScript（JSON埋め込み） |
| 用紙サイズ | N/A（データファイル） |
| 向き | N/A |
| ファイル名 | `page_client-reference-manifest.js`（各エントリポイントごと） |
| 出力方法 | `compilation.emitAsset` 経由（`.next/server/[entry]/page_client-reference-manifest.js`） |
| 文字コード | UTF-8 |

### PDF固有設定

N/A

### Excel固有設定

N/A

## 帳票レイアウト

### レイアウト概要

各エントリポイントごとに生成されるJavaScriptファイルで、グローバル変数にマニフェストデータをセットする。

```javascript
globalThis.__RSC_MANIFEST = globalThis.__RSC_MANIFEST || {};
globalThis.__RSC_MANIFEST["app/page"] = {
  "moduleLoading": { "prefix": "/_next/", "crossOrigin": "" },
  "ssrModuleMapping": { ... },
  "edgeSSRModuleMapping": { ... },
  "clientModules": { ... },
  "entryCSSFiles": { ... },
  "rscModuleMapping": { ... },
  "edgeRscModuleMapping": { ... }
}
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | moduleLoading.prefix | アセットURLプレフィックス | `compilation.outputOptions.publicPath` | 文字列 |
| 2 | moduleLoading.crossOrigin | CORS設定 | `compilation.outputOptions.crossOriginLoading` | 文字列/undefined |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | clientModules[resource] | クライアントモジュール参照情報 | Webpackモジュールグラフ | `{ id, name, chunks, async }` | N/A |
| 2 | ssrModuleMapping[clientId] | SSR用モジュールIDマッピング | `pluginState.ssrModules` | `{ '*': { id, name, chunks, async } }` | N/A |
| 3 | edgeSSRModuleMapping[clientId] | Edge SSR用マッピング | `pluginState.edgeSsrModules` | 同上 | N/A |
| 4 | rscModuleMapping[clientId] | RSC用マッピング | `pluginState.rscModules` | 同上 | N/A |
| 5 | edgeRscModuleMapping[clientId] | Edge RSC用マッピング | `pluginState.ssrModules`（edgeとして） | 同上 | N/A |
| 6 | entryCSSFiles[entryName] | エントリのCSSファイルリスト | `entrypoint.getFiles().filter(.css)` | `CssResource[]` | N/A |

### フッター部

N/A

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| App Routerエントリ | `app/` で始まるエントリポイント、またはメインApp/インターナルエントリ | Yes |
| クライアントコンポーネント | `'use client'` ディレクティブを持つモジュール（RSCレイヤー） | Yes |
| rootMainFilesの除外 | CLIENT_STATIC_FILES_RUNTIME_MAIN_APPのファイルはチャンクから除外 | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| N/A | エントリポイント順 | Webpackのエントリポイント順序 |

### 改ページ条件

N/A

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

N/A

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

N/A

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| グループ名 | `entryNameToGroupName(entryName)` | N/A | スロット・グループ・catchAll除去 |
| requiredChunks | `getAppPathRequiredChunks(entrypoint, rootMainFiles)` | N/A | チャンクIDとエンコード済みファイル名のペア配列 |
| ssrNamedModuleId | `relative(context, mod.resourceResolveData?.path \|\| resource)` | N/A | SSR用のモジュール名前ID |
| crossOriginMode | `crossOriginLoading === 'use-credentials' ? 'use-credentials' : ''` | N/A | 'anonymous'は空文字に変換 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[Webpackコンパイル] --> B[ClientReferenceManifestPlugin.apply]
    B --> C[compilation.hooks.processAssets.tap ANALYSE stage]
    C --> D[createAsset実行]
    D --> E[rootMainFiles除外セット構築]
    E --> F[各エントリポイントをイテレート]
    F --> G[manifestオブジェクト初期化]
    G --> H[entryCSSFiles構築]
    H --> I[requiredChunks取得]
    I --> J[モジュールをイテレート]
    J --> K[addClientReference/addSSRIdMapping/addRSCIdMapping]
    K --> L[グループ名でマニフェストをグルーピング]
    L --> M[同一グループのマニフェストをmerge]
    M --> N[各グループのマニフェストをemitAsset]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| publicPath未設定 | `compilation.outputOptions.publicPath` が文字列でない場合 | `Expected webpack publicPath to be a string when using App Router...` | assetPrefix設定の確認、webpack設定のpublicPath確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | App Routerのエントリ数 x クライアントコンポーネント数 |
| 目標出力時間 | Webpackコンパイル時間内 |
| 同時出力数上限 | エントリポイント数分（各エントリに1ファイル） |

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

クライアント参照マニフェストにはモジュールID、ファイルパス、チャンクURL等が含まれる。サーバー側ファイルとして `.next/server/` に格納され、クライアントには直接配信されないが、RSCペイロード経由でモジュールIDとチャンクURLはクライアントに伝達される。

## 備考

- エントリポイントごとに個別のマニフェストファイルが生成される（他のマニフェストと異なる点）
- `entryNameToGroupName` 関数でグループルート・スロット・catchAllを正規化し、同一グループのマニフェストをマージする
- ESM Resource（Edge用）のマッピングも同時に生成される（`next/dist/` → `next/dist/esm/` 変換）
- Barrel Optimization対応で、`__barrel_optimize__` プレフィックスのリソースを特別扱いする
- `experimentalInlineCss` が有効な場合、CSSの内容がマニフェストに直接埋め込まれる
- `PROCESS_ASSETS_STAGE_ANALYSE` ステージで実行（CSS mini extractプラグインのハッシュ更新後）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | flight-manifest-plugin.ts | `packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts` | `ClientReferenceManifest`型（99〜116行目）、`ManifestNode`型（54〜74行目）、`ClientReferenceManifestForRsc`型（76〜84行目） |
| 1-2 | constants.ts | `packages/next/src/shared/lib/constants.ts` | `CLIENT_REFERENCE_MANIFEST`定数（126行目） |

**読解のコツ**: ManifestNode型は `{ [moduleExport]: { id, name, chunks, async } }` という構造。ssrModuleMappingとedgeSSRModuleMappingは `{ [clientModuleId]: ManifestNode }` 型で、クライアント→SSRのモジュールID変換を提供。

#### Step 2: プラグインのライフサイクルを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | flight-manifest-plugin.ts | `packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts` | `ClientReferenceManifestPlugin.apply`メソッド（222〜234行目）。`PROCESS_ASSETS_STAGE_ANALYSE`ステージでの登録 |

**主要処理フロー**:
1. **222行目**: `apply`メソッドで`compilation.hooks.processAssets.tap`に登録
2. **229行目**: `PROCESS_ASSETS_STAGE_ANALYSE`ステージ（CSSハッシュ更新後）
3. **231行目**: `createAsset`メソッドが呼び出される

#### Step 3: マニフェスト構築ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | flight-manifest-plugin.ts | `packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts` | `createAsset`メソッド（236行目〜）。グループ分け、マニフェスト初期化、モジュール走査 |

**主要処理フロー**:
- **258〜266行目**: rootMainFilesの除外セット構築
- **268〜318行目**: 各エントリポイントのマニフェスト初期化・CSS情報収集
- **320行目**: `getAppPathRequiredChunks` でチャンク情報取得
- **321〜394行目**: `recordModule`関数（addClientReference/addSSRIdMapping/addRSCIdMapping）
- **164〜187行目**: `entryNameToGroupName`でグループ化
- **189〜205行目**: `mergeManifest`で同一グループのマニフェストをマージ

#### Step 4: プラグインステートを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | flight-manifest-plugin.ts | `packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts` | `pluginState`（46〜52行目）。ssrModules/edgeSsrModules/rscModules/edgeRscModulesの共有状態 |

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

```
next build
    |
    +-- Webpack compilation (client compiler)
            |
            +-- ClientReferenceManifestPlugin.apply() [222行目]
                    |
                    +-- createAsset() [236行目]
                            |
                            +-- getAppPathRequiredChunks() [118行目]
                            |
                            +-- recordModule() [321行目]
                            |       |
                            |       +-- addClientReference() [375行目]
                            |       +-- addSSRIdMapping() [396行目]
                            |       +-- addRSCIdMapping() [429行目]
                            |
                            +-- entryNameToGroupName() [164行目]
                            |
                            +-- mergeManifest() [189行目]
                            |
                            +-- compilation.emitAsset()
                                    → .next/server/[entry]/page_client-reference-manifest.js
```

### データフロー図

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

Client compiler modules            ClientReferenceManifestPlugin
  (use client modules) ----------> clientModules構築
pluginState.ssrModules ----------> ssrModuleMapping構築
pluginState.edgeSsrModules ------> edgeSSRModuleMapping構築
pluginState.rscModules ----------> rscModuleMapping構築
entrypoint CSS files -------------> entryCSSFiles構築
                                        |
                                        +-- グループ化・マージ
                                        +-- emitAsset -----------> .next/server/[entry]/page_client-reference-manifest.js
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| flight-manifest-plugin.ts | `packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts` | ソース | ClientReferenceManifestPlugin実装 |
| flight-client-entry-plugin.ts | `packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts` | ソース | pluginState（ssrModules等）の設定元 |
| constants.ts | `packages/next/src/shared/lib/constants.ts` | ソース | CLIENT_REFERENCE_MANIFEST定数 |
| app-render.tsx | `packages/next/src/server/app-render/app-render.tsx` | ソース | SSR時のマニフェスト利用 |
| manifest-loader.ts | `packages/next/src/shared/lib/turbopack/manifest-loader.ts` | ソース | Turbopack用のマニフェストローダー |
| build-context.ts | `packages/next/src/build/build-context.ts` | ソース | getProxiedPluginState関数 |
