# 通知設計書 9-ページ追加通知

## 概要

本ドキュメントは、新しいページが追加された際にブラウザへWebSocket経由で送信されるaddedPageメッセージの設計を記載する。

### 本通知の処理概要

開発サーバーのHMR（Hot Module Replacement）システムにおいて、新しいページファイルが追加されたことを検出し、ブラウザクライアントへWebSocket経由で通知する。

**業務上の目的・背景**：Next.jsの開発サーバーでは、ファイルシステムの変更をリアルタイムに検出してブラウザに反映する。新しいページが追加された際に、ブラウザ側でルーティング情報を更新し、開発者がページの再読み込みなしに新しいルートにアクセスできるようにすることが目的である。Hot Module Replacementの一環として、開発体験の向上に寄与する。

**通知の送信タイミング**：Turbopackの場合はエントリーポイント処理ループ内でルート変更を検出した際に送信される（`hot-reloader-turbopack.ts`行730-735）。Webpackの場合はクライアントコンパイル完了時にchunkNames差分で検出される（`hot-reloader-webpack.ts`行1587-1597）。

**通知の受信者**：開発サーバーにWebSocket接続しているすべてのブラウザクライアント。

**通知内容の概要**：追加されたページのルートパス（例：`/about`、`/blog/[slug]`）。

**期待されるアクション**：ブラウザ側のHMRクライアントがルーティングテーブルを更新し、新しいページへのナビゲーションを可能にする。Pages Routerの場合は開発用ページマニフェストの更新も併せて行われる。

## 通知種別

WebSocket（HMRメッセージ）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（WebSocket push） |
| 優先度 | 高 |
| リトライ | なし（WebSocket接続が切断された場合はページリロードで復旧） |

### 送信先決定ロジック

開発サーバーにWebSocket接続しているすべてのブラウザクライアントに対してブロードキャスト送信される。`hotReloader.send()`メソッドで全クライアントに送信。

## 通知テンプレート

### WebSocketメッセージの場合

| 項目 | 内容 |
|-----|------|
| プロトコル | WebSocket |
| 形式 | JSON |
| メッセージタイプ | `addedPage` |

### メッセージ構造

