# 通知設計書 29-コンパイル中通知

## 概要

本ドキュメントは、Next.js開発サーバーにおけるコンパイル中通知の設計を記述する。コンパイルが一定時間（3秒）以上かかる場合にコンソールへ「Compiling {trigger} ...」メッセージを出力し、開発者にコンパイル進行中であることを通知する仕組みについて定義する。

### 本通知の処理概要

開発サーバーでコンパイルが開始された際、3秒間のタイマーを設定し、コンパイルがその間に完了しなかった場合にコンソールへ「Compiling {trigger} ...」メッセージを出力する。これにより、開発者はコンパイルが進行中であることを認識できる。

**業務上の目的・背景**：開発中のコンパイルは通常数百ミリ秒で完了するが、初回コンパイルや大規模な変更時には数秒以上かかることがある。短時間で完了するコンパイルについてはログ出力を抑制し、3秒以上かかる場合のみ「コンパイル中」メッセージを表示することで、コンソールのノイズを低減しつつ、開発者にフィードバックを提供する。

**通知の送信タイミング**：`OutputState`のstoreが`loading: true`に更新された後、3000ミリ秒（`MAX_LOG_SKIP_DURATION_MS`）経過してもコンパイルが完了しない場合にコンソールへ出力される。

**通知の受信者**：開発者のターミナル（コンソール出力）。

**通知内容の概要**：`Compiling {trigger} ...` の形式のメッセージ。`trigger`はコンパイルのトリガーとなったページパスまたはルート名。`NEXT_TRIGGER_URL`環境変数が設定されており、triggerとURLが異なる場合は`Compiling {trigger} ({url}) ...`の形式。

**期待されるアクション**：開発者はコンパイルが進行中であることを認識し、完了を待つ。

## 通知種別

コンソールメッセージ（ターミナル出力）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（3秒遅延タイマー） |
| 優先度 | 低 |
| リトライ | なし |

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

`store`のstateサブスクリプション内で、`state.loading === true`かつ`trigger !== 'initial'`の場合にタイマーを設定する。

## 通知テンプレート

### メール通知の場合

該当なし（コンソールメッセージのため）

### 本文テンプレート

```
○ Compiling /dashboard ...
```

URL付きの場合：
```
○ Compiling /dashboard (/dashboard?param=value) ...
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| trigger | コンパイルトリガー（ページパス等） | `state.trigger`を`formatTrigger`で整形 | Yes |
| url | リクエストURL | `state.url` | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| サーバーイベント | OutputStateが`loading: true`に変化 | `trigger !== 'initial'`かつ3000ms以内にコンパイル未完了 | コンパイル開始から3秒経過 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| `trigger === 'initial'` | 初回コンパイル時はメッセージを表示しない |
| 3秒以内にコンパイル完了 | タイマーがクリアされるためメッセージは表示されない |
| `logging === false` | ログ出力が無効化されている場合 |
| 状態未変更 | `hasStoreChanged`がfalseを返す場合 |
| `bootstrap === true` | サーバー起動前のブートストラップ状態 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[OutputState: loading=true] --> B{trigger === initial?}
    B -->|Yes| C[タイマー設定スキップ]
    B -->|No| D[3000msタイマー設定]
    D --> E{3000ms経過前にコンパイル完了?}
    E -->|Yes| F[clearTimeout タイマーキャンセル]
    E -->|No| G[Log.wait Compiling trigger ...]
    F --> H[終了]
    G --> H
```

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

### 参照テーブル一覧

該当なし

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 該当なし | コンソール出力のみのため、エラーケースは存在しない | - |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし（3秒タイマーによる自然な制限あり） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（開発サーバー稼働中は常時出力可能）

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

本通知はローカル開発環境専用のコンソールメッセージであり、機密情報は含まれない。ページパスが表示されるが、開発環境のみで使用される。

## 備考

- `MAX_LOG_SKIP_DURATION_MS`は3000ミリ秒（3秒）に設定されている。
- `formatTrigger`関数により、`__metadata_id__`を含むメタデータルートは`/[id]`形式に整形され、末尾のスラッシュは除去される。
- `traceSpan`によりコンパイル時間のトレーシングが行われる（`compile-path`スパン）。
- `loadingLogTimer`は1つのタイマーのみ保持し、新しいコンパイル開始時に既存タイマーがあれば新規タイマーは設定しない。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | store.ts | `packages/next/src/build/output/store.ts` | 8-34行目: `OutputState`型定義。`bootstrap`/`loading`/`trigger`/`url`/`errors`/`warnings`/`typeChecking`の各状態を確認 |

**読解のコツ**: `OutputState`はunion型で、`bootstrap: true`の初期状態と`bootstrap: false`の状態に分かれ、後者はさらに`loading: true`と`loading: false`のunionになっている。

#### Step 2: コンパイル中通知のトリガーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | store.ts | `packages/next/src/build/output/store.ts` | 6行目: `MAX_LOG_SKIP_DURATION_MS = 3000`の定数定義 |
| 2-2 | store.ts | `packages/next/src/build/output/store.ts` | 84-131行目: `store.subscribe`内の`state.loading`ブランチ。タイマー設定ロジック |

**主要処理フロー**:
1. **84行目**: `store.subscribe(state => { ... })`でstateの変更を監視
2. **91行目**: `logging`が無効の場合は早期リターン
3. **99行目**: `bootstrap`状態の場合は早期リターン
4. **103行目**: `state.loading`がtrueの場合の処理開始
5. **104-105行目**: `trigger`と`triggerUrl`を更新
6. **107行目**: `trigger !== 'initial'`の条件判定
7. **111行目**: `loadingLogTimer`が未設定の場合のみタイマー設定
8. **113行目**: `setTimeout(() => { ... }, MAX_LOG_SKIP_DURATION_MS)`で3秒後にログ出力

#### Step 3: formatTrigger関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | store.ts | `packages/next/src/build/output/store.ts` | 36-47行目: `formatTrigger`関数。メタデータルートの整形と末尾スラッシュ除去 |

#### Step 4: コンパイル完了時のタイマークリアを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | store.ts | `packages/next/src/build/output/store.ts` | 174-186行目: コンパイル完了時の処理。`clearTimeout(loadingLogTimer)`でタイマーをキャンセル |

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

```
store.subscribe(state => { ... }) [store.ts:84]
    |
    +-- hasStoreChanged(state) [store.ts:62]
    |
    +-- [state.loading === true]
    |     |
    |     +-- formatTrigger(state.trigger) [store.ts:36]
    |     +-- trace('compile-path', ...) [store.ts:108]
    |     +-- setTimeout(() => {
    |     |     Log.wait(`Compiling ${trigger} ...`)
    |     |   }, 3000) [store.ts:113]
    |
    +-- [state.loading === false, コンパイル完了]
          |
          +-- clearTimeout(loadingLogTimer) [store.ts:178]
          +-- traceSpan.stop() [store.ts:182]
```

### データフロー図

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

consoleStore.setState({    ──> store.subscribe()                ──> ターミナルコンソール
  loading: true,                 formatTrigger()                     "○ Compiling /path ..."
  trigger: "/path",              3000ms setTimeout
  url: "/path?q=1"               条件判定
})                              (loadingLogTimer)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| store.ts | `packages/next/src/build/output/store.ts` | ソース | OutputState管理、コンソール出力ロジック |
| log.ts | `packages/next/src/build/output/log.ts` | ソース | Log.wait等のコンソール出力ユーティリティ |
| hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | ソース | consoleStore.setState呼び出し元（startBuilding） |
