Маршрутизация на основе файлов

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 поддерживает два вида плагинов:

  1. Плагины для анализа и изменения Markdown (Remark-плагины) Работают на уровне MDAST и позволяют добавлять, удалять или изменять узлы Markdown. Примеры: remark-gfm для поддержки расширенного синтаксиса GitHub, remark-frontmatter для обработки YAML-фронтматтеров.

  2. Плагины для работы с HTML (Rehype-плагины) Применяются после преобразования MDAST в HAST и позволяют модифицировать HTML. Примеры: rehype-slug для генерации идентификаторов заголовков, rehype-highlight для подсветки кода.

Последовательное использование плагинов создаёт цепочку обработки данных:

Markdown → MDAST → (Remark-плагины) → HAST → (Rehype-плагины) → HTML

Такое разделение позволяет точно контролировать, на каком этапе происходят изменения контента.


Интеграция с маршрутизацией на основе файлов

В проектах на JavaScript часто используется файловая структура для организации страниц, особенно в генераторах статических сайтов и фреймворках вроде Next.js. Remark и Rehype идеально подходят для обработки Markdown-файлов в качестве контента для маршрутов.

1. Сбор 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.

2. Преобразование Markdown в HTML

С помощью цепочки 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-файлом маршрут создаётся автоматически без ручного описания в коде.


Расширение функционала с Remark и Rehype

Добавление метаданных (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;
}

Это уменьшает нагрузку на сервер и ускоряет отдачу страниц.


Примеры комплексных плагинов

  • remark-toc — автоматически строит оглавление на основе заголовков.
  • rehype-highlight — подсвечивает код, используя синтаксис Markdown.
  • remark-emoji — преобразует текстовые смайлы в графические.

Плагины можно комбинировать в цепочки, формируя универсальный парсер Markdown → HTML с фронтматтером, стилями и ссылками.


Применение в современных фреймворках

  • Next.js / Nuxt.js: маршруты создаются динамически по файлам .md. Remark/Rehype обеспечивают парсинг и трансформацию контента.
  • Gatsby / Astro: Remark-плагины формируют GraphQL-схему для контента, Rehype отвечает за рендер HTML и интеграцию с компонентами.
  • Express / Koa: Remark/Rehype позволяют строить серверные маршруты для Markdown-файлов без привязки к фронтенду.

Такой подход обеспечивает полную автоматизацию генерации контента на основе файловой структуры и гибкость в применении сложных трансформаций текста и HTML.