# 帳票設計書 18-dynamic-css-manifest.js

## 概要

本ドキュメントは、Next.jsビルドプロセスにおいて生成される `dynamic-css-manifest.js`（動的CSSマニフェスト）の設計仕様を定義する。本マニフェストは、動的インポート（`next/dynamic`）に関連するCSSファイルの情報を管理し、Pages Router環境でのクライアントナビゲーション後にサーバーレンダリングされた `<link>` タグが誤って削除されることを防止する。

### 本帳票の処理概要

本マニフェストは、ReactLoadablePlugin により生成されるJSON/JSファイルであり、動的インポートされたコンポーネントに関連付けられたCSSファイルの一覧を保持する。Pages Router 環境かつ本番ビルド（Production）かつ Webpack 使用時のみ生成される特殊なマニフェストである。

**業務上の目的・背景**：Next.jsの Pages Router において、サーバーサイドレンダリング時に挿入された `<link>` タグ（CSS参照）が、クライアントサイドのナビゲーション後に不正に削除される問題（PR #72959 で修正）が存在した。本マニフェストは、動的インポートに関連するCSSファイルを追跡することで、クライアントナビゲーション時にこれらの CSS リンクが維持されるようにする。

**帳票の利用シーン**：`next build` コマンド実行時に、Pages Router を含むプロジェクトのWebpack本番ビルドで自動生成される。サーバーサイドレンダリング時（`load-components.ts`）に読み込まれ、クライアントナビゲーション時のCSS保持判定に使用される。エッジランタイム向けにはJS形式が使用される。

**主要な出力内容**：
1. 動的インポートに関連するCSSファイルパスの配列
2. JSON形式（`dynamic-css-manifest.json`）とJS形式（`server/dynamic-css-manifest.js`）の2種類

**帳票の出力タイミング**：Webpackコンパイルフェーズの `PROCESS_ASSETS_STAGE_ADDITIONS` ステージで `ReactLoadablePlugin` により生成される。Pages Router + Production + Webpack の条件を満たす場合のみ。

**帳票の利用者**：Next.jsフレームワーク内部のサーバーレンダリングエンジン（`load-components.ts`）。エッジランタイムでのSSR処理。

## 帳票種別

ビルドマニフェスト（JSON/JavaScript形式）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| N/A | CLIコマンド | `next build` | ビルド実行（Pages Router + Webpack時のみ） |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | JSON (.json) および JavaScript (.js) |
| 用紙サイズ | N/A（データファイル） |
| 向き | N/A |
| ファイル名 | `dynamic-css-manifest.json` / `server/dynamic-css-manifest.js` |
| 出力方法 | ファイルシステムへの書き込み |
| 文字コード | UTF-8 |

### JSON形式

```json
[
  "static/css/dynamic-component-abc123.css",
  "static/css/lazy-module-def456.css"
]
```

### JavaScript形式（エッジランタイム向け）

```javascript
self.__DYNAMIC_CSS_MANIFEST="[\"static/css/dynamic-component-abc123.css\"]"
```

## 帳票レイアウト

### レイアウト概要

CSSファイルパスの文字列配列として構成される。

