Remark и Rehype используют представление документа в виде дерева
абстрактного синтаксиса (AST — Abstract Syntax Tree). Каждый элемент
документа — это узел (node), который имеет тип
(type) и набор свойств (properties). В Remark
узлы чаще всего представляют части Markdown, а в Rehype —
HTML-структуру. Для расширения возможностей этих библиотек часто
требуется создавать новые типы узлов, не ограничиваясь
стандартным набором.
Ключевые свойства узла:
type — строка, определяющая тип узла.children — массив дочерних узлов (для
контейнеров).value — текстовое содержимое узла (для текстовых
элементов).position — объект с информацией о позиции узла в
исходном документе (опционально, но полезно для линтеров и
трансформеров).const myCustomNode = {
type: 'highlight', // уникальный идентификатор типа узла
value: 'Важный текст',
};
Remark позволяет использовать плагины для преобразования AST.
Простейший способ — воспользоваться функцией
transformer:
function highlightPlugin() {
return (tree) => {
visit(tree, 'paragraph', (node, index, parent) => {
if (node.children.some(child => child.value.includes('важно'))) {
const highlightNode = {
type: 'highlight',
value: node.children.map(child => child.value).join(' '),
};
parent.children.splice(index, 1, highlightNode);
}
});
};
}
Здесь:
visit — функция из unist-util-visit,
позволяющая обходить узлы.tree — корневой узел AST.highlightNode — новый тип узла, заменяющий абзац с
ключевым словом.После создания узла его нужно обработать при генерации HTML или
Markdown. В Rehype это делается через кастомные обработчики
(handlers):
const rehypeStringify = require('rehype-stringify');
const handlers = {
highlight: (h, node) => h(node, 'mark', { className: 'highlight' }, node.value),
};
Здесь:
h — гипотетический фабричный метод для генерации
HTML-узлов.'mark' — HTML-тег для подсветки.className — CSS-класс для стилизации.node.value — текст, который будет отображаться.В Remark можно использовать remark-rehype, чтобы
трансформировать Markdown-узлы в HTML, при этом кастомные узлы можно
добавить в цепочку плагинов.
Часто требуется создавать не один, а несколько новых типов узлов для комплексного синтаксиса. В этом случае рекомендуется:
Пример реестра типов:
const NODE_TYPES = {
HIGHLIGHT: 'highlight',
NOTE: 'note',
ALERT: 'alert',
};
Использование в плагине:
visit(tree, 'paragraph', (node, index, parent) => {
if (node.children.some(child => child.value.startsWith('!'))) {
const alertNode = { type: NODE_TYPES.ALERT, value: node.children.map(c => c.value).join(' ') };
parent.children.splice(index, 1, alertNode);
}
});
Новые узлы могут содержать дочерние элементы. Для этого необходимо:
children как массив узлов.Пример узла с дочерними элементами:
const noteNode = {
type: 'note',
children: [
{ type: 'text', value: 'Это заметка: ' },
{ type: 'highlight', value: 'важная часть' },
],
};
Для комплексных проектов рекомендуется использовать утилиты типа
unist-util-visit и unist-util-validate.
Основные шаги:
type,
children/value).position для удобства отладки и подсветки
ошибок.Пример проверки:
function validateNode(node) {
if (!node.type) throw new Error('Node type is required');
if (node.children && !Array.isArray(node.children)) throw new Error('Children must be an array');
}
type.Расширение дерева узлов с помощью собственных типов позволяет гибко добавлять новые семантические элементы, управлять их визуализацией и интегрировать сложные Markdown/HTML-паттерны без изменения базовых библиотек.