unist-util-map: трансформация через map

Библиотека unist-util-map используется для обхода и трансформации деревьев, соответствующих спецификации Unist. Она предоставляет функцию map, которая позволяет рекурсивно применять изменения к каждому узлу дерева, сохраняя структуру и порядок. Это особенно полезно при работе с Remark и Rehype, где дерево синтаксиса Markdown или HTML представляет собой иерархическую структуру узлов.


Основной синтаксис

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

const newTree = map(tree, (node) => {
  // возвращаем изменённый узел
  return { ...node, data: { ...node.data, transformed: true } };
});

Пояснения к параметрам:

  • tree – дерево узлов в формате Unist (MDAST для Remark или HAST для Rehype).
  • node – текущий узел, который обрабатывается функцией.
  • Возвращаемое значение – изменённый узел. Если возвращается null или undefined, узел будет удалён из дерева.

Ключевые возможности map

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

  2. Сохранение структуры Функция создает новое дерево, не изменяя исходное. Это обеспечивает иммутабельность данных, важную при построении пайплайнов обработки текста.

  3. Фильтрация узлов Возврат null позволяет удалить узел и все его поддеревья. Это дает возможность легко отфильтровывать ненужные элементы.


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

Изменение текста в узлах text

import { map } from 'unist-util-map';
import { unified } from 'unified';
import remarkParse from 'remark-parse';

const markdown = 'Привет, мир!';

const tree = unified().use(remarkParse).parse(markdown);

const newTree = map(tree, (node) => {
  if (node.type === 'text') {
    return { ...node, value: node.value.toUpperCase() };
  }
  return node;
});

console.log(newTree);

Результат: все текстовые узлы преобразованы в верхний регистр.


Удаление узлов определённого типа

const filteredTree = map(tree, (node) => {
  if (node.type === 'heading') {
    return null; // удаляем заголовки
  }
  return node;
});

Функция удаляет все заголовки из дерева, не затрагивая другие узлы.


Композиция с другими утилитами

unist-util-map отлично сочетается с библиотеками для работы с AST:

  • Remark (remark-parse, remark-stringify) – обработка Markdown.
  • Rehype (rehype-parse, rehype-stringify) – обработка HTML.
  • unist-util-visit – обход дерева для более сложных случаев, где не нужна замена узлов.

Пример интеграции с Remark для изменения ссылок:

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

const markdown = '[Google](http://google.com)';

const tree = unified().use(remarkParse).parse(markdown);

const newTree = map(tree, (node) => {
  if (node.type === 'link') {
    return { ...node, url: node.url.replace('http', 'https') };
  }
  return node;
});

const result = unified().use(remarkStringify).stringify(newTree);
console.log(result); // [Google](https://google.com)

Важные нюансы

  • Иммутабельность: всегда создается новая структура узлов. Не изменять исходный объект напрямую, иначе могут возникнуть непредсказуемые побочные эффекты.
  • Типы узлов: необходимо учитывать, что дерево может содержать разные типы узлов (текст, ссылки, заголовки, списки). Фильтрация или трансформация должна учитывать структуру дочерних узлов.
  • Сложные преобразования: если необходимо изменить структуру с добавлением или перемещением узлов, иногда удобнее использовать unist-util-visit вместе с map, чтобы комбинировать обход и замену узлов.

Практические советы

  1. Для массовых изменений текста или атрибутов узлов map эффективнее, чем ручная рекурсия.
  2. Для удаления узлов предпочтительнее возвращать null, чем мутировать массив children.
  3. Можно создавать цепочки трансформаций, используя несколько вызовов map для разных типов узлов.
  4. Для поддержки TypeScript рекомендуется явно типизировать узлы, например:
import type { Root, Text } from 'mdast';

const newTree = map(tree as Root, (node: Text) => {
  if (node.type === 'text') node.value = node.value + '!';
  return node;
});

unist-util-map является фундаментальной утилитой для работы с деревьями Markdown и HTML, предоставляя безопасный и декларативный способ трансформации узлов с сохранением структуры.