# 機能設計書 47-v-ifディレクティブ変換

## 概要

本ドキュメントは、Vue 3テンプレートコンパイラにおけるv-if/v-else-if/v-elseディレクティブのAST変換処理について記述する。条件分岐ディレクティブを三項演算子の連鎖（ConditionalExpression）に変換し、条件付きレンダリングのcodegenNodeを生成する。

### 本機能の処理概要

`transformIf` は構造ディレクティブ変換として実装されており、v-if/v-else-if/v-elseの一連の条件分岐を検出し、IfNode ASTノードを生成する。各分岐はブランチノード（IfBranchNode）として管理され、最終的にJavaScriptの三項演算子の連鎖（`condition ? consequent : alternate`）としてcodegenNodeが生成される。

**業務上の目的・背景**：条件付き表示はUIにおける基本パターンであり、v-ifディレクティブはVueテンプレート内で要素の表示/非表示をデータに基づいて制御する中核機能である。コンパイラ段階で効率的なブロックツリーコードに変換し、ランタイムの差分検出（パッチ）コストを最小化する。

**機能の利用シーン**：テンプレートコンパイルパイプラインのtransformフェーズで、v-if/v-else-if/v-elseディレクティブを持つ要素ノードに対して自動的に実行される。

**主要な処理内容**：
1. v-if条件式の検証と変換
2. IfNode/IfBranchNodeの生成
3. 隣接するv-else-if/v-elseの検出とブランチ追加
4. 各ブランチのcodegenNode生成（ConditionalExpression）
5. キーの自動割り当てとブロック化
6. 重複キーの検出と警告

**関連システム・外部連携**：@vue/runtime-coreのFRAGMENT、CREATE_COMMENTヘルパー

**権限による制御**：権限制御は存在しない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 4 | Index（メイン画面） | 補助機能 | v-if/v-else-if/v-elseの条件分岐コンパイル結果をリアルタイム確認 |

## 機能種別

テンプレートコンパイル（ディレクティブ変換）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| node | ElementNode | Yes | v-if/v-else-if/v-elseを持つ要素ノード | なし |
| dir | DirectiveNode | Yes | 条件分岐ディレクティブ（name: 'if'\|'else-if'\|'else'） | v-if/v-else-ifはexp必須 |
| context | TransformContext | Yes | 変換コンテキスト | なし |

### 入力データソース

パーサーが生成したAST内のv-if/v-else-if/v-elseディレクティブ付き要素ノード。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| IfNode | IfNode | 条件分岐のASTノード（v-ifの場合、元のElementNodeを置換） |
| codegenNode | IfConditionalExpression | 三項演算子の連鎖 |
| branches | IfBranchNode[] | 各分岐のブランチノード配列 |

### 出力先

ASTツリー内で元のElementNodeがIfNodeに置換される。v-else-if/v-elseはIfNodeのbranchesに追加される。

## 処理フロー

### 処理シーケンス

```
1. 条件式の検証
   └─ v-elseでなく式が空の場合: X_V_IF_NO_EXPRESSION エラー → trueを代入
2. 式の変換（prefixIdentifiers時）
   └─ processExpression(dir.exp) で識別子プレフィックス付与
3. v-if の場合
   └─ IfBranchNode生成 → IfNode生成 → replaceNode
   └─ processCodegen → exit callback登録
4. v-else-if / v-else の場合
   └─ 隣接するv-ifノードを兄弟から探索
   └─ 間のコメント/空白ノードを除去
   └─ IfBranchNode生成 → IfNode.branchesに追加
   └─ traverseNode(branch) で分岐を手動走査
   └─ processCodegen → exit callback実行
5. exit callback（コード生成）
   └─ isRoot: ifNode.codegenNode = createCodegenNodeForBranch
   └─ !isRoot: parentCondition.alternate = createCodegenNodeForBranch
6. createCodegenNodeForBranch
   └─ condition有り: ConditionalExpression生成
   └─ condition無し(else): BlockCodegenNode直接返却
7. createChildrenCodegenNode
   └─ key注入 + ブロック化
```

### フローチャート