```json
{
  "type": "addedPage",
  "data": ["/route-path"]
}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| data[0] | 追加されたページのルートパス | Turbopack: addedRoutes / Webpack: getRouteFromEntrypoint(addedPage) | Yes（nullの場合あり） |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| Turbopackルート変更 | エントリーポイント処理でaddedRoutes検出 | addedRoutesが非空 | hot-reloader-turbopack.ts行730-735 |
| Webpackコンパイル完了 | クライアントコンパイル完了時のchunkNames差分 | addedPagesが非空 | hot-reloader-webpack.ts行1587-1597 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 追加ルートなし | ルート変更検出の結果、追加がない場合 |
| 初回コンパイル | Webpackの場合、prevChunkNamesが未設定の初回は差分計算をスキップ |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ファイルシステム変更検出] --> B{バンドラー}
    B -->|Turbopack| C[エントリーポイント処理]
    B -->|Webpack| D[クライアントコンパイル完了]
    C --> E[addedRoutes計算]
    D --> F[chunkNames差分計算]
    E --> G{addedRoutes非空?}
    F --> H{addedPages非空?}
    G -->|Yes| I[各routeについてsend実行]
    H -->|Yes| J[各pageについてsend実行]
    G -->|No| K[終了]
    H -->|No| K
    I --> L[WebSocket経由でブラウザへ送信]
    J --> L
    L --> K
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし。データベースは使用しない。

### 更新テーブル一覧

該当なし。

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| WebSocket切断 | ブラウザ側の接続が切れた場合 | 次回接続時にsyncメッセージで状態同期 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 制限 | なし（変更検出のたびに送信） |

### 配信時間帯

制限なし。開発サーバー稼働中は常に有効。

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

- ルートパス情報のみが送信され、ファイルシステムの絶対パスやソースコードは含まれない
- WebSocket接続はlocalhostに限定されるのが一般的
- 開発環境でのみ使用される機能であり、プロダクション環境には影響しない

## 備考

- `HMR_MESSAGE_SENT_TO_BROWSER.ADDED_PAGE`はenum値`'addedPage'`として定義（hot-reloader-types.ts行20）
- メッセージの`data`フィールドは配列形式で、要素は1つのルートパス（またはnull）
- Turbopackでは`addedRoutes`がルート変更時に直接計算される
- Webpackでは`diff(chunkNames, prevChunkNames)`でSet差分を取り、`getRouteFromEntrypoint`でルートパスに変換
- ページ追加時には同時に`DEV_PAGES_MANIFEST_UPDATE`メッセージも送信される（Turbopack、行718-727）
- `AddedPageMessage`インターフェースで型定義されている（hot-reloader-types.ts行95-98）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | `HMR_MESSAGE_SENT_TO_BROWSER.ADDED_PAGE`（行20）のenum定義 |
| 1-2 | hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | `AddedPageMessage`インターフェース（行95-98）。typeとdata[page]の構造 |

**読解のコツ**: `HMR_MESSAGE_SENT_TO_BROWSER`はconst enumであり、コンパイル時にインライン化される。`data`フィールドはタプル型`[page: string | null]`。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | 行718-742のルート変更検出とメッセージ送信 |

**主要処理フロー**:
1. **行718**: `addedRoutes.length > 0 || removedRoutes.length > 0`の判定
2. **行720-727**: DEV_PAGES_MANIFEST_UPDATEの送信
3. **行730-735**: `addedRoutes`をループし、各ルートについて`ADDED_PAGE`メッセージを送信

#### Step 3: エントリーポイントを理解する（Webpack）

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | hot-reloader-webpack.ts | `packages/next/src/server/dev/hot-reloader-webpack.ts` | 行1584-1611のchunkNames差分計算とメッセージ送信 |

**主要処理フロー**:
- **行1578-1582**: `compilation.namedChunks`からchunkNamesを取得し、`getRouteFromEntrypoint`でフィルタ
- **行1584**: `prevChunkNames`が存在する場合のみ差分計算
- **行1587**: `diff(chunkNames, prevChunkNames)`で追加ページを取得
- **行1590-1597**: 追加ページをループし、`getRouteFromEntrypoint`でルートパスに変換後に送信
- **行1611**: `prevChunkNames`を更新

#### Step 4: メッセージ送信メカニズムを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | `NextJsHotReloaderInterface`のsendメソッド（行233） |

**主要処理フロー**:
- **行233**: `send(action: HmrMessageSentToBrowser): void`でWebSocket経由の送信

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

```
[Turbopack]
handleEntrypoints() (hot-reloader-turbopack.ts)
    │
    ├─ addedRoutes / removedRoutes 計算
    │
    ├─ hotReloader.send({ type: DEV_PAGES_MANIFEST_UPDATE }) (行720-727)
    │
    └─ for (route of addedRoutes) (行730)
           └─ hotReloader.send({ type: ADDED_PAGE, data: [route] }) (行731-734)

[Webpack]
onClientDone(stats) (hot-reloader-webpack.ts)
    │
    ├─ compilation.namedChunks → chunkNames (行1578-1582)
    │
    ├─ diff(chunkNames, prevChunkNames) → addedPages (行1587)
    │
    └─ for (addedPage of addedPages) (行1591)
           ├─ getRouteFromEntrypoint(addedPage) → page (行1592)
           └─ this.send({ type: ADDED_PAGE, data: [page] }) (行1593-1596)
```

### データフロー図

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

[Turbopack]
entrypoints変更 ──▶ addedRoutes計算 ──▶ hotReloader.send() ──▶ WebSocket → ブラウザ

[Webpack]
compilation.namedChunks ──▶ diff() ──▶ getRouteFromEntrypoint() ──▶ this.send() ──▶ WebSocket → ブラウザ
prevChunkNames ───────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | ソース | HMRメッセージ型定義・enum定義 |
| hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | ソース | Turbopack版HMR実装 |
| hot-reloader-webpack.ts | `packages/next/src/server/dev/hot-reloader-webpack.ts` | ソース | Webpack版HMR実装 |
