# 機能設計書 33-カスタムパーサー/プリンター登録

## 概要

本ドキュメントは、Prettierのカスタムパーサー/プリンター登録機能について記述する。この機能は、プラグインを通じてカスタムパーサーとプリンターを登録し、Prettierの言語サポートを拡張可能にする。

### 本機能の処理概要

カスタムパーサー/プリンター登録は、プラグインで定義されたパーサーとプリンターをPrettier内部で解決・初期化し、使用可能にする機能である。パーサーはソースコードをAST（抽象構文木）に変換し、プリンターはASTをPrettierの中間表現（Doc）に変換する役割を担う。

**業務上の目的・背景**：Prettierが標準でサポートしていない言語やフォーマットルールを追加するために、プラグイン開発者がカスタムパーサーとプリンターを登録できる仕組みが必要である。この機能により、PHP、Ruby、SQL等の追加言語サポートや、組織固有のフォーマットルールを実装したプラグインを作成できる。また、既存のパーサーを上書きして動作をカスタマイズすることも可能。

**機能の利用シーン**：
- 新しい言語のフォーマットサポートをプラグインとして追加する場合
- 既存言語のフォーマット動作をカスタマイズするプラグインを作成する場合
- 社内独自のDSL（Domain Specific Language）をフォーマットする場合

**主要な処理内容**：
1. パーサー名からプラグインを検索
2. パーサーの初期化（関数の場合は呼び出し）
3. AST形式からプリンターを検索
4. プリンターの初期化と正規化

**関連システム・外部連携**：Prettierプラグインシステム、各言語パーサー（Babel、TypeScript等）

**権限による制御**：特になし

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 14 | Usage - Plugins | 主画面 | カスタムプラグインでのパーサー・プリンター登録を説明 |

## 機能種別

プラグイン管理 / パーサー・プリンター解決

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| plugins | Array<Plugin> | Yes | 登録済みプラグインの配列 | 配列であること |
| parser | string | Yes | 使用するパーサー名 | 文字列であること |
| astFormat | string | No | AST形式名（プリンター検索時） | 文字列であること |

### 入力データソース

- Prettier API呼び出し時のオプション
- プラグインで定義されたparsersオブジェクト
- プラグインで定義されたprintersオブジェクト

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| parser | Parser | 初期化されたパーサーオブジェクト |
| printer | Printer | 正規化されたプリンターオブジェクト |

### 出力先

Prettierコアのフォーマット処理で使用

## 処理フロー

### 処理シーケンス

```
1. resolveParser関数呼び出し
   └─ パーサー名とプラグイン配列を受け取る
2. getParserPluginByParserName関数でプラグインを検索
   └─ プラグイン配列を後方から検索（オーバーライド対応）
3. initParser関数でパーサーを初期化
   └─ 関数の場合は呼び出し、オブジェクトの場合はそのまま返却
4. getPrinterPluginByAstFormat関数でプリンターを検索
   └─ AST形式名でプラグイン配列を後方から検索
5. initPrinter関数でプリンターを初期化
   └─ 関数の場合はawaitして呼び出し
6. normalizePrinter関数でプリンターを正規化
   └─ features、getVisitorKeys、embed、massageAstNode、printを正規化
```

### フローチャート

