# 機能設計書 38-ソースマップ生成

## 概要

本ドキュメントは、Tailwind CSSにおけるソースマップ生成機能（createSourceMap / createTranslationMap）の機能設計を記述する。

### 本機能の処理概要

コンパイル結果のソースマップを生成する機能である。ASTノードの元のソース位置（src）と生成後の位置（dst）の対応関係を「デコードされたソースマップ」（ECMA-426仕様準拠）として構築する。@importによる複数ソースファイルの統合にも対応する。

**業務上の目的・背景**：Tailwind CSSのコンパイル処理では、入力CSSファイル（@importで複数ファイルを統合する場合もある）から出力CSSを生成する。開発者がブラウザの開発者ツールでスタイルをデバッグする際、出力CSSの行番号から元の入力CSSの位置を特定するためにソースマップが必要である。本機能は、ASTに付与されたソース位置情報（src/dst）からデコードされたソースマップを構築し、後続のシリアライズ処理で各種形式（JSON、インラインBase64等）に変換できるようにする。

**機能の利用シーン**：
- PostCSSプラグイン、Viteプラグイン、CLIでのビルド時にソースマップを生成
- ブラウザ開発者ツールでのスタイルデバッグ
- 他のソースマップツール（@jridgewell/remapping等）との連携

**主要な処理内容**：
1. ASTを走査し、各ノードのsrc（元ソース位置）とdst（生成位置）を取得する
2. 行オフセットテーブル（LineTable）を使用して、バイトオフセットを行番号/列番号に変換する
3. 複数行にまたがるノードの各行に対してマッピングを生成する
4. マッピングを生成位置の昇順でソートする
5. DecodedSourceMap（ECMA-426準拠のオブジェクトグラフ）として返す

**関連システム・外部連携**：
- @jridgewell/remapping等の外部ツールで入力ソースマップと合成可能
- source-map-jsライブラリと互換性のある形式に変換可能（No.39参照）

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

## 関連画面

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

## 機能種別

計算処理（ソースマップの構築）

## 入力仕様

### 入力パラメータ

#### createSourceMap

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| ast | AstNode[] | Yes | ソース位置情報（src, dst）が付与されたASTノード配列 | 各ノードにsrcとdstが設定されていること |

#### createTranslationMap

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| original | string | Yes | 元のCSS文字列 | 有効なCSS文字列 |
| generated | string | Yes | 生成されたCSS文字列 | 有効なCSS文字列 |

### 入力データソース

compile/compileAst関数のbuild()呼び出し結果のASTが入力される。

## 出力仕様

### 出力データ

#### createSourceMap

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | DecodedSourceMap | デコードされたソースマップオブジェクト |

DecodedSourceMap構造:

| フィールド | 型 | 説明 |
|-----------|-----|------|
| file | string \| null | 出力ファイル名（現在は常にnull） |
| sources | DecodedSource[] | ソースファイル情報の配列 |
| mappings | DecodedMapping[] | マッピングの配列（生成位置の昇順） |

DecodedSource構造:

| フィールド | 型 | 説明 |
|-----------|-----|------|
| url | string \| null | ソースファイルのURL/パス |
| content | string \| null | ソースファイルの内容 |
| ignore | boolean | ソースマップ消費時に無視するか |

DecodedMapping構造:

| フィールド | 型 | 説明 |
|-----------|-----|------|
| originalPosition | OriginalPosition \| null | 元ソースの位置（行番号, 列番号, ソース情報） |
| generatedPosition | Position | 生成CSSの位置（行番号, 列番号） |
| name | string \| null | 名前（現在は常にnull） |

#### createTranslationMap

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | (node: AstNode) => Translation[] | ノードを受け取りTranslation配列を返す関数 |

### 出力先

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

## 処理フロー

### 処理シーケンス（createSourceMap）

