Библиотеки Remark и Rehype
используют абстрактное синтаксическое дерево (AST) для представления
Markdown или HTML-документов. Структура AST стандартизирована через
спецификацию unist, где каждый узел имеет тип
type, а некоторые узлы могут содержать детей в массиве
children. Для анализа и модификации AST применяется пакет
unist-util-visit, который обеспечивает удобный способ
обхода всех узлов дерева.
Для работы с обходом дерева необходимо установить соответствующий пакет:
npm install unist-util-visit
Подключение в коде:
const visit = require('unist-util-visit');
Функция visit принимает дерево и функцию-обработчик,
которая вызывается для каждого узла определённого типа.
visit(tree, [test], visitor)
Аргументы:
tree — корень AST, обычно возвращаемый
remark.parse() или rehype.parse().test — опциональный фильтр по типу узла. Может быть
строкой (например, 'text'), массивом типов или функцией
(node) => boolean.visitor — функция, вызываемая для каждого узла,
подходящего под фильтр.Функция visitor принимает три
аргумента:
function visitor(node, index, parent) {
// node — текущий узел
// index — позиция узла в родителе
// parent — родительский узел
}
const remark = require('remark');
const tree = remark.parse('Пример текста с **жирным** выделением.');
visit(tree, 'text', (node) => {
console.log(node.value);
});
В результате будут выведены все текстовые фрагменты, включая содержимое элементов Markdown, таких как параграфы или жирный текст.
visit поддерживает массив типов узлов для обхода:
visit(tree, ['text', 'emphasis'], (node) => {
console.log(node.type, node.value || '');
});
Такой подход позволяет одновременно обрабатывать несколько категорий узлов без необходимости писать несколько обходов.
Можно передавать функцию для динамической фильтрации узлов:
visit(tree, node => node.type === 'link' && node.url.includes('example.com'), (node) => {
console.log(`Ссылка на example.com: ${node.url}`);
});
Это особенно удобно при сложных условиях поиска узлов по различным свойствам, таким как атрибуты ссылок, заголовков или изображений.
visitor позволяет изменять узлы на лету:
visit(tree, 'text', (node) => {
node.value = node.value.toUpperCase();
});
После такой операции все текстовые узлы AST будут преобразованы в верхний регистр, и при последующем рендеринге Markdown или HTML отобразятся уже изменёнными.
Функция visitor может возвращать специальные значения
для управления обходом:
visit.SKIP — пропустить обход дочерних узлов текущего
узла.visit.EXIT — завершить обход всего дерева
полностью.Пример:
visit(tree, 'paragraph', (node) => {
if (node.children.length === 0) {
return visit.SKIP; // Пропускаем пустые параграфы
}
});
Аргументы index и parent позволяют
модифицировать дерево безопасно:
visit(tree, 'text', (node, index, parent) => {
if (node.value.includes('важно')) {
parent.children[index] = { type: 'strong', children: [node] };
}
});
В этом примере текстовый узел, содержащий слово “важно”,
оборачивается в узел strong. Такой подход применим для
автоматического форматирования или генерации аннотаций.
unist-util-visit часто используется совместно с другими
инструментами:
Пример с visit-parents:
const visitParents = require('unist-util-visit-parents');
visitParents(tree, 'text', (node, ancestors) => {
const paragraph = ancestors.find(n => n.type === 'paragraph');
if (paragraph) {
console.log('Текст внутри параграфа:', node.value);
}
});
visit обеспечивает рекурсивный обход AST с фильтрацией
по типам узлов.SKIP и
EXIT дают контроль над обходом.visit-parents,
unist-util-select) открывает мощные возможности для анализа
и трансформации Markdown и HTML.unist-util-visit является фундаментальной утилитой для
работы с AST в экосистеме Remark/Rehype, обеспечивая гибкий и
эффективный механизм обхода и модификации узлов.