Сохранение позиционной информации

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


Структура позиционной информации

Каждый узел AST в Remark или Rehype может содержать объект position. Этот объект имеет следующую структуру:

{
  start: { line: Number, column: Number, offset: Number },
  end: { line: Number, column: Number, offset: Number },
  indent: Number[]
}
  • start — начало узла:

    • line — номер строки, начиная с 1;
    • column — номер столбца, начиная с 1;
    • offset — смещение в символах от начала документа.
  • end — конец узла, аналогично start.

  • indent — массив, отражающий отступ узла на каждой строке (используется для блоков кода и списков).

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


Получение и использование позиции узлов

В Remark позиция доступна напрямую через node.position:

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

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

tree.children.forEach(node => {
  console.log(node.type, node.position);
});

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

  • Сопоставления ошибок парсинга с конкретными местами в исходном тексте.
  • Инструментов проверки Markdown, линтеров.
  • Подсветки синтаксиса и редакторов.

Взаимодействие Remark и Rehype

Remark работает с Markdown, а Rehype — с HTML. Для преобразований между ними используют remark-rehype. Важно понимать, что позиционная информация может быть частично утеряна при трансформации, если не использовать специальные опции.

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

const tree = unified()
  .use(remarkParse)
  .use(remarkRehype, { allowDangerousHtml: true, passThrough: ['position'] })
  .use(rehypeStringify)
  .processSync('# Пример');

console.log(tree.toString());

Опция passThrough позволяет сохранять позиционные данные для последующего использования в Rehype.


Тонкости позиционной информации

  1. Вложенные узлы Позиция родительского узла всегда включает диапазон всех дочерних узлов. Например, для блока списка list позиция будет от начала первого элемента до конца последнего.

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

  3. Смешанные типы При конвертации Markdown → HTML через Remark → Rehype позиция Markdown-узлов может не совпадать с HTML-узлами. Для точной синхронизации используют плагины, которые копируют position в новые узлы или создают data.position.


Практические сценарии

  • Линтеры Markdown Для проверки правил форматирования важно знать точное положение нарушений. Позиционная информация позволяет выдавать сообщения вида: «Ошибка в строке 12, колонка 5».

  • Редакторы WYSIWYG При визуальной подсветке Markdown-конструкций позиция узлов используется для выделения текста в редакторе в реальном времени.

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


Оптимизация работы с позициями

  • Минимизация объема AST При больших документах хранение всей позиции может замедлять обработку. В таких случаях используют опцию position: false для узлов, где точная позиция не требуется.

  • Копирование позиции При создании новых узлов вручную рекомендуется использовать Object.assign(newNode, { position: oldNode.position }), чтобы сохранить связь с исходным текстом.

  • Сериализация При экспорте AST в JSON сохраняются позиции, что позволяет работать с ними в других инструментах анализа.


Интеграция с плагинами

Многие плагины Remark и Rehype поддерживают сохранение позиционной информации:

  • remark-lint — проверяет Markdown и использует position для точных сообщений об ошибках.
  • rehype-highlight — подсветка синтаксиса может использовать position для подсветки определенных диапазонов кода.
  • remark-math + rehype-katex — математические формулы могут передавать позиции для ссылок на исходные выражения.

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