```
1. 遅延初期化のDefaultMap群を作成
   ├─ lineTables: Source → LineTable（行オフセットテーブル）
   └─ sourceTable: Source → DecodedSource（ソース情報）
2. DecodedSourceMapの初期化
   └─ file: null, sources: [], mappings: []
3. ASTの走査（walk）
   └─ 各ノードについて:
       a. srcとdstの両方が存在するか確認
       b. 元ソースの内容が存在するか確認
       c. 元ソースのLineTableと生成のLineTableを取得
       d. 元ソースのテキストを抽出
       e. テキストの各行について:
          └─ 空行でなければマッピングを生成（originalPosition + generatedPosition）
       f. 終了位置のマッピングも追加
4. ソースリストの構築
   └─ lineTablesのキーからsourceTableを経由してsourcesに追加
5. マッピングのソート
   └─ generatedPosition.line → generatedPosition.column →
       originalPosition.line → originalPosition.column の優先度でソート
6. DecodedSourceMapを返す
```

### フローチャート

```mermaid
flowchart TD
    A[開始: AST受け取り] --> B[DefaultMap初期化]
    B --> C[DecodedSourceMap初期化]
    C --> D[AST走査開始]
    D --> E{src && dst あり?}
    E -->|No| D
    E -->|Yes| F[LineTable取得]
    F --> G[元ソーステキスト抽出]
    G --> H[テキストの各行をループ]
    H --> I{空行でない?}
    I -->|Yes| J[マッピング生成・追加]
    I -->|No| H
    J --> H
    H --> K[終了位置マッピング追加]
    K --> D
    D --> L[sourcesリスト構築]
    L --> M[mappingsソート]
    M --> N[DecodedSourceMap返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-38-01 | 複数行マッピング | ソースマップは単一位置のみエンコードするため、複数行にまたがるノードは各行ごとにマッピングを生成する | ノードのテキストが改行を含む場合 |
| BR-38-02 | 空行スキップ | 空行（トリムして空文字列）に対してはマッピングを生成しない | trim() === '' |
| BR-38-03 | 遅延LineTable生成 | LineTableはDefaultMapにより初回アクセス時にのみ生成される | 常に |
| BR-38-04 | ソート順序 | マッピングはgeneratedPosition（行→列）の昇順でソートされる | 常に |
| BR-38-05 | ECMA-426準拠 | DecodedSourceMapはECMA-426仕様のデコードされたソースマップ記録型に準拠する | 常に |
| BR-38-06 | 元ソース内容保持 | DecodedSourceに元ソースの内容（content）を保持し、インラインソースマップを可能にする | 常に |

### 計算ロジック

行番号/列番号の変換:
- LineTableはソースコードの各行の開始バイトオフセットを保持する
- find(offset)は二分探索（O(log N)）で行番号を特定し、列番号を計算する
- Position.lineは1ベース、Position.columnは0ベース

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

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

データベース操作なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ソース内容なし | originalSource.contentがnullの場合 | マッピング生成をスキップ（returnで次のノードへ） |
| - | src/dstなし | ノードにsrcまたはdstが設定されていない場合 | マッピング生成をスキップ |

### リトライ仕様

リトライは不要。

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

トランザクション管理なし。

## パフォーマンス要件

- LineTableの生成はO(N)（Nはソースコードの文字数）
- LineTable.find()は二分探索でO(log N)（Nは行数）
- DefaultMapにより同一ソースのLineTableは再計算されない
- ASTの走査は各ノードに対して定数時間の処理

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

- ソースマップにはソースコードの内容が含まれる（content フィールド）。本番環境ではソースマップの公開範囲に注意が必要

## 備考

- ECMA-426仕様はまだ進化中であり、実装も仕様の変更に追随する可能性がある
- createTranslationMapは、元のCSSと生成されたCSSの行番号対応を提供する補助関数
- ソースマップの「生」の形式への変換はNo.39（ソースマップシリアライズ）で行われる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | source.ts | `packages/tailwindcss/src/source-maps/source.ts` | Source型とSourceLocation型の定義 |
| 1-2 | line-table.ts | `packages/tailwindcss/src/source-maps/line-table.ts` | Position型（14-20行目）とLineTable型（25-42行目） |
| 1-3 | source-map.ts | `packages/tailwindcss/src/source-maps/source-map.ts` | DecodedSourceMap、DecodedSource、DecodedMapping型（17-47行目） |

**読解のコツ**:
- `Source`: ファイルパス（file）とソースコード（code）を持つ。出力CSSの場合fileはnull
- `SourceLocation`: `[Source, start, end]`のタプル。startとendはバイトオフセット
- `Position`: 行番号（1ベース）と列番号（0ベース）

#### Step 2: LineTableの実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | line-table.ts | `packages/tailwindcss/src/source-maps/line-table.ts` | `createLineTable`関数（50-100行目） |

**主要処理フロー**:
1. **51行目**: `table = [0]`で行開始オフセットテーブルを初期化
2. **54-58行目**: ソースコードを走査し、改行位置（0x0A）の次のオフセットをテーブルに追加
3. **60-84行目**: `find(offset)`関数 - esbuildの二分探索アルゴリズムに基づく行番号検索
4. **86-94行目**: `findOffset(pos)`関数 - Position→バイトオフセットの逆変換

#### Step 3: ソースマップ生成のメインロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | source-map.ts | `packages/tailwindcss/src/source-maps/source-map.ts` | `createSourceMap`関数（76-163行目） |

**主要処理フロー**:
1. **79行目**: `lineTables = new DefaultMap(...)`で遅延初期化のLineTable生成
2. **80-84行目**: `sourceTable = new DefaultMap(...)`でDecodedSource生成
3. **87-91行目**: DecodedSourceMapの初期化
4. **94行目**: `walk(ast, (node) => { ... })`でAST走査開始
5. **95行目**: `if (!node.src || !node.dst) return`でsrc/dstチェック
6. **103行目**: 元ソーステキストのスライス抽出
7. **115-132行目**: テキストの各行に対するマッピング生成ループ
8. **134-144行目**: 終了位置マッピングの追加
9. **148-150行目**: sourcesリストの構築
10. **153-160行目**: mappingsの生成位置昇順ソート

#### Step 4: TranslationMapを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | source-map.ts | `packages/tailwindcss/src/source-maps/source-map.ts` | `createTranslationMap`関数（165-198行目） |

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

```
compile/compileAst (index.ts)
    │
    └─ createSourceMap (source-maps/source-map.ts:76)
           │
           ├─ DefaultMap<Source, LineTable>
           │      └─ createLineTable() (line-table.ts:50)
           │             └─ 二分探索によるfind()
           │
           ├─ DefaultMap<Source, DecodedSource>
           │      └─ ソースファイル情報生成
           │
           ├─ walk() (walk.ts)
           │      └─ ASTノードの走査
           │
           └─ map.mappings.sort()
                  └─ 生成位置の昇順ソート
