Управление процессом парсинга

Для начала работы с библиотекой Marked достаточно установить её через npm:

npm install marked

После установки библиотека предоставляет основной метод marked(), который выполняет преобразование Markdown в HTML. По умолчанию Marked использует стандартные правила CommonMark, что обеспечивает совместимость с большинством Markdown-документов.

import { marked } from 'marked';

const markdown = '# Заголовок\n\nЭто пример текста.';
const html = marked(markdown);
console.log(html);

В результате преобразования текст Markdown будет конвертирован в HTML с корректными тегами <h1>, <p> и так далее.


Настройка опций

Marked поддерживает богатый набор опций, позволяющих управлять процессом парсинга и генерации HTML. Основные опции:

  • gfm: включает поддержку расширенного синтаксиса GitHub Flavored Markdown (по умолчанию true).
  • breaks: позволяет вставлять переносы строк после каждой новой строки Markdown (false по умолчанию).
  • sanitize: отключает вставку небезопасного HTML, что важно при работе с внешними данными.
  • mangle: отвечает за автоматическое изменение email-адресов для защиты от спам-ботов.
  • headerIds: включение генерации уникальных идентификаторов для заголовков.
  • smartLists и smartypants: управление обработкой списков и типографических символов.

Пример использования опций:

marked.setOptions({
  gfm: true,
  breaks: true,
  sanitize: false,
  headerIds: true,
  smartLists: true,
  smartypants: true
});

Пользовательские рендереры

Marked позволяет полностью контролировать вывод HTML с помощью собственного рендерера. Класс Renderer предоставляет методы для всех элементов Markdown:

  • heading(text, level, raw, slugger)
  • paragraph(text)
  • link(href, title, text)
  • image(href, title, text)
  • list(body, ordered)
  • listitem(text)
  • code(code, language)
  • blockquote(quote)

Пример кастомного рендерера, который добавляет класс к заголовкам:

import { marked, Renderer } from 'marked';

const renderer = new Renderer();

renderer.heading = (text, level) => {
  return `<h${level} class="custom-header">${text}</h${level}>`;
};

const markdown = '# Пример заголовка';
const html = marked(markdown, { renderer });
console.log(html);

Такой подход позволяет добавлять кастомные классы, атрибуты или полностью изменять структуру HTML для отдельных элементов.


Лексический и синтаксический контроль

Marked разделяет процесс парсинга на два этапа: лексический (лексер) и синтаксический (парсер).

  1. Лексер (Lexer) преобразует исходный Markdown в токены:
import { Lexer } from 'marked';

const tokens = Lexer.lex('# Заголовок\n\nТекст абзаца');
console.log(tokens);

Токены содержат информацию о типе элемента (heading, paragraph, list) и его содержимом, что позволяет делать промежуточную обработку перед генерацией HTML.

  1. Парсер (Parser) конвертирует токены в HTML. Можно использовать кастомный парсер для изменения логики рендеринга:
import { Parser, Lexer } from 'marked';

const tokens = Lexer.lex('# Заголовок\n\nТекст абзаца');

class CustomParser extends Parser {
  paragraph(token) {
    return `<p class="custom-paragraph">${token.text}</p>`;
  }
}

const parser = new CustomParser();
const html = parser.parse(tokens);
console.log(html);

Использование кастомных лексеров и парсеров даёт полную свободу над структурой и стилем финального HTML.


Асинхронная обработка

Marked поддерживает асинхронное парсинг, что особенно полезно при интеграции с внешними источниками, например, для асинхронной подгрузки синтаксиса для подсветки кода.

import { marked } from 'marked';

const markdown = '```js\nconsole.log("Hello World");\n```';

const options = {
  async: true,
  highlight: async (code, lang) => {
    const hljs = await import('highlight.js');
    return hljs.default.highlight(code, { language: lang }).value;
  }
};

marked(markdown, options).then(html => {
  console.log(html);
});

Асинхронная функция highlight позволяет интегрировать внешние библиотеки для подсветки кода, не блокируя основной поток выполнения.


Прерывание и фильтрация элементов

Marked предоставляет возможность фильтровать отдельные токены перед генерацией HTML. Это делается через собственные лексеры или обработку токенов после Lexer.lex.

import { Lexer, Parser } from 'marked';

const tokens = Lexer.lex('# Заголовок\n\nТекст абзаца\n\n## Подзаголовок');

const filteredTokens = tokens.filter(token => token.type !== 'heading' || token.depth === 1);

const html = Parser.parse(filteredTokens);
console.log(html);

Такой подход позволяет исключать ненужные элементы, например, второстепенные заголовки или ссылки, до того, как они попадут в финальный HTML.


Настройка slugger для идентификаторов заголовков

Для генерации уникальных ID заголовков Marked использует класс Slugger. Его поведение можно изменять для поддержки собственных схем именования:

import { Slugger } from 'marked';

const slugger = new Slugger();

console.log(slugger.slug('Заголовок')); // zаgolovok
console.log(slugger.slug('Заголовок')); // zаgolovok-1

Создание своего slugger позволяет интегрировать собственные правила транслитерации или учитывать особенности локализации.


Расширение функционала через токены

Marked поддерживает добавление пользовательских типов токенов. Это удобно для создания собственных блоков, например, для заметок, предупреждений или специальных виджетов:

import { Lexer, Parser } from 'marked';

const markdown = ':::note\nЭто заметка\n:::';

const tokens = Lexer.lex(markdown);

tokens.forEach(token => {
  if (token.type === 'paragraph' && token.text.startsWith(':::note')) {
    token.type = 'note';
    token.text = token.text.replace(':::note', '');
  }
});

class CustomParser extends Parser {
  note(token) {
    return `<div class="note">${token.text}</div>`;
  }
}

const parser = new CustomParser();
const html = parser.parse(tokens);
console.log(html);

Таким образом, Marked становится не просто конвертером Markdown, а полноценным инструментом для построения динамических и стилизованных HTML-документов с гибким управлением процессом парсинга.