# 機能設計書 31-宣言の定数畳み込み（constantFoldDeclaration）

## 概要

本ドキュメントは、Tailwind CSSにおけるCSS宣言値の定数畳み込み処理（constantFoldDeclaration）の機能設計を記述する。

### 本機能の処理概要

CSS宣言値に含まれるcalc()式の定数畳み込み（コンパイル時の計算式簡約化）と、次元単位の正規化処理を行う機能である。

**業務上の目的・背景**：Tailwind CSSではユーティリティクラスの生成時にcalc()式や様々な単位の値が発生する。これらを可能な限りコンパイル時に簡約化することで、最終出力CSSのファイルサイズを削減し、ブラウザの実行時計算コストを低減する。特にspacing系ユーティリティ（margin、padding等）ではcalc()式が頻繁に生成されるため、定数畳み込みはパフォーマンス最適化の重要な要素である。

**機能の利用シーン**：候補クラスの正規化処理（canonicalizeCandidates）の中で使用される。具体的には、ユーティリティクラスの値をテーマ変数に変換する際、calc()式の簡約化と単位の正規化が必要となる場面で呼び出される。また、rem単位からpx単位への変換が必要なケースでも利用される。

**主要な処理内容**：
1. CSS宣言値をValueParserでAST（値ノードツリー）にパースする
2. ASTを走査し、次元値の正規化（ゼロ値の簡略化、単位変換）を行う
3. calc()式の2項演算（加算・減算・乗算・除算）を定数畳み込みする
4. 畳み込みが行われた場合のみ、ASTをCSS文字列に変換して返す

**関連システム・外部連携**：外部システムとの連携はない。内部的にValueParser（CSS値パーサー）とdimensions（次元解析ユーティリティ）に依存する。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面に直接関連しない内部処理機能 |

## 機能種別

計算処理（CSS値の定数畳み込みとi単位正規化）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| input | string | Yes | CSS宣言値の文字列（例: `calc(0.25rem * 256)`） | 有効なCSS値であること。calc()式は`calc(1 + 1)`形式（演算子の前後にスペースあり）を想定 |
| rem | number \| null | No | rem単位をpx単位に変換する際の基準値（デフォルト: null） | null または正の数値 |

### 入力データソース

候補の正規化処理（canonicalize-candidates.ts）から呼び出される。ユーティリティクラスの値解決過程で生成されたCSS宣言値が入力される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | string | 定数畳み込み・正規化済みのCSS値文字列。畳み込みが不要な場合は入力と同一の文字列を返す |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. CSS値文字列のパース
   └─ ValueParser.parse()で値AST（ノードツリー）を生成
2. ASTの走査（exit方向）
   └─ walk()でASTを深さ優先で走査（子ノードから親ノードへ）
3. 各ノードの処理判定
   ├─ wordノード: 次元値の正規化を試みる
   │   └─ canonicalizeDimension()で単位変換
   └─ functionノード（calc/無名関数）: 定数畳み込みを試みる
       └─ 5ノード構成（lhs, sep, op, sep, rhs）の場合のみ処理
4. 定数畳み込み処理（calc式）
   ├─ 乗算（*）: 0との乗算 → 0、同単位/無次元との乗算 → 計算結果
   ├─ 加算（+）: 同単位 → 計算結果
   ├─ 減算（-）: 同単位 → 計算結果
   └─ 除算（/）: ゼロ除算を除外、無次元同士 or 単位付き/無次元 → 計算結果
5. 結果の出力
   └─ 畳み込みが発生した場合: ValueParser.toCss()でCSS文字列化
   └─ 畳み込みが発生しなかった場合: 入力文字列をそのまま返す
