hast-util-to-html: продвинутая сериализация

hast-util-to-html — это библиотека для сериализации абстрактного синтаксического дерева HTML (HAST) в строку HTML. Она является неотъемлемой частью экосистемы unified, remark и rehype, позволяя гибко управлять выводом HTML из структурированных данных.


Основные возможности

  • Сериализация HAST в HTML с сохранением структуры и атрибутов элементов.
  • Поддержка безопасного экранирования контента для предотвращения XSS.
  • Гибкая настройка сериализации: можно изменять обработку тегов, атрибутов и текстового содержимого.
  • Поддержка плагинов rehype, позволяя расширять функциональность и интегрировать кастомные преобразования.

Установка и подключение

npm install hast-util-to-html

Импорт в проект:

import { toHtml } from 'hast-util-to-html';

Базовый синтаксис:

const html = toHtml(hastNode, options);
  • hastNode — объект HAST, например:
const hastNode = {
  type: 'element',
  tagName: 'p',
  properties: { className: ['text'] },
  children: [
    { type: 'text', value: 'Пример текста' }
  ]
};
  • options — объект с настройками сериализации.

Настройки сериализации

hast-util-to-html предоставляет множество параметров, которые позволяют детально управлять выводом:

  1. allowDangerousHtml Если установлено в true, позволяет включать raw HTML в сериализованный результат:
const html = toHtml(hastNode, { allowDangerousHtml: true });
  1. closeSelfClosing Управляет стилем самозакрывающихся тегов. Например, <br> вместо <br />.
const html = toHtml(hastNode, { closeSelfClosing: true });
  1. quote Определяет используемые кавычки для атрибутов: ' или ".
const html = toHtml(hastNode, { quote: '"' });
  1. space Контролирует обработку пробелов в тексте:

    • "html" — стандартная сериализация HTML
    • "xml" — строгое экранирование для XML
const html = toHtml(hastNode, { space: 'html' });
  1. tightSelfClosing Убирает пробел перед закрывающим слешем у самозакрывающихся тегов:
const html = toHtml(hastNode, { tightSelfClosing: true });

Сериализация текстовых узлов

HAST разделяет элементы и текстовые узлы. Текстовые узлы имеют структуру:

{ type: 'text', value: 'Текстовый контент' }

Сериализация выполняется с экранированием специальных символов (&, <, >, ") для предотвращения ошибок в HTML. Если используется allowDangerousHtml: true, можно вставлять raw HTML напрямую, но это несет риск безопасности.


Работа с атрибутами и классами

HAST хранит атрибуты в объекте properties. Некоторые особенности:

  • Массивы значений атрибутов (например, className) сериализуются корректно:
const hastNode = {
  type: 'element',
  tagName: 'div',
  properties: { className: ['container', 'main'] },
  children: []
};

toHtml(hastNode);
// <div class="container main"></div>
  • Булевы атрибуты сериализуются без значения:
const hastNode = {
  type: 'element',
  tagName: 'input',
  properties: { disabled: true },
  children: []
};

toHtml(hastNode);
// <input disabled>

Кастомизация обработки тегов

Можно передать функцию handlers для замены стандартной сериализации конкретных тегов:

import { toHtml } from 'hast-util-to-html';

const handlers = {
  img(node, context, defaultHandler) {
    node.properties.alt = node.properties.alt || 'Изображение';
    return defaultHandler(node, context);
  }
};

const html = toHtml(hastNode, { handlers });

Это позволяет автоматически добавлять атрибуты, изменять содержимое или полностью переопределять вывод конкретных элементов.


Интеграция с rehype

hast-util-to-html идеально сочетается с rehype:

import { unified } from 'unified';
import rehypeParse from 'rehype-parse';
import rehypeSanitize from 'rehype-sanitize';
import { toHtml } from 'hast-util-to-html';

const htmlString = '<div><p>Привет</p></div>';

const hast = unified()
  .use(rehypeParse, { fragment: true })
  .use(rehypeSanitize)
  .parse(htmlString);

const result = toHtml(hast);

Таким образом, можно создавать безопасные HTML-строки из HAST после любых преобразований rehype.


Производительность и масштабируемость

  • Библиотека легковесная и быстрая, подходит для работы с большим количеством узлов.
  • Поддерживает декомпозицию дерева, что позволяет обрабатывать большие документы частями.
  • Объединение с unified и плагинами rehype позволяет строить конвейеры трансформации, где HAST может модифицироваться перед сериализацией.

Частые ошибки и подводные камни

  1. Неверная структура HAST Ошибки возникают, если узлы имеют отсутствующие свойства type или tagName.
  2. Использование allowDangerousHtml без фильтрации Прямое включение raw HTML может привести к XSS-уязвимостям.
  3. Несоответствие кавычек в атрибутах При сериализации нестандартных символов кавычки могут конфликтовать с содержимым атрибута.

hast-util-to-html обеспечивает надежную и гибкую сериализацию HAST в HTML, позволяя детально управлять выводом, безопасностью и стилем. Использование этой библиотеки в сочетании с rehype открывает широкие возможности для преобразования, фильтрации и генерации HTML в проектах на JavaScript.