```mermaid
flowchart TD
    A[resolveParser呼び出し] --> B[getParserPluginByParserName]
    B --> C{プラグイン見つかった?}
    C -->|No| D[ConfigError throw]
    C -->|Yes| E[initParser]
    E --> F{パーサーは関数?}
    F -->|Yes| G[関数を呼び出し]
    F -->|No| H[そのまま返却]
    G --> I[パーサー返却]
    H --> I

    J[getPrinterPluginByAstFormat] --> K{プラグイン見つかった?}
    K -->|No| L[ConfigError throw]
    K -->|Yes| M[initPrinter]
    M --> N{プリンターは関数?}
    N -->|Yes| O[await関数呼び出し]
    N -->|No| P[そのまま正規化へ]
    O --> Q[normalizePrinter]
    P --> Q
    Q --> R[正規化済みプリンター返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-33-01 | 後方検索優先 | プラグイン配列を後方から検索し、後に登録されたプラグインが優先 | パーサー/プリンター検索時 |
| BR-33-02 | 遅延初期化対応 | パーサー/プリンターが関数の場合は呼び出して初期化 | 初期化時 |
| BR-33-03 | プリンター正規化 | プリンターはnormalizePrinterで正規化される | プリンター初期化時 |
| BR-33-04 | キャッシュ利用 | 正規化済みプリンターはWeakMapでキャッシュ | プリンター再利用時 |

### 計算ロジック

プラグイン検索:
```javascript
const plugin = plugins.findLast(
  (plugin) => plugin.parsers && Object.hasOwn(plugin.parsers, parserName),
);
```

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

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

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ConfigError | 指定されたパーサー名が見つからない場合 | パーサー名を確認、プラグインの読み込み状況を確認 |
| - | ConfigError | 指定されたAST形式のプリンターが見つからない場合 | AST形式名を確認、プラグインの設定を確認 |
| - | Error | parserNameが未指定の場合 | パーサー名を指定 |
| - | Error | astFormatが未指定の場合 | AST形式名を指定 |

### リトライ仕様

リトライは行わない。

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

本機能はトランザクション管理を必要としない。

## パフォーマンス要件

- WeakMapによるプリンター正規化結果のキャッシュ
- 遅延初期化による初期化コストの分散

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

- プラグインで登録されたパーサー/プリンターは任意のコードを実行できるため、信頼できるプラグインのみを使用すること

## 備考

- スタンドアロンビルドでは、プラグインを明示的にバンドルに追加する必要がある
- パーサーのastFormatプロパティでプリンターとの対応付けが行われる

---

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

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

### 推奨読解順序

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

パーサーとプリンターの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | parser-and-printer.js | `src/main/parser-and-printer.js` | パーサー/プリンター解決の主要関数 |

**読解のコツ**: パーサーはparsers[parserName]で取得され、プリンターはprinters[astFormat]で取得される。

#### Step 2: パーサー解決を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | parser-and-printer.js | `src/main/parser-and-printer.js` | resolveParser、getParserPluginByParserName、initParser関数 |

**主要処理フロー**:
1. **12-35行目**: getParserPluginByParserName - プラグイン配列を後方から検索
2. **58-61行目**: resolveParser - パーサー解決のエントリーポイント
3. **63-68行目**: initParser - パーサーの初期化（関数/オブジェクト対応）

#### Step 3: プリンター解決を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | parser-and-printer.js | `src/main/parser-and-printer.js` | getPrinterPluginByAstFormat、initPrinter、normalizePrinter関数 |

**主要処理フロー**:
- **37-56行目**: getPrinterPluginByAstFormat - AST形式でプリンター検索
- **70-77行目**: initPrinter - プリンターの初期化
- **79-177行目**: normalizePrinter - プリンターの正規化

#### Step 4: プリンター正規化の詳細を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | parser-and-printer.js | `src/main/parser-and-printer.js` | normalizePrinter関数の詳細 |

**主要処理フロー**:
- **79-84行目**: キャッシュ確認
- **94-101行目**: プリンタープロパティの分解
- **103-111行目**: features正規化とgetVisitorKeys生成
- **113-159行目**: embed、massageAstNode、printのProxy化
- **161-176行目**: 正規化済みプリンターの生成とキャッシュ保存

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

```
resolveParser (parser-and-printer.js:58)
    │
    └─ getParserPluginByParserName (parser-and-printer.js:12)
           │
           └─ plugins.findLast()
                  │
                  └─ initParser (parser-and-printer.js:63)
                         │
                         ├─ [関数] → parserOrParserInitFunction()
                         └─ [オブジェクト] → そのまま返却

getPrinterPluginByAstFormat (parser-and-printer.js:37)
    │
    └─ plugins.findLast()
           │
           └─ initPrinter (parser-and-printer.js:70)
                  │
                  ├─ [関数] → await printerOrPrinterInitFunction()
                  └─ [オブジェクト] → normalizePrinter
                         │
                         └─ normalizePrinter (parser-and-printer.js:81)
                                │
                                ├─ キャッシュ確認
                                ├─ normalizePrinterFeatures
                                ├─ createGetVisitorKeysFunction
                                ├─ massageAstNode Proxy化
                                ├─ embed Proxy化
                                ├─ print Proxy化
                                └─ キャッシュ保存
```

### データフロー図

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

parser名            resolveParser                      初期化済みパーサー
plugins配列 ────────▶ getParserPluginByParserName ────▶
                           │
                           ▼
                    plugins.findLast()
                           │
                           ▼
                      initParser
                           │
                    ┌──────┴──────┐
                    │             │
               [関数]         [オブジェクト]
                    │             │
                    ▼             ▼
               関数呼び出し    そのまま返却
                    │             │
                    └──────┬──────┘
                           ▼
                      パーサー返却


astFormat           getPrinterPluginByAstFormat      正規化済みプリンター
plugins配列 ────────▶ initPrinter ─────────────────▶
                           │
                           ▼
                   normalizePrinter
                           │
                    ┌──────┴──────┐
                    │             │
              [キャッシュ]    [新規]
                    │             │
                    ▼             ▼
              キャッシュ返却   正規化処理
                                  │
                                  ▼
                           プリンター返却
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| parser-and-printer.js | `src/main/parser-and-printer.js` | ソース | パーサー/プリンター解決の主要ロジック |
| create-get-visitor-keys-function.js | `src/main/create-get-visitor-keys-function.js` | ソース | VisitorKeys関数の生成 |
| front-matter/index.js | `src/main/front-matter/index.js` | ソース | Front Matter処理 |
| errors.js | `src/common/errors.js` | ソース | ConfigErrorの定義 |
| babel.js | `src/plugins/babel.js` | ソース | Babelパーサーの例 |
| estree.js | `src/plugins/estree.js` | ソース | Estreeプリンターの例 |
