# 機能設計書 20-babel-plugin-jest-hoist

## 概要

本ドキュメントは、Jestテスティングフレームワークにおける`babel-plugin-jest-hoist`パッケージの機能設計を記述する。このパッケージは、`jest.mock()`呼び出しをファイルの先頭にホイスティング（巻き上げ）するBabelプラグインである。

### 本機能の処理概要

**業務上の目的・背景**：JavaScriptのモジュールシステムでは、`import`文はファイルの先頭で実行される。しかし、`jest.mock()`呼び出しは通常コード内に記述されるため、インポートより後に評価されてしまう。このプラグインは、`jest.mock()`呼び出しをコード変換時に先頭へ移動させることで、モック設定がインポートより先に適用されるようにする。

**機能の利用シーン**：
- jest.mock()でモジュールをモック化する際
- jest.unmock()でモック解除する際
- jest.disableAutomock()/enableAutomock()使用時
- jest.deepUnmock()使用時

**主要な処理内容**：
1. `jest.mock()`、`jest.unmock()`等の呼び出し検出
2. モックファクトリ関数内の変数参照チェック
3. 許可された変数（mock接頭辞、ALLOWED_IDENTIFIERS）の検証
4. ホイスト可能な変数宣言の特定
5. 対象呼び出しのファイル先頭への移動
6. `_getJestObj()`ヘルパー関数の挿入

**関連システム・外部連携**：`babel-preset-jest`が内部的に使用、`@jest/globals`との連携

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | Mock Functionsページ | 参照画面 | jest.mock()のホイスティング動作説明 |
| 3 | Manual Mocksページ | 参照画面 | モック設定の仕組み説明 |

## 機能種別

Babelプラグイン / コード変換 / ホイスティング

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| babel | typeof @babel/core | Yes | Babelオブジェクト | Babel API |

### 入力データソース

Babelトランスパイル対象のソースコード

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| PluginObj | PluginObj | Babelプラグインオブジェクト |

### 出力先

Babel変換パイプライン

## 処理フロー

### 処理シーケンス

```
1. pre()フェーズ
   └─ jestObjGetterIdentifier初期化
2. visitor.ExpressionStatement
   └─ extractJestObjExprIfHoistable()で対象検出
3. jest呼び出しを_getJestObj()呼び出しに置換
4. hoistedJestGettersにマーク
5. post()フェーズ
   └─ BlockStatement走査
   └─ ホイスト対象をブロック先頭に移動
   └─ プログラム先頭に全ホイスト済み呼び出しを配置
```

### フローチャート

```mermaid
flowchart TD
    A[pre: 初期化] --> B[visitor: ExpressionStatement走査]
    B --> C{jest.mock等?}
    C -->|No| D[次の文へ]
    C -->|Yes| E[extractJestObjExprIfHoistable]
    E --> F{ホイスト可能?}
    F -->|No| G[エラースロー（禁止変数参照）]
    F -->|Yes| H[_getJestObj()呼び出しに置換]
    H --> I[hoistedJestGettersにマーク]
    I --> D

    J[post: ホイスティング実行] --> K[BlockStatementを走査]
    K --> L[マークされた呼び出しを収集]
    L --> M[ブロック先頭に移動]
    M --> N[プログラム先頭に配置]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 許可変数 | ALLOWED_IDENTIFIERS以外の変数はファクトリ内で使用不可 | jest.mock()ファクトリ |
| BR-02 | mock接頭辞 | `mock`で始まる変数名は許可 | 変数参照時 |
| BR-03 | 定数ホイスト | constでpure valueに初期化された変数はホイスト可能 | 変数参照時 |
| BR-04 | @jest/globalsインポート | @jest/globalsからのjestインポートは許可 | インポート時 |

### 計算ロジック

ホイスト可能判定: `FUNCTIONS[propertyName]?.(args) ?? false`

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| N/A | ReferenceError | 許可されていない変数参照 | mock接頭辞を使用するか変数を定義 |
| N/A | TypeError | ファクトリがインライン関数でない | インライン関数を使用 |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

WeakSetを使用してノード追跡し、メモリリークを防止。

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

特になし

## 備考

- istanbulのカバレッジ変数（__cov）は許可される
- @jest/globalsからのインポートは自動的にホイストされるため追加処理不要

---

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

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

### 推奨読解順序

#### Step 1: 定数と許可識別子を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `packages/babel-plugin-jest-hoist/src/index.ts` | ALLOWED_IDENTIFIERS, FUNCTIONS |

**主要処理フロー**:
- **25-27行目**: JEST_GLOBAL_NAME, JEST_GLOBALS_MODULE_NAME定数
- **29-31行目**: WeakSet定義（hoistedVariables, hoistedJestGetters, hoistedJestExpressions）
- **36-96行目**: ALLOWED_IDENTIFIERS - ファクトリ内で許可される識別子一覧
  - グローバルオブジェクト（Array, Object, Promise等）
  - Node.jsグローバル（require, module, __dirname等）
  - Jest固有（jest, expect）

#### Step 2: ホイスト判定ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/babel-plugin-jest-hoist/src/index.ts` | FUNCTIONS, extractJestObjExprIfHoistable |