```

### データフロー図

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

AstNode[]            ───▶ walk(ast)                      ───▶ DecodedSourceMap
(src/dst付き)               │                                    ├─ file: null
                            ├─ node.src → originalPosition        ├─ sources: [{
                            │   LineTable.find(offset)             │    url, content
                            │   → { line, column }                 │  }]
                            │                                      └─ mappings: [{
                            ├─ node.dst → generatedPosition           originalPosition,
                            │   LineTable.find(offset)                generatedPosition,
                            │   → { line, column }                    name: null
                            │                                       }]
                            └─ sort(mappings)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| source-map.ts | `packages/tailwindcss/src/source-maps/source-map.ts` | ソース | メイン処理: createSourceMap/createTranslationMap関数 |
| line-table.ts | `packages/tailwindcss/src/source-maps/line-table.ts` | ソース | LineTable: バイトオフセット→行/列変換 |
| source.ts | `packages/tailwindcss/src/source-maps/source.ts` | ソース | Source型/SourceLocation型定義 |
| walk.ts | `packages/tailwindcss/src/walk.ts` | ソース | ASTウォーカー |
| default-map.ts | `packages/tailwindcss/src/utils/default-map.ts` | ソース | DefaultMap: 遅延初期化キャッシュ |
| index.ts | `packages/tailwindcss/src/index.ts` | ソース | compile関数からcreateSourceMapを呼び出す |
