Библиотеки Remark и Rehype в экосистеме JavaScript предназначены для обработки разметки Markdown и HTML соответственно. Remark работает с Markdown AST (Abstract Syntax Tree), а Rehype — с HTML AST. Несмотря на их высокую совместимость и возможность конвейерной обработки через unified, возникают ситуации, когда спецификации этих форматов могут конфликтовать, что требует внимательного подхода.
paragraph,
heading, list, listItem,
code и link. Каждая нода несет строго
типизированную структуру, где вложенные элементы строго ограничены.element, text,
comment, с поддержкой произвольных атрибутов. В отличие от
MDAST, HAST допускает более свободное вложение элементов.Конфликты начинаются тогда, когда Markdown-параметры не имеют прямого
соответствия в HTML, или наоборот. Например, нода strong в
MDAST всегда должна содержать текст, а в HAST strong может
быть пустой или содержать вложенные элементы em,
span, a.
Remark предоставляет плагин remark-rehype, который
преобразует MDAST в HAST. Основные шаги этого процесса:
Создание соответствий нод Каждая MDAST-нода сопоставляется с HAST-нодой. Примеры:
paragraph → pheading → h1–h6 в зависимости
от уровняinlineCode → codeОбработка вложенности MDAST поддерживает
ограниченную вложенность (list → listItem →
paragraph), тогда как HAST допускает более свободное
сочетание тегов. Remark использует промежуточные преобразования, чтобы
сохранить смысл Markdown, не нарушая структуру HTML.
Событие конфликта типов Если в MDAST встречается
нестандартная нода, например кастомный плагин добавил
youtubeEmbed, remark-rehype не сможет
автоматически создать подходящую HAST-ноду. В этом случае используется
h-name и h-properties, позволяющие задать точное имя
HTML-тега и атрибуты:
const remark = require('remark');
const rehype = require('rehype');
const remarkRehype = require('remark-rehype');
const processor = remark()
.use(customPlugin)
.use(remarkRehype, {
handlers: {
youtubeEmbed(node) {
return {
type: 'element',
tagName: 'iframe',
properties: { src: node.url, frameborder: 0 },
children: []
};
}
}
});MDAST не поддерживает произвольные атрибуты HTML. При конверсии в HAST возникают следующие проблемы:
id и class для заголовков и
параграфов должны задаваться через данные свойства
(data или hProperties).Пример разрешения конфликта:
const remark = require('remark');
const rehype = require('rehype');
const remarkRehype = require('remark-rehype');
const processor = remark()
.use(remarkRehype, {
handlers: {
heading(node) {
const hNode = {
type: 'element',
tagName: `h${node.depth}`,
properties: node.data?.hProperties || {},
children: node.children.map(child => ({
type: 'text',
value: child.value
}))
};
if (!hNode.properties.className) {
hNode.properties.className = ['default-heading'];
}
return hNode;
}
}
});
Здесь мы гарантируем, что каждый заголовок будет иметь хотя бы один CSS-класс, предотвращая конфликт с плагинами Rehype, которые могут манипулировать классами.
Markdown ограничивает вложенность списков и заголовков строгими правилами синтаксиса, а HTML допускает произвольную вложенность. При конверсии возможны следующие ситуации:
Markdown list → HAST list
listItem → paragraph →
text.li с div и произвольными
элементами. Решение: либо оборачивать весь контент listItem
в p, либо разрешить прямое вложение блоков.Markdown link → HTML link с вложенными тегами
link содержит только текст или
inlineCode.strong или em.
Решение: использовать hChildren для ручного преобразования
вложенных нод.hProperties и
hName — явное управление именами тегов и
атрибутами.remark-rehype handlers.data.hProperties, чтобы Rehype мог корректно их
использовать.После конверсии HAST в HTML через rehype-stringify важно
учитывать:
element без children могут
сериализоваться как <tag></tag> или
<tag /> в зависимости от настроек.hProperties, должны быть
корректно сериализованы в HTML-атрибуты.number вместо
string в className) может вызвать ошибки в
браузере или при рендеринге React.data.hProperties).remark-rehype, с
явными обработчиками для нестандартных нод.hName, hProperties,
проверка вложенности.rehype-stringify,
проверка корректности атрибутов и структуры.Эта последовательность минимизирует конфликты спецификаций и обеспечивает стабильный конвейер обработки Markdown и HTML.