# 通知設計書 6-HMR警告

## 概要

本ドキュメントは、Vue.jsのHot Module Replacement（HMR）処理における警告通知機構の設計仕様を記述する。`packages/runtime-core/src/hmr.ts`に実装されており、HMR処理中にエラーが発生した場合や、ルートインスタンスの変更でフルリロードが必要な場合に`console.warn`で開発者に通知する。

### 本通知の処理概要

この通知は、HMR処理中の2つの状況で開発者に警告を発する。1つはreload処理でルートインスタンスや手動マウントインスタンスが変更されフルリロードが必要な場合、もう1つはHMR操作（createRecord、rerender、reload）の実行中にエラーが発生した場合である。

**業務上の目的・背景**：HMRはViteなどの開発サーバーと連携して、コード変更時にページ全体をリロードせずにモジュールを差し替える機能である。しかし、ルートコンポーネントの変更やHMR処理自体のエラーなど、部分更新では対応できない状況が存在する。この警告は、開発者にフルリロードの必要性やHMRエラーの発生を知らせ、適切な対応を促す。

**通知の送信タイミング**：HMRのreload処理中にルート/手動マウントインスタンスの更新方法がない場合（親もcreateAppのreloadもない場合）と、tryWrapラッパー内でHMR操作がエラーを投げた場合に発火する。

**通知の受信者**：アプリケーション開発者。ブラウザの開発者コンソールを通じて確認する。

**通知内容の概要**：`[HMR]`プレフィックス付きの警告メッセージが出力される。フルリロード要求の場合は「Root or manually mounted instance modified. Full reload required.」、エラー発生時は「Something went wrong during Vue component hot-reload. Full reload required.」というメッセージが出力される。

**期待されるアクション**：開発者がブラウザの手動リロードを行うか、エラーの原因を調査・修正する。

## 通知種別

コンソール警告（`console.warn`）/ コンソールエラー（`console.error`）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期 |
| 優先度 | 中（開発時のみ動作） |
| リトライ | なし |

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

`console.warn`のみに出力する。`__DEV__`がtrueの場合のみHMRランタイム全体が初期化される。

## 通知テンプレート

### コンソール出力の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | N/A（コンソール出力） |
| 送信元名称 | `[HMR]` |
| 件名 | N/A |
| 形式 | テキスト |

### 本文テンプレート

パターン1: ルートインスタンス変更時
```
[HMR] Root or manually mounted instance modified. Full reload required.
```

