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

## 概要

本ドキュメントは、Vue 3テンプレートコンパイラにおけるv-onディレクティブのAST変換処理について記述する。compiler-coreの基盤変換とcompiler-domの修飾子処理の2層構成で、イベントリスナーコードを生成する。

### 本機能の処理概要

v-onディレクティブ変換は、テンプレート内の `@click="handler"` や `v-on:click="handler"` をイベントリスナーのプロパティに変換する。メンバー式、インライン文、関数式を適切にラップし、修飾子（.stop, .prevent, .enter等）をランタイムガード関数（withModifiers, withKeys）で処理する。

**業務上の目的・背景**：イベントハンドリングはインタラクティブなUIの基盤であり、v-onディレクティブはVueテンプレート内でイベントリスナーを宣言的に記述する中核機能である。コンパイル時に最適なイベントハンドラコードを生成し、修飾子をランタイムガードに変換する。

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

**主要な処理内容**：
1. イベント名の解決（静的/動的引数、toHandlerKey変換）
2. ハンドラ式の種別判定（メンバー式、インライン文、関数式）
3. インライン文の関数式ラップ（`$event => (expr)` 形式）
4. ハンドラキャッシュの判定と適用
5. 修飾子の分類（keyModifiers, nonKeyModifiers, eventOptionModifiers）
6. withModifiers() / withKeys() ランタイムガードの注入
7. イベントオプション修飾子（.passive, .capture, .once）のイベント名サフィックス化

**関連システム・外部連携**：@vue/runtime-domのwithModifiers(), withKeys()

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 4 | Index（メイン画面） | 補助機能 | v-onイベントリスナーのコンパイル結果をリアルタイム確認 |

## 機能種別

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

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| dir | DirectiveNode (VOnDirectiveNode) | Yes | v-onディレクティブ（argは必須） | expとmodifiersの少なくとも一方が必要 |
| node | ElementNode | Yes | v-onを持つ要素ノード | なし |
| context | TransformContext | Yes | 変換コンテキスト | なし |
| augmentor | function | No | プラットフォーム固有の拡張関数（compiler-dom用） | なし |

### 入力データソース

パーサーが生成したAST内のv-onディレクティブ付き要素ノード。argなしのv-onは別途transformElementで処理される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| props | Property[] | イベント名キーとハンドラ値のペア |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. compiler-core: transformOn (基盤変換)
   └─ 式と修飾子の検証
      └─ 両方なし: X_V_ON_NO_EXPRESSION エラー
   └─ イベント名の解決
      ├─ 静的arg: toHandlerKey(camelize(rawName)) → 例: "onClick"
      ├─ 動的arg: toHandlerKey(arg) → CompoundExpression
      └─ vue:プレフィックス: "vnode-{name}" に変換
   └─ ハンドラ式の処理
      ├─ メンバー式: そのまま使用（キャッシュ可能）
      ├─ インライン文: $event => (expr) でラップ
      ├─ 関数式: そのまま使用
      └─ 空式: () => {} をデフォルト
   └─ ハンドラキャッシュ判定
      ├─ cacheHandlers有効 & !inVOnce & 非定数 & 非コンポーネントメンバー式
      └─ スコープ変数非参照時にキャッシュ
   └─ キャッシュ可能メンバー式のインボケーション変換
      └─ handler && handler(...args) 形式に変換

2. compiler-dom: transformOn (DOM固有変換)
   └─ baseTransformをaugmentorとして呼び出し
   └─ 修飾子の分類
      ├─ eventOptionModifiers: passive, once, capture
      ├─ nonKeyModifiers: stop, prevent, self, ctrl, shift, alt, meta, exact, middle
      └─ keyModifiers: enter, space等のキー名、left, right（キーボードイベント時）
   └─ click修飾子の正規化
      ├─ .right → onContextmenu
      └─ .middle → onMouseup
   └─ ランタイムガード注入
      ├─ nonKeyModifiers → withModifiers(handler, modifiers)
      └─ keyModifiers → withKeys(handler, modifiers)
   └─ イベントオプションのサフィックス化
      └─ .passive.capture → onClickPassiveCapture
