# 通知設計書 20-未prefix付きユーティリティクラスエラー

## 概要

本ドキュメントは、Tailwind CSSの`@apply`処理において、prefix設定がある環境でprefixなしのユーティリティクラスが`@apply`で使用された場合にエラーメッセージを出力する通知の設計を記述する。

### 本通知の処理概要

`@apply`ディレクティブの代入処理において、無効なcandidateが検出された際のコールバック（`onInvalidCandidate`）内で、`designSystem.theme.prefix`が設定されている環境で、candidateがprefixで始まっていない場合にエラー例外をスローする処理である。

**業務上の目的・背景**：Tailwind CSS v4ではprefixはvariantとして表記される。例えば、prefixが`tw`の場合、`bg-red-500`ではなく`tw:bg-red-500`と記述する必要がある。`@apply`内でprefixなしのユーティリティクラスを使用すると、そのクラスは見つからないため処理が失敗する。このエラーにより、開発者にprefixの付け忘れを具体的なcandidateと正しい使用例を示して通知し、迅速な修正を促す。

**通知の送信タイミング**：`@apply`の代入処理フェーズ（`substituteAtApply`関数内の`compileCandidates`呼び出し時）において、candidateの解決に失敗し`onInvalidCandidate`コールバックが呼び出された際、最初のチェックとしてprefix設定の有無とcandidateのprefix付与状況を検証する。

**通知の受信者**：CSSを記述・ビルドしている開発者。例外は上位のエラーハンドリングでキャッチされ、コンソールに表示される。

**通知内容の概要**：unprefixedなユーティリティクラス名と、正しいprefix付き表記を「Did you mean」形式で提案するエラーメッセージが出力される。

**期待されるアクション**：開発者は`@apply`ディレクティブ内のユーティリティクラスにprefixを付与する。例：`@apply bg-red-500;` を `@apply tw:bg-red-500;` に変更する。

## 通知種別

@applyエラー（Error例外 / throw）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（throw による例外スロー） |
| 優先度 | 高 |
| リトライ | なし |

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

例外のスローにより上位の処理層にエラーが伝播する。最終的な出力先は上位のエラーハンドラに依存する。

## 通知テンプレート

### メール通知の場合

該当なし（例外スローのみ）

### 本文テンプレート

```
Cannot apply unprefixed utility class `{candidate}`. Did you mean `{prefix}:{candidate}`?
```

例（prefix が `tw` の場合）:
```
Cannot apply unprefixed utility class `bg-red-500`. Did you mean `tw:bg-red-500`?
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| candidate | prefixなしのユーティリティクラス名 | onInvalidCandidate コールバックの引数 | Yes |
| designSystem.theme.prefix | 設定されているprefix値 | DesignSystemのtheme.prefix | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| コンパイル処理 | @apply代入時のonInvalidCandidate | designSystem.theme.prefix が設定済み かつ candidate が prefix で始まっていない | apply.ts 182-185行目 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| designSystem.theme.prefix が未設定の場合 | prefixが設定されていない環境では他のエラーチェックに進む |
| candidate が prefix で始まっている場合 | 正しくprefixが付いているがユーティリティ自体が不正な場合は他のチェックに進む |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[@apply代入処理] --> B[compileCandidatesの呼び出し]
    B --> C{candidate解決成功?}
    C -->|Yes| D[正常処理]
    C -->|No| E[onInvalidCandidate呼び出し]
    E --> F{prefix設定あり?}
    F -->|No| G[次のチェックへ]
    F -->|Yes| H{candidateがprefixで始まる?}
    H -->|Yes| G
    H -->|No| I["throw Error: unprefixed utility"]
```

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

### 参照テーブル一覧

該当なし（データベースは使用しない）

### 更新テーブル一覧

該当なし（データベースは使用しない）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 未prefix付きユーティリティ | prefix設定環境で@apply内のcandidateにprefixがない | candidateにprefixをvariant形式で付与 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0 |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A（例外がスローされ処理は中断） |

## 配信設定

### レート制限

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

### 配信時間帯

制限なし（CSSコンパイル時に随時発生）

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

ユーティリティクラス名とprefix値のみが出力されるため、機密情報の漏洩リスクはない。

## 備考

- このエラーは`onInvalidCandidate`コールバック内の最初のチェックとして実行される（182行目）
- `!candidate.startsWith(designSystem.theme.prefix)`でprefixの有無を判定（182行目）
- prefix付きの提案は`${designSystem.theme.prefix}:${candidate}`の形式でv4のvariant記法を使用（184行目）
- この後に続く他のチェック：ブロックリスト対象チェック（196行目）、未知のvariantチェック（203行目）、@import/@reference不足チェック（235行目）、汎用未知ユーティリティチェック（242行目）
- `compileCandidates`関数は`compile.ts`で定義され、`respectImportant: false`のオプションで呼び出される（179行目）

