# 帳票設計書 11-next-font-manifest

## 概要

本ドキュメントは、Next.jsビルドプロセスにおいて生成される `next-font-manifest`（フォントマニフェスト）の設計仕様を定義する。本マニフェストは、`next/font` で使用されるフォント情報（プリロード対象フォント等）を管理し、レンダリング時のフォントプリロード最適化を実現する。

### 本帳票の処理概要

本マニフェストは、Next.jsのビルド時に `NextFontManifestPlugin` によって生成されるJSON/JSファイルであり、各ページ・App Routerエントリに関連するフォントファイル情報を収集・マッピングする。

**業務上の目的・背景**：Webフォントのプリロードはページのパフォーマンスに直結する重要な最適化項目である。本マニフェストは、各ルートで使用されるフォントファイルを特定し、レンダリング時に適切な `<link rel="preload">` タグまたは `<link rel="preconnect">` タグを挿入するための基盤情報を提供する。不必要なフォントのプリロードを避けることで、ページの初期ロード時間を最適化する。

**帳票の利用シーン**：Next.jsアプリケーションのビルド時（`next build`）に自動生成され、サーバーサイドレンダリング時に参照される。Pages Router では `_document.tsx` でのレンダリング時、App Router では `app-render` でのコンポーネントツリー構築時に利用される。Turbopack使用時には `TurbopackManifestLoader` によるマージ処理も行われる。

**主要な出力内容**：
1. Pages Router のルートごとのプリロード対象フォントファイルマッピング（`pages`フィールド）
2. App Router のWebpackモジュールリクエストごとのプリロード対象フォントファイルマッピング（`app`フィールド）
3. App Routerでの size-adjust フォールバックフォント使用フラグ（`appUsingSizeAdjust`）
4. Pages Routerでの size-adjust フォールバックフォント使用フラグ（`pagesUsingSizeAdjust`）

**帳票の出力タイミング**：`next build` コマンド実行時のWebpackコンパイルフェーズ（`PROCESS_ASSETS_STAGE_ADDITIONS`ステージ）で生成される。開発モードでは `pnpm next dev` 時にTurbopackManifestLoaderを通じて動的に生成・更新される。

**帳票の利用者**：Next.jsフレームワーク内部のサーバーレンダリングエンジン。開発者が直接参照することは通常ないが、フォントプリロードの最適化挙動を確認する際にデバッグ用途で参照される場合がある。

## 帳票種別

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

## 利用画面

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

## 出力形式

### 基本仕様

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

### JSON形式

```json
{
  "pages": {
    "/": ["static/media/font-abc.p.woff2"],
    "/about": ["static/media/font-def.p.woff"]
  },
  "app": {
    "/path/to/app/page": ["static/media/font-ghi.p.woff2"]
  },
  "appUsingSizeAdjust": false,
  "pagesUsingSizeAdjust": false
}
```

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

```javascript
self.__NEXT_FONT_MANIFEST="{\"pages\":{},\"app\":{},\"appUsingSizeAdjust\":false,\"pagesUsingSizeAdjust\":false}"
```

## 帳票レイアウト

### レイアウト概要

マニフェストはJSONオブジェクトとして4つのトップレベルフィールドで構成される。

