# 通知設計書 22-未知のvariantエラー

## 概要

本ドキュメントは、Tailwind CSSの`@apply`ディレクティブにおいて、存在しないvariant（バリアント）が使用された場合に発生するエラー通知の設計を定義する。

### 本通知の処理概要

`@apply`ディレクティブで`hover:bg-blue-500`のようにvariant付きのユーティリティクラスが指定されたとき、そのvariant（例：`hover`）が実際に登録されていない場合にエラーメッセージを生成する通知処理である。単数のvariantと複数のvariantで異なるメッセージフォーマットを使い分ける。

**業務上の目的・背景**：Tailwind CSSではvariant（`hover:`, `focus:`, `dark:`など）を用いてユーティリティクラスに条件を付与できる。`@apply`でvariant付きクラスを使用する際、タイプミスやカスタムvariantの未定義によって無効なvariantが指定されることがある。本通知は、開発者が問題箇所と原因を即座に特定できるよう、未知のvariant名を明示したエラーメッセージを出力する。複数の未知variantが存在する場合は`Intl.ListFormat`を使用して英語の自然な列挙形式（"A and B"、"A, B, and C"など）でフォーマットされる。

**通知の送信タイミング**：CSS生成処理における`@apply`展開フェーズで、`compileCandidates`関数内の`onInvalidCandidate`コールバックが実行される際に、candidateをvariantとユーティリティに分解し、ユーティリティ部分が単独でコンパイル可能であるにもかかわらずvariant部分がコンパイルできない場合にエラーがthrowされる。

**通知の受信者**：CLIビルドコマンドまたはPostCSSプラグイン経由でビルドを実行した開発者。エラーはthrowされるため、ビルドツールのコンソール出力に表示される。

**通知内容の概要**：`Cannot apply utility class \`{candidate}\` because the {variant} variant does not exist.`（単数の場合）または`Cannot apply utility class \`{candidate}\` because the {variants} variants do not exist.`（複数の場合）というメッセージが出力される。

**期待されるアクション**：開発者は、指摘されたvariant名のタイプミスを修正するか、カスタムvariantを`@variant`ディレクティブで定義する。または`@apply`の使用箇所自体を見直す。

## 通知種別

ランタイムエラー（`throw new Error`）。ビルド時にCLIのstderrまたはビルドツールのエラー出力としてコンソールに表示される。

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（throwによるエラー伝播） |
| 優先度 | 高（ビルド処理を中断する致命的エラー） |
| リトライ | 無（コード修正が必要） |

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

ビルドプロセスの呼び出し元に対してエラーが伝播する。CLIの場合はプロセスのstderrに出力され、PostCSSプラグインの場合は`console.error`経由で出力された後、PostCSSのエラーとして再throwされる。

## 通知テンプレート

### メール通知の場合

本通知はメール送信ではなく、コンソールエラー出力である。

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | N/A |
| 送信元名称 | N/A |
| 件名 | N/A |
| 形式 | テキスト（stderr出力） |

### 本文テンプレート

単数のvariantが未知の場合：
```
Cannot apply utility class `{candidate}` because the `{variant}` variant does not exist.
```

