# 機能設計書 35-HMRランタイム

## 概要

本ドキュメントは、webpackにおけるHot Module Replacement（HMR）ランタイムコードについて記述する。hot/配下のモジュール群が、開発時のモジュールホットリロード・差分適用・ログ出力のクライアントサイドランタイムコードを提供する。

### 本機能の処理概要

HMRランタイムは、ブラウザやNode.js環境で実行されるクライアントサイドのHMR処理ロジックを提供する。webpack-dev-serverやカスタム開発サーバーからのアップデート通知を受信し、変更モジュールのチェック・ダウンロード・適用を行う。

**業務上の目的・背景**：HMRプラグイン（No.34）がビルド時にホットアップデートファイルを生成するのに対し、HMRランタイムはクライアント側でそのアップデートを受け取り適用する役割を担う。複数の通知方式（EventSource、ポーリング、UNIXシグナル）に対応し、様々な開発環境での利用を可能にする。

**機能の利用シーン**：
- webpack-dev-serverとの組み合わせ（dev-server.js）でブラウザ開発時に使用
- Node.jsプロセスでのHMR（signal.js / poll.js）で使用
- ログレベル制御（log.js）で開発時の出力を管理

**主要な処理内容**：
1. webpack-dev-serverからのwebpackHotUpdateイベント受信（dev-server.js）
2. module.hot.check()によるアップデートチェックとダウンロード
3. module.hot.apply()によるモジュール差し替え適用
4. 適用結果のログ出力（log-apply-result.js）
5. エラー時のフルリロードフォールバック
6. ポーリングベースのアップデートチェック（poll.js）
7. UNIXシグナルベースのアップデートトリガー（signal.js）

**関連システム・外部連携**：webpack-dev-server（EventSource/WebSocket経由）、Node.jsプロセスシグナル（SIGUSR2等）、ブラウザのwindow.location.reload()

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | HMRランタイムは開発時のインフラ機能であり、特定の画面に直接関連しない |

## 機能種別

クライアントサイドランタイム / イベント駆動処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| __webpack_hash__ | string | Yes | 現在のビルドハッシュ（webpackグローバル） | ランタイム注入 |
| webpackHotUpdate event | CustomEvent | Yes | ホットアップデート通知イベント | event.detail.currentHash |
| __resourceQuery | string | No | poll.jsのポーリング間隔 / signal.jsのシグナル名 | クエリパラメータ |

### 入力データソース

- webpack-dev-serverからのEventSource/WebSocketイベント
- Node.jsプロセスシグナル（SIGUSR2等）
- ポーリングタイマー

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| コンソールログ | string | HMR処理の進捗・結果・エラーメッセージ |
| モジュール更新 | - | module.hot.apply()によるインメモリのモジュール差し替え |
| ページリロード | - | HMR適用失敗時のフルリロード |

### 出力先

ブラウザコンソール / Node.jsプロセス標準出力

## 処理フロー

### 処理シーケンス

```
1. アップデート通知受信
   ├─ dev-server.js: EventSourceのwebpackHotUpdateイベント
   ├─ poll.js: setInterval定期チェック
   └─ signal.js: process.onシグナル受信
2. アップデート状態チェック
   └─ lastHashとcurrentHashの比較
3. ホットアップデートチェック
   └─ module.hot.check(true) でアップデートをダウンロード
4. 適用結果の処理
   ├─ 成功: updatedModules一覧をログ出力
   └─ 失敗: エラーログ出力 → フルリロード（abort/failステータス時）
5. 追加アップデートチェック
   └─ upToDate()がfalseの場合、再帰的にcheck()を呼び出し
```

### フローチャート

