# 機能設計書 62-エスケープ処理（escape/unescape）

## 概要

本ドキュメントは、Tailwind CSSにおけるCSSクラス名のエスケープ・アンエスケープ処理を行うユーティリティ関数`escape`および`unescape`の機能設計を記述する。CSS仕様（CSSOM）に準拠した識別子のシリアライズ・デシリアライズを提供する。

### 本機能の処理概要

**業務上の目的・背景**：CSSクラス名には、スラッシュ(`/`)、ドット(`.`)、特殊文字など、CSSセレクタとして直接使用できない文字が含まれる場合がある。例えば`red-1/2`というクラス名をCSSセレクタとして使用するには`red-1\/2`とエスケープする必要がある。本機能はCSSOM仕様に準拠した正確なエスケープ処理を提供し、Tailwind CSSが任意の文字列をCSSクラス名として安全に扱えるようにする。

**機能の利用シーン**：テーマ変数名のエスケープ（Theme）、コンパイル時のCSSセレクタ生成（compile.ts）、ユーティリティクラス名のアンエスケープ（utilities.ts）、v3互換プラグインAPIでのエスケープ処理（plugin-api.ts, plugin-functions.ts）など、CSS出力の生成過程全般で使用される。

**主要な処理内容**：
1. `escape`: 文字列をCSS識別子としてシリアライズする。制御文字はコードポイントエスケープ（`\xx `）に変換し、特殊文字はバックスラッシュエスケープ（`\x`）に変換する
2. `unescape`: エスケープされたCSS識別子を元の文字列に復元する。`\xx `形式のコードポイントエスケープと`\x`形式のバックスラッシュエスケープの両方に対応する

**関連システム・外部連携**：外部システムとの連携はない。CSSOM仕様（https://drafts.csswg.org/cssom/#serialize-an-identifier）に準拠した実装である。

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

## 関連画面

本機能は内部ユーティリティ関数であり、画面に直接関連しない。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | 補助機能 | CSSセレクタ生成時のエスケープ処理として間接的に全画面のCSS出力に寄与 |

## 機能種別

計算処理（文字列変換）

## 入力仕様

### 入力パラメータ

#### escape関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| value | string | Yes | エスケープ対象の文字列 | 引数が未指定の場合TypeErrorをスロー |

#### unescape関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| escaped | string | Yes | アンエスケープ対象の文字列 | 特になし |

### 入力データソース

呼び出し元モジュールからの関数引数として入力される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| escape戻り値 | string | CSSエスケープされた文字列 |
| unescape戻り値 | string | アンエスケープされた元の文字列 |

### 出力先

呼び出し元への戻り値として返却される。

## 処理フロー

### 処理シーケンス

#### escape関数

```
1. 引数チェック
   └─ 引数が0個の場合TypeErrorスロー
2. 入力をString型に変換
   └─ string = String(value)
3. 特殊ケース判定：先頭が'-'で長さ1の場合
   └─ '\-'を返却
4. 各文字を走査して変換
   ├─ 4a. NULL文字(U+0000)の場合
   │      └─ 置換文字(U+FFFD)に変換
   ├─ 4b. 制御文字(U+0001-U+001F)またはDEL(U+007F)の場合
   │      └─ コードポイントエスケープ '\XX ' に変換
   ├─ 4c. 先頭位置の数字(0-9)の場合
   │      └─ コードポイントエスケープ '\XX ' に変換
   ├─ 4d. 2番目位置の数字かつ先頭が'-'の場合
   │      └─ コードポイントエスケープ '\XX ' に変換
   ├─ 4e. U+0080以上、'-'、'_'、英数字の場合
   │      └─ そのまま出力
   └─ 4f. その他の場合
          └─ バックスラッシュエスケープ '\x' に変換
5. 変換結果を返却
```

#### unescape関数

```
1. 正規表現による置換
   └─ パターン: \\([\dA-Fa-f]{1,6}[\t\n\f\r ]?|[\S\s])
2. マッチごとに変換
   ├─ 2a. 16進数エスケープ（マッチ長 > 2）の場合
   │      └─ Number.parseInt → String.fromCodePoint
   └─ 2b. 単純バックスラッシュエスケープの場合
          └─ バックスラッシュの次の文字をそのまま返却
3. 変換結果を返却
```

