Remark — это библиотека для разбора и обработки Markdown-документов в формате JavaScript. Она предоставляет возможность преобразовать Markdown в абстрактное синтаксическое дерево (AST), известное как MDAST (Markdown Abstract Syntax Tree), которое удобно анализировать, модифицировать и конвертировать в другие форматы.
Для работы с Remark достаточно установить пакет через npm:
npm install remark
Импортировать библиотеку в проект:
import {remark} from 'remark';
Основной метод для парсинга — remark().parse(). Он
принимает текст Markdown и возвращает дерево MDAST:
import {remark} from 'remark';
const markdownText = `
# Заголовок 1
Это параграф с *курсивом* и **жирным текстом**.
`;
const tree = remark().parse(markdownText);
console.log(tree);
Структура MDAST состоит из узлов, каждый из которых имеет тип
(type) и массив дочерних узлов (children).
Пример дерева для вышеуказанного Markdown:
{
"type": "root",
"children": [
{
"type": "heading",
"depth": 1,
"children": [{ "type": "text", "value": "Заголовок 1" }]
},
{
"type": "paragraph",
"children": [
{ "type": "text", "value": "Это параграф с " },
{ "type": "emphasis", "children": [{ "type": "text", "value": "курсивом" }] },
{ "type": "text", "value": " и " },
{ "type": "strong", "children": [{ "type": "text", "value": "жирным текстом" }] },
{ "type": "text", "value": "." }
]
}
]
}
Основные типы узлов включают:
root — корневой элемент дерева.heading — заголовки Markdown (#,
##, …), с атрибутом depth.paragraph — абзацы текста.text — простой текст.emphasis — выделение курсивом.strong — выделение жирным.link — ссылки с атрибутами url и
title.list и listItem — списки и элементы
списка.code — блоки кода с атрибутом lang для
указания языка.Каждый узел может содержать дополнительные поля, например,
position, где хранится информация о номере строки и позиции
в исходном Markdown.
Для обработки дерева используется унифицированная концепция AST. Например, можно рекурсивно обходить дерево и изменять узлы:
import {visit} from 'unist-util-visit';
visit(tree, 'text', (node) => {
node.value = node.value.replace(/Markdown/g, 'MD');
});
visit позволяет выполнять действия только на узлах
определённого типа, что упрощает фильтрацию и модификацию дерева.
Remark поддерживает плагины для расширения функциональности.
Например, плагин remark-parse добавляет поддержку
расширенного синтаксиса:
import remarkParse from 'remark-parse';
import {remark} from 'remark';
const processor = remark().use(remarkParse);
const tree = processor.parse(markdownText);
Другие популярные плагины:
remark-gfm — поддержка GitHub Flavored Markdown
(таблицы, чекбоксы, автоссылки).remark-frontmatter — для парсинга YAML- и
TOML-фронтматтеров.remark-slug — автоматическая генерация id
для заголовков.После преобразования Markdown в MDAST часто возникает задача конвертировать дерево в HTML. Для этого используется Rehype, который работает с HAST (HTML AST):
import {remark} from 'remark';
import rehype from 'rehype';
import remark2rehype from 'remark-rehype';
import html from 'rehype-stringify';
const markdown = '# Заголовок';
const htmlContent = await remark()
.use(remark2rehype)
.use(html)
.process(markdown);
console.log(String(htmlContent));
Этот процесс включает два шага:
remark-rehype.rehype-stringify.Для подсветки синтаксиса в Markdown можно использовать комбинацию
MDAST и Rehype с rehype-prism:
import remark from 'remark';
import remark2rehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import rehypePrism from 'rehype-prism-plus';
const markdown = '```javascript\nconsole.log("Hello");\n```';
const result = await remark()
.use(remark2rehype)
.use(rehypePrism)
.use(rehypeStringify)
.process(markdown);
console.log(String(result));
Такой подход позволяет не только парсить Markdown, но и преобразовывать его в стилизованный HTML с подсветкой синтаксиса.
Для сложных трансформаций часто используют рекурсивный обход дерева:
function analyzeNode(node) {
if (node.type === 'heading' && node.depth === 1) {
console.log('Найден главный заголовок:', node.children[0].value);
}
if (node.children) {
node.children.forEach(analyzeNode);
}
}
analyzeNode(tree);
Такой метод полезен для генерации оглавлений, проверки структуры документа и создания кастомных плагинов для Remark.