```mermaid
flowchart TD
    A[アップデート通知受信] --> B{upToDate?}
    B -->|Yes| C[処理終了]
    B -->|No| D[module.hot.check]
    D --> E{updatedModules?}
    E -->|null| F{ブラウザ環境?}
    F -->|Yes| G[window.location.reload]
    F -->|No| H[手動リスタート要求]
    E -->|存在| I[log-apply-result]
    I --> J{upToDate?}
    J -->|No| D
    J -->|Yes| K[App is up to date]
    D --> L{エラー発生?}
    L -->|Yes| M{status: abort/fail?}
    M -->|Yes| G
    M -->|No| N[エラーログ出力]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | HMR有効チェック | module.hotがfalsy場合は即座にエラーをスロー | 各ランタイムモジュール起動時 |
| BR-02 | アイドル状態チェック | module.hot.status()が"idle"の場合のみアップデートチェックを開始 | dev-server.js, signal.js |
| BR-03 | フルリロードフォールバック | abort/failステータスの場合、ブラウザ環境ではwindow.location.reload() | check()エラー時 |
| BR-04 | ログレベル制御 | logLevel（info/warning/error）によりコンソール出力を制御 | log.js |
| BR-05 | ポーリング間隔 | __resourceQuery値（デフォルト10分=600000ms）でポーリング間隔を設定 | poll.js |
| BR-06 | シグナル名 | __resourceQuery値（デフォルトSIGUSR2）でリッスンするシグナルを設定 | signal.js |

### 計算ロジック

upToDate判定: lastHash.indexOf(__webpack_hash__) >= 0 で現在のビルドが最新かどうかを判定する。lastHashに現在のwebpackハッシュが含まれていればアップデート不要と判断する。

## データベース操作仕様

### 操作別データベース影響一覧

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Error | module.hotがundefined（HMR無効） | "[HMR] Hot Module Replacement is disabled."をスロー |
| - | ランタイムエラー | module.hot.check()失敗 | abort/failステータスならフルリロード、その他はエラーログ |
| - | ランタイムエラー | アップデートファイルが見つからない | "Cannot find update"警告とフルリロード案内 |

### リトライ仕様

poll.jsではsetIntervalにより定期的にリトライが行われる。dev-server.jsではサーバーからの次のアップデート通知まで待機する。

## トランザクション仕様

該当なし

## パフォーマンス要件

- EventSourceによるサーバーからのプッシュ通知で即時更新
- poll.jsのポーリング間隔は設定可能（デフォルト10分）

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

- HMRランタイムは開発環境専用であり、本番ビルドには含めないこと
- EventSource接続はHTTP/HTTPS上で行われ、接続先URLはビルド時に決定

## 備考

- emitter.jsはNode.jsのEventsモジュールをラップしたEventEmitterインスタンスを提供
- emitter-event-target.jsはブラウザのEventTarget APIに対応
- only-dev-server.jsはdev-server.jsの簡易版

---

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

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

### 推奨読解順序

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

HMRランタイムの通信メカニズムを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | emitter.js | `hot/emitter.js` | 1-2行目: EventEmitterインスタンスのエクスポート |
| 1-2 | log.js | `hot/log.js` | 1-78行目: ログレベル制御の全体構造（info/warning/error） |

**読解のコツ**: hot/配下のファイルはブラウザまたはNode.js上で直接実行されるランタイムコードである。webpackの__webpack_hash__や__resourceQuery等のマジックコメント変数がビルド時に置換される点に注意すること。

#### Step 2: メインのHMRクライアントを理解する

dev-server.jsがwebpack-dev-serverとの連携を行うメインクライアントである。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | dev-server.js | `hot/dev-server.js` | 6-90行目: module.hot有効チェック、check関数、EventSourceイベントリスナー |

**主要処理フロー**:
1. **6行目**: module.hotの有効チェック
2. **9-11行目**: lastHash変数とupToDate関数の定義
3. **13-62行目**: check関数 - module.hot.check(true)でアップデートをチェック・適用
4. **17-33行目**: updatedModulesがnullの場合のフルリロード処理
5. **45-61行目**: エラー時のabort/failステータスチェックとフルリロード
6. **65-75行目**: hotEmitterのwebpackHotUpdateイベントハンドラ
7. **77-84行目**: EventTarget/EventEmitterの分岐によるリスナー登録

#### Step 3: 代替通知方式を理解する

ポーリング方式とシグナル方式の代替クライアントを読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | poll.js | `hot/poll.js` | 6-41行目: setIntervalによる定期チェック方式 |
| 3-2 | signal.js | `hot/signal.js` | 6-66行目: UNIXシグナルによるチェック方式 |

**主要処理フロー**:
- **poll.js 8行目**: __resourceQueryからポーリング間隔を取得（デフォルト600000ms）
- **poll.js 38行目**: setInterval(checkForUpdate, hotPollInterval)
- **signal.js 52行目**: process.on(__resourceQuery.slice(1) || "SIGUSR2", ...)

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

```
dev-server.js (ブラウザ環境)
    |
    +-- hotEmitter (EventSource/EventEmitter)
    |      +-- webpackHotUpdateイベント受信
    |
    +-- check()
    |      +-- module.hot.check(true)
    |      |      └─ [HMRプラグインが生成したランタイム処理]
    |      +-- log-apply-result(updatedModules)
    |      +-- upToDate() → 追加チェック or 完了
    |
    +-- window.location.reload() [エラー時フォールバック]

poll.js (ポーリング環境)
    +-- setInterval(checkForUpdate, interval)
           +-- module.hot.check(true) → 同上

signal.js (Node.js環境)
    +-- process.on(signal, callback)
           +-- module.hot.check() / module.hot.apply()
```

### データフロー図

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

webpack-dev-server ──────> EventSource/WebSocket
  (webpackHotUpdate)          |
                              v
                         dev-server.js                ──> コンソールログ
                              |
                              +-> module.hot.check()
                              |      |
                              |      v
                              +-> module.hot.apply()  ──> モジュール差し替え
                              |
                              +-> エラー時             ──> window.location.reload()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| dev-server.js | `hot/dev-server.js` | ランタイム | webpack-dev-serverとの連携HMRクライアント |
| only-dev-server.js | `hot/only-dev-server.js` | ランタイム | dev-server.jsの簡易版 |
| poll.js | `hot/poll.js` | ランタイム | ポーリングベースのHMRクライアント |
| signal.js | `hot/signal.js` | ランタイム | UNIXシグナルベースのHMRクライアント |
| log.js | `hot/log.js` | ランタイム | HMRログレベル制御ユーティリティ |
| log-apply-result.js | `hot/log-apply-result.js` | ランタイム | HMR適用結果のログ出力 |
| emitter.js | `hot/emitter.js` | ランタイム | EventEmitterラッパー |
| emitter-event-target.js | `hot/emitter-event-target.js` | ランタイム | EventTargetベースのエミッター |
| load-http.js | `hot/load-http.js` | ランタイム | HTTPモジュール遅延読み込み |