```

### フローチャート

```mermaid
flowchart TD
    A[開始: input文字列受け取り] --> B[ValueParser.parseでAST生成]
    B --> C[ASTをwalkで走査]
    C --> D{ノード種別判定}
    D -->|word| E{値は'0'か?}
    E -->|Yes| C
    E -->|No| F[canonicalizeDimension実行]
    F --> G{正規化成功?}
    G -->|No| C
    G -->|Yes| H[ノード置換, folded=true]
    H --> C
    D -->|function calc/空| I{ノード数=5?}
    I -->|No| C
    I -->|Yes| J[lhs, operator, rhsを取得]
    J --> K{演算可能?}
    K -->|No| C
    K -->|Yes| L[算術演算実行]
    L --> M[結果ノードで置換, folded=true]
    M --> C
    C --> N{走査完了?}
    N -->|No| D
    N -->|Yes| O{folded=true?}
    O -->|Yes| P[ValueParser.toCssで文字列化して返却]
    O -->|No| Q[入力文字列をそのまま返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-31-01 | ゼロ値の簡略化 | `0px`、`0rem`、`0em`等の長さ0の値を`0`に変換する | 値が0かつ長さ単位の場合 |
| BR-31-02 | 単位変換（長さ） | in→px、cm→px、mm→px、q→px、pc→px、pt→pxに変換する | 該当する長さ単位を持つ値 |
| BR-31-03 | rem→px変換 | rem単位をpx単位に変換する（rem引数が非nullの場合のみ） | rem引数が非nullで、値がrem単位の場合 |
| BR-31-04 | 単位変換（角度） | grad→deg、rad→deg、turn→degに変換する | 該当する角度単位を持つ値 |
| BR-31-05 | 単位変換（時間） | ms→sに変換する | ms単位の値 |
| BR-31-06 | 単位変換（周波数） | kHz→Hzに変換する | kHz単位の値 |
| BR-31-07 | 0との乗算 | `calc(0 * 100vw)` → `0` に簡約する | 乗算の片方が無次元の0の場合 |
| BR-31-08 | ゼロ除算の防止 | 除数が0の場合は定数畳み込みを行わない | 除算で右辺が0の場合 |

### 計算ロジック

単位変換の計算式:
- 1in = 96px
- 1cm = 96/2.54 px
- 1mm = 96/2.54/10 px
- 1q = 96/2.54/10/4 px
- 1pc = 96/6 px
- 1pt = 96/72 px
- 1rem = rem引数値 px
- 1grad = 0.9deg
- 1rad = 180/PI deg
- 1turn = 360deg
- 1ms = 0.001s
- 1kHz = 1000Hz

calc()畳み込みの条件:
- 乗算: 同単位同士、無次元*単位付き、単位付き*無次元
- 加算/減算: 同単位同士のみ
- 除算: 無次元/無次元、単位付き/無次元（ゼロ除算除外）

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

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

データベース操作なし。純粋な関数型の計算処理である。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 畳み込み不可 | 異なる単位同士の加減算、次元解析不能な値 | エラーを発生させず、入力値をそのまま返す |
| - | ゼロ除算 | calc式の右辺が0の除算 | 畳み込みをスキップし、calc式を維持する |

### リトライ仕様

リトライは不要。入力に対して常に決定的な結果を返す。

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

トランザクション管理なし。ステートレスな純粋関数である。

## パフォーマンス要件

- ASTの走査はexit方向（子→親）で行われるため、ネストされたcalc式も1回の走査で畳み込める
- 畳み込みが発生しなかった場合はtoCss変換をスキップし、入力文字列をそのまま返すことで不要な処理を回避する

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

特になし。外部入力を直接受け取らない内部処理関数である。

## 備考

- calc()式の入力は「演算子の前後にスペースがある」形式（例: `calc(1 + 1)`）を前提としている
- 5ノード構成（左辺、区切り、演算子、区切り、右辺）のcalc式のみが畳み込みの対象となる
- ネストされたcalc式は、walkのexit方向走査により内側から順に畳み込まれる

---

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

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

### 推奨読解順序

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

まず、CSS値のAST構造とdimensionsユーティリティの型を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | dimensions.ts | `packages/tailwindcss/src/utils/dimensions.ts` | 次元文字列（例: `64rem`）を`[数値, 単位]`タプルにパースするDefaultMap |
| 1-2 | value-parser.ts | `packages/tailwindcss/src/value-parser.ts` | CSS値のAST構造（word, function, separatorノード）を理解する |

**読解のコツ**: `dimensions`はDefaultMapであり、`.get()`呼び出し時に正規表現でパースした結果をキャッシュする。戻り値は`[number, string | null]`のタプルで、`null`は無次元を表す。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | constant-fold-declaration.ts | `packages/tailwindcss/src/constant-fold-declaration.ts` | メインの`constantFoldDeclaration`関数の全体構造 |

**主要処理フロー**:
1. **8行目**: `constantFoldDeclaration`関数の定義。input文字列とオプションのremパラメータを受け取る
2. **10行目**: `ValueParser.parse(input)`で値ASTを生成
3. **12-112行目**: `walk(valueAst, { exit(valueNode) { ... } })`でASTをexit方向に走査
4. **18-28行目**: wordノードの正規化処理 - `canonicalizeDimension()`を呼び出し
5. **31-110行目**: calc/無名関数ノードの定数畳み込み処理
6. **42行目**: ノード数が5でない場合は早期リターン（2項演算のみ対応）
7. **44-46行目**: 左辺(lhs)、演算子(operator)、右辺(rhs)の取得
8. **51-58行目**: 0との乗算の特別処理（結果を`0`にする）
9. **65-109行目**: 各演算子（*, +, -, /）ごとの畳み込み処理
10. **114行目**: foldedフラグに基づき、変換結果またはそのままの入力を返す

#### Step 3: 次元正規化を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | constant-fold-declaration.ts | `packages/tailwindcss/src/constant-fold-declaration.ts` | `canonicalizeDimension`関数（117-151行目） |

**主要処理フロー**:
- **117行目**: `canonicalizeDimension`関数の定義
- **118-119行目**: dimensions.getで値と単位を取得
- **125行目**: 値が0かつ長さ単位なら`'0'`を返す
- **128-150行目**: switchによる単位ごとの変換処理（in→px、cm→px、rem→px等）

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | canonicalize-candidates.ts | `packages/tailwindcss/src/canonicalize-candidates.ts` | `constantFoldDeclaration`がどのように呼び出されるかを確認する |

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

```
canonicalizeCandidates (canonicalize-candidates.ts)
    │
    └─ constantFoldDeclaration (constant-fold-declaration.ts:8)
           │
           ├─ ValueParser.parse() (value-parser.ts)
           │      └─ 値AST生成
           │
           ├─ walk() (walk.ts)
           │      └─ AST走査（exit方向）
           │
           ├─ canonicalizeDimension() (constant-fold-declaration.ts:117)
           │      └─ dimensions.get() (utils/dimensions.ts)
           │
           ├─ WalkAction.ReplaceSkip() (walk.ts)
           │      └─ ノード置換
           │
           └─ ValueParser.toCss() (value-parser.ts)
                  └─ ASTからCSS文字列生成
```

### データフロー図

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

CSS値文字列          ───▶ ValueParser.parse()           ───▶ 値AST
(例: "calc(0.25rem          │
 * 256)")                    ├─ walk(exit方向)
                             │   ├─ wordノード
                             │   │   └─ canonicalizeDimension()
                             │   └─ functionノード(calc)
                             │       └─ 算術演算
                             │
                             └─ ValueParser.toCss()      ───▶ 簡約化されたCSS値文字列
                                                               (例: "64rem" または
                                                                入力remが16の場合 "1024px")
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| constant-fold-declaration.ts | `packages/tailwindcss/src/constant-fold-declaration.ts` | ソース | メイン処理: CSS値の定数畳み込みと次元正規化 |
| dimensions.ts | `packages/tailwindcss/src/utils/dimensions.ts` | ソース | 次元文字列（値+単位）のパースユーティリティ |
| value-parser.ts | `packages/tailwindcss/src/value-parser.ts` | ソース | CSS値のパーサー。値ASTの生成とCSS文字列への変換 |
| walk.ts | `packages/tailwindcss/src/walk.ts` | ソース | ASTウォーカー。WalkAction.ReplaceSkipによるノード置換を提供 |
| infer-data-type.ts | `packages/tailwindcss/src/utils/infer-data-type.ts` | ソース | isLength関数を提供。ゼロ値長さ判定に使用 |
| canonicalize-candidates.ts | `packages/tailwindcss/src/canonicalize-candidates.ts` | ソース | 呼び出し元。候補の正規化処理 |
