Remark и Rehype — это экосистемы для обработки Markdown и HTML в JavaScript, основанные на концепции универсальных синтаксических деревьев (unist). Центральным элементом их работы является композиция плагинов, которая позволяет создавать цепочки обработки текста от парсинга до генерации выходного кода.
Каждый плагин — это функция, которая принимает узел синтаксического дерева и контекст, а затем изменяет или анализирует дерево. Плагины могут быть двух типов:
Функция плагина может иметь следующую сигнатуру:
function myPlugin(options) {
return function transformer(tree, file) {
// tree — это AST, file — объект VFile
}
}
tree — синтаксическое дерево документа.file — объект VFile, содержащий информацию
о файле, включая путь, исходный текст и сообщения об ошибках.Плагины могут быть синхронными и
асинхронными. Асинхронные плагины возвращают
Promise.
Композиция плагинов позволяет создавать цепочку обработчиков, где каждый плагин выполняется последовательно. Стандартная схема:
Пример цепочки Remark:
import { remark } from 'remark';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
import remarkSlug from 'remark-slug';
const processor = remark()
.use(remarkParse) // парсинг Markdown в AST
.use(remarkSlug) // добавление id заголовкам
.use(remarkStringify); // генерация Markdown из AST
const output = processor.processSync('# Заголовок').toString();
Здесь remarkSlug действует между парсингом и генерацией,
изменяя AST, но не вмешиваясь напрямую в чтение или запись файла.
1. Порядок подключения плагинов
Порядок важен, потому что каждый плагин видит AST, которое уже изменено предыдущими плагинами. Например:
2. Передача параметров
Большинство плагинов поддерживают конфигурацию через объект опций:
remarkSlug({ prefix: 'section-' });
Это позволяет гибко настраивать поведение плагина без изменения его кода.
3. Асинхронные плагины
Для асинхронной работы используется .process вместо
.processSync:
const output = await remark()
.use(remarkParse)
.use(asyncPlugin)
.use(remarkStringify)
.process('# Асинхронный пример');
Если хотя бы один плагин асинхронный, вся цепочка должна выполняться асинхронно.
Rehype работает по аналогичному принципу, но с HTML AST:
import { rehype } from 'rehype';
import rehypeParse from 'rehype-parse';
import rehypeStringify from 'rehype-stringify';
import rehypeHighlight from 'rehype-highlight';
const processor = rehype()
.use(rehypeParse, { fragment: true }) // парсинг HTML
.use(rehypeHighlight) // подсветка кода
.use(rehypeStringify); // генерация HTML
const html = processor.processSync('<pre><code>const x = 1;</code></pre>').toString();
Особенность Rehype в том, что дерево представляет собой HTML-структуру, а не Markdown. Плагины могут изменять узлы, добавлять атрибуты, классы, обрабатывать элементы по селекторам.
Композиция позволяет строить сложные пайплайны:
remark-rehype для
конвертации Markdown → HTML.Пример объединения:
import remark from 'remark';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
const processor = remark()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeAutolinkHeadings, { beh * avior: 'wrap' })
.use(rehypeStringify);
const html = processor.processSync('# Заголовок').toString();
Здесь Markdown сначала парсится в mdast, затем конвертируется в hast, и только после этого применяются Rehype-плагины.
function myCompositePlugin() {
return function transformer(tree, file) {
pluginA()(tree, file);
pluginB()(tree, file);
}
}
Композиция плагинов Remark и Rehype формирует мощный и гибкий механизм обработки Markdown и HTML. Понимание порядка подключения, типов плагинов, синхронности и конфигурации позволяет строить надежные, расширяемые и повторно используемые пайплайны для любых проектов.