Фазы парсинга, трансформации и сериализации

Парсинг: превращение текста в AST

Remark и Rehype работают с текстовыми данными через концепцию AST (Abstract Syntax Tree). В Remark исходным форматом является Markdown, а в Rehype — HTML. Парсинг — это первый этап обработки, на котором исходный текст превращается в дерево узлов.

  • Remark использует remark-parse для преобразования Markdown в дерево типа mdast (Markdown AST). Узлы дерева имеют структуру { type, children, value, data }.
  • Rehype применяет rehype-parse для создания hast (HTML AST). Узлы имеют аналогичную структуру { type, tagName, properties, children }.

Парсер осуществляет лексический и синтаксический анализ. На лексическом уровне текст разбивается на токены (заголовки, списки, параграфы), на синтаксическом — строится иерархия узлов.

Пример структуры узла mdast для заголовка:

{
  type: 'heading',
  depth: 2,
  children: [
    { type: 'text', value: 'Пример заголовка' }
  ]
}

Эта структура позволяет применять универсальные алгоритмы трансформации, независимо от исходного формата.


Трансформация: работа с деревом узлов

После парсинга следует трансформация AST. Этот этап является ключевым для обработки Markdown и HTML. Он включает:

  • Модификацию существующих узлов — изменение текста, атрибутов, типов.
  • Добавление новых узлов — вставка изображений, ссылок, метаданных.
  • Удаление узлов — очистка нежелательных элементов, фильтрация контента.

Remark и Rehype предоставляют систему плагинов, которые принимают AST и возвращают изменённое дерево. Плагины могут быть:

  • Синхронными или асинхронными
  • Универсальными (работают с любыми деревьями)
  • Специализированными (например, remark-autolink-headings или rehype-highlight)

Пример синхронной трансформации Markdown:

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

function capitalizeHeadings() {
  return (tree) => {
    visit(tree, 'heading', (node) => {
      node.children.forEach(child => {
        if (child.type === 'text') {
          child.value = child.value.toUpperCase();
        }
      });
    });
  };
}

visit позволяет рекурсивно обходить все узлы и применять изменения в реальном времени.


Сериализация: генерация текста из AST

На последнем этапе происходит сериализация AST обратно в текстовый формат:

  • Remark использует remark-stringify для генерации Markdown.
  • Rehype применяет rehype-stringify для генерации HTML.

Сериализация учитывает иерархию узлов и их свойства. Важные моменты:

  • Отступы и форматирование должны сохранять читаемость текста.
  • Атрибуты HTML или свойства Markdown должны корректно транслироваться.
  • Плагинная система позволяет контролировать финальный вид текста, включая обработку нестандартных узлов.

Пример сериализации:

import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';

const processor = unified()
  .use(remarkParse)
  .use(capitalizeHeadings)
  .use(remarkStringify);

const markdown = '# пример заголовка';
const result = processor.processSync(markdown).toString();
// Результат: '# ПРИМЕР ЗАГОЛОВКА'

Взаимодействие фаз: цепочка обработки

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

  1. Парсинг → исходный текст превращается в AST.
  2. Трансформация → дерево узлов модифицируется через плагины.
  3. Сериализация → дерево преобразуется обратно в текст.

Такой подход позволяет:

  • Легко комбинировать плагины, создавая мощные конвейеры обработки Markdown и HTML.
  • Изолировать изменения на уровне AST, не рискуя нарушить исходный текст.
  • Создавать кастомные форматы, например, генерацию PDF, с использованием промежуточных AST.

Основные различия между Remark и Rehype на этих фазах

Фаза Remark (Markdown) Rehype (HTML)
Парсинг remark-parsemdast rehype-parsehast
Трансформация Плагины remark-* Плагины rehype-*
Сериализация remark-stringify rehype-stringify
Основной тип узлов Заголовки, параграфы, списки Теги, атрибуты, текст

Эта таблица отражает фундаментальное различие: Remark ориентирован на структурирование текста, Rehype — на работу с документами HTML.


Продвинутые возможности

  • Асинхронные плагины позволяют обращаться к API или файловой системе во время трансформации.
  • Слияние AST: можно объединять Markdown и HTML через промежуточные форматы, например mdast → hast.
  • Семантическая трансформация: изменение структуры документа без потери содержимого, например, автоматическое добавление якорей для заголовков или генерация таблицы содержания.

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