Различия в обработке HTML и Markdown

Основы работы с Remark и Rehype

Remark и Rehype — это экосистема инструментов для обработки текстового контента в формате Markdown и HTML. Remark работает с Markdown и предоставляет возможность его разборки в синтаксическое дерево (MDAST — Markdown Abstract Syntax Tree), модификации этого дерева и генерации нового Markdown-кода. Rehype предназначен для работы с HTML: он разбирает HTML в HAST (HTML Abstract Syntax Tree), позволяет трансформировать структуру документа и генерировать обновлённый HTML.

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

Разборка и синтаксические деревья

MDAST (Markdown AST) формируется на основе структуры Markdown:

  • Заголовки (#, ##, ###) превращаются в узлы типа heading.
  • Параграфы (текст без разметки) становятся узлами paragraph.
  • Списки (- элемент или 1. элемент) создают узлы list и listItem.
  • Элементы кода и блоки кода — узлы code.

Пример структуры MDAST для документа:

{
  type: 'root',
  children: [
    {
      type: 'heading',
      depth: 2,
      children: [{ type: 'text', value: 'Раздел' }]
    },
    {
      type: 'paragraph',
      children: [{ type: 'text', value: 'Пример текста.' }]
    }
  ]
}

HAST (HTML AST) формируется на основе элементов HTML:

  • Теги <h1><h6> становятся узлами element с тегом tagName.
  • Параграфы <p> — узлы element с tagName: 'p'.
  • Списки <ul> и <ol> создают узлы element с дочерними li.
  • Атрибуты тега (class, id, src) сохраняются в объекте properties.

Пример структуры HAST:

{
  type: 'element',
  tagName: 'p',
  properties: {},
  children: [{ type: 'text', value: 'Пример HTML текста.' }]
}

Преобразования между Markdown и HTML

Remark и Rehype тесно интегрируются через плагин remark-rehype. Он позволяет конвертировать MDAST в HAST, сохраняя семантику документа. Основная задача этого шага — корректно сопоставить Markdown-узлы HTML-эквивалентам.

  • Узлы heading<h1><h6> в зависимости от depth.
  • paragraph<p>.
  • link<a> с href.
  • image<img> с src и alt.

Пример использования:

import {remark} from 'remark';
import remarkRehype from 'remark-rehype';
import {rehypeStringify} from 'rehype-stringify';

const markdown = '# Заголовок\nТекст параграфа';
const html = await remark()
  .use(remarkRehype)
  .use(rehypeStringify)
  .process(markdown);

console.log(String(html));
// <h1>Заголовок</h1><p>Текст параграфа</p>

Отличия в обработке контента

1. Синтаксис и семантика Markdown ориентирован на упрощённый синтаксис и минимальное количество тегов. HTML более строгий, с полным набором тегов, атрибутов и вложенных структур. Remark не понимает все HTML-элементы сразу, тогда как Rehype полностью работает с ними.

2. Поддержка атрибутов и стилей Markdown узлы обычно не содержат сложных атрибутов. Любые классы или стили добавляются через специальные синтаксические расширения (например, remark-directive). HTML узлы в HAST имеют объект properties, где сохраняются все атрибуты (class, id, style), что позволяет детально управлять визуализацией.

3. Обработка inline vs block Markdown различает блочные (paragraph, heading, list) и строчные элементы (strong, emphasis, link). В HAST эта дифференциация сохраняется через теги (<p> vs <span>/<a>), что важно учитывать при трансформациях, чтобы не нарушить структуру документа.

4. Расширяемость через плагины Remark и Rehype используют плагины для модификации AST. Remark чаще применяется для анализа и модификации текста (например, автоматическое исправление ссылок, генерация оглавления). Rehype используется для постобработки HTML: добавление атрибутов, оптимизация изображений, конвертация тегов в компоненты React.

Ошибки и исключения при обработке

  • Markdown не валиден в строгом HTML-контексте без конвертации через remark-rehype. Например, блоки кода с синтаксисом Markdown (`````) напрямую в HTML будут восприниматься как текст.
  • Rehype строго следит за вложенностью тегов. Некорректно закрытые теги (<div><p></div>) вызывают ошибки или непредсказуемую структуру HAST.
  • Переход из HTML в Markdown (через rehype-remark) может терять атрибуты или специфические теги, так как Markdown не поддерживает произвольные HTML-атрибуты.

Практическая схема работы

  1. Разбор Markdown: remark.parse() → MDAST.
  2. Трансформация MDAST: плагины Remark модифицируют AST.
  3. Конвертация в HTML: remark-rehype → HAST.
  4. Трансформация HAST: плагины Rehype изменяют структуру HTML.
  5. Генерация HTML: rehype.stringify() → итоговый HTML.

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

Итоговая дифференциация

Характеристика Remark (Markdown) Rehype (HTML)
Основной формат Markdown HTML
Синтаксическое дерево MDAST HAST
Поддержка атрибутов Ограниченная Полная
Блочные/строчные различия Явное разделение Через теги
Использование плагинов Анализ, модификация текста Постобработка HTML
Ошибки при некорректной разметке Мягкие Строгие, требуют закрытия тегов

Различия между Markdown и HTML при обработке через Remark и Rehype критически важны для построения корректного пайплайна генерации веб-контента и автоматизированных преобразований текста.