# 帳票設計書 15-コードフレーム

## 概要

本ドキュメントは、Vue.jsの共有ユーティリティパッケージ `@vue/shared` に実装されている `generateCodeFrame` 関数の出力仕様を定義する帳票設計書である。この関数は、ソースコード中のエラー箇所を行番号とキャレット（^）で視覚的に示す文字列（コードフレーム）を生成する。コンパイルエラーやランタイムワーニングの位置表示に広く使用される。

### 本帳票の処理概要

**業務上の目的・背景**：Vueのテンプレートコンパイラやスクリプトコンパイラがエラーを検出した際、エラー箇所をユーザーに分かりやすく伝えることは開発体験（DX）の向上において極めて重要である。`generateCodeFrame` は、エラーが発生したソースコードの該当行とその前後のコンテキスト行を行番号付きで表示し、エラー箇所をキャレット（^）で下線表示する文字列を生成する。これにより、開発者はエラーメッセージだけでなく、視覚的にエラー位置を即座に特定できる。

**帳票の利用シーン**：(1) テンプレートコンパイル時のエラー表示（compiler-sfc/compileTemplate.ts 265行目）、(2) スクリプトコンパイル時のエラー表示（compiler-sfc/compileScript.ts 968行目）、(3) SSRコンパイル時のエラー表示（server-renderer/helpers/ssrCompile.ts 75行目）、(4) ランタイムワーニング時のエラー位置表示（vue/src/index.ts 82行目）、(5) SFCスクリプトコンテキストのエラー表示（compiler-sfc/script/context.ts 117行目、160行目）。

**主要な出力内容**：
1. エラー箇所の前後2行（range = 2）を含むコンテキスト行（行番号付き）
2. エラー箇所の行（行番号付き）
3. エラー箇所を示すキャレット行（^の連続で範囲を表示）
4. エラーが複数行にまたがる場合は後続行にもキャレット行を表示

**帳票の出力タイミング**：コンパイラやランタイムがエラーまたはワーニングを検出した際に、エラーメッセージの補足情報として `generateCodeFrame` が呼び出される。ソースコード文字列とエラー箇所の開始/終了オフセットを受け取り、整形済みのコードフレーム文字列を返す。

**帳票の利用者**：Vueアプリケーション開発者（コンソールに表示されるエラーメッセージの読者）、Vueコンパイラ内部の各モジュール（エラーメッセージ生成時に呼び出す）、IDE/エディタプラグイン開発者。

## 帳票種別

テキスト出力（エラー位置表示用のコードフレーム文字列）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| N/A | コンソール出力（ブラウザ/Node.js） | N/A | コンパイルエラーまたはランタイムワーニング発生時に自動出力 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | プレーンテキスト文字列（改行区切り） |
| 用紙サイズ | N/A（コンソール出力） |
| 向き | N/A |
| ファイル名 | N/A（文字列として返却） |
| 出力方法 | 関数の戻り値として文字列を返却（呼び出し元がconsole.warnやerror.messageに組み込む） |
| 文字コード | UTF-8 |

### PDF固有設定

N/A

### Excel固有設定

N/A

## 帳票レイアウト

### レイアウト概要

コードフレームは以下の構造で生成される。エラー箇所の行を中心に前後2行のコンテキストが表示され、エラー箇所はキャレット（^）で示される。

```
┌─────────────────────────────────────┐
│  行番号|  コンテキスト行（前方）       │  ← range行分
│  行番号|  コンテキスト行（前方）       │
│  行番号|  エラー行                    │  ← start位置を含む行
│       |  ^^^^^^^^                   │  ← キャレット行（エラー範囲表示）
│  行番号|  コンテキスト行（後方）       │  ← range行分
│  行番号|  コンテキスト行（後方）       │
└─────────────────────────────────────┘
```

出力例（`key="one"` がエラー箇所の場合）:
```
1|  <div>
2|    <template key="one"></template>
 |               ^^^^^^^^^
3|    <ul>
4|      <li v-for="foobar">hi</li>
```

### ヘッダー部

N/A（コードフレームにはヘッダー部の概念はない）

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | 行番号 | ソースコードの行番号（1始まり） | 行インデックス + 1 | 数値 + パディング | 最大3桁 |
| 2 | 区切り文字 | 行番号とコード行の区切り | 固定値 "\|  " | 文字列 | 固定 |
| 3 | コード行 | ソースコードの該当行 | source.split(改行) | 文字列 | ソース依存 |
| 4 | キャレット行 | エラー箇所を示す "^" の連続 | start/endオフセットから算出 | "   \|  " + スペース + "^"連続 | ソース依存 |

### フッター部

N/A

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| source | ソースコード文字列 | Yes |
| start | エラー範囲の開始オフセット（0ベース、デフォルト: 0） | No（デフォルト値あり） |
| end | エラー範囲の終了オフセット（0ベース、デフォルト: source.length） | No（デフォルト値あり） |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | ソースコードの行番号順 | 昇順 |

### 改ページ条件

N/A（単一の文字列出力）

## データベース参照仕様

### 参照テーブル一覧

