Remark и Rehype — это мощные экосистемы для обработки Markdown и HTML в JavaScript. Одной из ключевых задач при работе с этими библиотеками является трансформация путей, например, ссылок и изображений, что позволяет интегрировать контент с локальной файловой системой, CDN или другими источниками.
AST (Abstract Syntax Tree) Remark и Rehype
используют дерево синтаксического анализа (AST) для представления
документа. Для Markdown используется mdast (Markdown AST),
для HTML — hast (HTML AST). В узлах дерева хранятся
свойства элементов, включая пути к ресурсам:
link.url для ссылокimage.url для изображенийПлагины Трансформация осуществляется через плагины, которые принимают AST и изменяют его узлы. Плагины могут быть синхронными или асинхронными.
Абсолютные и относительные пути
path.resolve, path.join).Замена ресурсов на CDN или локальные копии
Обход дерева AST
unist-util-visit для обхода
Markdown AST.hast-util-visit для обхода HTML
AST. Это позволяет находить все узлы с путями и изменять их
программно.import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
import { visit } from 'unist-util-visit';
import path from 'path';
const markdown = `
[Ссылка на файл](./docs/file.md)

`;
const processor = unified()
.use(remarkParse)
.use(() => tree => {
visit(tree, 'link', node => {
node.url = path.join('/static', node.url);
});
visit(tree, 'image', node => {
node.url = path.join('/static', node.url);
});
})
.use(remarkStringify);
processor.process(markdown).then(file => {
console.log(String(file));
});
Разбор примера:
visit(tree, 'link', callback) — обходит все ссылки
(link) в AST.node.url = path.join('/static', node.url) —
трансформирует относительный путь в путь внутри папки
/static.image.HTML AST строится через hast. Пути к изображениям и
ссылкам хранятся в атрибутах src и href.
import { unified } from 'unified';
import rehypeParse from 'rehype-parse';
import rehypeStringify from 'rehype-stringify';
import { visit } from 'hast-util-visit';
import path from 'path';
const html = `
<a href="./docs/page.html">Страница</a>
<img src="./images/photo.png" alt="Фото">
`;
const processor = unified()
.use(rehypeParse, { fragment: true })
.use(() => tree => {
visit(tree, 'element', node => {
if (node.tagName === 'a' && node.properties.href) {
node.properties.href = path.join('/static', node.properties.href);
}
if (node.tagName === 'img' && node.properties.src) {
node.properties.src = path.join('/static', node.properties.src);
}
});
})
.use(rehypeStringify);
processor.process(html).then(file => {
console.log(String(file));
});
Ключевые моменты:
element.tagName позволяет фильтровать конкретные
элементы (a, img).node.properties содержит все атрибуты элемента, включая
пути.fs.existsSync.async плагин с await.remark-rehype
для конвейера: Markdown → HTML → трансформация путей.import remarkRehype from 'remark-rehype';
const processor = unified()
.use(remarkParse)
.use(() => tree => {
visit(tree, 'image', node => {
node.url = `/cdn/images/${path.basename(node.url)}`;
});
})
.use(remarkRehype)
.use(() => tree => {
visit(tree, 'element', node => {
if (node.tagName === 'a' && node.properties.href.startsWith('./')) {
node.properties.href = `/docs/${node.properties.href.slice(2)}`;
}
});
})
.use(rehypeStringify);
В этом конвейере Markdown изображения перенаправляются на CDN, а HTML-ссылки преобразуются в абсолютные пути для документации. Такой подход гарантирует единообразие всех путей на выходе.
visit для обхода AST, это
универсальный метод и для Remark, и для Rehype.path.join и path.resolve для
безопасного формирования путей между ОС.Трансформация путей — базовый, но критически важный этап при подготовке контента для веб-приложений и статических сайтов на основе Remark и Rehype. Она обеспечивает правильную загрузку ресурсов и единообразие ссылок в проекте.