Двухпроходный рендеринг
Решение
Разделить применение шаблонов и сериализацию HTML. Публичная функция render по умолчанию возвращает HTML; режим output: 'bemjson' возвращает готовое дерево BEMJSON.
Внутри движка renderToBemJson применяет все структурные режимы и режимы отображения, затем renderToHtml сериализует результат и выполняет минимизацию HTML. Эти внутренние функции не экспортируются отдельно из корня пакета.
Контекст
Если шаблоны и сериализация выполняются за один проход, сериализатору нужны результаты шаблонов соседних узлов. Выбор шаблона оказывается связан, например, с решением опустить закрывающий тег li. Явное промежуточное дерево передаёт сериализатору уже определённые теги и атрибуты.
Альтернативы
Один совмещённый проход. Он обходится без промежуточного результата, но связывает применение шаблонов с сериализацией и затрудняет отдельный просмотр готового дерева.
Отдельные языки шаблонов для данных и HTML. Они разделяют обязанности, но требуют двух правил написания шаблонов. nuckty сохраняет единый синтаксис деклараций nu.
Аргумент
Первый проход вычисляет все режимы шаблонов, включая tag, attrs, content, replace и wrap. Второй не применяет шаблонов. Результат каждого этапа можно исследовать и проверять отдельно.
Автор описывает блок в одном файле .nu.ts. Движок не позволяет управляющему шаблону повторно применяться к тому же узлу; произвольная рекурсия в пользовательском коде всё ещё возможна.
Последствия
Для просмотра готового дерева используйте render(tree, { templates, output: 'bemjson' }), для фрагмента HTML — render(tree, { templates }). Параметр document: true добавляет doctype и окончание документа.
В разработке и в продакшене действует один режим рендеринга. Второй проход не гарантирует допустимость произвольного HTML: валидация остаётся отдельной командой.