Форматтер
Phasis поставляет встроенный форматтер JavaScript на основе собственного парсера. Он заново выводит исходный код с правилами разметки, совместимыми с prettier, полностью на PHP: без Node.js, интерпретируемых пакетов форматтера и внешних процессов.
formatSource
public static function formatSource(
string $source,
string $sourceType = 'script',
?FormatOptions $options = null,
): string
Единая точка входа в Phasis\Formatter\Formatter. $sourceType выбирает язык: script или module для JavaScript, json или css. JavaScript разбирает код с указанной целью, заново привязывает комментарии и выводит его с заданной шириной строки.
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 типичного проекта форматируется от начала до конца менее чем за секунду в одном процессе.