# 通知設計書 16-CssSyntaxError

## 概要

本ドキュメントは、Tailwind CSSのCSSパーサーにおいて構文エラーが検出された場合に、ソースファイル・行・列情報を含むエラーを生成する`CssSyntaxError`クラスの設計を記述する。

### 本通知の処理概要

CSSパーサー（`css-parser.ts`）がCSSの構文解析中にエラーを検出した際、ソースファイルの位置情報（ファイル名、行番号、列番号）を含む`CssSyntaxError`例外を生成・スローする処理である。この例外はCLIのエラーハンドリングやPostCSSプラグインのエラー処理でキャッチされ、開発者に通知される。

**業務上の目的・背景**：CSSファイルの構文エラーは開発中に頻繁に発生する。正確なソース位置情報を含むエラーメッセージを提供することで、開発者は問題箇所を迅速に特定・修正できる。IDEとの連携により、エラー箇所へのジャンプも可能になる。これにより開発効率が向上し、不正なCSSの検出が確実に行われる。

**通知の送信タイミング**：CSSパーサーの`parse`関数がCSSテキストを解析する際に、以下の構文エラーが検出された場合にスローされる：(1) 無効なカスタムプロパティ、(2) 無効な宣言、(3) 閉じ括弧の不足、(4) 開き括弧の不足、(5) 終端されていない文字列。

**通知の受信者**：エラーをキャッチする上位の処理層（CLI、PostCSSプラグイン等）を経由して、最終的にCSSをビルドしている開発者に通知される。