```
┌─────────────────────────────────────┐
│  pages: { [route]: fontFile[] }     │
├─────────────────────────────────────┤
│  app: { [entry]: fontFile[] }       │
├─────────────────────────────────────┤
│  appUsingSizeAdjust: boolean        │
├─────────────────────────────────────┤
│  pagesUsingSizeAdjust: boolean      │
└─────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | pages | Pages Routerの各ルートに対応するプリロード対象フォントファイル | Webpackエントリポイントの auxiliaryFiles | `{ [route: string]: string[] }` |
| 2 | app | App Routerの各エントリに対応するプリロード対象フォントファイル | next-font-loaderモジュールの buildInfo.assets | `{ [entry: string]: string[] }` |
| 3 | appUsingSizeAdjust | App Routerでsize-adjustフォールバックが使われているか | フォントファイル名に `-s` を含むかどうか | `boolean` |
| 4 | pagesUsingSizeAdjust | Pages Routerでsize-adjustフォールバックが使われているか | フォントファイル名に `-s` を含むかどうか | `boolean` |

### 明細部

N/A（固定構造のマニフェスト）

### フッター部

N/A

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| フォントファイル拡張子 | `.woff`, `.woff2`, `.eot`, `.ttf`, `.otf` のいずれかの拡張子を持つファイル | Yes |
| プリロード対象判定 | ファイル名に `.p.` が含まれるフォントファイル（`next-font-loader` が preload: true の場合に生成） | Yes |
| App Routerエントリ判定 | chunkGroup名が `app/` で始まるエントリポイント | Yes（app フィールド用） |
| size-adjust判定 | ファイル名に `-s` が含まれるかどうか | Yes（sizeAdjustフラグ用） |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | ルートパス（pages キー） | エントリポイントの列挙順 |
| 2 | エントリパス（app キー） | Turbopack時はキーのアルファベット順 |

### 改ページ条件

N/A（データファイル）

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

### 参照テーブル一覧

N/A（データベースを使用しない。Webpackコンパイル結果から生成）

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

N/A

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| プリロード対象フォント | `fontFiles.filter(file => /\.p\.(woff\|woff2\|eot\|ttf\|otf)$/.test(file))` | N/A | `.p.` を含むフォントファイルのみ抽出 |
| sizeAdjust使用判定 | `fontFiles.some(file => file.includes('-s'))` | N/A | ファイル名に `-s` を含むかで判定 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[Webpackコンパイル開始] --> B[NextFontManifestPlugin.apply]
    B --> C[PROCESS_ASSETS_STAGE_ADDITIONS]
    C --> D{App Routerあり?}
    D -->|Yes| E[app/配下のエントリを走査]
    E --> F[next-font-loaderモジュールからフォントファイル収集]
    F --> G[プリロード対象フィルタリング]
    D -->|No| H[Pages Routerエントリを走査]
    G --> H
    H --> I[エントリポイントのauxiliaryFilesからフォント収集]
    I --> J[プリロード対象フィルタリング]
    J --> K[JSONマニフェスト生成]
    K --> L[server/next-font-manifest.json出力]
    K --> M[server/next-font-manifest.js出力]
    L --> N[終了]
    M --> N
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| フォントファイルなし | next/fontの使用がないプロジェクト | エラーなし（空のマニフェストが生成） | 対処不要 |
| モジュール情報なし | mod.buildInfo.assetsが未定義 | エラーなし（スキップ） | 対処不要 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | プロジェクト内のフォント使用ルート数に依存（通常数十件程度） |
| 目標出力時間 | Webpackコンパイルフェーズ内で完了（追加オーバーヘッドは最小） |
| 同時出力数上限 | 1（ビルドプロセスごとに1回） |

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

本マニフェストにはフォントファイルのパス情報のみが含まれ、機密情報は含まれない。フォントファイルパスは公開アセットとしてクライアントに配信されるため、セキュリティリスクは低い。

## 備考

- Turbopack使用時は `TurbopackManifestLoader` クラスの `loadFontManifest` / `mergeFontManifests` / `writeNextFontManifest` メソッドにより、個別のページマニフェストがマージされて最終的なマニフェストが生成される。
- Google Aurora チームがsize-adjustの使用統計を収集するため、`appUsingSizeAdjust` / `pagesUsingSizeAdjust` フラグが追加されている（`data-size-adjust="true"` 属性の付与に使用）。
- プリロード対象のフォントファイルは `next-font-loader` により `[name].p.[ext]` の命名規則で出力される。

---

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

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

### 推奨読解順序

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

まず、マニフェストの型定義を確認し、出力データの構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | next-font-manifest-plugin.ts | `packages/next/src/build/webpack/plugins/next-font-manifest-plugin.ts` | 7-16行目: `NextFontManifest` 型定義。pages, app, appUsingSizeAdjust, pagesUsingSizeAdjust の4フィールド |
| 1-2 | constants.ts | `packages/next/src/shared/lib/constants.ts` | 94行目: `NEXT_FONT_MANIFEST` 定数定義（値: `'next-font-manifest'`） |

**読解のコツ**: `NextFontManifest` 型は pages と app で異なるキー構造を持つ。pages はルートパス、app は webpack モジュールリクエストパスがキーとなる。

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

プラグインの登録と実行タイミングを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | next-font-manifest-plugin.ts | `packages/next/src/build/webpack/plugins/next-font-manifest-plugin.ts` | 55-62行目: `NextFontManifestPlugin` クラスと `apply` メソッド。`compiler.hooks.make.tap` でコンパイルフックに登録 |

**主要処理フロー**:
1. **63行目**: `compiler.hooks.make.tap` でコンパイルフェーズにフックを登録
2. **65-68行目**: `compilation.hooks.processAssets.tap` で `PROCESS_ASSETS_STAGE_ADDITIONS` ステージにアセット処理を登録
3. **71-76行目**: 空の `nextFontManifest` オブジェクトを初期化

#### Step 3: App Routerフォント収集処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | next-font-manifest-plugin.ts | `packages/next/src/build/webpack/plugins/next-font-manifest-plugin.ts` | 78-123行目: App Router向けフォントファイル収集。`traverseModules` でモジュールを走査し、`next-font-loader` 経由のフォントファイルを抽出 |

**主要処理フロー**:
- **82-83行目**: `traverseModules` でコンパイル結果のモジュールを走査
- **85行目**: モジュールリクエストに `/next-font-loader/index.js?` が含まれるかチェック
- **94-96行目**: フォントファイル拡張子でフィルタリング
- **104行目**: `getPreloadedFontFiles` でプリロード対象をフィルタリング

#### Step 4: Pages Routerフォント収集処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | next-font-manifest-plugin.ts | `packages/next/src/build/webpack/plugins/next-font-manifest-plugin.ts` | 127-155行目: Pages Router向けフォントファイル収集。エントリポイントのチャンクから auxiliaryFiles を取得 |

#### Step 5: マニフェスト出力処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | next-font-manifest-plugin.ts | `packages/next/src/build/webpack/plugins/next-font-manifest-plugin.ts` | 157-173行目: JSON形式とJS形式の2種類のマニフェストファイルを `compilation.emitAsset` で出力 |

#### Step 6: Turbopack時のマニフェストローダーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | manifest-loader.ts | `packages/next/src/shared/lib/turbopack/manifest-loader.ts` | 640-697行目: `loadFontManifest`, `mergeFontManifests`, `writeNextFontManifest` メソッド群。個別ページのマニフェストを読み込み・マージ・書き込みする処理 |

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

```
next build (packages/next/src/build/index.ts)
    |
    +-- Webpack Compilation
    |       |
    |       +-- NextFontManifestPlugin.apply()
    |               |
    |               +-- compiler.hooks.make.tap()
    |                       |
    |                       +-- compilation.hooks.processAssets.tap()
    |                               |
    |                               +-- traverseModules() [App Router]
    |                               |       +-- getPreloadedFontFiles()
    |                               |       +-- getPageIsUsingSizeAdjust()
    |                               |
    |                               +-- entrypoints iteration [Pages Router]
    |                               |       +-- getPreloadedFontFiles()
    |                               |       +-- getPageIsUsingSizeAdjust()
    |                               |
    |                               +-- compilation.emitAsset() [.json]
    |                               +-- compilation.emitAsset() [.js]
    |
    +-- [Turbopack] TurbopackManifestLoader
            |
            +-- loadFontManifest()
            +-- mergeFontManifests()
            +-- writeNextFontManifest()
