rehype-stringify: настройка вывода

rehype-stringify является плагином для Rehype, отвечающим за преобразование дерева HTML-узлов (HAST, HTML Abstract Syntax Tree) обратно в строку HTML. Он играет ключевую роль на этапе рендеринга после обработки и трансформации HTML-документа другими плагинами Rehype.

Основная функция плагина — конвертация HAST в корректный HTML-код, с учётом структуры узлов, атрибутов, текстового содержимого и вложенности. Настройка rehype-stringify позволяет управлять форматом выходного HTML, включая пробелы, отступы, переносы строк и обработку специальных символов.


Подключение и базовое использование

import { unified } from 'unified';
import rehypeParse from 'rehype-parse';
import rehypeStringify from 'rehype-stringify';

const processor = unified()
  .use(rehypeParse, { fragment: true })
  .use(rehypeStringify);

const html = '<div><p>Пример текста</p></div>';

processor.process(html).then(file => {
  console.log(String(file));
});

В этом примере:

  • rehype-parse превращает HTML в дерево HAST.
  • rehype-stringify преобразует HAST обратно в строку HTML.

Основные опции rehype-stringify

Плагин предоставляет несколько ключевых опций для настройки формата вывода:

closeSelfClosing

Определяет стиль закрытия самозакрывающихся тегов:

.use(rehypeStringify, { closeSelfClosing: true });
  • true<img />, <br /> закрываются в стиле XHTML.
  • false<img>, <br> без слэша, стандарт HTML5.

quote

Управляет типом кавычек для атрибутов:

.use(rehypeStringify, { quote: '"' });
  • "\"" — двойные кавычки (по умолчанию).
  • "'" — одинарные кавычки.

entities

Настройка кодирования специальных символов:

.use(rehypeStringify, { entities: 'utf8' });
  • 'utf8' — вывод в UTF-8 без HTML-сущностей.
  • 'xml' — кодирование в формате XML (&lt;, &gt;, &amp;).
  • 'escape' — экранирование только специальных символов.

allowDangerousCharacters

Позволяет включать в вывод символы, которые обычно экранируются:

.use(rehypeStringify, { allowDangerousCharacters: true });

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


Форматирование и отступы

rehype-stringify по умолчанию не добавляет автоматические переносы строк или отступы. Для красивого форматирования часто применяют связку с rehype-pretty-code или rehype-format. Пример использования с кастомной функцией форматирования:

import rehypeFormat from 'rehype-format';

const processor = unified()
  .use(rehypeParse, { fragment: true })
  .use(rehypeFormat, { indent: 2 })
  .use(rehypeStringify);

processor.process('<div><p>Текст</p></div>').then(file => {
  console.log(String(file));
});
  • Опция indent задаёт количество пробелов для вложенных элементов.
  • rehypeFormat подготавливает HAST к красивому выводу, а rehype-stringify делает финальный рендер.

Обработка нестандартных тегов и атрибутов

Иногда дерево HAST содержит кастомные теги или нестандартные атрибуты. По умолчанию rehype-stringify корректно обрабатывает все узлы, но важно учитывать:

  • Атрибуты с null или undefined пропускаются.
  • Булевы атрибуты (например, checked, disabled) выводятся как checked или disabled без значения.
  • Пользовательские теги (<my-component>) выводятся без изменений, если HAST их содержит.
const processor = unified()
  .use(rehypeParse, { fragment: true })
  .use(rehypeStringify);

const html = '<my-component custom-attr="value"></my-component>';

processor.process(html).then(file => {
  console.log(String(file));
});

Результат: <my-component custom-attr="value"></my-component>


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

Для проектов, где Markdown обрабатывается через Remark, часто используется комбинация:

import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';

const processor = unified()
  .use(remarkParse)
  .use(remarkRehype)
  .use(rehypeStringify);
  • Markdown → AST Remark → AST Rehype → HTML.
  • rehype-stringify здесь является завершающим этапом.
  • Все предыдущие плагины могут добавлять атрибуты, классы, вставлять элементы, которые корректно сериализуются в HTML.

Особенности производительности

  • rehype-stringify достаточно быстрый для большинства проектов, так как его задача ограничена сериализацией HAST.
  • Для очень больших документов полезно использовать опции entities и closeSelfClosing для минимизации лишних преобразований.
  • При необходимости дополнительного форматирования стоит применять отдельные плагины (rehype-format, rehype-pretty-code), так как встроенного «pretty print» нет.

Пример комплексной настройки

const processor = unified()
  .use(rehypeParse, { fragment: true })
  .use(rehypeFormat, { indent: 4 })
  .use(rehypeStringify, {
    closeSelfClosing: false,
    quote: "'",
    entities: 'xml',
    allowDangerousCharacters: false
  });

processor.process('<div><img src="image.png"></div>').then(file => {
  console.log(String(file));
});

Результат:

<div>
    <img src='image.png'>
</div>
  • Используются одинарные кавычки.
  • Самозакрывающийся тег <img> представлен без слэша.
  • Форматирование с отступами 4 пробела.
  • Символы экранированы в стиле XML.

rehype-stringify обеспечивает гибкую и надёжную сериализацию HAST в HTML с широкими возможностями настройки формата и безопасности вывода.