```
┌─────────────────────────────────────────────┐
│  DynamicCssManifest = string[]              │
│                                             │
│  例:                                        │
│  [                                          │
│    "static/css/component-a.css",            │
│    "static/css/component-b.css"             │
│  ]                                          │
└─────────────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | CSSファイルパス | 動的インポートに関連するCSSファイルの相対パス | Webpackチャンクグラフのファイルリスト | `string[]` |

### 明細部

N/A（単純配列）

### フッター部

N/A

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| Pages Router | Pages Routerを使用するプロジェクトであること | Yes |
| 本番ビルド | `dev` が `false`（本番ビルド）であること | Yes |
| Webpack使用 | Webpackバンドラーを使用していること（Turbopackでは生成されない） | Yes |
| shouldCreateDynamicCssManifest | 上記3条件を満たすフラグが `true` であること | Yes |

### ソート順

N/A

### 改ページ条件

N/A

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

N/A（Webpackコンパイル結果から生成）

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| JS形式変換 | `self.__DYNAMIC_CSS_MANIFEST=${JSON.stringify(JSON.stringify(dynamicCssManifest))}` | N/A | 二重JSON.stringify |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[Webpackコンパイル] --> B[ReactLoadablePlugin]
    B --> C{shouldCreateDynamicCssManifest?}
    C -->|Yes| D[動的インポート関連CSS収集]
    C -->|No| E[スキップ]
    D --> F[dynamic-css-manifest.json出力]
    D --> G[server/dynamic-css-manifest.js出力]
    F --> H[終了]
    G --> H
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| CSS情報なし | 動的インポートにCSSが含まれない場合 | エラーなし（空配列のマニフェスト） | 対処不要 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 動的インポートに関連するCSSファイル数（通常数件〜数十件） |
| 目標出力時間 | 即座 |
| 同時出力数上限 | 1 |

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

CSSファイルパス情報のみを含み、機密情報は含まれない。

## 備考

- `DynamicCssManifest` 型は `load-components.ts` の49行目で `string[]` として定義されている。
- 本マニフェストはPages Router環境でのみ必要。App Routerでは異なるCSS管理メカニズムが使用される。
- PR #72959 で導入された修正。サーバーレンダリングされた `<link>` タグがクライアントナビゲーション後に削除される問題を解決する。
- 定数名 `DYNAMIC_CSS_MANIFEST` は `constants.ts` の138行目で定義（値: `'dynamic-css-manifest'`）。
- ミドルウェアプラグイン（middleware-plugin.ts 128行目）で、本番かつ非App Dir環境でエッジSSRファイルリストに追加される。
- エッジランタイムでは `self.__DYNAMIC_CSS_MANIFEST` / クライアントでは `window.__DYNAMIC_CSS_MANIFEST` として参照される。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | load-components.ts | `packages/next/src/server/load-components.ts` | 44-49行目: `DynamicCssManifest` 型定義（`string[]`）。コメントで「Pages dir && Production && Webpack」限定と記載 |
| 1-2 | constants.ts | `packages/next/src/shared/lib/constants.ts` | 138行目: `DYNAMIC_CSS_MANIFEST` 定数（値: `'dynamic-css-manifest'`） |

**読解のコツ**: このマニフェストは非常にシンプルな `string[]` 型であり、CSSファイルパスの配列にすぎない。

#### Step 2: 生成処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | react-loadable-plugin.ts | `packages/next/src/build/webpack/plugins/react-loadable-plugin.ts` | 221-236行目: `shouldCreateDynamicCssManifest` が true の場合に JSON と JS の両形式で出力 |

**主要処理フロー**:
- **221行目**: `if (shouldCreateDynamicCssManifest)` で条件チェック
- **222-225行目**: `dynamic-css-manifest.json` を `compilation.emitAsset` で出力
- **228-235行目**: `server/dynamic-css-manifest.js` をエッジランタイム向けに出力

#### Step 3: エッジランタイムでの利用を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | middleware-plugin.ts | `packages/next/src/build/webpack/plugins/middleware-plugin.ts` | 127-129行目: `!opts.dev && !meta.edgeSSR.isAppDir` の条件で `server/${DYNAMIC_CSS_MANIFEST}.js` をファイルリストに追加 |
| 3-2 | load-components.ts | `packages/next/src/server/load-components.ts` | 80行目: `dynamicCssManifest` フィールドとしてLoadComponentsReturnTypeに含まれる |

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

```
next build
    |
    +-- Webpack Compilation
    |       |
    |       +-- ReactLoadablePlugin.apply()
    |               |
    |               +-- buildManifest() [動的インポート情報収集]
    |               +-- if (shouldCreateDynamicCssManifest)
    |                       +-- compilation.emitAsset('dynamic-css-manifest.json')
    |                       +-- compilation.emitAsset('server/dynamic-css-manifest.js')
    |
    +-- MiddlewarePlugin
    |       +-- getEntryFiles()
    |               +-- if (!dev && !isAppDir)
    |                       +-- server/dynamic-css-manifest.js をリストに追加
    |
    +-- loadComponents()
            +-- dynamicCssManifest の読み込み
```

### データフロー図

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

Webpack dynamic imports    ReactLoadablePlugin              .next/dynamic-css-manifest.json
  (next/dynamic CSS)   --> (動的CSS収集)                --> .next/server/dynamic-css-manifest.js
                           shouldCreateDynamicCssManifest
                           チェック

                           loadComponents()                 サーバーレンダリング時の
  manifest.json        --> (マニフェスト読み込み)       --> <link>タグ保持判定
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| react-loadable-plugin.ts | `packages/next/src/build/webpack/plugins/react-loadable-plugin.ts` | ソース | マニフェスト生成（221-236行目） |
| load-components.ts | `packages/next/src/server/load-components.ts` | ソース | DynamicCssManifest型定義・読み込み |
| constants.ts | `packages/next/src/shared/lib/constants.ts` | ソース | DYNAMIC_CSS_MANIFEST定数 |
| middleware-plugin.ts | `packages/next/src/build/webpack/plugins/middleware-plugin.ts` | ソース | エッジファイルリストへの追加 |
| route-loader.ts | `packages/next/src/client/route-loader.ts` | ソース | クライアント `__DYNAMIC_CSS_MANIFEST` 参照 |
| index.ts | `packages/next/src/build/index.ts` | ソース | ビルドプロセスでのマニフェストファイルパス設定 |
