Простейший pipeline обработки

Для работы с Remark и Rehype требуется установить соответствующие пакеты через npm:

npm install remark remark-html rehype rehype-stringify
  • remark — парсер и обработчик Markdown.
  • remark-html — плагин для преобразования Markdown в HTML.
  • rehype — процессор для работы с HTML-деревьями.
  • rehype-stringify — сериализатор HTML в строку.

Создание простого pipeline

Pipeline обработки данных строится вокруг концепции последовательного применения плагинов, которые модифицируют дерево документа (AST — Abstract Syntax Tree). Основная структура выглядит так:

import { remark } from 'remark';
import remarkHtml from 'remark-html';

const markdown = `
# Заголовок первого уровня

Текстовый абзац с **жирным текстом**.
`;

const processed = await remark()
  .use(remarkHtml)
  .process(markdown);

console.log(String(processed));

Объяснение кода

  1. remark() создаёт процессор Markdown.
  2. Метод .use() подключает плагин, в данном случае remark-html, который преобразует AST Markdown в HTML.
  3. Метод .process() выполняет все подключённые плагины над исходной строкой Markdown.
  4. Возвращаемое значение processed — объект типа VFile, который хранит результат обработки и метаданные. Для получения строки используется String(processed).

Работа с Rehype

Rehype обрабатывает уже HTML-деревья. Он удобен для дальнейшей трансформации HTML после конвертации из Markdown:

import { rehype } from 'rehype';
import rehypeStringify from 'rehype-stringify';
import rehypeParse from 'rehype-parse';

const html = `

Заголовок

Параграф текста

`; const processedHtml = await rehype() .data('settings', { fragment: true }) .use(rehypeParse, { fragment: true }) .use(rehypeStringify) .process(html); console.log(String(processedHtml));
  • rehypeParse разбивает HTML на AST.
  • rehypeStringify собирает AST обратно в строку.
  • Опция fragment: true указывает, что обрабатывается фрагмент HTML, а не полный документ с и .

Комбинирование Remark и Rehype

Для создания мощного pipeline Markdown → HTML → модификации можно объединять Remark и Rehype:

import { remark } from 'remark';
import remarkHtml from 'remark-html';
import { rehype } from 'rehype';
import rehypeHighlight from 'rehype-highlight';
import rehypeStringify from 'rehype-stringify';

const markdown = `
\`\`\`js
console.log('Hello, world!');
\`\`\`
`;

const html = await remark()
  .use(remarkHtml)
  .process(markdown);

const highlightedHtml = await rehype()
  .data('settings', { fragment: true })
  .use(rehypeHighlight)
  .use(rehypeStringify)
  .process(html);

console.log(String(highlightedHtml));
  • rehype-highlight добавляет подсветку синтаксиса для кода.
  • Использование .data('settings', { fragment: true }) предотвращает добавление лишних тегов и при обработке фрагмента.

Обработка AST и пользовательские плагины

Remark и Rehype используют общую структуру AST, где каждый узел имеет свойства type, children и value. На этом строятся пользовательские плагины:

function remarkUppercase() {
  return (tree) => {
    visit(tree, 'text', (node) => {
      node.value = node.value.toUpperCase();
    });
  };
}

import { visit } from 'unist-util-visit';

const result = await remark()
  .use(remarkUppercase)
  .use(remarkHtml)
  .process('Привет, мир!');

console.log(String(result));
  • visit позволяет обходить все узлы AST и применять изменения к узлам определённого типа.
  • В данном примере все текстовые узлы преобразуются в верхний регистр до конвертации в HTML.

Асинхронная обработка и потоковые данные

Remark и Rehype поддерживают асинхронные плагины, что удобно при работе с сетевыми запросами, чтением файлов или динамическими трансформациями:

function remarkFetchData() {
  return async (tree) => {
    const data = await fetchSomeData();
    visit(tree, 'text', (node) => {
      node.value += ` (${data})`;
    });
  };
}
  • Любой плагин может быть асинхронным, если возвращает Promise.
  • Pipeline автоматически обрабатывает асинхронные плагины, сохраняя порядок применения.

Настройка pipeline для больших проектов

Для крупного проекта рекомендуется:

  1. Разделять плагины по функциональным блокам: обработка Markdown, модификация AST, генерация HTML, постобработка.
  2. Использовать .freeze() для кэширования неизменяемых частей AST.
  3. Применять VFile для хранения метаданных документа (название файла, путь, дата изменения).
  4. Интегрировать с системами сборки, например Webpack или Vite, через специальные загрузчики remark-loader или rehype-loader.

Оптимизация производительности

  • Минимизировать количество обходов AST, комбинируя несколько мелких плагинов в один.
  • Использовать фрагменты HTML вместо полного документа, если не требуется структура / .
  • Для больших массивов Markdown-файлов применить пакет unified-engine для пакетной обработки с асинхронными потоками.