```

### データフロー図

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

next-font-loader          NextFontManifestPlugin            .next/server/next-font-manifest.json
  (フォントファイル)  --->   (フォント情報収集・             .next/server/next-font-manifest.js
                             プリロード判定)
Webpack entrypoints  --->    (ルートごとマッピング)  --->
  (チャンク情報)

                         TurbopackManifestLoader
Turbopack partial     -->  (マージ処理)             --->    .next/server/next-font-manifest.json
  manifests                                                 .next/server/next-font-manifest.js
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| next-font-manifest-plugin.ts | `packages/next/src/build/webpack/plugins/next-font-manifest-plugin.ts` | ソース | Webpackプラグイン。マニフェスト生成の主要ロジック |
| constants.ts | `packages/next/src/shared/lib/constants.ts` | ソース | `NEXT_FONT_MANIFEST` 定数定義 |
| manifest-loader.ts | `packages/next/src/shared/lib/turbopack/manifest-loader.ts` | ソース | Turbopack時のマニフェストロード・マージ処理 |
| index.ts | `packages/next/src/build/index.ts` | ソース | ビルドプロセスのエントリポイント |
| middleware-plugin.ts | `packages/next/src/build/webpack/plugins/middleware-plugin.ts` | ソース | エッジランタイム用のフォントマニフェスト参照 |
| route-loader.ts | `packages/next/src/client/route-loader.ts` | ソース | クライアントサイドでの `__NEXT_FONT_MANIFEST` 参照 |
| next-server.ts | `packages/next/src/server/next-server.ts` | ソース | サーバーサイドでのマニフェスト読み込み |
| route-module.ts | `packages/next/src/server/route-modules/route-module.ts` | ソース | ルートモジュールでのマニフェスト利用 |
