Markdown-it — это мощная библиотека для обработки Markdown в JavaScript, построенная на модульной архитектуре. Одним из ключевых элементов её гибкости является возможность создания собственных токенов, позволяющих расширять стандартный синтаксис Markdown и внедрять кастомные правила парсинга.
В Markdown-it любой элемент Markdown сначала преобразуется в токен. Токен — это объект с полями, описывающими тип элемента, его содержимое, уровень вложенности и метаданные:
{
type: 'paragraph_open',
tag: 'p',
attrs: null,
map: [0, 1],
nesting: 1,
level: 0,
children: null,
content: '',
markup: '',
info: ''
}
paragraph_open, inline,
text).1 —
открывающий, 0 — самозакрывающий, -1 —
закрывающий.inline.Создание собственного токена требует понимания этой структуры и правильного взаимодействия с парсером.
Markdown-it строит процесс обработки Markdown на цепочке правил. Правила бывают двух типов:
paragraph, heading,
blockquote).strong, em, link).Добавление собственного правила осуществляется через API:
md.inline.ruler.before('emphasis', 'my_token', myRule);
'emphasis' — существующее правило, перед которым
вставляется новое.'my_token' — уникальное имя правила.myRule — функция, реализующая логику разбора.Рассмотрим создание токена для кастомного маркера
%%text%%, который будет преобразовываться в
<mark>text</mark>.
function myRule(state, silent) {
let pos = state.pos;
let max = state.posMax;
if (state.src[pos] !== '%' || state.src[pos + 1] !== '%') return false;
let start = pos + 2;
let end = state.src.indexOf('%%', start);
if (end === -1) return false;
if (!silent) {
let token = state.push('mark_open', 'mark', 1);
token.markup = '%%';
let textToken = state.push('text', '', 0);
textToken.content = state.src.slice(start, end);
state.push('mark_close', 'mark', -1);
}
state.pos = end + 2;
return true;
}
md.inline.ruler.before('emphasis', 'mark', myRule);
Объяснение кода:
state.src — исходная строка Markdown.state.pos — текущая позиция парсинга.silent — режим “проверки”, не создающий токены (важно
для синтаксиса).state.push() — метод создания нового токена. Передается
тип токена, HTML-тег и уровень вложенности.После этого парсер будет преобразовывать %%highlight%%
в:
<mark>highlight</mark>
Для блочных элементов используется block parser.
Например, для создания специального блока :::note:
function noteBlock(state, startLine, endLine, silent) {
let pos = state.bMarks[startLine] + state.tShift[startLine];
let max = state.eMarks[startLine];
if (state.src.slice(pos, pos + 5) !== ':::note') return false;
if (silent) return true;
let nextLine = startLine + 1;
while (nextLine < endLine) {
if (state.src.slice(state.bMarks[nextLine], state.eMarks[nextLine]).trim() === ':::') break;
nextLine++;
}
let token = state.push('note_open', 'div', 1);
token.attrs = [['class', 'note']];
token.map = [startLine, nextLine];
let contentToken = state.push('inline', '', 0);
contentToken.content = state.getLines(startLine + 1, nextLine, state.tShift[startLine + 1], true);
contentToken.children = [];
state.push('note_close', 'div', -1);
state.line = nextLine + 1;
return true;
}
md.block.ruler.before('fence', 'note', noteBlock, { alt: [] });
Особенности блочного парсинга:
startLine и endLine — номера строк для
текущего блока.state.getLines() — позволяет получить содержимое
блока.Markdown-it поддерживает расширяемые рендереры:
md.renderer.rules.mark_open = () => '<mark>';
md.renderer.rules.mark_close = () => '</mark>';
md.renderer.rules.note_open = (tokens, idx) => '<div class="note">';
md.renderer.rules.note_close = () => '</div>';
Это позволяет точно контролировать HTML-вывод для собственных токенов, включая добавление классов, атрибутов и структуры.
children или отдельные
push’и.Кастомные токены можно вставлять до или после
существующих правил. Это важно для корректного парсинга Markdown, чтобы
новые токены не конфликтовали с встроенными элементами (em,
strong, link).
md.inline.ruler.after('emphasis', 'my_token', myRule);
md.block.ruler.before('fence', 'my_block', myBlockRule);
Использование before и after позволяет
точно позиционировать парсинг нового синтаксиса.
Создание собственных токенов в Markdown-it открывает широкие возможности для кастомизации синтаксиса Markdown и управления рендерингом контента, позволяя строить сложные структуры и расширенные элементы документации или блогов.