```mermaid
flowchart TD
    A[ディレクティブ検出] --> B{dir.name?}
    B -->|if| C[条件式検証]
    C --> D[IfBranchNode生成]
    D --> E[IfNode生成 + replaceNode]
    E --> F[exit callback登録]
    B -->|else-if/else| G[隣接v-if探索]
    G --> H{v-ifノード発見?}
    H -->|No| I[エラー: X_V_ELSE_NO_ADJACENT_IF]
    H -->|Yes| J[IfBranchNode生成]
    J --> K[branches.push]
    K --> L[traverseNode: 分岐走査]
    L --> M[exit callback実行]
    F --> N[codegenNode生成]
    M --> N
    N --> O{isRoot?}
    O -->|Yes| P[ifNode.codegenNode = ConditionalExpression]
    O -->|No| Q[parentCondition.alternate = ConditionalExpression]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-47-01 | 隣接ノード探索 | v-else-if/v-elseは直前の兄弟v-ifまたはv-else-ifと結合 | v-else-if/v-else検出時 |
| BR-47-02 | 自動キー割り当て | 各分岐にインクリメンタルなキー値を自動割り当て | 常時 |
| BR-47-03 | 重複キー検出 | 同一v-ifチェーン内で同じkeyが使われた場合に警告 | DEVモードまたは非ブラウザ時 |
| BR-47-04 | else分岐のコメント | v-elseの前のコメントノードはブランチのchildrenに含める | DEVモード時 |
| BR-47-05 | Transition除外 | Transition/transition内のv-if直子ではコメント含めない | Transition親の場合 |
| BR-47-06 | 空式のフォールバック | v-if/v-else-ifの式が空の場合、trueを代入してエラーを報告 | 式が空の場合 |

### 計算ロジック

キーの計算: 兄弟ノード中の先行するIfNode.branches.lengthの合計値に基づく。

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

データベース操作は存在しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| X_V_IF_NO_EXPRESSION | コンパイルエラー | v-if/v-else-ifの式が空の場合 | 条件式を指定 |
| X_V_ELSE_NO_ADJACENT_IF | コンパイルエラー | v-else/v-else-ifに隣接するv-ifがない場合 | v-ifを先行させる |
| X_V_IF_SAME_KEY | コンパイル警告 | 同一v-ifチェーン内でkeyが重複 | 異なるkeyを使用 |

### リトライ仕様

リトライ仕様は存在しない。

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

トランザクション処理は存在しない。

## パフォーマンス要件

- 各ブランチはブロック化（convertToBlock）されるため、パッチ時に効率的な差分検出が可能
- ForNodeが子要素の場合はフラグメントのネストを最適化

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

特別なセキュリティ考慮事項はない。

## 備考

- `createStructuralDirectiveTransform(/^(?:if|else|else-if)$/, ...)` で正規表現マッチング
- v-else分岐にはconditionが設定されない（undefined）
- `getParentCondition()` で三項演算子チェーンの末尾を探索してalternateを設定
- `isSameKey()` はAttributeNodeとDirectiveNodeの両方のkey比較に対応

---

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

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

### 推奨読解順序

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

IfNodeとIfBranchNodeの型定義を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ast.ts | `packages/compiler-core/src/ast.ts` | IfNode型: type, loc, branches |
| 1-2 | ast.ts | `packages/compiler-core/src/ast.ts` | IfBranchNode型: type, loc, condition, children, userKey, isTemplateIf |

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

transformIfの実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | vIf.ts | `packages/compiler-core/src/transforms/vIf.ts` | transformIf定義（44-82行目）: 正規表現による3つのディレクティブのマッチング |
| 2-2 | vIf.ts | `packages/compiler-core/src/transforms/vIf.ts` | processIf関数（85-205行目）: v-if/v-else-if/v-elseの処理分岐 |

**主要処理フロー**:
1. **44-45行目**: 正規表現 `/^(?:if|else|else-if)$/` でマッチ
2. **95-104行目**: 条件式の検証（空式の場合true代入+エラー）
3. **116-126行目**: v-ifの場合 - IfNode生成とreplaceNode
4. **127-204行目**: v-else-if/v-elseの場合 - 隣接v-if探索とブランチ追加
5. **63-79行目**: exit callback - codegenNodeの生成

#### Step 3: コード生成ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | vIf.ts | `packages/compiler-core/src/transforms/vIf.ts` | createCodegenNodeForBranch（219-238行目）: ConditionalExpressionの生成 |
| 3-2 | vIf.ts | `packages/compiler-core/src/transforms/vIf.ts` | createChildrenCodegenNode（240-304行目）: 子ブロック生成とkey注入 |
| 3-3 | vIf.ts | `packages/compiler-core/src/transforms/vIf.ts` | getParentCondition（335-349行目）: 三項演算子チェーンの末尾探索 |

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

```
transformIf (構造ディレクティブ変換)
    │
    └─ processIf(node, dir, context, processCodegen)
           │
           ├─ [v-if] createIfBranch → IfNode生成 → replaceNode
           │      └─ processCodegen(ifNode, branch, isRoot=true)
           │
           └─ [v-else-if/v-else] 隣接v-if探索
                  ├─ createIfBranch → branches.push
                  ├─ traverseNode(branch, context)
                  └─ processCodegen(sibling, branch, isRoot=false)

processCodegen (exit callback)
    │
    ├─ [isRoot] ifNode.codegenNode = createCodegenNodeForBranch
    │      └─ createConditionalExpression(condition, children, comment)
    │             └─ createChildrenCodegenNode(branch, keyIndex)
    │
    └─ [!isRoot] getParentCondition → alternate = createCodegenNodeForBranch
```

### データフロー図

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

v-if="cond1"              ──▶ IfNode                         ──▶ cond1
  <A/>                         branches[0]                        ? <A key=0/>
v-else-if="cond2"         ──▶ branches[1]                    ──▶ : cond2
  <B/>                                                            ? <B key=1/>
v-else                    ──▶ branches[2]                    ──▶ : <C key=2/>
  <C/>
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| vIf.ts | `packages/compiler-core/src/transforms/vIf.ts` | ソース | transformIf, processIf, createCodegenNodeForBranchの実装 |
| transform.ts | `packages/compiler-core/src/transform.ts` | ソース | createStructuralDirectiveTransformの定義 |
| compile.ts | `packages/compiler-core/src/compile.ts` | ソース | transformIfの登録 |
| ast.ts | `packages/compiler-core/src/ast.ts` | ソース | IfNode, IfBranchNode, ConditionalExpression型定義 |
| runtimeHelpers.ts | `packages/compiler-core/src/runtimeHelpers.ts` | ソース | FRAGMENT, CREATE_COMMENTシンボル |
| parser.ts | `packages/compiler-core/src/parser.ts` | ソース | cloneLoc関数 |
