Remark и Rehype — это мощные экосистемы для работы с Markdown и HTML в JavaScript. Remark работает с Markdown, превращая его в дерево синтаксиса (MDAST — Markdown Abstract Syntax Tree), а Rehype работает с HTML и XML, оперируя деревом HTML (HAST — HTML Abstract Syntax Tree). Вместе они образуют полный стек для парсинга, трансформации и генерации контента из Markdown и HTML.
Remark и Rehype построены на концепции плагинов, которые позволяют выполнять цепочку трансформаций над деревьями. Плагин — это функция, которая принимает AST и изменяет его согласно определённым правилам, например: добавляет классы к заголовкам, вставляет ссылки, преобразует изображения в компоненты.
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
const processor = unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeStringify);
const markdown = '# Пример заголовка';
processor.process(markdown).then(file => {
console.log(String(file));
});
В этом примере Markdown парсится через Remark, преобразуется в HTML с помощью Rehype и выводится как строка.
Remark поддерживает два вида плагинов:
Плагины для анализа и изменения Markdown
(Remark-плагины) Работают на уровне MDAST и позволяют
добавлять, удалять или изменять узлы Markdown. Примеры:
remark-gfm для поддержки расширенного синтаксиса GitHub,
remark-frontmatter для обработки
YAML-фронтматтеров.
Плагины для работы с HTML (Rehype-плагины)
Применяются после преобразования MDAST в HAST и позволяют модифицировать
HTML. Примеры: rehype-slug для генерации идентификаторов
заголовков, rehype-highlight для подсветки кода.
Последовательное использование плагинов создаёт цепочку обработки данных:
Markdown → MDAST → (Remark-плагины) → HAST → (Rehype-плагины) → HTML
Такое разделение позволяет точно контролировать, на каком этапе происходят изменения контента.
В проектах на JavaScript часто используется файловая структура для организации страниц, особенно в генераторах статических сайтов и фреймворках вроде Next.js. Remark и Rehype идеально подходят для обработки Markdown-файлов в качестве контента для маршрутов.
Файлы хранятся в структуре:
/content
/posts
first-post.md
second-post.md
/pages
about.md
Для динамической маршрутизации необходимо получить список файлов и
построить маршруты. В Node.js это делается через fs и
path:
import fs from 'fs';
import path from 'path';
const postsDir = path.join(process.cwd(), 'content/posts');
const filenames = fs.readdirSync(postsDir);
const posts = filenames.map(filename => {
const filePath = path.join(postsDir, filename);
const content = fs.readFileSync(filePath, 'utf-8');
return { slug: filename.replace('.md', ''), content };
});
Каждый файл превращается в объект с slug для маршрута и
content для обработки Remark.
С помощью цепочки Remark → Rehype Markdown-файлы превращаются в HTML:
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
async function markdownToHtml(markdown) {
const result = await unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeStringify)
.process(markdown);
return String(result);
}
Теперь каждый маршрут может рендерить готовый HTML, извлечённый из Markdown.
Использование файловой структуры позволяет автоматически создавать маршруты. Пример для Express.js:
import express from 'express';
const app = express();
posts.forEach(post => {
app.get(`/posts/${post.slug}`, async (req, res) => {
const html = await markdownToHtml(post.content);
res.send(html);
});
});
app.listen(3000);
С каждым новым Markdown-файлом маршрут создаётся автоматически без ручного описания в коде.
Добавление метаданных (frontmatter):
import remarkFrontmatter from 'remark-frontmatter';
import remarkParseYaml from 'remark-parse-yaml';
const processor = unified()
.use(remarkParse)
.use(remarkFrontmatter, ['yaml'])
.use(remarkParseYaml)
.use(remarkRehype)
.use(rehypeStringify);
Теперь YAML-фронтматтер каждого файла можно использовать для заголовка, даты публикации и тегов, что делает маршрутизацию более динамичной.
Стилизация HTML и добавление идентификаторов заголовков:
import rehypeSlug from 'rehype-slug';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
const processor = unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeSlug)
.use(rehypeAutolinkHeadings, { beh * avior: 'wrap' })
.use(rehypeStringify);
Каждый заголовок получает уникальный id, что позволяет
строить якорные ссылки и оглавления для маршрутов.
При большом количестве Markdown-файлов желательно кэшировать результат обработки, чтобы не парсить файлы при каждом запросе:
const cache = {};
async function getPostHtml(slug) {
if (cache[slug]) return cache[slug];
const post = posts.find(p => p.slug === slug);
const html = await markdownToHtml(post.content);
cache[slug] = html;
return html;
}
Это уменьшает нагрузку на сервер и ускоряет отдачу страниц.
Плагины можно комбинировать в цепочки, формируя универсальный парсер Markdown → HTML с фронтматтером, стилями и ссылками.
.md. Remark/Rehype обеспечивают парсинг и
трансформацию контента.Такой подход обеспечивает полную автоматизацию генерации контента на основе файловой структуры и гибкость в применении сложных трансформаций текста и HTML.