
# フォーマッター

Phasisには、独自パーサーを基盤とするネイティブJavaScriptフォーマッターが含まれています。ソースをprettier互換のレイアウト規則で再出力し、すべてPHP内で処理します。Node.jsも、インタープリター型のフォーマッターバンドルも、外部プロセスも不要です。

## formatSource

```php
public static function formatSource(
    string $source,
    string $sourceType = 'script',
    ?FormatOptions $options = null,
): string
```

`Phasis\Formatter\Formatter`の単一呼び出し用エントリーポイントです。`$sourceType`で言語を選びます。JavaScriptには`script`または`module`、`json`または`css`を指定します。JavaScriptは指定されたゴールに従って解析し、コメントを再付与して、設定された行幅に合わせて出力します。

```php
use Phasis\Formatter\Formatter;
use Phasis\Formatter\FormatOptions;

$formatted = Formatter::formatSource(
    "const a={x:1,y:2};",
    'script',
    new FormatOptions(useTabs: true, singleQuote: true),
);
// "const a = { x: 1, y: 2 };\n"
```

無効なソースの場合、パーサーと同じ`Phasis\Exceptions\SyntaxError`が行、列、オフセットとともにスローされます。

## FormatOptions

レイアウトオプションはprettier 3.xに準じ、デフォルト値もprettierと同じです:

| オプション | デフォルト | 説明 |
| --------------- | ---------- | -------------------------------------------------------------- |
| `printWidth` | `80` | レイアウトを収める目標の行幅。 |
| `tabWidth` | `2` | インデント1段あたりのスペース数（タブの幅の計算にも使用）。 |
| `useTabs` | `false` | スペースの代わりにタブでインデントします。 |
| `semi` | `true` | 文末のセミコロン。 |
| `singleQuote` | `false` | エスケープが少なくなる場合は`'`を`"`より優先します。 |
| `trailingComma` | `'all'` | `'all'`、`'es5'`、`'none'`のいずれか。 |
| `bracketSpacing` | `true` | オブジェクトの波括弧内のスペース: `{ a }`または`{a}`。 |
| `arrowParens` | `'always'` | 引数が1つのアロー関数で、`'always'`または`'avoid'`を使います。 |

## CSS

`formatSource($css, 'css')`はprettierのスタイルシートレイアウトでスタイルシートを整形します。セレクターリストではセレクターを1行に1つずつ配置し、宣言も1行に1つずつ並べます。結合子と値の間隔を正規化し、単純な値リストは行を埋めるように配置します。影とグラデーションのカンマ区切りリストは1行に1項目ずつ並べ、関数の引数は全体をまとめて折り返します。空行とコメントは維持し、Tailwindの`@apply`のような不明なat-ruleの前置部分には手を加えません。長い複合セレクターは子孫結合子の位置で改行し、長すぎる擬似クラスの引数は括弧内で折り返します。

## JSON

`formatSource($json, 'json')`はJavaScriptパーサーとプリンターを再利用し、prettierのJSONルールで整形します。キーは引用符付きのまま、`singleQuote`に関係なく文字列は常に二重引用符になり、末尾カンマは使われず、ドキュメント内の`//`コメントは保持されます。

## レイアウトの規則

プリンターはprettierのドキュメントモデルを実装しています。グループは行幅に収まる場合は展開され、収まらない場合は改行されます。関数呼び出しでは末尾の関数またはオブジェクト引数に他の引数が寄り添い、長くなりすぎたメンバーチェーンは各ドットの前で改行されます。複数行で記述されたオブジェクトは複数行のままになり、文の間に保持される空行は最大1行です。対応するオプションを指定してprettierで整形された入力は、実際のコードでもバイト単位で一致します。適合性テストでは、prettier自体で生成したフィクスチャと厳密に照合しています。

## コメント

コメントはトークン化中に収集され（`Parser::setCollectComments(true)`、ツール向けにパーサー上で参照可能）、先行、後続、ぶら下がりのいずれかとしてツリーに再付与されます。行コメントは元の行に残り、同じ行の末尾にあるコメントはその文に付きます。コメント周辺の空行も維持され、`/** ... */`ブロックは再インデントされ、`*`の位置も揃います。

## 保証事項

整形後の出力には次の3つの不変条件があり、テストスイートで検証されています:

- 出力を再解析すると、位置情報を除いて同等のプログラムになります。
- 入力中のすべてのコメントが出力にも残ります。
- 整形は冪等です。整形済みの出力を再整形しても変化しません。

## パフォーマンス

整形はパーサー並みの速度で実行されます。実際のファイルは数十ミリ秒で整形でき、一般的なプロジェクトの手書きJavaScriptも、単一プロセスで最初から最後まで通常1秒未満で整形されます。