### フローチャート

```mermaid
flowchart TD
    A[escape: 入力 value] --> B{引数あり?}
    B -->|No| C[TypeError スロー]
    B -->|Yes| D{長さ1 かつ '-'?}
    D -->|Yes| E["return '\\-'"]
    D -->|No| F[各文字を走査]
    F --> G{文字種判定}
    G -->|NULL| H["U+FFFD に置換"]
    G -->|制御文字/DEL| I["\\XX でエスケープ"]
    G -->|先頭の数字| I
    G -->|安全な文字| J[そのまま出力]
    G -->|その他| K["\\x でエスケープ"]
    H --> L{次の文字?}
    I --> L
    J --> L
    K --> L
    L -->|Yes| G
    L -->|No| M[return result]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-62-01 | CSSOM仕様準拠 | CSS仕様に定められた識別子シリアライズルールに従う | 常時 |
| BR-62-02 | NULL文字置換 | U+0000はU+FFFD(REPLACEMENT CHARACTER)に置換する | NULL文字検出時 |
| BR-62-03 | 制御文字エスケープ | U+0001-U+001FおよびU+007Fはコードポイントエスケープに変換する | 制御文字検出時 |
| BR-62-04 | 先頭数字エスケープ | 先頭位置の数字はコードポイントエスケープに変換する | 先頭が数字の場合 |
| BR-62-05 | ハイフン数字エスケープ | 先頭がハイフンかつ2番目が数字の場合、2番目をコードポイントエスケープに変換する | 先頭'-'+2番目数字 |
| BR-62-06 | 安全文字通過 | U+0080以上、'-'、'_'、英数字はエスケープ不要 | 安全な文字の場合 |

### 計算ロジック

コードポイントエスケープの形式: `\` + 16進数文字列 + スペース（例: `\31 ` は `1`）

アンエスケープ正規表現: `\\([\dA-Fa-f]{1,6}[\t\n\f\r ]?|[\S\s])`
- 16進数1-6桁 + オプションの空白文字: コードポイントとして解釈
- それ以外: バックスラッシュの次の文字をそのまま返す

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

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

データベース操作はない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| TypeError | 引数エラー | escape関数に引数が未指定の場合 | TypeErrorがスローされる |

### リトライ仕様

リトライ処理はない。

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

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

## パフォーマンス要件

- escape関数はO(n)の文字走査で処理を完了する
- unescape関数は正規表現のreplaceによるO(n)処理

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

- CSSインジェクション防止の観点で、ユーザー入力をCSSセレクタに含める際にescape関数を使用することが推奨される
- NULL文字の置換（U+FFFD）により、不正な文字列の混入を防止する

## 備考

- 本実装はCSS Working Groupの仕様 https://drafts.csswg.org/cssom/#serialize-an-identifier に準拠している
- ブラウザのCSS.escape()と同等の機能を提供する
- サロゲートペアや孤立サロゲートの特別処理は不要（コメントに記載あり）

---

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

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

### 推奨読解順序

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

本関数群は独自のデータ型定義を持たず、文字列の入出力のみで動作する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | escape.ts | `packages/tailwindcss/src/utils/escape.ts` | 関数シグネチャとCSS仕様リンクのコメントを確認 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | escape.ts | `packages/tailwindcss/src/utils/escape.ts` | **2-73行目**: escape関数の全体実装 |

**主要処理フロー**:
1. **2行目**: CSSOM仕様へのリンクコメント
2. **3-5行目**: 引数チェック。引数が0個の場合TypeErrorをスロー
3. **13-20行目**: 特殊ケース。先頭が'-'で長さ1の場合`\-`を返却
4. **29-32行目**: NULL文字(U+0000)をU+FFFD(REPLACEMENT CHARACTER)に置換
5. **34-49行目**: 制御文字・先頭数字・ハイフン後数字をコードポイントエスケープ（`\XX `形式）
6. **55-66行目**: 安全な文字（U+0080以上、ハイフン、アンダースコア、英数字）はそのまま出力
7. **70行目**: その他の文字はバックスラッシュエスケープ（`\x`形式）

**読解のコツ**: 文字コードはUnicode参照（U+XXXX形式）でコメントされている。0x0030-0x0039は数字'0'-'9'、0x0041-0x005Aは'A'-'Z'、0x0061-0x007Aは'a'-'z'に対応する。

#### Step 3: unescape関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | escape.ts | `packages/tailwindcss/src/utils/escape.ts` | **75-81行目**: unescape関数の実装 |

**主要処理フロー**:
1. **76行目**: 正規表現パターン `\\([\dA-Fa-f]{1,6}[\t\n\f\r ]?|[\S\s])` でマッチ
2. **77-78行目**: 16進数エスケープ（マッチ長 > 2）はparseInt+fromCodePointで変換
3. **79行目**: 単純エスケープはバックスラッシュの次の文字を返却

#### Step 4: テストコードで挙動を確認する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | escape.test.ts | `packages/tailwindcss/src/utils/escape.test.ts` | escape/unescapeの基本的な変換テストケース |

#### Step 5: 利用箇所を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | theme.ts | `packages/tailwindcss/src/theme.ts` | テーマ変数名のエスケープ・アンエスケープに使用 |
| 5-2 | compile.ts | `packages/tailwindcss/src/compile.ts` | コンパイル時のCSSセレクタ生成でescapeを使用 |
| 5-3 | index.ts | `packages/tailwindcss/src/index.ts` | メインコンパイルエントリでescape/unescapeを使用 |

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

```
[呼び出し元]
    │
    ├─ theme.ts (Theme)
    │      ├─ escape(key)   ── テーマ変数名のエスケープ
    │      └─ unescape(key) ── テーマ変数名の復元
    │
    ├─ compile.ts (compileCandidates)
    │      └─ escape(className) ── CSSセレクタ生成時のクラス名エスケープ
    │
    ├─ index.ts (compile)
    │      ├─ escape(name)   ── ディレクティブ処理でのエスケープ
    │      └─ unescape(name) ── ディレクティブ処理でのアンエスケープ
    │
    ├─ utilities.ts
    │      └─ unescape(value) ── ユーティリティ値のアンエスケープ
    │
    └─ compat/
           ├─ plugin-api.ts
           │      └─ escape(name) ── v3プラグインAPIでのエスケープ
           └─ plugin-functions.ts
                  └─ unescape(value) ── v3プラグイン関数でのアンエスケープ
