# 機能設計書 19-バリアントパーサー（parseVariant）

## 概要

本ドキュメントは、バリアント文字列を解析しVariant構造体に変換するparseVariant関数の設計について記述する。static・functional・compound・arbitraryの各種バリアントに対応する。

### 本機能の処理概要

parseVariant関数は、`hover`、`@media(width>=768px)`、`group-hover`、`[&_p]` のようなバリアント文字列を受け取り、構造化されたVariantオブジェクトに変換する。

**業務上の目的・背景**：Tailwind CSSのバリアントシステムでは、ユーティリティクラスにプレフィックスとして付与されるバリアント文字列を解析し、その種類（static / functional / compound / arbitrary）を判定して適切な処理ルートに振り分ける必要がある。parseVariant はこのバリアント解析の中核であり、parseCandidate から呼び出されるサブパーサーである。

**機能の利用シーン**：parseCandidate内でバリアント部分を解析する際、`@variant` ディレクティブのパラメータを解析する際、IntelliSenseでバリアントの構造を解析する際に使用される。

**主要な処理内容**：
1. arbitrary バリアント `[selector]` の検出と処理
2. static バリアント（値なし、修飾子なし）の検出
3. functional バリアント（値あり、修飾子オプション）の検出
4. compound バリアント（内部に別のバリアントを含む）の検出
5. 修飾子 `/modifier` の分離とパース
6. findRoots による複数ルート候補の探索
7. 値の形式判定（arbitrary `[val]` / CSS変数 `(--var)` / named）

**関連システム・外部連携**：DesignSystem（design-system.ts）を通じてVariantsの登録情報を参照する。parseCandidate（candidate.ts）から呼び出される。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | レイアウト | 補助機能 | [&_h1]の任意値バリアント文字列をVariant構造体に解析する |

## 機能種別

解析処理 / データ変換処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| variant | string | Yes | バリアント文字列（例: `hover`, `@media(width>=768px)`, `[&_p]`） | 空文字は無効 |
| designSystem | DesignSystem | Yes | デザインシステムオブジェクト | - |

### 入力データソース

- parseCandidate 内でのバリアント部分の文字列
- `@variant` ディレクティブのパラメータ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Variant | Variant / null | arbitrary / static / functional / compound のいずれかの種別を持つ構造体。無効な場合は null |

### 出力先

- parseCandidate の parsedCandidateVariants 配列に格納
- design-system.ts の parsedVariants キャッシュに格納

## 処理フロー

### 処理シーケンス

```
1. arbitrary バリアント判定
   ├─ `[` で始まり `]` で終わる場合
   ├─ セレクタのデコード
   ├─ 相対セレクタ判定（>, +, ~）
   └─ `&` 不在の場合は `&:is(…)` でラップ

2. static / functional / compound バリアント判定
   ├─ segment(variant, '/') で修飾子を分離
   ├─ findRoots でルート候補を探索
   └─ variants.kind(root) で種別判定
       ├─ 'static': 値・修飾子なしを確認
       ├─ 'functional': 値の形式判定（arbitrary / CSS変数 / named）
       └─ 'compound': 値をバリアントとして再帰パース
```

### フローチャート

