# 機能設計書 68-JSX

## 概要

本ドキュメントは、CoffeeScriptのJSX機能の設計を記述する。JSXは、ReactなどのUIライブラリで使用されるXML風の構文をCoffeeScript内で使用可能にする言語機能である。

### 本機能の処理概要

CoffeeScriptのJSX機能は、ReactやPreactなどのUIライブラリで使用されるJSX構文をネイティブにサポートする。`<Component />`のようなタグ構文を認識し、適切なJavaScriptコード（通常は`React.createElement`呼び出し）に変換される形式で出力する。Babelなどのトランスパイラと組み合わせることで、完全なJSXサポートを実現する。

**業務上の目的・背景**：
ReactはUIライブラリのデファクトスタンダードとなっており、JSXはReact開発において広く使用されている。CoffeeScriptでもJSXをサポートすることで、既存のReactプロジェクトでCoffeeScriptを使用したり、CoffeeScriptの簡潔な構文とReactの宣言的UIを組み合わせることが可能になる。

**機能の利用シーン**：
- Reactコンポーネントの定義（例：`<div className="container">`）
- コンポーネント合成（例：`<Parent><Child /></Parent>`）
- 条件付きレンダリング（例：`{if condition then <A /> else <B />}`）
- リストレンダリング（例：`<ul>{items.map (item) -> <li>{item}</li>}</ul>`）
- フラグメント（例：`<></>` または `<React.Fragment>`）
- スプレッド属性（例：`<Component {...props} />`）

**主要な処理内容**：
1. `<TagName`の検出とJSX_TAGトークン生成
2. JSX属性（`key={value}`）の解析
3. 自己閉じタグ（`/>`）の処理
4. 子要素とテキストコンテンツの解析
5. 閉じタグ（`</TagName>`）のマッチング検証
6. 補間式（`{expression}`）の処理
7. フラグメント構文のサポート

**関連システム・外部連携**：
JSXはlexer.coffeeの`jsxToken`メソッドで解析され、JSXElementノードとしてASTに組み込まれる。出力されるJavaScriptはJSX構文を維持し、Babelの`@babel/plugin-transform-react-jsx`などで最終的なJavaScriptに変換される。

**権限による制御**：
本機能はコンパイル時の言語機能であり、権限による制御は適用されない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面関連なし（言語機能） |

## 機能種別

言語構文変換 / UIテンプレート

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| tagName | String | Yes | JSXタグ名（例：`div`、`Component`） | 識別子またはドット区切り |
| attributes | Array | No | JSX属性のリスト | - |
| children | Array | No | 子要素のリスト | - |
| selfClosing | Boolean | Yes | 自己閉じタグかどうか | - |

### 入力データソース

CoffeeScriptソースコード内のJSX構文

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| JavaScript | String | JSX構文を含むJavaScript |
| AST | Object | JSXElementノード |

### 出力先

JavaScriptコード（標準出力またはファイル）

## 処理フロー

### 処理シーケンス

```
1. JSXタグ開始の検出
   └─ Lexer.jsxToken()で`<`を検出
2. タグ名の解析
   └─ JSX_IDENTIFIERまたはJSX_FRAGMENT_IDENTIFIERでマッチ
3. 属性の解析
   └─ 3.1 属性名の解析（key）
   └─ 3.2 `=`の解析
   └─ 3.3 属性値の解析（文字列または`{expression}`）
4. タグ終了の解析
   └─ 4.1 `/>`の場合：自己閉じタグ
   └─ 4.2 `>`の場合：子要素の解析を開始
5. 子要素の解析
   └─ matchWithInterpolationsで子コンテンツを解析
6. 閉じタグの検証
   └─ タグ名が開きタグと一致することを確認
```

### フローチャート

