Remark и Rehype — это мощные инструменты для парсинга и трансформации Markdown и HTML в экосистеме JavaScript. Одной из ключевых задач при обработке контента является генерация якорных ссылок для заголовков. Якорь позволяет ссылаться на конкретный раздел документа и автоматически формирует URL-фрагмент, который может быть использован для навигации.
В Markdown заголовки представлены синтаксисом #,
##, ### и так далее. Remark преобразует
Markdown в AST (Abstract Syntax Tree), где каждый
заголовок представлен узлом типа heading. Для генерации
якорей необходимо пройтись по дереву и добавить каждому заголовку
атрибут id.
Для генерации якорей часто используют плагин
remark-slug. Его задача — автоматически формировать
уникальные идентификаторы для заголовков:
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkSlug from 'remark-slug';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
const markdown = `
# Введение
## Основные понятия
### Подробности реализации
`;
const processor = unified()
.use(remarkParse)
.use(remarkSlug)
.use(remarkRehype)
.use(rehypeStringify);
processor.process(markdown).then(file => {
console.log(String(file));
});
После применения remark-slug заголовки будут содержать
атрибуты id, сгенерированные на основе текста
заголовка:
<h1 id="введение">Введение</h1>
<h2 id="основные-понятия">Основные понятия</h2>
<h3 id="подробности-реализации">Подробности реализации</h3>
По умолчанию remark-slug преобразует текст заголовка в
строку в стиле kebab-case, удаляя специальные символы.
Для более гибкого контроля можно использовать
remark-autolink-headings в сочетании с
remark-slug, чтобы добавлять ссылки непосредственно
к заголовкам:
import remarkAutolinkHeadings from 'remark-autolink-headings';
const processor = unified()
.use(remarkParse)
.use(remarkSlug)
.use(remarkAutolinkHeadings, {
beh * avior: 'prepend', // вставка ссылки перед заголовком
linkProperties: { className: 'anchor-link' },
content: {
type: 'text',
value: '#'
}
})
.use(remarkRehype)
.use(rehypeStringify);
В результате заголовки будут иметь кликабельный якорь:
<h2 id="основные-понятия">
<a href="#основные-понятия" class="anchor-link">#</a>
Основные понятия
</h2>
После генерации id с помощью Remark, Rehype
предоставляет возможность дополнительно модифицировать
HTML. Например, можно добавлять иконки к якорям, оборачивать
заголовки в контейнеры или генерировать карту содержимого (TOC). Работа
ведется через обход AST типа hast:
import { visit } from 'unist-util-visit';
function rehypeAddAnchorIcons() {
return (tree) => {
visit(tree, 'element', (node) => {
if (['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(node.tagName)) {
const id = node.properties.id;
if (id) {
node.children.unshift({
type: 'element',
tagName: 'span',
properties: { className: ['anchor-icon'] },
children: [{ type: 'text', value: '?' }]
});
}
}
});
};
}
После применения плагина каждый заголовок будет содержать визуальный индикатор якоря:
<h3 id="подробности-реализации">
<span class="anchor-icon">?</span>
Подробности реализации
</h3>
Иногда требуется использовать кастомные правила генерации
id, например, на основе translit или сокращений.
Для этого можно написать собственный Remark-плагин:
function remarkCustomSlug() {
return (tree) => {
visit(tree, 'heading', (node) => {
const textNode = node.children.find(n => n.type === 'text');
if (textNode) {
// Простейший translit: заменить пробелы на дефисы и привести к нижнему регистру
node.data = {
hProperties: {
id: textNode.value.toLowerCase().replace(/\s+/g, '-')
}
};
}
});
};
}
Такой подход позволяет полностью контролировать формат якорей, избегая конфликтов и дублирования.
При работе с многоязычными документами важно учитывать
юникодные символы. Некоторые стандартные плагины не
нормализуют кириллицу или иероглифы. В таких случаях рекомендуется
использовать библиотеку slugify:
import slugify from 'slugify';
function remarkI18nSlug() {
return (tree) => {
visit(tree, 'heading', (node) => {
const textNode = node.children.find(n => n.type === 'text');
if (textNode) {
node.data = {
hProperties: {
id: slugify(textNode.value, { lower: true, strict: true })
}
};
}
});
};
}
Это гарантирует, что идентификаторы будут корректны для URL, независимо от языка текста.
Генерация якорей особенно полезна в статических сайтах и документации, где Markdown автоматически конвертируется в HTML. Использование Remark + Rehype позволяет создавать динамические оглавления, реализовать плавную прокрутку по якорям и управлять стилями ссылок через CSS. Например, можно автоматически строить список всех заголовков:
import { visit } from 'unist-util-visit';
function buildTOC(tree) {
const toc = [];
visit(tree, 'heading', node => {
const textNode = node.children.find(n => n.type === 'text');
if (textNode && node.data && node.data.hProperties.id) {
toc.push({ title: textNode.value, id: node.data.hProperties.id });
}
});
return toc;
}
Результат можно использовать для генерации бокового меню или интерактивного индекса страницы.
Гибкость Remark и Rehype позволяет полностью контролировать процесс генерации якорных ссылок: от автоматической конвертации заголовков до кастомной обработки для сложных сценариев с многоязычным контентом и динамическими интерфейсами. Это делает библиотеку незаменимым инструментом для современных веб-проектов с Markdown и HTML.