В библиотеке Marked токенайзер отвечает за разбор входного Markdown-текста на отдельные токены — элементы структуры документа, такие как заголовки, списки, ссылки, изображения, блоки кода и др. Пользовательские токенайзеры позволяют расширять стандартный набор токенов или изменять существующую логику парсинга, что делает Marked гибким инструментом для нестандартного Markdown.
Пользовательский токенайзер — это объект, содержащий методы для распознавания конкретного синтаксиса. Каждый метод должен возвращать объект токена с ключевыми свойствами:
type — тип токена (например, 'heading',
'link', 'custom').raw — исходный текст, соответствующий токену.text — обработанный текст токена (например, без
Markdown-разметки).level для заголовка, href для
ссылки).Метод токенайзера получает один аргумент — строку Markdown для
разбора, и возвращает объект токена или undefined, если
данный токен не подходит.
const customTokenizer = {
myTag(src) {
const match = /^\[mytag\](.+?)\[\/mytag\]/.exec(src);
if (match) {
return {
type: 'myTag',
raw: match[0],
text: match[1]
};
}
}
};
Для подключения токенайзера к Marked используется свойство
tokenizer в опциях парсера. Можно создавать полные копии
стандартного токенайзера и расширять его собственными методами или
добавлять новые методы к объекту.
const marked = require('marked');
const myTokenizer = {
...marked.defaults.tokenizer,
myTag(src) {
const match = /^\[mytag\](.+?)\[\/mytag\]/.exec(src);
if (match) {
return {
type: 'myTag',
raw: match[0],
text: match[1]
};
}
}
};
marked.use({ tokenizer: myTokenizer });
После этого Marked будет распознавать синтаксис
[mytag]...[/mytag] как отдельный токен.
Для сложных синтаксисов часто требуется обрабатывать несколько случаев одновременно или использовать рекурсию. В этом случае токенайзер может вызывать другие методы для разбора вложенных структур.
Пример: токен для пользовательского блока с вложенным Markdown:
const myBlockTokenizer = {
myBlock(src) {
const match = /^:::\s*myblock\s*\n([\s\S]+?)\n:::/m.exec(src);
if (match) {
// Разбор вложенного Markdown с помощью стандартного токенайзера
const innerTokens = marked.lexer(match[1]);
return {
type: 'myBlock',
raw: match[0],
tokens: innerTokens
};
}
}
};
Такой подход позволяет создавать настраиваемые блоки, которые поддерживают любую Markdown-разметку внутри.
Порядок методов Marked обходит методы токенайзера в порядке их объявления. Методы с более специфическим синтаксисом должны идти выше общих, иначе стандартные токены могут «поглотить» пользовательский синтаксис.
Возврат undefined Если метод
токенайзера не распознает текст, он должен вернуть
undefined. Это позволяет парсеру переходить к следующему
методу или стандартному токену.
Работа с регулярными выражениями Использование
^ в начале регулярного выражения критично, так как Marked
проверяет текст с начала строки. Для многострочных блоков рекомендуется
флаг m.
Поддержка вложенности При создании токенов,
содержащих Markdown внутри, необходимо использовать
marked.lexer или рекурсивные вызовы пользовательских
токенайзеров, чтобы корректно создавать дерево токенов.
Токен для цветного текста:
const colorTokenizer = {
colorText(src) {
const match = /\[color=(.+?)\](.+?)\[\/color\]/.exec(src);
if (match) {
return {
type: 'colorText',
raw: match[0],
color: match[1],
text: match[2]
};
}
}
};
Токен для встроенных виджетов:
const widgetTokenizer = {
widget(src) {
const match = /\{\{widget:(.+?)\}\}/.exec(src);
if (match) {
return {
type: 'widget',
raw: match[0],
id: match[1]
};
}
}
};
После создания токенайзера необходимо обновить рендерер, чтобы Marked корректно отображал новые токены:
const renderer = {
myTag(token) {
return `<span class="my-tag">${token.text}</span>`;
},
myBlock(token) {
return `<div class="my-block">${marked.parser(token.tokens)}</div>`;
}
};
marked.use({ renderer });
Такой подход полностью интегрирует пользовательский синтаксис с Markdown-рендерингом, включая вложенные элементы.
type и raw.marked.lexer.Пользовательские токенайзеры делают библиотеку Marked мощным инструментом для реализации нестандартных расширений Markdown, позволяя создавать уникальные блоки, встроенные виджеты и любые другие элементы с кастомной логикой разбора и рендеринга.