
# Форматтер

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

Параметры разметки соответствуют 3.x prettier и по умолчанию используют значения 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-rule, например Tailwind из `@apply`. Составные селекторы, не помещающиеся в строку, переносятся после комбинаторов потомков, а слишком длинные аргументы псевдоклассов переносятся внутри скобок.

## JSON

`formatSource($json, 'json')` повторно использует парсер JavaScript и форматтеры с правилами JSON от prettier: ключи остаются в кавычках, строки — в двойных кавычках независимо от `singleQuote`, завершающие запятые не появляются, а комментарии `//` внутри документа сохраняются.

## Правила форматирования

Форматтер реализует модель документов prettier: группы разворачиваются, если помещаются в ширину строки, и переносятся в противном случае; аргументы вызова примыкают к следующему аргументу-функции или объекту; цепочки свойств переносятся перед каждой точкой, если не помещаются в строку; объекты, записанные в несколько строк, остаются многострочными; между инструкциями сохраняется не более одной пустой строки. На коде, отформатированном prettier с совпадающими параметрами, результат побайтно совпадает для реальных программ; соответствие проверяется набором тестов на фикстурах, созданных самим prettier.

## Комментарии

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

## Гарантии

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

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

## Производительность

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