Добавление новых узлов

Remark и Rehype оперируют деревьями абстрактного синтаксиса (AST, Abstract Syntax Tree), где каждый элемент документа представлен отдельным узлом (node). Узлы имеют тип (type), содержимое (value или children) и дополнительные свойства (data, position, attributes). Понимание структуры узла и иерархии дерева является ключевым для добавления и модификации контента.

В Remark все узлы соответствуют Markdown-сущностям: paragraph, heading, text, link, list, listItem. В Rehype узлы соответствуют HTML-элементам: element, text, comment. Узлы могут быть листовыми (не имеют детей, например text) и контейнерными (могут содержать другие узлы, например paragraph или element).


Создание новых узлов

Для создания нового узла используют простой объект с обязательными свойствами:

const paragraphNode = {
  type: 'paragraph',
  children: [
    { type: 'text', value: 'Это новый параграф.' }
  ]
};

Ключевые моменты:

  • type задаёт тип узла, определяющий его роль в дереве.
  • children содержит массив дочерних узлов, если узел контейнерный.
  • Для листовых узлов, таких как text или html, достаточно указать value.

Rehype использует аналогичный подход, но вместо Markdown-типов применяет HTML-типизацию:

const divNode = {
  type: 'element',
  tagName: 'div',
  properties: { className: ['highlight'] },
  children: [
    { type: 'text', value: 'Содержимое блока' }
  ]
};

Здесь добавляются свойства:

  • tagName — имя HTML-тега.
  • properties — объект с атрибутами элемента.
  • children — дочерние узлы, которые могут включать текст или другие элементы.

Вставка узлов в существующее дерево

Для вставки нового узла в дерево используют прямое изменение массива children родительского узла. Например, добавление параграфа в конец документа Remark:

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

const tree = unified().use(remarkParse).parse('# Заголовок');

const newNode = {
  type: 'paragraph',
  children: [{ type: 'text', value: 'Новый параграф в документе.' }]
};

tree.children.push(newNode);

Для Rehype добавление элемента в существующий контейнер:

const divNode = { type: 'element', tagName: 'div', children: [] };
const textNode = { type: 'text', value: 'Привет, мир!' };

divNode.children.push(textNode);

Важные аспекты:

  • Вставка в начало массива создаёт элемент в начале документа.
  • Использование методов массивов (push, unshift, splice) позволяет гибко управлять порядком узлов.
  • В Rehype следует учитывать properties для корректного рендеринга HTML.

Манипуляции с вложенными узлами

Часто требуется вставлять узлы не только в корень документа, но и в конкретные контейнеры, например, в определённый параграф или список:

const listNode = {
  type: 'list',
  ordered: true,
  children: [
    { type: 'listItem', children: [{ type: 'paragraph', children: [{ type: 'text', value: 'Первый пункт' }] }] }
  ]
};

const newListItem = {
  type: 'listItem',
  children: [{ type: 'paragraph', children: [{ type: 'text', value: 'Второй пункт' }] }]
};

listNode.children.push(newListItem);

Здесь важно поддерживать иерархию типов: listlistItemparagraphtext. Нарушение этой структуры может привести к некорректной интерпретации Markdown или HTML при генерации.


Использование утилит для работы с узлами

Remark и Rehype имеют обширный экосистемный набор пакетов для удобной работы с AST:

  • unist-util-visit — обход дерева и модификация узлов по типу.
  • unist-builder — создание узлов с автоматическим заполнением обязательных полей.
  • hastscript — генерация HTML-узлов для Rehype с синтаксисом, похожим на JSX.

Пример создания параграфа с использованием unist-builder:

import { u } from 'unist-builder';

const paragraphNode = u('paragraph', [u('text', 'Текст параграфа')]);

unist-builder упрощает создание узлов, автоматически подставляя массив children и проверяя корректность структуры.


Встраивание узлов с динамическим содержимым

Для генерации узлов с переменным содержимым используют функции:

function createHeading(level, text) {
  return {
    type: 'heading',
    depth: level,
    children: [{ type: 'text', value: text }]
  };
}

tree.children.push(createHeading(2, 'Подзаголовок второго уровня'));

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


Советы по добавлению новых узлов

  • Всегда проверять тип родителя и допустимые дочерние узлы.
  • Для вставки HTML-узлов использовать Rehype и element с properties.
  • Для массовых изменений дерева применять unist-util-visit вместо ручного обхода.
  • Использовать утилиты (unist-builder, hastscript) для уменьшения числа ошибок при создании узлов.
  • Поддерживать консистентность данных, чтобы Markdown или HTML корректно рендерились в конечный документ.