```

### フローチャート

```mermaid
flowchart TD
    A[v-on検出] --> B[compiler-core: transformOn]
    B --> C[イベント名解決]
    C --> D{ハンドラ式の種別?}
    D -->|メンバー式| E[そのまま使用]
    D -->|インライン文| F[$event => でラップ]
    D -->|関数式| G[そのまま使用]
    D -->|空| H[() => {} をデフォルト]
    E --> I{キャッシュ可能?}
    F --> I
    G --> I
    H --> I
    I -->|Yes| J[context.cache]
    I -->|No| K[そのまま]
    J --> L[compiler-dom: transformOn]
    K --> L
    L --> M{修飾子あり?}
    M -->|No| N[baseResult返却]
    M -->|Yes| O[修飾子分類]
    O --> P[withModifiers/withKeys注入]
    P --> Q[イベントオプションサフィックス化]
    Q --> R[結果返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-49-01 | イベント名変換 | 非要素・vnodeイベント・小文字名はcamelize+toHandlerKey | 静的arg時 |
| BR-49-02 | 大文字保持 | 通常要素の大文字含むイベント名は`on:{rawName}`形式で保持 | 要素タグ + 大文字含む名前時 |
| BR-49-03 | vue:プレフィックス | `vue:mounted`は`vnode-mounted`に変換 | vue:で始まる名前時 |
| BR-49-04 | vnodeフック警告 | `vnode`で始まるイベント名はDEVモードで警告 | DEV + vnode始まり |
| BR-49-05 | clickの正規化 | .right→onContextmenu, .middle→onMouseup | nonKeyModifiersにright/middle含む場合 |
| BR-49-06 | left/right分類 | キーボードイベントならkeyModifier、それ以外ならnonKeyModifier | イベントタイプに依存 |
| BR-49-07 | isHandlerKeyマーク | 生成されたpropsのkeyにisHandlerKey=trueを設定 | 常時 |

### 計算ロジック

修飾子の分類はイベント名とイベントタイプに基づいて決定される。`left`/`right`は`onkeyup`/`onkeydown`/`onkeypress`の場合のみkeyModifierとして扱われる。

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| X_V_ON_NO_EXPRESSION | コンパイルエラー | expもmodifiersも存在しない場合 | ハンドラ式を指定 |
| X_VNODE_HOOKS | コンパイルエラー（DEV） | vnodeで始まるイベント名を使用した場合 | vue:プレフィックスを使用 |

### リトライ仕様

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

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

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

## パフォーマンス要件

- ハンドラキャッシュにより、コンポーネントの不要な再レンダリングを防止
- メンバー式のキャッシュ時はインボケーション形式に変換して最新値アクセスを保証

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

- インライン文はテンプレートからのユーザー入力を含む可能性があるため、CSP環境での動作に注意

## 備考

- argなしのv-on（`v-on="handlers"`形式）はtransformElement.tsで処理される
- `shouldCache && isMemberExp` の場合、`handler && handler(...args)` 形式に変換される理由は、キャッシュされたハンドラが常に最新の値を参照するため
- compiler-domのtransformOnはcompiler-coreのtransformOnをaugmentorパターンで拡張している

---

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

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

### 推奨読解順序

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

VOnDirectiveNodeの型定義を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | vOn.ts (core) | `packages/compiler-core/src/transforms/vOn.ts` | VOnDirectiveNode型（19-27行目）: argが必須、expはSimpleExpressionNode |

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

compiler-coreのtransformOnを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | vOn.ts (core) | `packages/compiler-core/src/transforms/vOn.ts` | transformOn（29-180行目）: イベント名解決、ハンドラ処理、キャッシュ判定 |

**主要処理フロー**:
1. **36-38行目**: 式と修飾子の検証
2. **40-73行目**: イベント名の解決（静的/動的/vue:プレフィックス）
3. **76-81行目**: 空式の処理
4. **83-86行目**: ハンドラ式の種別判定（isMemberExp, isInlineStatement）
5. **89-127行目**: 式の変換とキャッシュ判定
6. **138-153行目**: インライン文のラップ
7. **170-175行目**: ハンドラキャッシュの適用
8. **178行目**: isHandlerKeyマーク

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

compiler-domのtransformOnを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | vOn.ts (dom) | `packages/compiler-dom/src/transforms/vOn.ts` | transformOn（108-154行目）: 修飾子処理のDOM固有実装 |
| 3-2 | vOn.ts (dom) | `packages/compiler-dom/src/transforms/vOn.ts` | resolveModifiers（33-90行目）: 修飾子の3種分類 |

**主要処理フロー**:
1. **109行目**: baseTransform(dir, node, context, baseResult => ...)でaugmentor呼び出し
2. **114-115行目**: resolveModifiersで修飾子を3種類に分類
3. **118-123行目**: right/middle clickの正規化
4. **125-130行目**: nonKeyModifiers → withModifiers()
5. **132-141行目**: keyModifiers → withKeys()
6. **143-148行目**: eventOptionModifiers → サフィックス化

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

```
compiler-dom transformOn(dir, node, context)
    │
    └─ compiler-core transformOn(dir, node, context, augmentor)
           │
           ├─ イベント名解決
           │      ├─ toHandlerKey(camelize(rawName))
           │      └─ [動的] TO_HANDLER_KEY ヘルパー
           │
           ├─ ハンドラ式処理
           │      ├─ isMemberExpression判定
           │      ├─ isFnExpression判定
           │      ├─ processExpression（prefixIdentifiers時）
           │      └─ $event => (expr) ラップ
           │
           ├─ キャッシュ判定 → context.cache
           │
           └─ augmentor(ret) → compiler-dom処理
                  │
                  ├─ resolveModifiers → 3種分類
                  │
                  ├─ transformClick（right/middle正規化）
                  │
                  ├─ withModifiers(handler, nonKeyModifiers)
                  │
                  ├─ withKeys(handler, keyModifiers)
                  │
                  └─ イベントオプションサフィックス化
```

### データフロー図

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

@click.stop.prevent="fn" ──▶ core: eventName = "onClick"
                               handler = fn
                          ──▶ dom: resolveModifiers
                               nonKeyModifiers: [stop, prevent]
                          ──▶ withModifiers(fn, ["stop","prevent"])
                                                             ──▶ { onClick: withModifiers(fn, ...) }
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| vOn.ts | `packages/compiler-core/src/transforms/vOn.ts` | ソース | 基盤transformOn（ターゲット非依存） |
| vOn.ts | `packages/compiler-dom/src/transforms/vOn.ts` | ソース | DOM固有transformOn（修飾子処理） |
| runtimeHelpers.ts | `packages/compiler-dom/src/runtimeHelpers.ts` | ソース | V_ON_WITH_MODIFIERS, V_ON_WITH_KEYS |
| runtimeHelpers.ts | `packages/compiler-core/src/runtimeHelpers.ts` | ソース | TO_HANDLER_KEY |
| compile.ts | `packages/compiler-core/src/compile.ts` | ソース | transformOnの登録 |