```mermaid
flowchart TD
    A["バリアント文字列入力"] --> B{"[...]形式?"}
    B -->|Yes| C["arbitrary バリアント処理"]
    C --> D{"@で始まり&含む?"}
    D -->|Yes| E["null を返す（非推奨）"]
    D -->|No| F["セレクタデコード"]
    F --> G{相対セレクタ?}
    G -->|Yes| H["relative: true"]
    G -->|No| I{"& 含む?"}
    I -->|No| J["&:is(selector) でラップ"]
    I -->|Yes| K["そのまま使用"]
    B -->|No| L["segment('/') で修飾子分離"]
    L --> M["findRoots でルート探索"]
    M --> N{"variants.kind(root)?"}
    N -->|static| O["static Variant を返す"]
    N -->|functional| P["値の形式判定"]
    N -->|compound| Q["値をバリアントとして再帰パース"]
    P --> R["functional Variant を返す"]
    Q --> S["compound Variant を返す"]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-19-01 | arbitrary @ルール制限 | `[@media…{&:hover}]` のような@ルール+セレクタの混合は非推奨で null を返す | arbitrary バリアントで `@` と `&` を含む場合 |
| BR-19-02 | 相対セレクタ | `>`, `+`, `~` で始まるセレクタは相対セレクタとして扱う | arbitrary バリアントの先頭文字が相対セレクタ記号の場合 |
| BR-19-03 | & 自動追加 | arbitrary バリアントで `&` が含まれず相対セレクタでもない場合、`&:is(selector)` でラップする | & 不在かつ非相対の場合 |
| BR-19-04 | static制約 | static バリアントは値も修飾子も持てない | static種別の場合 |
| BR-19-05 | 複数修飾子禁止 | `/` による修飾子は1つまで。2つ以上は null を返す | 修飾子が2つ以上の場合 |
| BR-19-06 | CSS変数接頭辞 | `(...)` 形式の arbitrary 値は `--` で始まる必要がある | `()` 形式の場合 |
| BR-19-07 | compound再帰 | compound バリアントの値は内部バリアントとして再帰的にパースされる | compound種別の場合 |
| BR-19-08 | compound互換性 | compound バリアントは、内部バリアントとの互換性チェック（compoundsWith）が必要 | compound種別の場合 |

### 計算ロジック

findRoots でバリアント名のハイフン区切りの各位置でルート候補を生成し、Variants レジストリに登録されているルートを探索する。最初にマッチしたルートの種別に基づいて解析を進める。

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

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | null | 無効なバリアント文字列の場合 | null を返す |
| - | null | static バリアントに値が指定された場合 | null を返す |
| - | null | static バリアントに修飾子が指定された場合 | null を返す |
| - | null | compound互換性チェックに失敗した場合 | null を返す |

### リトライ仕様

リトライ処理は存在しない。

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

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

## パフォーマンス要件

- parseVariant の結果は DefaultMap でキャッシュされる（design-system.ts 77行目）
- 1つのバリアント文字列に対してfindRootsが複数候補を生成するが、最初のマッチで処理が決定される

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

ユーザ入力のバリアント文字列をパースするが、CSS仕様の範囲内で処理される。isValidArbitrary によるサニタイズが行われる。

## 備考

- parseVariant は candidate.ts 内の parseCandidate と同一ファイルに定義されている
- DesignSystem.parseVariant はキャッシュ付きのラッパーであり、同一の入力に対して再パースを避ける
- compound バリアントのパースは再帰的であり、`group-hover` のような単純なケースから `group-has-hover` のような多段ネストにも対応する

---

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

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

### 推奨読解順序

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

Variant 型の4種類の定義を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | candidate.ts | `packages/tailwindcss/src/candidate.ts` | Variant 型（103-162行目）: arbitrary / static / functional / compound の4種類の定義 |

**読解のコツ**: Variant型はdiscriminated union（タグ付き共用体）であり、`kind` プロパティで型を判別する。compound バリアントは `variant` プロパティで再帰的に別のVariantを保持する。

#### Step 2: parseVariant関数を理解する

メインの解析ロジックを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | candidate.ts | `packages/tailwindcss/src/candidate.ts` | parseVariant関数（661行目以降）。arbitrary / static / functional / compound の各分岐処理 |

**主要処理フロー**:
1. **663行目**: `[...]` 形式のarbitraryバリアント判定
2. **677行目**: `@` と `&` を含むarbitraryバリアントの非推奨チェック
3. **679行目**: `decodeArbitraryValue` でセレクタをデコード
4. **688行目**: 相対セレクタ（`>`, `+`, `~`）の判定
5. **696-698行目**: `&` 不在時の `&:is(…)` ラップ
6. **712行目**: `segment(variant, '/')` で修飾子を分離
7. **721-723行目**: `findRoots` でルート候補を探索
8. **726行目**: `designSystem.variants.kind(root)` で種別判定
9. **727-738行目**: static バリアントの処理
10. **740-810行目**: functional バリアントの処理（arbitrary / CSS変数 / named値）
11. **812行目以降**: compound バリアントの処理（再帰パース）

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

parseVariant がどこから呼び出されるかを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | candidate.ts | `packages/tailwindcss/src/candidate.ts` | parseCandidate内での呼び出し（341行目）。バリアントの逆順パース |
| 3-2 | design-system.ts | `packages/tailwindcss/src/design-system.ts` | parsedVariants キャッシュ（77行目）。DefaultMapによるメモ化 |

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

```
parseVariant(variant, designSystem)
    │
    ├─ [arbitrary判定]
    │      ├─ decodeArbitraryValue(selector)
    │      ├─ isValidArbitrary(selector)
    │      └─ return { kind: 'arbitrary', selector, relative }
    │
    └─ [static/functional/compound判定]
           ├─ segment(variant, '/')
           ├─ findRoots(variantWithoutModifier, predicate)
           │      └─ designSystem.variants.has(root)
           │
           ├─ designSystem.variants.kind(root)
           │
           ├─ [static] → return { kind: 'static', root }
           │
           ├─ [functional]
           │      ├─ parseModifier(modifier) [修飾子パース]
           │      ├─ decodeArbitraryValue(value) [arbitrary値]
           │      └─ return { kind: 'functional', root, value, modifier }
           │
           └─ [compound]
                  ├─ parseVariant(value, designSystem) [再帰呼び出し]
                  ├─ designSystem.variants.compoundsWith(root, innerVariant)
                  └─ return { kind: 'compound', root, variant, modifier }
```

### データフロー図

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

"[&_p]"              ───▶ arbitrary判定              ───▶ { kind: 'arbitrary',
                            decodeArbitraryValue            selector: '& p',
                                                            relative: false }

"hover"              ───▶ findRoots → static判定     ───▶ { kind: 'static',
                                                            root: 'hover' }

"@md"                ───▶ findRoots → functional判定 ───▶ { kind: 'functional',
                            値なし                          root: '@md',
                                                            value: null, ... }

"group-hover"        ───▶ findRoots → compound判定   ───▶ { kind: 'compound',
                            再帰parseVariant("hover")       root: 'group',
                                                            variant: { kind: 'static', root: 'hover' }, ... }
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| candidate.ts | `packages/tailwindcss/src/candidate.ts` | ソース | parseVariant関数の本体、Variant型定義 |
| design-system.ts | `packages/tailwindcss/src/design-system.ts` | ソース | parsedVariantsキャッシュ（77行目） |
| variants.ts | `packages/tailwindcss/src/variants.ts` | ソース | Variantsクラス。has / kind / compoundsWith メソッド |
| segment.ts | `packages/tailwindcss/src/utils/segment.ts` | ソース | ネストを考慮した文字列分割 |
| decode-arbitrary-value.ts | `packages/tailwindcss/src/utils/decode-arbitrary-value.ts` | ソース | arbitrary値のデコード |
| is-valid-arbitrary.ts | `packages/tailwindcss/src/utils/is-valid-arbitrary.ts` | ソース | arbitrary値のバリデーション |
