Блоки кода в Markdown часто сопровождаются метаданными, которые позволяют управлять подсветкой синтаксиса, выполнением кода или интеграцией с различными инструментами. Remark и Rehype предоставляют гибкие механизмы для работы с этими метаданными через AST (Abstract Syntax Tree).
В Markdown блок кода оформляется тройными обратными кавычками (```), за которыми может следовать язык программирования и дополнительные данные:
```javascript linenos highlight="2,3"
console.log('Hello World');
В этом примере:
- `javascript` — язык для подсветки.
- `linenos` — пользовательский флаг, например, для отображения номеров строк.
- `highlight="2,3"` — указание строк для подсветки.
В AST Remark этот блок кодов представлен узлом типа `code`:
```json
{
"type": "code",
"lang": "javascript",
"meta": "linenos highlight=\"2,3\"",
"value": "console.log('Hello World');"
}
type всегда "code".lang — язык программирования.meta — строка метаданных, которую можно анализировать
для кастомной логики.value — текст кода внутри блока.Метаданные приходят в виде строки и требуют парсинга для использования. Часто применяют регулярные выражения или специализированные утилиты для извлечения ключей и значений.
Пример функции для разбора метаданных:
function parseMeta(meta) {
const result = {};
if (!meta) return result;
const regex = /(\w+)(?:="([^"]+)")?/g;
let match;
while ((match = regex.exec(meta)) !== null) {
const key = match[1];
const value = match[2] || true;
result[key] = value;
}
return result;
}
Использование:
const metaString = 'linenos highlight="2,3"';
const parsed = parseMeta(metaString);
// parsed = { linenos: true, highlight: "2,3" }
Remark предоставляет возможность работать с AST через плагины. Для
блоков кода это обычно делается с помощью remark-parse и
собственного плагина для обработки code узлов.
Пример плагина для выделения строк:
const visit = require('unist-util-visit');
function remarkHighlightLines() {
return (tree) => {
visit(tree, 'code', (node) => {
const meta = parseMeta(node.meta);
if (meta.highlight) {
node.data = node.data || {};
node.data.highlightLines = meta.highlight.split(',').map(Number);
}
if (meta.linenos) {
node.data = node.data || {};
node.data.showLineNumbers = true;
}
});
};
}
visit используется для обхода всех узлов типа
code.node.data хранит структурированные данные для
последующей передачи в Rehype или рендерер.После обработки Remark-плагином AST может быть преобразован в HTML
через remark-rehype. Метаданные, сохранённые в
node.data, можно использовать для добавления атрибутов или
классов к элементам <pre> и
<code>:
const rehype = require('rehype');
const remarkRehype = require('remark-rehype');
const html = require('rehype-stringify');
function rehypeAddAttributes() {
return (tree) => {
visit(tree, 'element', (node) => {
if (node.tagName === 'code' && node.data) {
if (node.data.highlightLines) {
node.properties = node.properties || {};
node.properties['data-highlight'] = node.data.highlightLines.join(',');
}
if (node.data.showLineNumbers) {
node.properties = node.properties || {};
node.properties['className'] = (node.properties.className || []).concat('line-numbers');
}
}
});
};
}
Результирующий HTML может выглядеть так:
<pre class="line-numbers"><code class="language-javascript" data-highlight="2,3">
console.log('Hello World');
</code></pre>
Подсветка строк Метаданные позволяют задавать конкретные строки для акцента, что удобно для демонстраций и учебных материалов.
Нумерация строк Класс line-numbers
может быть обработан сторонними библиотеками, такими как PrismJS, для
отображения нумерации.
Фильтры кода Можно скрывать определённые блоки,
помеченные специальными метками, например skip или
demo.
Динамические параметры Любые дополнительные параметры (например, темы подсветки, автоисполнение) могут передаваться через метаданные и использоваться при рендеринге.
node.data после разбора.
Это гарантирует, что Rehype сможет безопасно использовать их для
генерации HTML.Метаданные блоков кода в Remark/Rehype обеспечивают мощный механизм для управления отображением, интерактивностью и дополнительной семантикой кода, оставаясь гибкими и расширяемыми для любых учебных и производственных задач.