Remark и Rehype являются универсальными инструментами для работы с деревьями синтаксиса Markdown и HTML соответственно. Создание собственного плагина позволяет гибко модифицировать структуру документа, добавлять новые элементы, фильтровать или преобразовывать содержимое на лету.
Плагин в экосистеме unified — это функция, которая возвращает функцию трансформации или объект с методами для обработки дерева. Типичная структура плагина выглядит следующим образом:
function myPlugin(options) {
return (tree, file) => {
// обработка дерева
};
}
options — объект с настройками плагина, которые можно
задавать при подключении.tree — синтаксическое дерево Markdown
(Remark) или HTML (Rehype).file — объект, содержащий информацию о файле, включая
метаданные и ошибки.Функция, возвращаемая плагином, вызывается каждый раз при обработке документа.
Дерево синтаксиса представляет собой вложенную структуру узлов. Узлы
имеют свойства type, children и специфические
атрибуты в зависимости от типа (value,
tagName, properties). Например, для
Markdown:
{
type: 'heading',
depth: 2,
children: [
{ type: 'text', value: 'Пример заголовка' }
]
}
Для HTML через Rehype:
{
type: 'element',
tagName: 'p',
children: [
{ type: 'text', value: 'Абзац текста' }
]
}
Обход дерева часто выполняется с помощью библиотеки
unist-util-visit:
import { visit } from 'unist-util-visit';
function myPlugin() {
return (tree) => {
visit(tree, 'text', (node) => {
node.value = node.value.toUpperCase();
});
};
}
В этом примере каждый узел текста преобразуется в верхний регистр.
Плагины обычно принимают конфигурацию через объект
options, чтобы поведение можно было изменять без
переписывания кода:
function replaceTextPlugin(options = {}) {
const { find = '', replace = '' } = options;
return (tree) => {
visit(tree, 'text', (node) => {
node.value = node.value.replace(new RegExp(find, 'g'), replace);
});
};
}
Подключение и использование:
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
const processor = unified()
.use(remarkParse)
.use(replaceTextPlugin, { find: 'foo', replace: 'bar' })
.use(remarkStringify);
const result = processor.processSync('foo baz').toString();
// Результат: 'bar baz'
Плагины могут быть асинхронными, если требуется выполнять внешние запросы или сложные вычисления:
function asyncPlugin() {
return async (tree) => {
const nodes = [];
visit(tree, 'text', (node) => {
nodes.push(node);
});
for (const node of nodes) {
const newValue = await fetchSomeData(node.value);
node.value = newValue;
}
};
}
Для интеграции с асинхронными плагинами необходимо использовать
process вместо processSync.
Rehype-плагины строятся аналогично, но работают с узлами
element и text:
function addClassToParagraphs() {
return (tree) => {
visit(tree, 'element', (node) => {
if (node.tagName === 'p') {
node.properties = node.properties || {};
node.properties.className = [
...(node.properties.className || []),
'custom-paragraph'
];
}
});
};
}
Это позволяет динамически добавлять классы, атрибуты или изменять структуру HTML без ручного редактирования исходного кода.
Remark и Rehype можно комбинировать через
remark-rehype:
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
const processor = unified()
.use(remarkParse)
.use(replaceTextPlugin, { find: 'foo', replace: 'bar' })
.use(remarkRehype)
.use(addClassToParagraphs)
.use(rehypeStringify);
В этом примере Markdown обрабатывается Remark-плагином, затем преобразуется в HTML и модифицируется Rehype-плагином.
visit или
unist-util-visit-parents для обхода дерева.properties
для модификации атрибутов HTML.Этот подход к построению плагинов позволяет создавать мощные инструменты для анализа и трансформации Markdown и HTML, обеспечивая высокую степень контроля над структурой документов.