複数のvariantが未知の場合（Intl.ListFormatで英語の自然な列挙形式にフォーマット）：
```
Cannot apply utility class `{candidate}` because the `{variant1}` and `{variant2}` variants do not exist.
Cannot apply utility class `{candidate}` because the `{variant1}`, `{variant2}`, and `{variant3}` variants do not exist.
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| N/A | N/A | N/A | 添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| candidate | @applyで指定されたフルクラス名（variant付き） | `@apply`ディレクティブのパラメータ | Yes |
| unknownVariants | 未知のvariant名の配列 | `segment(candidate, ':')`で分割後、`candidatesToCss`で検証して`null`を返したvariant | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ビルド処理 | `@apply`ディレクティブの展開処理 | candidateにvariantが含まれ、ユーティリティ単体はコンパイル可能だが、variantがコンパイル不可の場合 | 未知のvariantの使用を検出 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| variantがすべて有効 | すべてのvariantが`candidatesToCss`でCSSを生成できる場合はエラーにならない |
| ユーティリティ自体が無効 | ユーティリティ部分が単独でコンパイルできない場合は、このチェックをスキップして後続のエラーチェック（No.23, No.24）に委ねる |
| prefix/blocklistエラーが先に該当 | prefixエラー（No.20）やブロックリストエラー（No.21）が先に該当する場合はそちらが優先される |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A["@applyディレクティブ検出"] --> B["compileCandidates実行"]
    B --> C["onInvalidCandidate呼び出し"]
    C --> D{"candidateにvariant(':')が含まれる?"}
    D -->|"No"| E["後続チェックへ（No.23, 24）"]
    D -->|"Yes"| F["segment(candidate, ':')でvariant分離"]
    F --> G{"ユーティリティ単体がコンパイル可能?"}
    G -->|"No"| E
    G -->|"Yes"| H["各variantを個別にcandidatesToCssで検証"]
    H --> I{"未知のvariantあり?"}
    I -->|"No"| E
    I -->|"Yes"| J{"未知variant数 = 1?"}
    J -->|"Yes"| K["単数形エラーメッセージをthrow"]
    J -->|"No"| L["Intl.ListFormatで複数variant列挙エラーをthrow"]
    K --> M["ビルド処理中断"]
    L --> M
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| N/A | N/A | データベースは使用しない |

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

本通知ではデータベースを参照しない。`designSystem.candidatesToCss()`メソッドを使用してvariantの有効性をインメモリで検証する。

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| N/A | N/A | データベース更新なし |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 未知のvariantエラー（単数） | candidate内の1つのvariantがDesignSystemに登録されていない | variantのタイプミスを修正、またはカスタムvariantを定義する |
| 未知のvariantエラー（複数） | candidate内の複数のvariantがDesignSystemに登録されていない | 各variantのタイプミスを修正、またはカスタムvariantを定義する |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A（コード修正が必要） |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし。ビルド実行時に即座にエラーが出力される。

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

エラーメッセージにはユーティリティクラス名とvariant名のみが含まれ、個人情報やシークレットは含まれない。

## 備考

- `Intl.ListFormat`はECMAScript 2020で標準化されたAPIであり、Node.js 12以降で利用可能。
- variant検証では`{variant}:[--tw-variant-check:1]`という一時的なcandidateを生成して`candidatesToCss`に渡すことで、variantの有効性を個別に確認している（apply.ts:211行目）。
- `segment`関数は`:`で文字列を分割するが、`[...]`内のコロンは無視するユーティリティ関数である。

---

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

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

### 推奨読解順序

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

variant検証で使用されるDesignSystemのメソッドを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | design-system.ts | `packages/tailwindcss/src/design-system.ts` | `candidatesToCss`メソッド（63行目）の型定義。candidateの配列を受け取り、各candidateに対応するCSS文字列またはnullを返す |
| 1-2 | segment.ts | `packages/tailwindcss/src/utils/segment.ts` | `segment`関数の実装。candidateを`:`で分割してvariantとユーティリティに分ける |

**読解のコツ**: `candidatesToCss`は内部的に`compileCandidates`を呼び出してCSS生成を試みる。variant検証では`{variant}:[--tw-variant-check:1]`のようなダミーcandidateを使用する。

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

`@apply`展開処理内のvariantエラー判定ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | apply.ts | `packages/tailwindcss/src/apply.ts` | 202-229行目のvariant検証ブロック。`segment(candidate, ':')`で分割後、各variantの有効性を確認する |

**主要処理フロー**:
1. **203行目**: `segment(candidate, ':')`でcandidateをvariantとユーティリティに分割
2. **204行目**: `parts.length > 1`でvariant付きであるか判定
3. **205行目**: `parts.pop()`で末尾のユーティリティ部分を取得
4. **209行目**: ユーティリティ単体が`candidatesToCss`でコンパイル可能か確認
5. **210-211行目**: 各variantを`{variant}:[--tw-variant-check:1]`形式で検証
6. **213行目**: `null`を返したvariantを`unknownVariants`として収集
7. **215-227行目**: 未知variantの数に応じて単数形/複数形のエラーメッセージを生成

#### Step 3: Intl.ListFormatの使用を理解する

複数の未知variantがある場合のフォーマット処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | apply.ts | `packages/tailwindcss/src/apply.ts` | 220-226行目。`Intl.ListFormat('en', { style: 'long', type: 'conjunction' })`で英語の自然な列挙形式にフォーマットする |

**主要処理フロー**:
- **220行目**: `new Intl.ListFormat('en', { style: 'long', type: 'conjunction' })`でフォーマッタを生成
- **224-226行目**: `formatter.format(unknownVariants.map(...))`でvariant名をバッククォート付きでフォーマットし、エラーメッセージに組み込む

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

```
substituteAtApply (apply.ts:10)
    |
    +-- compileCandidates(candidates, designSystem, {...}) (apply.ts:178)
           |
           +-- onInvalidCandidate(candidate) (apply.ts:180)
                  |
                  +-- prefix チェック (apply.ts:182-186)
                  |
                  +-- invalidCandidates チェック (apply.ts:196-199)
                  |
                  +-- segment(candidate, ':') (apply.ts:203)
                  |      |
                  |      +-- designSystem.candidatesToCss([utility]) (apply.ts:209)
                  |      |
                  |      +-- designSystem.candidatesToCss(variants) (apply.ts:210-211)
                  |      |
                  |      +-- unknownVariants フィルタリング (apply.ts:213)
                  |             |
                  |             +-- 単数: throw new Error (apply.ts:216-218)
                  |             |
                  |             +-- 複数: Intl.ListFormat → throw new Error (apply.ts:220-226)
                  |
                  +-- theme空チェック (apply.ts:235-239)
                  +-- 汎用エラー (apply.ts:242)
```

### データフロー図

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

CSSファイル中の            segment(candidate, ':')           throw new Error
@apply hover:unknown:      candidatesToCss([utility])        "Cannot apply utility class
  bg-blue-500       ───>   candidatesToCss(variants)   ───>  `hover:unknown:bg-blue-500`
                           unknownVariants フィルタ           because the `unknown` variant
                           Intl.ListFormat                    does not exist."
                                                              ↓
DesignSystem               variant有効性判定              ビルド処理中断
.candidatesToCss()  ───>   null = 未知variant
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| apply.ts | `packages/tailwindcss/src/apply.ts` | ソース | @apply展開処理。未知variant判定ロジック（202-229行目） |
| compile.ts | `packages/tailwindcss/src/compile.ts` | ソース | candidateコンパイル処理。onInvalidCandidateコールバックを呼び出す |
| design-system.ts | `packages/tailwindcss/src/design-system.ts` | ソース | DesignSystem型定義。candidatesToCssメソッドを提供 |
| segment.ts | `packages/tailwindcss/src/utils/segment.ts` | ソース | candidateを`:`で分割するユーティリティ関数 |
