Библиотека Marked предоставляет гибкую систему расширений, которая позволяет модифицировать процесс разбора Markdown и генерации HTML. Расширения делятся на несколько типов: лексические (lexer extensions) и рендерные (renderer extensions). Каждый тип имеет собственный способ регистрации и область применения.
Лексические расширения позволяют вмешиваться в процесс токенизации текста Markdown. Они работают на уровне исходного текста и могут определять новые типы токенов или модифицировать существующие.
Структура лексического расширения:
const myLexerExtension = {
name: 'customLexer',
level: 'block', // или 'inline'
start(src) {
// Определяет позицию начала потенциального совпадения
return src.match(/^\[.*\]/)?.index;
},
tokenizer(src, tokens) {
const match = /^\[([^\]]+)\]\(([^)]+)\)/.exec(src);
if (match) {
return {
type: 'customLink',
raw: match[0],
text: match[1],
href: match[2]
};
}
},
childTokens: [],
};
'block' для
блочных элементов, 'inline' для встроенных.type,
raw и другими пользовательскими.Регистрация лексического расширения происходит через:
import { marked } from 'marked';
marked.use({ extensions: [myLexerExtension] });
После этого все вызовы marked.parse() будут учитывать
добавленные токены.
Рендерные расширения позволяют изменять HTML, генерируемый из токенов. Они работают после этапа токенизации и управляют выводом конкретных элементов.
Пример рендерного расширения:
const myRendererExtension = {
name: 'customRenderer',
level: 'inline',
renderer(token) {
if (token.type === 'customLink') {
return `<a href="${token.href}" class="custom">${token.text}</a>`;
}
}
};
Регистрация идентична лексической:
marked.use({ extensions: [myRendererExtension] });
Можно комбинировать несколько расширений, используя единый вызов
marked.use():
marked.use({
extensions: [myLexerExtension, myRendererExtension]
});
При этом порядок элементов в массиве имеет значение:
Это позволяет создавать сложные цепочки обработки Markdown, например, добавлять нестандартные блоки и одновременно задавать для них специфический HTML.
Если два расширения пытаются обработать один и тот же кусок текста, Marked использует следующее правило:
Чтобы избежать конфликтов, рекомендуется:
level и
start().const emojiLexer = {
name: 'emoji',
level: 'inline',
tokenizer(src) {
const match = /:([a-z_]+):/.exec(src);
if (match) {
return {
type: 'emoji',
raw: match[0],
text: match[1]
};
}
}
};
const emojiRenderer = {
name: 'emojiRenderer',
level: 'inline',
renderer(token) {
if (token.type === 'emoji') {
return `<span class="emoji">${token.text}</span>`;
}
}
};
marked.use({ extensions: [emojiLexer, emojiRenderer] });
В этом примере текст Hello :smile: будет преобразован в
HTML <span class="emoji">smile</span>.
level позволяет разграничить область действия
(block vs inline).start и tokenizer критичны для
производительности и точности.Расширения в Marked делают библиотеку мощной и гибкой, позволяя адаптировать Markdown-парсер под любые специфические требования.