N/A（データベースアクセスは行わない）

### テーブル別参照項目詳細

N/A

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| range（前後行数） | 固定値 2 | N/A | codeframe.ts 1行目で定数定義 |
| start/end境界処理 | Math.max(0, Math.min(value, source.length)) | N/A | 不正な値（負数、source.length超過）への防御（9-10行目） |
| start > end判定 | start > endの場合は空文字列を返却 | N/A | 12行目 |
| 行分割 | source.split(/(\r?\n)/) で行と改行シーケンスを分離 | N/A | \r\nとの改行の両方に対応（18行目） |
| パディング算出 | ' '.repeat(Math.max(3 - String(line).length, 0)) | N/A | 行番号を3桁右寄せ（35行目） |
| キャレット長計算 | Math.max(1, end > count ? lineLength - pad : end - start) | N/A | エラー範囲が行末を超える場合の処理（46-49行目） |
| 複数行キャレット | end > countの場合、後続行にもキャレットを表示 | N/A | 51-56行目 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[generateCodeFrame呼び出し] --> B[start/endの境界チェック]
    B --> C{start > end?}
    C -->|Yes| D[空文字列を返却]
    C -->|No| E[ソースコードを行分割]
    E --> F[改行シーケンスを分離]
    F --> G[各行をループ]
    G --> H{現在のcountがstart以上?}
    H -->|No| I[count += 行長 + 改行長]
    I --> G
    H -->|Yes| J[前後range行のウィンドウを生成]
    J --> K[エラー行の行番号 + コード行をpush]
    K --> L[キャレット行を計算してpush]
    L --> M{end > count かつ次の行あり?}
    M -->|Yes| N[後続行の行番号 + コード行をpush]
    N --> O[後続行のキャレットをpush]
    O --> M
    M -->|No| P[結果を改行で結合して返却]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| 不正なstart/end | startまたはendが負数やsource.lengthを超過 | なし（空文字列または範囲クランプで対応） | Math.max/Math.minで範囲内にクランプ（9-10行目） |
| start > end | startがendより大きい | なし（空文字列を返却） | 空文字列を返却（12行目） |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 通常はSFCファイル1件分のソースコード（数十〜数百行） |
| 目標出力時間 | エラー表示用であり即座に完了（数ミリ秒以内） |
| 同時出力数上限 | エラー箇所数に依存（通常は少数） |

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

- コードフレームにはソースコードの一部が含まれるため、プロダクション環境でのエラーメッセージにはソースコードが露出する可能性がある
- プロダクションビルドでは通常、コンパイルエラーは発生しないため実質的なリスクは低い

## 備考

- range定数は2に固定されており（codeframe.ts 1行目）、エラー行の前後2行がコンテキストとして表示される
- Windows（\r\n）とUnix（\n）の両方の改行シーケンスに対応している（18行目の正規表現 `/(\r?\n)/`）
- 行番号は最大3桁のパディングが適用される（35行目）
- `generateCodeFrame` は `@vue/shared` パッケージで定義され、`@vue/compiler-core`（72行目）、`@vue/compiler-sfc`（35行目）、`vue`（19行目）の各パッケージから再エクスポートされている
- テストケースはshared/__tests__/codeframe.spec.tsに定義されており、行頭付近、中間行、行末付近、複数行ハイライト、不正入力値、Windows/Unix改行のケースがカバーされている

---

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

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

### 推奨読解順序

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

generateCodeFrame関数の入出力を理解する。入力はプリミティブ型（string, number）、出力もstring型である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | codeframe.ts | `packages/shared/src/codeframe.ts` | generateCodeFrame関数シグネチャ（3-7行目）。source: string, start: number = 0, end: number = source.length を受け取りstringを返す |
| 1-2 | codeframe.ts | `packages/shared/src/codeframe.ts` | range定数（1行目）。固定値2で前後2行のコンテキストを表示 |

**読解のコツ**: この関数は純粋関数であり、外部状態に依存しない。入力のソースコード文字列とオフセット位置から、整形済みのコードフレーム文字列を生成するだけのユーティリティ関数である。

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

関数本体のアルゴリズムを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | codeframe.ts | `packages/shared/src/codeframe.ts` | 境界チェック（9-12行目）。start/endのクランプとstart > endの早期リターン |
| 2-2 | codeframe.ts | `packages/shared/src/codeframe.ts` | 行分割と改行シーケンス分離（18-22行目）。split(/(\r?\n)/)でキャプチャグループを使い、行と改行を交互に取得 |
| 2-3 | codeframe.ts | `packages/shared/src/codeframe.ts` | メインループ（26-62行目）。各行のオフセットを累積し、startを超えた時点でウィンドウ表示を開始 |

**主要処理フロー**:
1. **9-10行目**: start/endをMath.max/Math.minで[0, source.length]にクランプ
2. **12行目**: start > endなら空文字列を返却
3. **18行目**: source.split(/(\r?\n)/)で行と改行シーケンスに分割
4. **21-22行目**: filterで偶数インデックス（行）と奇数インデックス（改行）を分離
5. **26-62行目**: 各行を走査しながらオフセットを累積
6. **30行目**: countがstart以上になったらエラー行を特定
7. **31行目**: i - range から i + range（またはend > countの間）までのウィンドウを表示
8. **34-37行目**: 行番号を3桁パディングして行内容と結合
9. **43-50行目**: エラー行（j === i）の場合、padとlengthを計算してキャレット行を追加
10. **51-56行目**: 後続行（j > i）でend > countの場合、追加のキャレット行を表示

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

