При работе с 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);
});
Для каждого узла будут выведены точные координаты его начала и конца. Это особенно полезно для:
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.
Вложенные узлы Позиция родительского узла всегда
включает диапазон всех дочерних узлов. Например, для блока списка
list позиция будет от начала первого элемента до конца
последнего.
Пустые элементы Элементы без содержимого, такие как пустые строки, сохраняют позицию, соответствующую их началу и концу в документе. Это позволяет корректно вставлять или удалять контент без нарушения исходной структуры.
Смешанные типы При конвертации 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 поддерживают сохранение позиционной информации:
position для точных сообщений об ошибках.position для подсветки определенных диапазонов
кода.Правильное управление позиционной информацией обеспечивает точность и предсказуемость всех трансформаций AST, делая Remark и Rehype надежными инструментами для анализа, генерации и редактирования Markdown и HTML.