Remark и Rehype предоставляют мощные возможности для обработки Markdown и HTML, позволяя расширять функциональность через плагины. Одной из ключевых задач при разработке плагинов является работа с метаданными документа, которые могут включать информацию о заголовках, авторах, датах публикации, тегах, а также служить для передачи данных между различными этапами обработки AST (Abstract Syntax Tree).
Метаданные в Remark и Rehype обычно хранятся в объекте
data, ассоциированном с корневым узлом AST
(root). В Remark это объект VFile.data, в
Rehype — аналогичная структура внутри node.data.
Пример структуры метаданных в Remark:
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
const processor = unified()
.use(remarkParse)
.use(() => (tree, file) => {
file.data.title = "Пример документа";
file.data.tags = ["javascript", "remark"];
})
.use(remarkStringify);
const markdown = "# Заголовок";
const file = processor.processSync(markdown);
console.log(file.data.title); // "Пример документа"
Ключевой момент: file.data сохраняет любые
пользовательские данные, что позволяет плагинам обмениваться
информацией без изменения самого контента.
Markdown-документы часто содержат frontmatter —
YAML-блок в начале файла, используемый для хранения метаданных. Remark
предоставляет плагин remark-frontmatter и
remark-mdx-frontmatter для его разбора.
Пример извлечения метаданных:
import frontmatter from 'remark-frontmatter';
import yaml from 'js-yaml';
unified()
.use(remarkParse)
.use(frontmatter, ['yaml'])
.use(() => (tree, file) => {
tree.children.forEach(node => {
if (node.type === 'yaml') {
const data = yaml.load(node.value);
file.data.metadata = data;
}
});
});
В этом примере YAML-блок автоматически парсится и сохраняется в
file.data.metadata. Такой подход позволяет использовать
метаданные в дальнейшем, например, при генерации оглавления или
добавлении динамических элементов на основе тегов.
Remark и Rehype обеспечивают возможность сквозного хранения данных, благодаря чему один плагин может собирать информацию, а другой использовать её для трансформации AST.
Пример передачи метаданных между плагинами:
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
const addMetadata = () => (tree, file) => {
file.data.custom = { processed: true };
};
const useMetadata = () => (tree, file) => {
if (file.data.custom?.processed) {
tree.children.push({
type: 'paragraph',
children: [{ type: 'text', value: 'Документ обработан с использованием метаданных' }],
});
}
};
unified()
.use(remarkParse)
.use(addMetadata)
.use(useMetadata)
.use(remarkStringify)
.processSync('# Заголовок');
Здесь первый плагин устанавливает флаг processed, а
второй использует его для добавления нового параграфа. Это
демонстрирует, как метаданные позволяют согласованно расширять
функциональность без модификации исходного документа
напрямую.
Помимо глобальных метаданных, Remark и Rehype позволяют хранить данные непосредственно в узлах AST. Это полезно для анализа конкретных элементов документа, таких как заголовки, ссылки или изображения.
Пример добавления метаданных к узлам:
const annotateHeadings = () => (tree) => {
visit(tree, 'heading', node => {
node.data = node.data || {};
node.data.level = node.depth;
node.data.id = node.children.map(c => c.value).join('-').toLowerCase();
});
};
Метаданные узлов (node.data) могут содержать
идентификаторы, уровни заголовков, ссылки на внешние ресурсы или любые
другие дополнительные сведения. Это позволяет динамически
формировать оглавления, карты ссылок и аннотации.
file.data для глобальных
метаданных документа.node.data для локальных
метаданных, связанных с конкретными узлами.file.data.myPlugin = { ... }).Rehype работает аналогично Remark, но с HTML AST (hast).
Метаданные в Rehype также хранятся в node.data и могут
использоваться для трансформации HTML.
Пример добавления атрибута к каждому элементу параграфа на основе метаданных:
const addParagraphData = () => (tree) => {
visit(tree, 'element', node => {
if (node.tagName === 'p') {
node.data = node.data || {};
node.data.customId = `p-${Math.random().toString(36).substr(2, 5)}`;
}
});
};
Такой подход позволяет строить динамические ID для элементов HTML, которые могут использоваться в скриптах, стилях или при генерации интерактивного контента.
file.data) и локальными
(node.data) данными обеспечивает чистую архитектуру
плагинов.Метаданные становятся ключевым инструментом при создании сложных плагинов, обеспечивая согласованное управление дополнительной информацией на всех этапах трансформации Markdown и HTML.