**通知内容の概要**：`{ファイル名}:{行番号}:{列番号}: {エラーメッセージ}`の形式でソース位置情報を含むエラーメッセージが生成される。エラーの種類に応じて「Missing closing }」「Missing opening {」「Unterminated string」「Invalid declaration」「Invalid custom property」等のメッセージが含まれる。

**期待されるアクション**：開発者はエラーメッセージに含まれるファイル名・行番号・列番号を参照し、CSSファイルの当該箇所を修正する。

## 通知種別

CSS構文エラー（Error例外 / throw）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（throw による例外スロー） |
| 優先度 | 高 |
| リトライ | なし（上位層のエラーハンドリングに依存） |

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

例外のスローにより上位の処理層にエラーが伝播する。最終的な出力先は上位のエラーハンドラに依存する（CLIの場合はstderr、PostCSSの場合はPostCSSのエラー機構等）。

## 通知テンプレート

### メール通知の場合

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

### 本文テンプレート

ソース位置情報ありの場合:
```
{source.file}:{line}:{column + 1}: {message}
```

ソース位置情報なしの場合:
```
{message}
```

エラーメッセージの例:
- `Invalid custom property, expected a value`
- `Invalid declaration: \`{buffer.trim()}\``
- `Missing closing } at {parent.selector}` または `Missing closing } at {parent.name} {parent.params}`
- `Missing opening {`
- `Missing opening (`
- `Unterminated string: {部分文字列}`

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| source.file | ソースファイルのパス | ParseOptionsのfromパラメータ | No |
| source.code | ソースコード全文 | parse関数の入力文字列 | No |
| line | エラー発生行番号 | createLineTable(source.code).find(loc)による計算 | No |
| column | エラー発生列番号 | createLineTable(source.code).find(loc)による計算 | No |
| message | エラーメッセージ本体 | CssSyntaxErrorコンストラクタの第1引数 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| パーサー処理 | カスタムプロパティ解析 | 値が不正（colonIdx === -1等） | css-parser.ts 298行目 |
| パーサー処理 | 宣言解析（セミコロン終端） | parseDeclarationがnullを返す | css-parser.ts 367行目 |
| パーサー処理 | 宣言解析（閉じ波括弧終端） | parseDeclarationがnullを返す | css-parser.ts 490行目 |
| パーサー処理 | ブロック終了 | closingBracketStackが空（対応する開き括弧なし） | css-parser.ts 427行目 |
| パーサー処理 | 閉じ丸括弧 | closingBracketStackの末尾が')' でない | css-parser.ts 533行目 |
| パーサー処理 | パース完了 | 閉じられていないブロックが残存 | css-parser.ts 575-586行目 |
| パーサー処理 | 文字列解析 | セミコロン後の改行で文字列が終端されていない | css-parser.ts 689行目 |
| パーサー処理 | 文字列解析 | 改行で文字列が終端されていない | css-parser.ts 709行目 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 正常なCSS構文の場合 | パーサーが正常に解析を完了した場合はエラーはスローされない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[parse関数呼び出し] --> B[CSS文字列を1文字ずつ走査]
    B --> C{構文エラー検出?}
    C -->|No| D[次の文字へ]
    C -->|Yes| E[エラー位置情報を構成]
    E --> F{source情報あり?}
    F -->|Yes| G["SourceLocation [source, start, end] を構成"]
    F -->|No| H[loc = null]
    G --> I[new CssSyntaxError throw]
    H --> I
    I --> J[CssSyntaxErrorコンストラクタ]
    J --> K{loc あり?}
    K -->|Yes| L[createLineTableで行列計算]
    L --> M["メッセージ = file:line:col: message"]
    K -->|No| N[メッセージそのまま]
    M --> O[例外スロー]
    N --> O
```

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

### 参照テーブル一覧

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

### 更新テーブル一覧

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

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 無効なカスタムプロパティ | カスタムプロパティのパースでコロンが見つからない等 | CSSファイルのカスタムプロパティ宣言を修正 |
| 無効な宣言 | 宣言のパースに失敗 | CSSファイルの宣言構文を修正 |
| 閉じ波括弧不足 | ルール/アットルールのブロックが閉じられていない | 対応する}を追加 |
| 開き波括弧不足 | 対応する開き括弧がないのに}が出現 | CSSの括弧対応を修正 |
| 開き丸括弧不足 | 対応する開き括弧がないのに)が出現 | CSSの括弧対応を修正 |
| 終端されていない文字列 | クォートで囲まれた文字列が行内で閉じられていない | 閉じクォートを追加 |

### リトライ仕様

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

## 配信設定

### レート制限

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

### 配信時間帯

制限なし（CSSパース時に随時発生）

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

エラーメッセージにファイルパスとCSSの一部が含まれる可能性がある。ローカルでの実行であるため特段のリスクはないが、エラーログにファイルパスが記録される点に留意する。

## 備考

- `CssSyntaxError`はErrorクラスを継承し、`name`プロパティに`'CssSyntaxError'`を設定する（55行目）
- `Error.captureStackTrace`が利用可能な場合はスタックトレースをキャプチャする（58-60行目）
- `loc`プロパティ（`SourceLocation | null`）でソース位置情報を保持する（44行目）
- ソース位置情報は`[Source, startOffset, endOffset]`のタプル形式で表現される
- `createLineTable`関数により、バイトオフセットから行番号・列番号への変換が行われる（49行目）

---

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

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

### 推奨読解順序

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

CssSyntaxErrorクラスとSourceLocation型を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | css-parser.ts | `packages/tailwindcss/src/css-parser.ts` | 43-62行目: CssSyntaxErrorクラス定義 |
| 1-2 | source.ts | `packages/tailwindcss/src/source-maps/source.ts` | SourceLocation型定義（[Source, start, end]タプル） |
| 1-3 | line-table.ts | `packages/tailwindcss/src/source-maps/line-table.ts` | createLineTable関数（オフセットから行列への変換） |

**読解のコツ**: CssSyntaxErrorのコンストラクタはlocが存在する場合に`file:line:column: message`形式にメッセージを整形する。columnは0始まりのため+1される（50行目）。

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

parse関数がCSSパーサーのエントリーポイント。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | css-parser.ts | `packages/tailwindcss/src/css-parser.ts` | 64行目: parse関数の定義 |
| 2-2 | css-parser.ts | `packages/tailwindcss/src/css-parser.ts` | 65行目: ParseOptionsからsource情報を構成 |

**主要処理フロー**:
1. **64-65行目**: parse関数の入口。opts.fromがある場合にSourceオブジェクトを生成
2. **88行目**: メインのforループ開始（1文字ずつ走査）
3. **298行目**: カスタムプロパティの無効値エラー
4. **367行目**: 宣言の無効構文エラー（セミコロン終端時）
5. **427行目**: 対応する開き括弧のない閉じ括弧エラー
6. **490行目**: 宣言の無効構文エラー（閉じ波括弧終端時）
7. **533行目**: 対応する開き括弧のない閉じ丸括弧エラー
8. **573-587行目**: 閉じられていないブロックのエラー（ルール・アットルール）
9. **689行目**: セミコロン後の改行による文字列終端エラー
10. **709行目**: 改行による文字列終端エラー

#### Step 3: 各エラーパターンの詳細を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | css-parser.ts | `packages/tailwindcss/src/css-parser.ts` | 642-717行目: parseString関数（文字列パースとエラー検出） |

**主要処理フロー**:
- **684-693行目**: セミコロン+改行パターンでの終端されていない文字列検出
- **705-713行目**: 改行パターンでの終端されていない文字列検出

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

```
parse(input, opts?) [css-parser.ts:64]
    |
    +-- メインループ (i = 0..input.length)
    |       |
    |       +-- [カスタムプロパティ解析エラー] throw CssSyntaxError [298行目]
    |       +-- [宣言解析エラー] throw CssSyntaxError [367行目, 490行目]
    |       +-- [閉じ括弧不足] throw CssSyntaxError [427行目]
    |       +-- [開き括弧不足] throw CssSyntaxError [533行目]
    |       +-- parseString(input, i, ...) [css-parser.ts:642]
    |               |
    |               +-- [終端エラー] throw CssSyntaxError [689行目, 709行目]
    |
    +-- [パース完了後の検証]
            +-- [閉じ括弧不足] throw CssSyntaxError [575行目, 582行目]

CssSyntaxError(message, loc) [css-parser.ts:43]
    |
    +-- createLineTable(source.code) [49行目]
    |       +-- .find(loc[1]) -> { line, column }
    |
    +-- super(`${file}:${line}:${col}: ${message}`) [50行目]
```

### データフロー図

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

CSS文字列        --->  parse(input, { from })
 + ファイルパス         |
                       v
                  文字単位の走査
                       |
                  [構文エラー検出]
                       |
                       v
                  SourceLocation構成
                  [source, startOffset, endOffset]
                       |
                       v
                  CssSyntaxError(msg, loc)
                       |
                       v
                  createLineTable -> line, column    --->  "file:line:col: message"
                                                           (Error例外として伝播)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| css-parser.ts | `packages/tailwindcss/src/css-parser.ts` | ソース | CSSパーサー本体（parse関数、CssSyntaxErrorクラス） |
| source.ts | `packages/tailwindcss/src/source-maps/source.ts` | ソース | Source型、SourceLocation型の定義 |
| line-table.ts | `packages/tailwindcss/src/source-maps/line-table.ts` | ソース | createLineTable関数（バイトオフセットから行列番号への変換） |
| ast.ts | `packages/tailwindcss/src/ast.ts` | ソース | AST型定義（AstNode, Rule, AtRule, Declaration, Comment） |
