HTML на выходе
Движок сериализует дерево BEMJSON в минимальный HTML5 без служебных отступов. Переносы из текста, кода и raw сохраняются, поэтому результат не обязательно состоит из одной строки.
Минимизация по 30 правилам
Правила описывают сериализацию дерева. Допустимость структуры зависит от входного дерева и шаблонов. Проверяйте конкретный артефакт командой валидации; тесты минимизации не подтверждают валидность любого документа.
Обёртка документа
- При
document: trueпервыми байтами добавляется<!doctype html>. - Открывающий
<html>опускается, если у него нет выводимых атрибутов или классов и начальный комментарий не переместится за пределы корневого элемента. - Открывающий
<head>опускается, если у него нет выводимых атрибутов или классов и он пуст либо начинается с элемента. - Открывающий
<body>опускается, если у него нет выводимых атрибутов или классов и он пуст либо начинается не с пробельного ASCII-символа, комментария или элементаmeta,noscript,link,script,style,template. - При
document: trueдвижок добавляет два перевода строки. Фрагмент без этой опции их не получает. Граница последнего абзаца или пустой обёртки сохраняется явно, если без неё эти переводы строки попали бы в другой элемент.
Сериализатор рассматривает готовые дочерние узлы после разворачивания массивов и фрагментов tag: false; пустые позиции и пустые строки ничего не выводят. Атрибутами считаются в том числе БЭМ-классы и классы примесей, явный cls и сгенерированный data-bem: открывающий тег с любым из них сохраняется. При отдельном рендеринге head/body обёртки сохраняются, поскольку родитель неизвестен. Доверенный raw выводится без изменений; соседний тег сохраняется, если содержимое raw не позволяет доказать безопасность опускания.
Пустые элементы
area, base, br, col, embed, hr, img, input, link, meta, param, source, track, wbr:
<имя атрибуты>— без/>.- Внутри SVG пустые
path,circle,ellipse,line,polygon,polyline,rect,useиstopиспользуют/>. У других элементов SVG закрывающий тег сохраняется. Для потомковforeignObjectдействуют правила HTML; вложенныйsvgснова включает правила SVG.
В поддерживаемый набор пустых тегов также входит устаревший param.
При сопоставлении имён тегов и логических атрибутов регистр ASCII-букв не учитывается; авторское написание сохраняется. Поэтому IMG остаётся пустым тегом, а смешанный регистр SVG и foreignObject не меняет контекст сериализации. Имена атрибутов с Unicode не превращаются в известные логические имена.
Логические атрибуты
muted, autoplay, playsinline, async, defer, disabled, checked, selected, …:
- Только имя, без
=и значения.
У hidden есть строковые значения: например, until-found сохраняется. В attrs значение hidden: true выводит атрибут без значения, а hidden: false опускает его.
Кавычки у атрибутов
Сначала движок экранирует символы HTML, затем выбирает внешние кавычки для уже закодированного значения:
- значение пустое →
alt=""(кавычки обязательны); - кавычки нужны, если значение содержит любой из символов: пробельный символ,
",',`,=,<,>; - иначе — без кавычек:
имя=значение. - Если кавычки нужны, используются двойные
"...".
Запятая в значении не требует кавычек — data-tags=a,b остаётся без них.
Исходные ", < и > к этому моменту превращены в HTML-сущности и сами по себе не требуют внешних кавычек. Например, значение из одного " может выводиться как data-x=". Генерируемый data-bem всегда берётся в кавычки, как описано ниже.
Опускание закрывающих тегов
</html>и</body>опускаются, если следующий комментарий не переместится к другому родителю.</head>сохраняется перед пробельным ASCII-символом или комментарием. На границе с непрозрачнымrawтег сохраняется, если безопасность опускания неизвестна.</li>опускается перед следующимliи в концеul,olилиmenu. Правило едино для списков Markdown и шаблонов. ПолеtightListсохранено для совместимости и больше не меняет сериализацию.</td>и</th>опускаются перед следующимtd/thили в конце строки.</tr>опускается перед следующимtrили в конце таблицы либо группы строк.</thead>опускается непосредственно передtbodyилиtfoot.</tbody>опускается перед следующимtbodyилиtfoot, либо в конце таблицы.</tfoot>опускается в конце таблицы.</p>опускается перед тегами из стандарта HTML и в конце известного HTML-родителя, кромеa,audio,del,ins,map,noscript,videoили автономного пользовательского элемента. Он сохраняется перед текстом, пробелами, строчными элементами и непрозрачнымraw, в инородном содержимом и во фрагменте с неизвестным родителем. Служебные переводы строки остаются вне последнего абзаца.- Остальные необязательные закрывающие теги опускаются по таблице ниже. Обязательные закрывающие теги сохраняются.
| Закрывающий тег | Опускается перед |
|---|---|
dt | dt или dd |
dd | dt, dd или концом группы определений |
rt, rp | rt, rp или концом ruby |
option | option, optgroup, hr или концом контейнера вариантов |
optgroup | optgroup, hr или концом родителя внутри select |
caption, colgroup | Всем, кроме пробельного ASCII-символа или комментария; перед неизвестным raw тег сохраняется |
Открывающий <colgroup> без выводимых атрибутов опускается, только если первый дочерний узел — col, а предыдущий соседний узел — не другой colgroup с опущенным закрывающим тегом. Открывающий <tbody> без выводимых атрибутов опускается, только если первый дочерний узел — tr, а предыдущий соседний узел — не thead, tbody или tfoot с опущенным закрывающим тегом. Пустые группы и группы, начинающиеся с текста или комментария, сохраняют открывающий тег. Эти ограничения не дают соседним группам слиться.
Правила опускания относятся к HTML-элементам, а не к элементам SVG или MathML. Для HTML-потомков в точках интеграции SVG/MathML снова действуют правила HTML. Минимизация предполагает допустимое входное дерево и не исправляет нарушения модели содержимого. Регрессионные тесты сравнивают структуру, пространства имён, атрибуты, текст и комментарии после разбора с выводом, в котором все теги заданы явно.
Пробелы и комментарии
- Движок не добавляет между элементами пробелы, отступы и служебные переносы.
- Буквальные пробелы из исходника и шаблона сохраняются (внутри текста и атрибутов не вырезаются).
- Комментарии HTML (
<!-- -->) не выбрасываются и не переформатулируются — выводятся как есть. - Внутри
<pre><code>внутренний текст сохраняется как есть, включая завершающий\n.
Особые случаи
- Узлы
rawвыводятся без изменений — без экранирования и без минимизации. - Сериализатор сохраняет встроенный HTML, переданный через
raw, в том числе одинарные кавычки. Мост Markdown может переписать пути изображений до сериализации. - Скрипты внутри
rawсохраняются дословно; обычный строковыйcontentэкранируется.
Частные случаи
- Значение
dу пути SVG (d="m14.5...c...") содержит последовательности видаc/— их нельзя разбирать как теги. data-bemвсегда выводится в двойных кавычках.- Символы UTF-8 подставляются напрямую (
—, кириллица); сериализатор экранирует текст и значения атрибутов; доверенныйrawостаётся без изменений.
Почему не html-minifier
Движок минимизирует типизированное дерево во время сериализации, поэтому отдельный постобработчик готовой строки ему не требуется. Это архитектурный выбор, а не доказательство того, что другие инструменты отклоняют любой HTML с пропущенными необязательными тегами. Подробнее — в решении о модели данных.
Один режим
Отдельного режима разработки с «красивым» HTML нет. В продакшене используется тот же режим: вывод всегда минимальный. Отлаживать страницу удобнее через режим output: 'bemjson' (см. двухпроходный рендеринг), а не через форматированный HTML.