
# Форматувальник

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` задає мову: `script` або `module` для JavaScript, `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`        | Кількість пробілів на рівень відступу (і виміряна ширина табуляції).     |
| `useTabs`       | `false`    | Використовувати табуляції замість пробілів для відступів.                            |
| `semi`          | `true`     | Крапки з комою після інструкцій.                                          |
| `singleQuote`   | `false`    | Надавати перевагу `'` над `"`, якщо потрібно менше екранувань.              |
| `trailingComma` | `'all'`    | `'all'`, `'es5'` або `'none'`.                                 |
| `bracketSpacing`| `true`     | Пробіли всередині фігурних дужок об’єкта: `{ a }` проти `{a}`.             |
| `arrowParens`   | `'always'` | `'always'` або `'avoid'` для стрілкових функцій з одним параметром.  |

## CSS

`formatSource($css, 'css')` форматує таблиці стилів за правилами розмітки таблиць стилів prettier: один селектор на рядок у списках селекторів, одне оголошення на рядок, нормалізовані пробіли навколо комбінаторів і значень, компактні списки простих значень, списки тіней і градієнтів із комою після кожного елемента на окремому рядку, аргументи функцій або всі в одному рядку, або кожен на окремому, збереження порожніх рядків і коментарів, а також незмінені прелюдії невідомих at-правил, як-от Tailwind `@apply`. Довгі складені селектори розбиваються на комбінаторах нащадків, а надто довгі аргументи псевдокласів переносяться всередині дужок.

## JSON

`formatSource($json, 'json')` повторно використовує парсер і принтери JavaScript із правилами JSON від prettier: ключі залишаються в лапках, рядки завжди беруться в подвійні лапки незалежно від `singleQuote`, кінцевих ком немає, а коментарі `//` усередині документа зберігаються.

## Правила розмітки

Принтер реалізує модель документа prettier: групи згортаються, якщо вміщуються у ширину друку, інакше переносяться; аргументи виклику прилягають до кінцевого аргументу-функції або об’єкта; ланцюжки властивостей розриваються перед кожною крапкою, якщо виходять за межі рядка; об’єкти, записані в кількох рядках, залишаються багаторядковими; між інструкціями зберігається не більш ніж один порожній рядок. Для коду, відформатованого prettier з відповідними параметрами, форматувальник створює байт-у-байт ідентичний результат на реальному коді; набір перевірок відповідності підтверджує це за допомогою зразків, згенерованих самим prettier.

## Коментарі

Коментарі збираються під час токенізації (`Parser::setCollectComments(true)`, доступні в парсері для інструментів) і повертаються до дерева як початкові, кінцеві або висячі вкладення. Коментарі рядка залишаються на своїх рядках, кінцеві коментарі в тому самому рядку залишаються при своїй інструкції, порожні рядки навколо коментарів зберігаються, а блоки `/** ... */` отримують відступи, вирівняні за полем `*`.

## Гарантії

Для кожного відформатованого результату виконуються три інваріанти, перевірені набором тестів:

- Результат повторно розбирається в еквівалентну програму (без урахування розташування).
- Усі коментарі з вхідного коду зберігаються у результаті.
- Форматування ідемпотентне: повторне форматування результату нічого не змінює.

## Продуктивність

Форматування працює зі швидкістю парсера: файли з реального світу форматуються за десятки мілісекунд, а типовий написаний вручну JavaScript у звичайному проєкті повністю форматується за значно менше ніж секунду в одному процесі.
