HAST (Hypertext Abstract Syntax Tree) — это абстрактное синтаксическое дерево, которое представляет структуру HTML-документа. Оно используется в экосистеме Rehype для анализа, трансформации и генерации HTML. Каждая нода HAST содержит тип, свойства и дочерние элементы. Стандартная структура ноды выглядит следующим образом:
{
type: 'element' | 'text' | 'comment' | ...,
tagName: 'div' | 'p' | 'span', // для type === 'element'
properties: { className: ['example'], id: 'unique' },
children: [ /* массив дочерних нод */ ]
}
Обход дерева нужен для анализа или модификации его структуры. Существует два основных подхода: рекурсивный обход и использование специализированных утилит из экосистемы Unified.
Рекурсивный метод наиболее прямолинеен. Он позволяет пройтись по каждой ноде и при необходимости изменить её свойства или дочерние элементы.
function walk(node, callback) {
callback(node);
if (node.children && node.children.length > 0) {
node.children.forEach(child => walk(child, callback));
}
}
Пример использования:
walk(tree, node => {
if (node.type === 'element' && node.tagName === 'p') {
node.properties.className = [...(node.properties.className || []), 'highlight'];
}
});
Здесь каждое <p> получает дополнительный класс
highlight. Рекурсивный обход полезен, когда структура
дерева известна и не слишком сложна.
unist-util-visitДля крупных проектов рекурсивный обход вручную может быть неудобен.
Утилита unist-util-visit позволяет выполнять обход дерева в
стиле Visitor Pattern.
import { visit } from 'unist-util-visit';
visit(tree, 'element', node => {
if (node.tagName === 'h1') {
node.properties.id = node.children[0].value.toLowerCase().replace(/\s+/g, '-');
}
});
Особенности visit:
HAST или
MDAST).'element',
'text', 'comment').Можно использовать также фильтр по тегам:
visit(tree, node => node.type === 'element' && node.tagName === 'a', node => {
node.properties.target = '_blank';
});
HAST дерево допускает модификацию во время обхода. Это позволяет добавлять, удалять или заменять ноды:
visit(tree, 'element', (node, index, parent) => {
if (node.tagName === 'script') {
parent.children.splice(index, 1); // удаление скрипта
}
});
Параметры callback:
node — текущая нода.index — индекс ноды в массиве
parent.children.parent — родительская нода.Это важно для безопасного удаления или вставки элементов в дереве.
Для анализа больших деревьев часто используют комбинированный подход: сначала обход, затем фильтрация интересующих элементов:
const links = [];
visit(tree, 'element', node => {
if (node.tagName === 'a' && node.properties.href) {
links.push(node.properties.href);
}
});
В результате получается массив всех ссылок в HTML, что удобно для генерации карты сайта или проверки валидности ссылок.
Плагины Rehype используют обход HAST дерева для расширения возможностей:
rel="noopener" для
ссылок).Пример простого плагина:
export default function rehypeAddTargetBlank() {
return (tree) => {
visit(tree, 'element', node => {
if (node.tagName === 'a') {
node.properties.target = '_blank';
}
});
};
}
unist-util-visit| Параметр | Рекурсивный обход | unist-util-visit |
|---|---|---|
| Простота | Высокая для простых деревьев | Высокая для любых деревьев |
| Управление индексами | Нужно вручную | Передаётся автоматически |
| Удаление/вставка | Сложнее | Легко через параметры callback |
| Масштабируемость | Ограничена большими деревьями | Отличная |
forEach — лучше использовать
visit с parent и index.children или properties.unist-util-*
для упрощения кода и повышения читаемости.Обход HAST дерева — фундаментальная операция при работе с HTML в экосистеме Unified. Глубокое понимание структуры нод и эффективные методы обхода позволяют создавать безопасные, масштабируемые и функциональные плагины для Rehype.