パターン2: HMRエラー発生時
```
{エラーオブジェクト}                    ← console.error
[HMR] Something went wrong during Vue component hot-reload. Full reload required.  ← console.warn
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| N/A | N/A | N/A | コンソール出力のため添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| e | エラーオブジェクト（tryWrapの場合） | try-catchで捕捉 | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| HMR処理 | reload中のルートインスタンス更新不可 | 親インスタンスなし、appContext.reloadなし、かつブラウザ環境でない場合 | hmr.ts L167-170 |
| HMR処理 | createRecord/rerender/reload実行中のエラー | tryWrap内でcatch | hmr.ts L196-208 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| `__DEV__` がfalse | HMRランタイム全体が初期化されないため、警告も発生しない |
| instance.parent が存在 | 親コンポーネント経由で更新可能なため、ルート変更警告は発生しない |
| instance.appContext.reload が存在 | createApp()経由のルートインスタンスでreloadメソッドが利用可能 |
| typeof window !== 'undefined' | ブラウザ環境ではwindow.location.reload()が実行されるため警告は出ない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[HMR reload処理開始] --> B[各インスタンスの更新処理]
    B --> C{instance.ceReload?}
    C -->|Yes| D[カスタムエレメントのリロード]
    C -->|No| E{instance.parent?}
    E -->|Yes| F[親コンポーネントの再レンダリングをキュー]
    E -->|No| G{instance.appContext.reload?}
    G -->|Yes| H[アプリルートのリロード]
    G -->|No| I{typeof window !== 'undefined'?}
    I -->|Yes| J[window.location.reload]
    I -->|No| K["console.warn('[HMR] Root or manually mounted...')"]
    K --> L[終了]

    M[tryWrap: HMR操作実行] --> N{エラー発生?}
    N -->|Yes| O[console.error: エラーオブジェクト]
    O --> P["console.warn('[HMR] Something went wrong...')"]
    N -->|No| Q[正常終了]
    P --> L
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| N/A | N/A | データベースは使用しない |

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

本通知はデータベースを使用しない。以下のインメモリデータ構造を参照する。

| 参照データ | 用途 | 取得方法 |
|-----------|------|---------|
| map (Map<string, ...>) | HMRコンポーネント登録マップ | モジュールスコープのMap |
| instance.parent | 親コンポーネント参照 | ComponentInternalInstance |
| instance.appContext.reload | ルートリロードメソッド | appContextから取得 |
| hmrDirtyComponents | HMR更新対象のダーティマーキング | Map<ConcreteComponent, Set> |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| N/A | N/A | 警告自体はデータ更新を行わない |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| HMR操作エラー | createRecord/rerender/reload中の例外 | tryWrapでcatchし、console.error + console.warnで通知 |
| ルート更新不可 | 更新手段がない場合 | console.warnで通知（フルリロード要求） |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし。開発サーバー実行中に動作する。

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

- `__DEV__`がtrueの場合のみ動作するため、本番環境への影響なし
- HMRランタイム全体がツリーシェイキングにより本番ビルドから除去される

## 備考

- HMRランタイムは`__VUE_HMR_RUNTIME__`としてグローバルに公開される（vue-loader等のツールから利用される）
- `isHmrUpdating`フラグはHMR更新中であることを示し、スロットコンテンツの更新を強制する
- `hmrDirtyComponents`マップはコンポーネント定義のダーティマーキングに使用され、レンダラーがパッチ時にコンポーネントを置換する
- reload処理では、カスタムエレメント（`instance.ceReload`）の場合は専用のリロードパスが使用される
- `SchedulerJobFlags.DISPOSED`チェック（L100, L152）により、既にdisposeされたジョブの更新を防止する

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | hmr.ts | `packages/runtime-core/src/hmr.ts` | L17-20: hmrDirtyComponents Map（ダーティコンポーネント管理） |
| 1-2 | hmr.ts | `packages/runtime-core/src/hmr.ts` | L22-26: HMRRuntimeインターフェース（createRecord, rerender, reload） |
| 1-3 | hmr.ts | `packages/runtime-core/src/hmr.ts` | L41-50: mapデータ構造（id -> { initialDef, instances }） |

**読解のコツ**: HMRは`__hmrId`でコンポーネントを識別する。各コンポーネントIDに対してinitialDef（初期定義）とinstances（アクティブなインスタンスのSet）が管理される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | hmr.ts | `packages/runtime-core/src/hmr.ts` | L33-38: __VUE_HMR_RUNTIME__のグローバル登録。tryWrapでラップされたcreateRecord, rerender, reload |
| 2-2 | hmr.ts | `packages/runtime-core/src/hmr.ts` | L196-208: tryWrap関数。HMR操作のエラーハンドリングラッパー |

**主要処理フロー**:
1. **L33-38**: `__DEV__`条件下でHMRランタイムをグローバルに公開
2. **L197**: HMR操作（fn）を実行
3. **L200**: エラー発生時、`console.error(e)`でエラー本体を出力
4. **L202-205**: `console.warn`で`[HMR] Something went wrong...`メッセージを出力

#### Step 3: reload処理のフルリロード警告を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | hmr.ts | `packages/runtime-core/src/hmr.ts` | L107-182: reload関数全体。コンポーネント定義の更新、ダーティマーキング、インスタンス更新の流れ |
| 3-2 | hmr.ts | `packages/runtime-core/src/hmr.ts` | L140-170: インスタンス更新の分岐処理。ceReload、parent、appContext.reload、window.location.reload、console.warn |

**主要処理フロー**:
- **L140-143**: カスタムエレメントの場合は`ceReload`を使用
- **L145-159**: 親コンポーネントがある場合は`queueJob`で親の再レンダリングをキュー
- **L160-162**: createApp()のルートインスタンスは`appContext.reload()`を使用
- **L163-165**: ブラウザ環境では`window.location.reload()`で強制リロード
- **L166-170**: 上記いずれにも該当しない場合（非ブラウザ環境のルート）、`console.warn`で警告

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

```
__VUE_HMR_RUNTIME__              [hmr.ts:34]
    |
    +-- createRecord = tryWrap(createRecord)  [hmr.ts:35]
    +-- rerender = tryWrap(rerender)          [hmr.ts:36]
    +-- reload = tryWrap(reload)              [hmr.ts:37]
            |
            +-- reload(id, newComp)           [hmr.ts:107]
            |       |
            |       +-- updateComponentDef()  [hmr.ts:184]
            |       +-- queueJob()            [hmr.ts:149]
            |       +-- appContext.reload()    [hmr.ts:161]
            |       +-- window.location.reload() [hmr.ts:164]
            |       +-- console.warn()        [hmr.ts:167] ← 警告1
            |
            +-- tryWrap(fn)                   [hmr.ts:196]
                    |
                    +-- catch(e)
                        +-- console.error(e)  [hmr.ts:201]
                        +-- console.warn()    [hmr.ts:202] ← 警告2
```

### データフロー図

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

id (string)           +-- reload()                      +-- console.warn()
  |                   |     |                           |     "[HMR] Root or..."
newComp (HMRComponent)|     +-- インスタンス走査         |
  +----------------->|     +-- 更新分岐処理        ---->+-- window.location.reload()
                      |     |                           |     (ブラウザ環境)
                      +--   +-- tryWrap()               +-- console.error() + warn()
                                  |                           "[HMR] Something went..."
                                  +-- catch(e)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| hmr.ts | `packages/runtime-core/src/hmr.ts` | ソース | HMRランタイム全体（registerHMR、unregisterHMR、createRecord、rerender、reload、tryWrap） |
| scheduler.ts | `packages/runtime-core/src/scheduler.ts` | ソース | queueJob、queuePostFlushCb（更新のスケジューリング） |
| component.ts | `packages/runtime-core/src/component.ts` | ソース | ComponentInternalInstance型、isClassComponent判定 |