generateCodeFrameが各パッケージでどのように使用されているかを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | compileTemplate.ts | `packages/compiler-sfc/src/compileTemplate.ts` | 265行目でコンパイルエラーメッセージにコードフレームを付加 |
| 3-2 | compileScript.ts | `packages/compiler-sfc/src/compileScript.ts` | 968行目でスクリプトコンパイルエラーにコードフレームを付加 |
| 3-3 | context.ts | `packages/compiler-sfc/src/script/context.ts` | 117行目、160行目でSFCスクリプトコンテキストのエラーメッセージにコードフレームを付加 |
| 3-4 | ssrCompile.ts | `packages/server-renderer/src/helpers/ssrCompile.ts` | 75行目でSSRコンパイルエラーにコードフレームを付加 |
| 3-5 | index.ts | `packages/vue/src/index.ts` | 82行目でランタイムワーニングにコードフレームを付加 |

#### Step 4: テストケースを理解する

テストケースからエッジケースの挙動を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | codeframe.spec.ts | `packages/shared/__tests__/codeframe.spec.ts` | 行頭付近（14-18行目）、中間行（20-25行目）、行末付近（27-31行目）のテスト |
| 4-2 | codeframe.spec.ts | `packages/shared/__tests__/codeframe.spec.ts` | 複数行ハイライト（33-45行目）のテスト |
| 4-3 | codeframe.spec.ts | `packages/shared/__tests__/codeframe.spec.ts` | 不正入力値（47-51行目）：-Infinity, Infinity等 |
| 4-4 | codeframe.spec.ts | `packages/shared/__tests__/codeframe.spec.ts` | Windows/Unix改行（79-96行目）：\r\nと\nの両方のケース |

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

```
[各呼び出し元]
├─ compileTemplate.ts (265行目)
│      └─ generateCodeFrame(source, start, end)
├─ compileScript.ts (968行目)
│      └─ generateCodeFrame(source, start, end)
├─ context.ts (117行目, 160行目)
│      └─ generateCodeFrame(source, start, end)
├─ ssrCompile.ts (75行目)
│      └─ generateCodeFrame(source, start, end)
└─ vue/src/index.ts (82行目)
       └─ generateCodeFrame(source, start, end)

generateCodeFrame(source, start, end)
    │
    ├─ 境界チェック（Math.max/Math.min）
    ├─ source.split(/(\r?\n)/) → 行分割
    ├─ filter() → 行/改行分離
    ├─ ループ: オフセット累積
    │      ├─ エラー行特定
    │      ├─ 行番号 + コード行 push
    │      ├─ キャレット行計算 + push
    │      └─ 複数行キャレット処理
    └─ res.join('\n') → 結果文字列
```

### データフロー図

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

source（文字列） ──────▶ generateCodeFrame           ──▶ コードフレーム文字列
start（数値）   ──────▶   ├─ 境界チェック               "1|  <div>"
end（数値）     ──────▶   ├─ 行分割                    "2|    <template key="one">"
                         ├─ オフセット累積              " |               ^^^^^^^^^"
                         ├─ エラー行特定               "3|    <ul>"
                         ├─ 行番号付加                 "4|      <li>..."
                         ├─ キャレット行計算
                         └─ 結合・返却
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| codeframe.ts | `packages/shared/src/codeframe.ts` | ソース | generateCodeFrame関数の実装（全64行） |
| index.ts | `packages/shared/src/index.ts` | ソース | codeframeモジュールの再エクスポート（7行目） |
| index.ts | `packages/compiler-core/src/index.ts` | ソース | generateCodeFrameの再エクスポート（72行目） |
| index.ts | `packages/compiler-sfc/src/index.ts` | ソース | generateCodeFrameの再エクスポート（35行目） |
| index.ts | `packages/vue/src/index.ts` | ソース | generateCodeFrameの利用とエクスポート（19行目、82行目） |
| compileTemplate.ts | `packages/compiler-sfc/src/compileTemplate.ts` | ソース | テンプレートコンパイルエラー時の呼び出し元（265行目） |
| compileScript.ts | `packages/compiler-sfc/src/compileScript.ts` | ソース | スクリプトコンパイルエラー時の呼び出し元（968行目） |
| context.ts | `packages/compiler-sfc/src/script/context.ts` | ソース | SFCスクリプトコンテキストエラー時の呼び出し元（117行目、160行目） |
| ssrCompile.ts | `packages/server-renderer/src/helpers/ssrCompile.ts` | ソース | SSRコンパイルエラー時の呼び出し元（75行目） |
| codeframe.spec.ts | `packages/shared/__tests__/codeframe.spec.ts` | テスト | generateCodeFrameの単体テスト |