**主要処理フロー**:
- **113-117行目**: FUNCTIONS.mock - mock呼び出しの検証
  - 引数1つの場合: StringLiteralチェック
  - 引数2-3の場合: ファクトリ関数とスコープ外変数チェック
- **118-203行目**: ファクトリ内変数参照の検証ロジック
  - IDVisitorでReferencedIdentifier収集
  - スコープチェーンを辿って変数バインディング検索
  - 許可リスト/mock接頭辞/カバレッジ変数のチェック
- **205-208行目**: FUNCTIONS.unmock, deepUnmock, disableAutomock, enableAutomock

#### Step 3: jest呼び出し検出を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.ts | `packages/babel-plugin-jest-hoist/src/index.ts` | isJestObject, extractJestObjExprIfHoistable |

**主要処理フロー**:
- **210-244行目**: isJestObject - jestオブジェクトかどうかの判定
  - グローバルjest
  - @jest/globalsからのインポート
  - JestGlobals.jest形式
- **251-301行目**: extractJestObjExprIfHoistable - ホイスト可能なjest呼び出しの抽出

#### Step 4: プラグイン本体を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.ts | `packages/babel-plugin-jest-hoist/src/index.ts` | jestHoist関数 |

**主要処理フロー**:
- **304-339行目**: pre() - _getJestObj()ヘルパー生成
  - jestObjGetterIdentifier生成
  - 関数テンプレートでヘルパー関数を構築
- **341-356行目**: visitor.ExpressionStatement - jest呼び出しの検出と置換
- **359-402行目**: post() - ホイスティング実行
  - BlockStatement走査
  - CallExpressionとVariableDeclaratorの収集
  - ブロック/プログラム先頭への移動

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

```
jestHoist (index.ts:304)
    │
    ├─ pre() (index.ts:321)
    │      └─ declareJestObjGetterIdentifier()
    │             └─ createJestObjectGetter (template)
    │
    ├─ visitor.ExpressionStatement (index.ts:342)
    │      │
    │      └─ extractJestObjExprIfHoistable (index.ts:251)
    │             │
    │             ├─ isJestObject (index.ts:210)
    │             │
    │             └─ FUNCTIONS[propertyName]() (index.ts:113)
    │                    │
    │                    └─ IDVisitor.ReferencedIdentifier
    │
    └─ post() (index.ts:359)
           │
           ├─ BlockStatement traverse
           │
           ├─ CallExpression収集 (hoistedJestGetters)
           │
           ├─ VariableDeclarator収集 (hoistedVariables)
           │
           └─ program.node.body.unshift()
```

### データフロー図

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

ソースコード ───────────▶ jestHoist() ────────────────▶ 変換後コード
                              │
pre()                         │
    │                         │
    └── _getJestObj()挿入     │

visitor                       │
    │                         │
    ├── jest.mock検出         │
    │                         │
    ├── ファクトリ検証        │
    │                         │
    └── _getJestObj()置換     │

post()                        │
    │                         │
    ├── ホイスト対象収集      │
    │                         │
    └── 先頭に移動            │
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/babel-plugin-jest-hoist/src/index.ts` | ソース | プラグイン本体実装 |
