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

## 概要

本ドキュメントは、Vue 3テンプレートコンパイラにおけるv-modelディレクティブのAST変換処理について記述する。compiler-coreの基盤変換とcompiler-domのDOM要素固有変換の2層構成で、双方向バインディングコードを生成する。

### 本機能の処理概要

v-modelディレクティブ変換は、テンプレート内の `v-model` 構文を、`modelValue` プロパティと `onUpdate:modelValue` イベントハンドラの組み合わせに変換する。コンポーネントに対してはプロパティ+イベントのペアを、ネイティブHTML要素に対してはランタイムディレクティブ（vModelText, vModelCheckbox等）の参照を注入する。

**業務上の目的・背景**：フォーム要素やコンポーネントとの双方向データバインディングはVueの中核的なUI構築パターンであり、v-modelは宣言的な構文でこれを実現する。コンパイラ段階で適切なプロパティとイベントのコードに変換することで、ランタイムの処理を最小化する。

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

**主要な処理内容**：
1. 式の検証（空式、propsへのバインド、constへのバインド）
2. modelValue プロパティと onUpdate:modelValue イベントのペア生成
3. ref/maybeRefのアンラップ処理（SFC inline mode）
4. コンポーネントv-modelのmodifiers生成
5. DOM要素タイプ別ランタイムディレクティブの選択（Text/Checkbox/Radio/Select/Dynamic）

**関連システム・外部連携**：@vue/runtime-domのvModelText, vModelCheckbox, vModelRadio, vModelSelect, vModelDynamic

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 4 | Index（メイン画面） | 補助機能 | v-modelの双方向バインディングコンパイル結果をリアルタイム確認 |

## 機能種別

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

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| dir | DirectiveNode | Yes | v-modelディレクティブ | exp必須、各種バインディング検証 |
| node | ElementNode | Yes | v-modelを持つ要素ノード | なし |
| context | TransformContext | Yes | 変換コンテキスト | なし |

### 入力データソース

パーサーが生成したAST内のv-modelディレクティブ付き要素/コンポーネントノード。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| props | Property[] | modelValue + onUpdate:modelValue + modifiers |
| needRuntime | symbol \| undefined | DOM要素の場合、ランタイムディレクティブのヘルパーシンボル |

### 出力先

DirectiveTransformResultとして、transformElement処理に合流する。

## 処理フロー

### 処理シーケンス

```
1. compiler-core: transformModel (基盤変換)
   └─ 式の検証
      ├─ 式が空: X_V_MODEL_NO_EXPRESSION エラー
      ├─ propsバインディング: X_V_MODEL_ON_PROPS エラー
      ├─ constバインディング: X_V_MODEL_ON_CONST エラー
      └─ 不正な式: X_V_MODEL_MALFORMED_EXPRESSION エラー
   └─ propName / eventName の決定
      ├─ arg指定あり: arg → propName, `onUpdate:${arg}` → eventName
      └─ arg指定なし: 'modelValue' → propName, 'onUpdate:modelValue' → eventName
   └─ assignmentExpの生成
      ├─ SETUP_REF: (.value = $event) 形式
      ├─ SETUP_LET/MAYBE_REF: isRef()チェック付き代入
      └─ 通常: (exp = $event) 形式
   └─ props配列の組み立て
      ├─ modelValue: dir.exp
      ├─ onUpdate:modelValue: assignmentExp
      └─ modifiers（コンポーネントの場合）
   └─ ハンドラキャッシュ（prefixIdentifiers時）

2. compiler-dom: transformModel (DOM固有変換)
   └─ baseTransformの結果を取得
   └─ コンポーネントの場合: baseResultをそのまま返却
   └─ DOM要素の場合:
      ├─ arg指定チェック: X_V_MODEL_ARG_ON_ELEMENT エラー
      └─ 要素タイプ別ディレクティブ選択
         ├─ input[type=radio]: V_MODEL_RADIO
         ├─ input[type=checkbox]: V_MODEL_CHECKBOX
         ├─ input[type=file]: エラー（読み取り専用）
         ├─ input[type=dynamic]: V_MODEL_DYNAMIC
         ├─ textarea: V_MODEL_TEXT
         ├─ select: V_MODEL_SELECT
         └─ input[type=text等]: V_MODEL_TEXT
      └─ modelValueプロップの除去（ランタイムのbinding.valueに渡されるため）
```

