Работа с процессором unified

Процессор unified является центральным элементом экосистемы Remark и Rehype, обеспечивая единый способ обработки и трансформации структурированных текстов и документов. В основе лежит концепция дерева синтаксиса (AST), которое используется для представления Markdown или HTML в виде объектов JavaScript. Это позволяет применять универсальные плагины и производить сложные преобразования данных.

Процессор создаётся с помощью функции unified(), которая возвращает объект с методами use, parse, stringify и run. Основное назначение этих методов:

  • use(plugin, options) — подключение плагинов с настройками. Плагины могут изменять дерево, добавлять новые узлы или преобразовывать существующие.
  • parse(input) — разбор входной строки в AST.
  • run(tree, file, callback) — асинхронная обработка дерева с применением всех подключённых плагинов.
  • stringify(tree) — генерация итогового текста из дерева.

Пример создания базового процессора для Markdown:

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

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

const markdown = '# Заголовок\n\nТекст параграфа';
const tree = processor.parse(markdown);
const output = processor.stringify(tree);

Архитектура AST

AST (Abstract Syntax Tree) состоит из узлов, каждый из которых описывает отдельный элемент документа. Узлы имеют поля:

  • type — тип узла (root, heading, paragraph, text и др.).
  • children — массив дочерних узлов.
  • value — текстовое содержимое для листовых узлов.

Для примера Markdown-строки:

# Пример
Текст параграфа

AST будет выглядеть так:

{
  "type": "root",
  "children": [
    {
      "type": "heading",
      "depth": 1,
      "children": [{ "type": "text", "value": "Пример" }]
    },
    {
      "type": "paragraph",
      "children": [{ "type": "text", "value": "Текст параграфа" }]
    }
  ]
}

Каждый плагин может обходить дерево, проверять типы узлов и модифицировать их. Для этого часто используют утилиту unist-util-visit:

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

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

Плагины Remark и Rehype

Remark работает с Markdown, Rehype — с HTML. Оба используют unified и совместимы на уровне AST. Плагины бывают трёх типов:

  1. Парсеры — преобразуют исходный текст в AST (remark-parse, rehype-parse).
  2. Трансформеры — изменяют дерево (remark-slug, rehype-autolink-headings).
  3. Стрингифайеры — превращают AST обратно в текст (remark-stringify, rehype-stringify).

Пример цепочки обработки Markdown с трансформацией заголовков:

import remarkParse from 'remark-parse';
import remarkSlug from 'remark-slug';
import remarkStringify from 'remark-stringify';

const processor = unified()
  .use(remarkParse)
  .use(remarkSlug)       // добавляет id для заголовков
  .use(remarkStringify);

const result = processor.processSync('# Заголовок');
console.log(result.toString());

Асинхронная обработка и цепочки плагинов

Unifed позволяет создавать цепочки плагинов, которые могут выполняться асинхронно. Метод process возвращает промис:

processor.process(markdown).then(file => {
  console.log(String(file));
});

Асинхронность особенно важна при работе с плагинами, которые загружают внешние ресурсы или выполняют сетевые запросы.

Конвертация между Markdown и HTML

Для конвертации Markdown в HTML объединяют Remark и Rehype через remark-rehype:

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 html = processor.processSync('# Заголовок').toString();

Промежуточное дерево остаётся AST, но меняется его тип: из mdast (Markdown AST) оно преобразуется в hast (HTML AST).

Работа с файлами и виртуальными узлами

Unified поддерживает работу с файловой структурой через объект VFile. Он хранит:

  • path — путь к файлу.
  • contents — содержимое файла.
  • messages — предупреждения и ошибки плагинов.

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

import { VFile } from 'vfile';

const file = new VFile({ path: 'example.md', contents: '# Заголовок' });
processor.process(file).then(result => {
  console.log(result.toString());
});

Практические приёмы работы с AST

  1. Обход и модификация узлов: visit и visitParents позволяют менять узлы, добавлять новые, удалять ненужные.
  2. Фильтрация узлов по типу: удобно для анализа документа, подсчёта заголовков или ссылок.
  3. Композиция плагинов: подключение нескольких трансформеров позволяет строить сложные пайплайны для генерации документации, статических сайтов и конвертации форматов.

Ключевые особенности unified

  • Единая API для разных форматов (Markdown, HTML, MDX).
  • Поддержка синхронной и асинхронной обработки.
  • Расширяемость через плагины.
  • Чистая работа с AST, позволяющая реализовать сложные трансформации текста.
  • Совместимость с экосистемой unist, что облегчает создание собственных плагинов.

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