Marked — это мощная библиотека для парсинга Markdown в HTML, созданная для высокой производительности и гибкой интеграции в различные JavaScript-приложения. Одним из ключевых преимуществ Marked является поддержка потоковой обработки, позволяющей парсить и рендерить Markdown постепенно, без необходимости загружать весь документ в память.
Для потоковой обработки используется объект
Marked.Renderer в сочетании с Marked.Parser.
Однако в последних версиях библиотеки потоковая обработка реализуется
через transform streams, что позволяет подключать
Marked напрямую к потокам данных Node.js. Это особенно полезно для
больших файлов или для рендеринга Markdown по мере его поступления,
например, из сети или базы данных.
Простейший пример потокового рендеринга:
import fs from 'fs';
import { marked } from 'marked';
import { Transform } from 'stream';
const markdownStream = fs.createReadStream('large.md', 'utf8');
const rendererStream = new Transform({
transform(chunk, encoding, callback) {
try {
const html = marked.parse(chunk.toString());
this.push(html);
callback();
} catch (err) {
callback(err);
}
}
});
markdownStream.pipe(rendererStream).pipe(process.stdout);
Пояснение к коду:
fs.createReadStream читает файл частями, уменьшая
нагрузку на память.Transform обрабатывает каждый полученный фрагмент
Markdown с помощью marked.parse.Marked позволяет настраивать поведение парсера и рендерера через объект опций. Это особенно важно при потоковой обработке, так как фрагменты Markdown могут быть неполными. Ключевые параметры:
gfm: включает поддержку расширенного синтаксиса
GitHub-flavored Markdown.breaks: управляет обработкой переносов строк.smartLists и smartypants: улучшают
обработку списков и типографики.headerIds и mangle: настройка генерации
идентификаторов для заголовков и защиты email-адресов.Пример с настройкой потокового рендерера:
import { marked } from 'marked';
marked.setOptions({
gfm: true,
breaks: true,
smartLists: true,
headerIds: true,
mangle: false
});
Эти настройки автоматически применяются при каждом вызове
marked.parse в потоке.
Для больших документов часто требуется рендерить Markdown по
блокам, чтобы корректно обрабатывать таблицы, списки и
вложенные элементы. Потоковая обработка по блокам достигается с помощью
Lexer и Parser:
import { marked } from 'marked';
const lexer = new marked.Lexer();
const tokens = lexer.lex('# Заголовок\n\nТекст абзаца\n\n- Пункт 1\n- Пункт 2');
const parser = new marked.Parser();
const html = parser.parse(tokens);
console.log(html);
Lexer разбивает Markdown на
токены.Parser конвертирует токены в HTML.Marked поддерживает асинхронный рендеринг, что позволяет подключать асинхронные расширения или плагины. Это удобно, если блоки Markdown требуют внешних данных (например, вставка кода с подсветкой):
import { marked } from 'marked';
const renderer = {
async code(code, lang) {
const highlighted = await highlightCodeAsync(code, lang);
return `<pre><code class="lang-${lang}">${highlighted}</code></pre>`;
}
};
marked.use({ renderer });
const html = await marked.parseAsync('# Пример кода\n\n```js\nconsole.log("Hello");\n```');
console.log(html);
Особенности:
parseAsync обрабатывает Markdown асинхронно.marked.parse для всех данных сразу; лучше
parseAsync для отдельных фрагментов.Lexer и Parser отдельно,
когда требуется более точный контроль за структурой документа.Потоковая обработка в Marked обеспечивает не только экономное использование ресурсов, но и открывает возможности для сложных сценариев рендеринга Markdown в реальном времени, включая веб-приложения, редакторы и генераторы документации.