В контексте обработки Markdown и HTML в экосистеме JavaScript блочные элементы занимают ключевое место. Remark и Rehype позволяют создавать, изменять и анализировать дерево синтаксических узлов (AST), где каждый узел может быть либо блочным, либо встроенным. Понимание различий между этими типами узлов и их правильное использование обеспечивает гибкость при трансформации и генерации контента.
Блочные элементы — это элементы, которые занимают всю доступную ширину контейнера и формируют отдельные блоки текста или структуры документа. В Markdown к блочным элементам относятся:
# Заголовок)> Цитата)--- или ***)В Rehype блочные элементы соответствуют HTML-тегам уровня блока,
таким как <p>, <div>,
<section>, <ul>,
<ol>, <blockquote>,
<pre> и
<h1>–<h6>.
Узел блочного элемента в AST Remark/Rehype имеет стандартные свойства:
type — тип узла (paragraph,
heading, list, blockquote,
code и т.д.)children — массив вложенных узлов (текстовых или других
элементов)data — дополнительные данные или метаинформация (часто
используется в плагинах)position — информация о позиции в исходном документе
(начальная и конечная строки, столбцы)Пример блочного узла абзаца в Remark:
{
type: 'paragraph',
children: [
{
type: 'text',
value: 'Это пример текста абзаца.'
}
],
position: {
start: { line: 1, column: 1 },
end: { line: 1, column: 28 }
}
}
Remark предоставляет возможность создавать узлы программно через
утилиту unist-builder или напрямую формируя объекты узлов.
Пример создания заголовка и абзаца:
import { u } from 'unist-builder';
const heading = u('heading', { depth: 2 }, [u('text', 'Заголовок второго уровня')]);
const paragraph = u('paragraph', [u('text', 'Содержимое абзаца')]);
Эти узлы затем могут быть добавлены в массив children
корневого документа (root), формируя полноценное дерево
Markdown-документа.
Блочные элементы могут содержать другие блочные элементы, если это поддерживается спецификацией Markdown. Например, список может содержать несколько абзацев:
{
type: 'list',
ordered: false,
children: [
{
type: 'listItem',
children: [
{
type: 'paragraph',
children: [{ type: 'text', value: 'Первый элемент списка' }]
},
{
type: 'paragraph',
children: [{ type: 'text', value: 'Дополнительная строка в элементе' }]
}
]
}
]
}
Особенность работы с такими вложениями — необходимость поддерживать корректную структуру AST, чтобы парсеры и рендереры корректно интерпретировали документ.
Remark и Rehype позволяют применять плагины для изменения дерева AST. Типичные операции с блочными элементами включают:
append,
prepend)replaceWith,
map)filter)<blockquote> в
<div class="quote">)Пример плагина для замены всех заголовков третьего уровня на заголовки второго уровня:
function transformHeadings() {
return (tree) => {
visit(tree, 'heading', (node) => {
if (node.depth === 3) {
node.depth = 2;
}
});
};
}
Блочные элементы могут содержать встроенные элементы
(inline), такие как emphasis,
strong, link или image. Это
позволяет комбинировать структуры: абзац может содержать текст,
выделенный жирным или курсивом, ссылки и изображения.
Пример абзаца с встроенными элементами:
{
type: 'paragraph',
children: [
{ type: 'text', value: 'Это ' },
{ type: 'strong', children: [{ type: 'text', value: 'важный' }] },
{ type: 'text', value: ' текст с ' },
{ type: 'emphasis', children: [{ type: 'text', value: 'выделением' }] }
]
}
При использовании Rehype блочные элементы конвертируются в
соответствующие HTML-теги. Remark может сначала трансформировать
Markdown в AST, затем через remark-rehype этот AST
преобразуется в HTML. Это обеспечивает строгую типизацию блоков и
удобство последующей стилизации:
import { remark } from 'remark';
import remarkRehype from 'remark-rehype';
import { rehypeStringify } from 'rehype-stringify';
const markdown = '# Заголовок\n\nАбзац текста.';
const html = await remark()
.use(remarkRehype)
.use(rehypeStringify)
.process(markdown);
console.log(String(html));
// <h1>Заголовок</h1><p>Абзац текста.</p>
Списки требуют отдельного внимания, так как элементы списка
(listItem) могут включать несколько абзацев или вложенные
списки. Правильное формирование структуры обеспечивает корректный
рендеринг:
ordered: true) сохраняют
нумерациюordered: false) отображаются с
маркерамиchildren
соответствующего элементаКаждый блочный узел содержит координаты начала и конца
(position), что позволяет точно определять место элемента в
исходном Markdown. Это полезно для инструментов линтинга, подсветки
ошибок и интерактивного редактирования документов.
Если требуется, могу следующим блоком подробно разобрать специфику обработки блочных элементов в Rehype с кастомными атрибутами и классами, включая динамическое добавление CSS-классов к заголовкам и блокам. Это логичное продолжение текущей главы.