Миграция с других Markdown процессоров

Remark и Rehype представляют собой современные и модульные инструменты для работы с Markdown и HTML в экосистеме JavaScript. Переход с других Markdown процессоров, таких как Marked, markdown-it или Showdown, требует понимания архитектуры этих библиотек, их плагинной системы и подхода к AST (Abstract Syntax Tree).

Архитектура и подход к AST

Remark использует unist (Universal Syntax Tree), что позволяет унифицировать работу с различными синтаксическими деревьями. Это отличает его от традиционных процессоров, которые работают на уровне строк и регулярных выражений. AST Remark предоставляет доступ к каждой ноде Markdown, что делает возможной точную модификацию, анализ и трансформацию документа.

Rehype выполняет аналогичную роль для HTML: она оперирует HTML-деревом как AST, позволяя интегрировать Markdown с HTML-процессорами. Основная идея заключается в том, что Markdown сначала парсится Remark в AST, затем через remark-rehype конвертируется в HTML-дерево, и после этого Rehype применяет свои трансформации и плагины.

Преимущества перехода на Remark/Rehype

  1. Модульность: вместо единого монолитного парсера можно использовать плагины для отдельных задач — например, добавление таблиц, footnotes, кастомные синтаксис-расширения.
  2. AST-доступ: возможность работы с деревом позволяет создавать сложные трансформации, которых сложно достичь в традиционных строковых процессорах.
  3. Совместимость с современными инструментами: интеграция с Vite, Next.js, ESM-модулями, что особенно важно для современных фронтенд-проектов.
  4. Расширяемость через плагины: как для Remark, так и для Rehype существует множество готовых плагинов, а также возможность написания собственных, что делает библиотеку адаптивной под любые задачи.

Основные шаги миграции

  1. Установка пакетов:
npm install remark remark-html remark-parse rehype rehype-stringify remark-rehype
  1. Парсинг Markdown:

Пример с Remark:

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

const markdown = `# Заголовок

Текст с **жирным выделением**.`;

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

console.log(String(result));

При использовании других процессоров подобная операция часто выполнялась одной функцией marked(markdown). Здесь же процесс более модульный: сначала AST, потом конвертация в HTML.

  1. Добавление плагинов: Remark позволяет вставлять плагины между парсингом и генерацией HTML. Например, добавление поддержки footnotes:
import remarkFootnotes from 'remark-footnotes';

await remark()
  .use(remarkFootnotes, {inlineNotes: true})
  .use(remarkHtml)
  .process(markdown);

Подобный подход сложнее реализовать в старых процессорах, где требуется прямое вмешательство в рендер-функцию.

  1. Конвертация Markdown → HTML через Rehype: Для более сложной обработки HTML, например, добавления классов или модификации тегов, используется цепочка Remark → Rehype:
import {remark} from 'remark';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import rehypeAddClasses from 'rehype-add-classes';

const htmlResult = await remark()
  .use(remarkRehype)
  .use(rehypeAddClasses, {h1: 'title'})
  .use(rehypeStringify)
  .process(markdown);

console.log(String(htmlResult));

Этот подход открывает возможности сложного кастомного HTML, чего традиционные Markdown процессоры не обеспечивают.

Особенности миграции и подводные камни

  • Синтаксис-плагины: Markdown процессоры по-разному поддерживают нестандартные синтаксисы (таблицы, footnotes, математические формулы). Для Remark могут понадобиться отдельные плагины.
  • Производительность: AST-подход увеличивает точность и гибкость, но может быть медленнее при массовой генерации документов.
  • Совместимость с плагинами старых процессоров: большинство плагинов для markdown-it или Showdown несовместимы напрямую, потребуется их переписывать или искать аналоги для Remark/Rehype.
  • Работа с HTML внутри Markdown: в старых процессорах HTML часто рендерился «как есть». В цепочке Remark → Rehype нужно явно указывать, как обрабатывать HTML-ноды, чтобы избежать XSS или некорректного рендеринга.

Рекомендации по переходу

  • Разделять обработку Markdown и HTML: сначала формировать AST Remark, затем преобразовывать в Rehype для финальной кастомизации HTML.
  • Использовать готовые плагины для популярных задач: footnotes, таблицы, подсветка кода через rehype-prism или rehype-highlight.
  • При миграции большого кода создавать тесты на корректность рендеринга каждого блока Markdown, чтобы избежать разночтений с предыдущим процессором.
  • Постепенная замена старых функций через отдельные промежуточные модули помогает избежать сбоев при рефакторинге.

Практический пример: полный конвейер

  1. Парсинг Markdown через Remark с поддержкой footnotes.
  2. Конвертация в HTML через remark-rehype.
  3. Добавление классов к тегам через rehype-add-classes.
  4. Подсветка кода через rehype-highlight.
  5. Вывод HTML через rehype-stringify.
import {remark} from 'remark';
import remarkRehype from 'remark-rehype';
import remarkFootnotes from 'remark-footnotes';
import rehypeStringify from 'rehype-stringify';
import rehypeAddClasses from 'rehype-add-classes';
import rehypeHighlight from 'rehype-highlight';

const result = await remark()
  .use(remarkFootnotes, {inlineNotes: true})
  .use(remarkRehype)
  .use(rehypeAddClasses, {h1: 'title', pre: 'code-block'})
  .use(rehypeHighlight)
  .use(rehypeStringify)
  .process(markdown);

console.log(String(result));

Эта последовательность полностью покрывает основные сценарии миграции с других Markdown процессоров, позволяя сохранить совместимость с существующим контентом и одновременно получить преимущества модульного, расширяемого подхода Remark/Rehype.