```mermaid
flowchart TD
    A[<検出] --> B{JSXコンテキスト?}
    B -->|No| C[通常の比較演算子]
    B -->|Yes| D[タグ名解析]
    D --> E{Fragment?}
    E -->|Yes| F[Fragment処理]
    E -->|No| G[JSX_TAGトークン生成]
    F --> H[属性解析]
    G --> H
    H --> I{タグ終了}
    I -->|/>| J[自己閉じ]
    I -->|>| K[子要素解析]
    J --> L[JSXElementノード生成]
    K --> M[閉じタグ検出]
    M --> N{タグ名一致?}
    N -->|No| O[エラー: mismatched tag]
    N -->|Yes| L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-68-1 | タグ名マッチング | 開きタグと閉じタグの名前が一致する必要がある | 閉じタグ処理時 |
| BR-68-2 | コンテキスト判定 | `<`がJSXかどうかは前後のコンテキストで判定 | 前のトークンがCALLABLEでない等 |
| BR-68-3 | 属性値のカンマ | JSX属性間には暗黙のカンマが追加される | 属性解析時 |
| BR-68-4 | 子要素の隣接禁止 | 隣接するJSX要素は親でラップする必要がある | 複数のトップレベルJSX時 |
| BR-68-5 | ドット表記タグ | `<Foo.Bar />`のようなドット表記をサポート | タグ名にドットを含む場合 |

### 計算ロジック

- JSXコンテキスト判定: `@jsxDepth > 0` または適切なトークンの後
- タグ名解析: `JSX_IDENTIFIER.exec(@chunk[1...])`

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

### 操作別データベース影響一覧

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| SyntaxError | 構文エラー | expected corresponding JSX closing tag for X | 開きタグと閉じタグの名前を一致させる |
| SyntaxError | 構文エラー | missing closing > after tag name | タグを正しく閉じる |
| SyntaxError | 構文エラー | Adjacent JSX elements must be wrapped | フラグメントまたは親要素でラップ |

### リトライ仕様

コンパイル時エラーのため、リトライは適用されない。

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

本機能はデータベーストランザクションを使用しない。

## パフォーマンス要件

- コンパイル時間: JSX要素の数とネスト深さに比例
- Babelとの連携時: トランスパイル時間が追加で必要

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

- ユーザー入力をJSX内に直接埋め込む場合はXSSに注意
- `dangerouslySetInnerHTML`の使用は慎重に

## 備考

- CoffeeScriptのJSXは、Babelと組み合わせて使用することを想定
- `--jsx`オプションは不要（自動検出）
- Reactの新しいJSX Transform（React 17+）との互換性あり

---

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

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

### 推奨読解順序

#### Step 1: LexerのjsxToken処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | lexer.coffee | `src/lexer.coffee` | jsxTokenメソッド（626-741行目） |

**読解のコツ**:
- 626行目: `jsxToken`メソッドの開始
- 630-660行目: `<`の処理（タグ開始）
- 662-674行目: `/>`の処理（自己閉じ）
- 691-725行目: `>`の処理（タグ終了、子要素解析）

**主要処理フロー**:
1. **630行目**: `<`検出時にJSX_IDENTIFIERでマッチ
2. **645-647行目**: JSX_TAGトークン生成
3. **656-658行目**: CALL_START、`[`トークン生成（属性配列として表現）
4. **662-674行目**: `/>`で自己閉じタグを処理
5. **700-701行目**: `matchWithInterpolations`で子要素解析

#### Step 2: atJSXTagヘルパーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | lexer.coffee | `src/lexer.coffee` | atJSXTagメソッド（743-748行目） |

**読解のコツ**:
- `jsxDepth`でJSXのネスト深さを追跡
- `ends`スタックで`/>`タグを追跡

#### Step 3: 文法定義を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | grammar.coffee | `src/grammar.coffee` | Identifier規則内のJSX_TAG（184-189行目） |

**主要処理フロー**:
- **184-189行目**: JSX_TAGから`JSXTag`ノードを生成
- 位置データ（tagNameLocationData等）を保持

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

```
CoffeeScript.compile()
    │
    ├─ Lexer.tokenize()
    │      └─ jsxToken()
    │             ├─ JSX_TAG トークン生成
    │             ├─ 属性トークン生成
    │             ├─ matchWithInterpolations() [子要素]
    │             └─ 閉じタグ検証
    │
    ├─ parser.parse()
    │      └─ Identifier規則 → JSXTag ノード
    │             └─ 属性配列、子要素を含む
    │
    └─ compileNode()
           └─ JSX構文を維持して出力
```

### データフロー図

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

CoffeeScript           Lexer           Parser              Compiler
JSX構文            →  トークン列  →   JSXElement      →    JavaScript
                                    ノード               JSX構文

例:
<Button onClick={handleClick}>Click</Button>
    ↓
[JSX_TAG, IDENTIFIER, ':', '{', IDENTIFIER, '}', ..., CALL_END]
    ↓
JSXElement {
  tagName: JSXTag('Button'),
  attributes: [
    { key: 'onClick', value: IdentifierLiteral('handleClick') }
  ],
  children: [StringLiteral('Click')]
}
    ↓
<Button onClick={handleClick}>Click</Button>
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| lexer.coffee | `src/lexer.coffee` | ソース | jsxToken（626-741行目）、atJSXTag（743-748行目） |
| grammar.coffee | `src/grammar.coffee` | ソース | JSX_TAG規則（184-189行目） |
| nodes.coffee | `src/nodes.coffee` | ソース | JSXTag、JSXElementクラス |
| rewriter.coffee | `src/rewriter.coffee` | ソース | JSX属性の暗黙カンマ処理 |