---

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

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

### 推奨読解順序

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

DesignSystemのprefix設定とcompileCandidatesの引数構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | design-system.ts | `packages/tailwindcss/src/design-system.ts` | theme.prefixプロパティの定義 |
| 1-2 | compile.ts | `packages/tailwindcss/src/compile.ts` | 11-17行目: compileCandidates関数のシグネチャとオプション（onInvalidCandidate） |

**読解のコツ**: `onInvalidCandidate`はcompileCandidatesのオプションとして渡されるコールバック関数で、候補の解決に失敗した際に呼び出される。

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

@apply代入処理内のcompileCandidates呼び出しとonInvalidCandidate定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | apply.ts | `packages/tailwindcss/src/apply.ts` | 158-296行目: @apply代入のメインループ |
| 2-2 | apply.ts | `packages/tailwindcss/src/apply.ts` | 178行目: compileCandidates呼び出し |
| 2-3 | apply.ts | `packages/tailwindcss/src/apply.ts` | 180-243行目: onInvalidCandidate コールバック全体 |

**主要処理フロー**:
1. **161-162行目**: @applyノードの検出
2. **164-171行目**: paramsからcandidateリストとオフセットを構築
3. **177-178行目**: compileCandidatesの呼び出し
4. **180行目**: `onInvalidCandidate: (candidate) => {` コールバック定義開始
5. **182行目**: `designSystem.theme.prefix`チェック
6. **182行目**: `!candidate.startsWith(designSystem.theme.prefix)`チェック
7. **183-185行目**: throw Error（未prefix付きユーティリティエラー）

#### Step 3: onInvalidCandidate内の後続チェックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | apply.ts | `packages/tailwindcss/src/apply.ts` | 196-199行目: ブロックリスト対象ユーティリティチェック |
| 3-2 | apply.ts | `packages/tailwindcss/src/apply.ts` | 203-230行目: 未知のvariantチェック |
| 3-3 | apply.ts | `packages/tailwindcss/src/apply.ts` | 235-238行目: @import/@reference不足チェック |
| 3-4 | apply.ts | `packages/tailwindcss/src/apply.ts` | 242行目: 汎用未知ユーティリティチェック |

**主要処理フロー**:
- **182-185行目**: prefixチェック（本通知）
- **196-199行目**: ブロックリストチェック（No.21）
- **203-228行目**: variantチェック（No.22）
- **235-238行目**: @import/@referenceチェック（No.23）
- **242行目**: 汎用エラー（No.24）

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

```
substituteAtApply(ast, designSystem) [apply.ts:10]
    |
    +-- [代入フェーズ] for parent of sorted [158行目]
            |
            +-- walk(parent.nodes) [161行目]
            |       |
            |       +-- compileCandidates(candidates, designSystem, opts) [178行目]
            |               |
            |               +-- onInvalidCandidate(candidate) [180行目]
            |                       |
            |                       +-- [prefix設定あり & prefixなし]
            |                       |       -> throw Error [183-185行目]
            |                       |
            |                       +-- [ブロックリスト対象]
            |                       |       -> throw Error [197-199行目]
            |                       |
            |                       +-- [未知variant]
            |                       |       -> throw Error [216-227行目]
            |                       |
            |                       +-- [theme空 = @import不足]
            |                       |       -> throw Error [236-238行目]
            |                       |
            |                       +-- [汎用]
            |                               -> throw Error [242行目]
```

### データフロー図

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

CSS AST              --->  @apply代入処理
(@apply bg-red-500)         |
                            v
                       compileCandidates()
                            |
                       [candidate解決失敗]
                            |
                            v
                       onInvalidCandidate("bg-red-500")
                            |
                       prefix: "tw" あり
                       "bg-red-500".startsWith("tw") = false
                            |
                            v
                       throw Error    --->  "Cannot apply unprefixed
                                             utility class `bg-red-500`.
                                             Did you mean `tw:bg-red-500`?"
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| apply.ts | `packages/tailwindcss/src/apply.ts` | ソース | @apply代入処理・onInvalidCandidate内のprefixチェック |
| compile.ts | `packages/tailwindcss/src/compile.ts` | ソース | compileCandidates関数（onInvalidCallbackの呼び出し元） |
| design-system.ts | `packages/tailwindcss/src/design-system.ts` | ソース | DesignSystem定義（theme.prefixプロパティ） |
| walk.ts | `packages/tailwindcss/src/walk.ts` | ソース | AST走査ユーティリティ |
