# 通知設計書 22-JSX closing tag error

## 概要

本ドキュメントは、CoffeeScriptコンパイラにおけるJSX閉じタグの不一致エラー（JSX closing tag error）の通知仕様を記述する。

### 本通知の処理概要

本通知は、JSX構文を使用したCoffeeScriptソースコードにおいて、開きタグと閉じタグの名前が一致しない場合に発生するエラーメッセージである。

**業務上の目的・背景**：CoffeeScript 2.0以降、JSX（JavaScript XML）構文がサポートされており、Reactコンポーネントの記述が可能となっている。JSXでは、HTMLライクな構文で要素を定義するが、開きタグと閉じタグの名前は一致している必要がある。この検証を行うことで、不正なJSX構文を早期に検出し、実行時エラーを防止する。

**通知の送信タイミング**：コンパイル時、Lexerが JSXタグをトークン化する際に、閉じタグ（`</tagName>`）を検出したタイミングで、対応する開きタグとの名前一致を検証する。

**通知の受信者**：CoffeeScriptコンパイラを実行している開発者。コンソール出力またはエラーオブジェクトとして通知される。

**通知内容の概要**：エラーメッセージ「expected corresponding JSX closing tag for {tagName}」とともに、開きタグの位置情報が提供される。これにより開発者は、どの開きタグに対して閉じタグが不一致であるかを特定できる。

**期待されるアクション**：開発者は、閉じタグの名前を開きタグと一致するように修正するか、またはタグ構造を見直す必要がある。

## 通知種別

コンソールエラー出力 / SyntaxError例外

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（コンパイル処理中に即時発生） |
| 優先度 | 高（コンパイルエラー） |
| リトライ | なし（即時エラー終了） |

### 送信先決定ロジック

コンパイラを実行しているプロセスの標準エラー出力、またはAPIとして呼び出された場合はSyntaxError例外として呼び出し元に返される。

## 通知テンプレート

### コンソール出力の場合

| 項目 | 内容 |
|-----|------|
| 出力先 | stderr |
| 形式 | テキスト |

### 本文テンプレート

```
[ファイル名]:[行番号]:[列番号]: error: expected corresponding JSX closing tag for {tagName}
[該当行のソースコード]
[エラー位置を示すキャレット]
```

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| filename | ソースファイル名 | コンパイルオプション | Yes |
| tagName | 開きタグの名前 | jsxTag.name | Yes |
| line | エラー発生行番号（開きタグ位置） | jsxTag.origin.data.tagNameToken[2] | Yes |
| column | エラー発生列番号 | jsxTag.origin.data.tagNameToken[2] | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| コンパイル処理 | JSX閉じタグの解析 | 閉じタグ名 !== 開きタグ名 | 閉じタグの名前が対応する開きタグと一致しない場合 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| タグ名一致 | 開きタグと閉じタグの名前が一致している場合はエラーなし |
| 自己終了タグ | `<br />` のような自己終了タグは閉じタグ検証対象外 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[JSX開きタグをトークン化] --> B[jsxTagスタックにプッシュ]
    B --> C[JSX内部コンテンツ処理]
    C --> D[閉じタグ発見]
    D --> E{タグ名一致チェック}
    E -->|不一致| F[error関数呼び出し]
    E -->|一致| G[正常処理継続]
    F --> H[SyntaxError生成]
    H --> I[エラーメッセージ出力]
    I --> J[コンパイル中断]
    G --> K[endToken生成]
    K --> L[jsxDepth減算]
    L --> M[処理完了]
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（ファイルベースのコンパイル処理）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| SyntaxError | 閉じタグ名と開きタグ名の不一致 | 閉じタグ名を開きタグと同じ名前に修正 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 制限 | なし（コンパイル時エラー） |

### 配信時間帯

制限なし（コンパイル実行時に即時通知）

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

- エラーメッセージにはJSXタグ名が含まれる
- コンポーネント名がセキュリティ上の意味を持つ場合は、ログ管理に注意

## 備考

- JSXサポートはCoffeeScript 2.0で導入された
- JSXフラグメント（`<></>`）も同様の検証対象となる
- ネストしたJSX要素では、最も内側から順に検証される

---

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

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

### 推奨読解順序

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

JSXタグ情報を保持するスタック構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | lexer.coffee | `src/lexer.coffee` | jsxTagスタックの構造（jsxTag.name, jsxTag.properties, jsxTag.origin） |

**読解のコツ**: Lexer内でJSXタグは階層構造で管理され、開きタグ発見時にスタックにプッシュされ、閉じタグ発見時にポップされる。

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

JSXトークン化処理のエントリーポイントを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | lexer.coffee | `src/lexer.coffee` | jsxToken関数（JSXトークン化のメイン処理） |

**主要処理フロー**:
1. **開きタグ検出**: `<tagName>` パターンをマッチング
2. **jsxTagスタック管理**: 開きタグ情報をスタックにプッシュ
3. **閉じタグ検出**: `</tagName>` パターンをマッチング

#### Step 3: エラー検出処理を理解する

閉じタグ検出時のタグ名検証ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | lexer.coffee | `src/lexer.coffee` | 703-706行目 - タグ名一致チェックとエラー呼び出し |

**主要処理フロー**:
- **703-704行目**: JSX_IDENTIFIERでマッチした閉じタグ名と、スタック内の開きタグ名を比較
- **704行目**: プロパティ（ドット区切り）も含めた完全一致を検証
- **705-706行目**: 不一致時にerror関数を呼び出し、開きタグの位置情報を渡す

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

```
CoffeeScript.compile()
    │
    ├─ Lexer.tokenize()
    │      └─ jsxToken()
    │             ├─ 開きタグ検出 → jsxTagスタックにpush
    │             │
    │             ├─ matchWithInterpolations() - コンテンツ処理
    │             │
    │             └─ 閉じタグ検出
    │                    ├─ JSX_IDENTIFIER.exec() - タグ名取得
    │                    ├─ タグ名比較
    │                    │      └─ 不一致 → @error() → throwSyntaxError()
    │                    └─ 一致 → endToken生成
    │
    └─ エラー時: SyntaxError例外をスロー
```

### データフロー図

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

<div>...</span>       JSX_IDENTIFIER.exec()           エラーメッセージ
     │                       │                        "expected corresponding
     ▼                       ▼                         JSX closing tag for div"
開きタグ: div         match[1] = "span"
閉じタグ: span               │
     │                       ▼
     │               jsxTag.name = "div"
     │               "span" !== "div"
     │                       │
     └───────────────────────┴──▶ @error()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| lexer.coffee | `src/lexer.coffee` | ソース | JSXトークン化、タグ名検証 |
| helpers.coffee | `src/helpers.coffee` | ソース | throwSyntaxError関数 |
| error_messages.coffee | `test/error_messages.coffee` | テスト | エラーメッセージのテストケース（1613行目） |