```

### データフロー図

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

escape:
  "red-1/2" ──────▶ escape("red-1/2")
                         │
                         ├─ '/' は安全文字でない
                         └─ バックスラッシュエスケープ        ───▶ "red-1\\/2"

unescape:
  "red-1\\/2" ────▶ unescape("red-1\\/2")
                         │
                         └─ 正規表現で\\/ をマッチ            ───▶ "red-1/2"
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| escape.ts | `packages/tailwindcss/src/utils/escape.ts` | ソース | escape/unescape関数の実装 |
| escape.test.ts | `packages/tailwindcss/src/utils/escape.test.ts` | テスト | escape/unescapeのユニットテスト |
| theme.ts | `packages/tailwindcss/src/theme.ts` | ソース | テーマ変数名のエスケープ処理で利用 |
| compile.ts | `packages/tailwindcss/src/compile.ts` | ソース | CSSセレクタ生成でescapeを利用 |
| index.ts | `packages/tailwindcss/src/index.ts` | ソース | メインコンパイルでescape/unescapeを利用 |
| utilities.ts | `packages/tailwindcss/src/utilities.ts` | ソース | ユーティリティ値のアンエスケープで利用 |
| plugin-api.ts | `packages/tailwindcss/src/compat/plugin-api.ts` | ソース | v3互換APIでescapeを利用 |
| plugin-functions.ts | `packages/tailwindcss/src/compat/plugin-functions.ts` | ソース | v3互換関数でunescapeを利用 |