### フローチャート

```mermaid
flowchart TD
    A[v-model検出] --> B[compiler-core: transformModel]
    B --> C{式の検証}
    C -->|エラー| D[エラー返却]
    C -->|OK| E[propName/eventName決定]
    E --> F[assignmentExp生成]
    F --> G[props配列組み立て]
    G --> H[compiler-dom: transformModel]
    H --> I{コンポーネント?}
    I -->|Yes| J[baseResultを返却]
    I -->|No| K{要素タイプ?}
    K -->|input| L{type属性?}
    L -->|radio| M[V_MODEL_RADIO]
    L -->|checkbox| N[V_MODEL_CHECKBOX]
    L -->|file| O[エラー]
    L -->|動的| P[V_MODEL_DYNAMIC]
    L -->|text等| Q[V_MODEL_TEXT]
    K -->|select| R[V_MODEL_SELECT]
    K -->|textarea| S[V_MODEL_TEXT]
    K -->|その他| T[エラー: X_V_MODEL_ON_INVALID_ELEMENT]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-48-01 | propsバインド禁止 | defineProps()で定義されたpropsにv-modelをバインドすることは禁止 | bindingType === PROPS時 |
| BR-48-02 | constバインド禁止 | constで定義された値にv-modelをバインドすることは禁止 | bindingType === LITERAL_CONST \|\| SETUP_CONST時 |
| BR-48-03 | ファイル入力禁止 | input[type=file]にv-modelを使用することは禁止（読み取り専用） | type=file時 |
| BR-48-04 | modelValue除去 | DOM要素のv-modelではmodelValueプロップをランタイム用に除去 | DOM要素時 |
| BR-48-05 | modifiers伝播 | コンポーネントv-modelではmodifiersをpropsとして伝播 | コンポーネント + modifiersあり時 |
| BR-48-06 | ハンドラキャッシュ | スコープ変数を参照しないハンドラはキャッシュ対象 | prefixIdentifiers && cacheHandlers時 |

### 計算ロジック

modifiersプロパティ名: arg有り → `${arg.content}Modifiers`、arg無し → `modelModifiers`

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| X_V_MODEL_NO_EXPRESSION | コンパイルエラー | v-model式が空の場合 | バインドする変数を指定 |
| X_V_MODEL_ON_PROPS | コンパイルエラー | propsにバインドした場合 | ローカル変数を使用 |
| X_V_MODEL_ON_CONST | コンパイルエラー | constにバインドした場合 | letまたはrefを使用 |
| X_V_MODEL_MALFORMED_EXPRESSION | コンパイルエラー | 不正な式の場合 | 有効なJS式を指定 |
| X_V_MODEL_ON_SCOPE_VARIABLE | コンパイルエラー | v-forスコープ変数にバインドした場合 | ローカル変数にコピー |
| X_V_MODEL_ARG_ON_ELEMENT | DOMエラー | DOM要素にv-model引数を指定した場合 | コンポーネントでのみ使用 |
| X_V_MODEL_ON_FILE_INPUT_ELEMENT | DOMエラー | file inputにバインドした場合 | file inputにはv-model不可 |
| X_V_MODEL_ON_INVALID_ELEMENT | DOMエラー | 非フォーム要素にバインドした場合 | input/textarea/selectで使用 |
| X_V_MODEL_UNNECESSARY_VALUE | DOM警告 | v-modelとv-bind:valueが共存 | v-bind:valueを除去 |

### リトライ仕様

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

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

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

## パフォーマンス要件

- ハンドラキャッシュにより、不要な再レンダリングを防止
- ランタイムディレクティブの選択はコンパイル時に決定されるため、ランタイムのオーバーヘッドは最小

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

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

## 備考

- compiler-coreのtransformModelはターゲット非依存の基盤変換
- compiler-domのtransformModelはDOM固有の拡張（ランタイムディレクティブ選択）
- `V_MODEL_DYNAMIC` は`:type`が動的バインディングの場合に使用され、ランタイムでtype判定を行う
- `checkDuplicatedValue()` はv-modelとv-bind:valueの共存をDEVモードで警告する

---

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

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

### 推奨読解順序

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

DirectiveTransformResultの構造を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | transform.ts | `packages/compiler-core/src/transform.ts` | DirectiveTransformResult: props, needRuntime, ssrTagParts |

#### Step 2: 基盤変換を理解する

compiler-coreのtransformModelを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | vModel.ts (core) | `packages/compiler-core/src/transforms/vModel.ts` | transformModel（23-165行目）: 式検証、プロパティ生成、ハンドラ生成 |

**主要処理フロー**:
1. **25-30行目**: exp空チェック
2. **43-58行目**: propsバインディング、constバインディングのチェック
3. **86-91行目**: propName/eventNameの決定
4. **95-120行目**: assignmentExpの生成（ref/maybeRef/通常の分岐）
5. **122-127行目**: propsの組み立て（modelValue + onUpdate:modelValue）
6. **141-162行目**: コンポーネントmodifiersの生成

#### Step 3: DOM固有変換を理解する

compiler-domのtransformModelを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | vModel.ts (dom) | `packages/compiler-dom/src/transforms/vModel.ts` | transformModel（20-127行目）: DOM要素タイプ別のディレクティブ選択 |

**主要処理フロー**:
1. **21行目**: baseTransform(dir, node, context)で基盤結果取得
2. **23-25行目**: コンポーネントの場合はbaseResultを返却
3. **48-100行目**: input/textarea/selectのタイプ別分岐
4. **104-106行目**: needRuntimeにランタイムディレクティブシンボルを設定
5. **118-124行目**: modelValueプロップの除去

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

```
compiler-dom transformModel(dir, node, context)
    │
    ├─ compiler-core transformModel(dir, node, context) [baseTransform]
    │      ├─ 式検証 (5種類のエラーチェック)
    │      ├─ propName/eventName決定
    │      ├─ assignmentExp生成
    │      ├─ props配列組み立て
    │      └─ modifiers生成 (コンポーネント時)
    │
    ├─ [コンポーネント] baseResult返却
    │
    └─ [DOM要素] 要素タイプ別処理
           ├─ input[type判定]
           │      ├─ radio → V_MODEL_RADIO
           │      ├─ checkbox → V_MODEL_CHECKBOX
           │      ├─ file → エラー
           │      ├─ 動的type → V_MODEL_DYNAMIC
           │      └─ text等 → V_MODEL_TEXT
           ├─ select → V_MODEL_SELECT
           ├─ textarea → V_MODEL_TEXT
           └─ modelValueプロップ除去
```

### データフロー図

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

v-model="msg"            ──▶ compiler-core transformModel
                               │
                               ├─ modelValue: msg          ──▶ Property
                               ├─ onUpdate:modelValue:     ──▶ Property
                               │   ($event) => (msg = $event)
                               │
                          ──▶ compiler-dom transformModel
                               │
                               └─ needRuntime: V_MODEL_TEXT ──▶ DirectiveTransformResult
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| vModel.ts | `packages/compiler-core/src/transforms/vModel.ts` | ソース | 基盤transformModel（ターゲット非依存） |
| vModel.ts | `packages/compiler-dom/src/transforms/vModel.ts` | ソース | DOM固有transformModel（ランタイムディレクティブ選択） |
| runtimeHelpers.ts | `packages/compiler-dom/src/runtimeHelpers.ts` | ソース | V_MODEL_TEXT等のシンボル定義 |
| compile.ts | `packages/compiler-core/src/compile.ts` | ソース | transformModelの登録 |
| options.ts | `packages/compiler-core/src/options.ts` | ソース | BindingTypes定義 |
