В экосистеме Remark и Rehype позиционная информация играет ключевую роль при анализе и трансформации документов. Она обеспечивает точное указание на расположение узлов в исходном тексте, что важно для инструментов линтинга, синтаксического анализа, подсветки ошибок и генерации исходного кода с сохранением исходной структуры.
Каждый узел AST (Abstract Syntax Tree) в Remark или
Rehype может содержать объект position,
который описывает, где именно в исходном тексте находится этот узел.
Формат position обычно включает два свойства:
start — начальная позиция узла.end — конечная позиция узла.Каждое из этих свойств содержит следующие координаты:
Пример структуры позиционной информации:
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "Hello, world!",
"position": {
"start": { "line": 1, "column": 1, "offset": 0 },
"end": { "line": 1, "column": 14, "offset": 13 }
}
}
],
"position": {
"start": { "line": 1, "column": 1, "offset": 0 },
"end": { "line": 1, "column": 14, "offset": 13 }
}
}
Позиционная информация позволяет точно локализовать ошибки. Например, линтеры на основе Remark могут выдавать сообщения об ошибках с указанием строки и столбца:
import { lint } from "remark-lint";
import { unified } from "unified";
import remarkParse from "remark-parse";
const processor = unified()
.use(remarkParse)
.use(lint);
const file = processor.processSync("Hello **world**!");
file.messages.forEach(msg => {
console.log(`Ошибка: ${msg.reason} на строке ${msg.line}, колонка ${msg.column}`);
});
Вывод будет включать точное положение выделенного элемента в тексте.
При трансформации AST знание позиции узлов помогает корректно заменять или удалять части текста без нарушения структуры документа. Пример замены текста:
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkStringify from "remark-stringify";
const processor = unified().use(remarkParse).use(remarkStringify);
const tree = processor.parse("Hello world!");
const textNode = tree.children[0].children[0];
// Замена текста с использованием позиции
const start = textNode.position.start.offset;
const end = textNode.position.end.offset;
const original = "Hello world!";
const updated = original.slice(0, start) + "Hi universe!" + original.slice(end);
console.log(updated); // Hi universe!
Таким образом, позиция позволяет выполнять операции с точной привязкой к исходному тексту, сохраняя контекст документа.
Для Rehype, который работает с HTML, структура
position аналогична Remark, но дополнительно учитывает
атрибуты и сущности HTML. Это важно при обработке сложных
документов:
startTag — начало тега, включая < и
имя.endTag — конец тега, включая > и
возможные закрывающие символы.start и end — позиция всего узла, как у
Remark.Пример узла Rehype с позицией:
{
"type": "element",
"tagName": "p",
"children": [
{
"type": "text",
"value": "Пример текста",
"position": {
"start": { "line": 2, "column": 4, "offset": 15 },
"end": { "line": 2, "column": 16, "offset": 27 }
}
}
],
"position": {
"start": { "line": 2, "column": 1, "offset": 12 },
"end": { "line": 2, "column": 20, "offset": 31 }
}
}
position, но
при ручном создании узлов нужно задавать позиции явно.offset для операций с исходной
строкой — это надежнее, чем работать с line и
column напрямую, особенно при мультистрочных узлах.position, например, синтетические узлы, создаваемые
плагинами. При обработке лучше использовать условие
if (node.position).Позиционная информация является фундаментальным инструментом для точного анализа, трансформации и визуализации текста в проектах, использующих Remark и Rehype. Она позволяет связывать абстрактные узлы AST с конкретными местами в исходном документе, обеспечивая детальный контроль над процессом обработки текста.