В Remark и Rehype документ сначала преобразуется в синтаксическое дерево: MDAST (Markdown Abstract Syntax Tree) для Remark и HAST (HTML Abstract Syntax Tree) для Rehype. Эти деревья представляют структуру документа в виде узлов, где каждый узел имеет тип, дочерние элементы и дополнительные свойства. Пример узла MDAST:
{
"type": "heading",
"depth": 2,
"children": [
{
"type": "text",
"value": "Пример заголовка"
}
]
}
Валидация синтаксических деревьев необходима для:
Валидация выполняется на основе три уровня проверки:
type) – проверка допустимых
типов узлов для конкретного дерева.value, depth, url и
др.).children) – проверка
структуры вложенности, например, заголовок не может содержать блоки,
только текстовые узлы.Пример функции валидации узла MDAST:
function validateNode(node) {
const validTypes = ['heading', 'paragraph', 'text', 'link', 'list', 'listItem'];
if (!validTypes.includes(node.type)) {
throw new Error(`Недопустимый тип узла: ${node.type}`);
}
switch (node.type) {
case 'heading':
if (typeof node.depth !== 'number' || node.depth < 1 || node.depth > 6) {
throw new Error(`Неправильная глубина заголовка: ${node.depth}`);
}
break;
case 'link':
if (!node.url || typeof node.url !== 'string') {
throw new Error('Ссылка должна содержать корректный URL');
}
break;
}
if (node.children && Array.isArray(node.children)) {
node.children.forEach(validateNode);
}
}
Remark позволяет использовать плагины для валидации на этапе парсинга. Создание кастомного плагина включает:
tree.file.message().Пример плагина:
const visit = require('unist-util-visit');
function remarkValidate() {
return (tree, file) => {
visit(tree, (node) => {
try {
validateNode(node);
} catch (error) {
file.message(error.message, node);
}
});
};
}
const processor = require('remark')()
.use(remarkValidate);
Rehype работает аналогично, но с HAST. Валидация обычно фокусируется на:
src, alt,
href).<a> внутри <a>,
<ul> вне <li>).Пример функции валидации HAST:
function validateHastNode(node) {
const validTypes = ['element', 'text', 'comment'];
if (!validTypes.includes(node.type)) {
throw new Error(`Недопустимый тип HAST узла: ${node.type}`);
}
if (node.type === 'element') {
if (!node.tagName) {
throw new Error('Элемент должен иметь tagName');
}
if (node.children) {
node.children.forEach(validateHastNode);
}
}
}
unist-util-visit –
упрощает обход дерева и фильтрацию по типу узла.node.position в MDAST/HAST).zod или yup для строгой типизации узлов.Валидация синтаксических деревьев в Remark и Rehype является критически важным этапом для обеспечения корректности, безопасности и совместимости документов с последующей обработкой. Правильно организованный валидатор предотвращает ошибки на раннем этапе и позволяет строить надёжные системы трансформации контента.