Создание простого расширения

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

npm install marked

Импортировать библиотеку в проект можно следующим образом:

import { marked } from 'marked';

После импорта можно настроить глобальные параметры парсера:

marked.setOptions({
  renderer: new marked.Renderer(),
  gfm: true,             // Включение GitHub Flavored Markdown
  breaks: false,         // Разрешение на перенос строк
  sanitize: false,       // Автоматическая очистка HTML
  smartLists: true,      // Интеллектуальные списки
  smartypants: false     // Замена кавычек и тире на типографские
});

Основы создания собственного расширения

Расширение в Marked позволяет модифицировать или добавлять поведение парсера, например, новые синтаксические конструкции или обработку специальных блоков. Основными способами создания расширения являются:

  1. Лексические расширения (Lexer extensions) – для распознавания новых токенов.
  2. Рендеринговые расширения (Renderer extensions) – для управления преобразованием токенов в HTML.
  3. Синтаксические расширения (Tokenizer extensions) – позволяют внедрять собственные правила разборки Markdown.

Создание токенизатора

Токенизатор отвечает за преобразование текста в токены. Пример токенизатора для распознавания специальных блоков типа :::note:

const noteTokenizer = {
  name: 'note',
  level: 'block', // блоковый элемент
  start(src) {
    return src.match(/:::note/)?.index;
  },
  tokenizer(src, tokens) {
    const rule = /^:::note\s+([\s\S]+?)\s+:::/;
    const match = rule.exec(src);
    if (match) {
      return {
        type: 'note',
        raw: match[0],
        text: match[1].trim()
      };
    }
  }
};

Добавление токенизатора в Marked:

marked.use({ extensions: [noteTokenizer] });

Создание собственного рендерера

Рендерер отвечает за преобразование токенов в HTML. Для обработки нового типа токена note необходимо добавить метод в объект рендерера:

const renderer = {
  note(token) {
    return `<div class="note">${marked.parseInline(token.text)}</div>`;
  }
};

marked.use({ renderer });

Теперь текст вида:

:::note
Это важное уведомление
:::

будет преобразован в:

<div class="note">Это важное уведомление</div>

Работа с цепочкой расширений

Marked позволяет использовать несколько расширений одновременно. Расширения объединяются в массив, передаваемый через marked.use():

marked.use({
  extensions: [noteTokenizer, spoilerTokenizer],
  renderer: customRenderer
});

Порядок расширений важен: токенизаторы обрабатываются по очереди, а рендереры – по типу токена.

Настройка приоритетов и уровня элементов

Каждое расширение можно настраивать по уровню:

  • block – для блочных элементов (div, section, списки).
  • inline – для встроенных элементов (span, a, em).

Также можно определить приоритет обработки, чтобы новые токены обрабатывались раньше или после стандартных элементов Markdown. Для этого используется метод start(src) в токенизаторе, который возвращает позицию первого совпадения.

Пример комплексного расширения

Создание расширения, которое добавляет и блок note, и встроенный блок spoiler:

const spoilerTokenizer = {
  name: 'spoiler',
  level: 'inline',
  start(src) { return src.indexOf('||'); },
  tokenizer(src) {
    const match = /^\|\|([\s\S]+?)\|\|/.exec(src);
    if (match) {
      return {
        type: 'spoiler',
        raw: match[0],
        text: match[1]
      };
    }
  }
};

const customRenderer = {
  note(token) {
    return `<div class="note">${marked.parseInline(token.text)}</div>`;
  },
  spoiler(token) {
    return `<span class="spoiler">${token.text}</span>`;
  }
};

marked.use({
  extensions: [noteTokenizer, spoilerTokenizer],
  renderer: customRenderer
});

С помощью этого подхода можно создавать полноценные расширения, которые добавляют новые синтаксические элементы, сохраняют совместимость с стандартным Markdown и интегрируются в существующие проекты.

Рекомендации по разработке расширений

  • Минимизировать пересечение с стандартными токенами, чтобы не нарушать базовый Markdown.
  • Использовать parseInline внутри рендерера для обработки вложенного Markdown.
  • Тестировать расширения на больших текстах, чтобы убедиться в стабильности и корректности обработки.
  • Документировать типы токенов, чтобы другие разработчики могли легко интегрировать расширение.

Такой подход обеспечивает гибкость и расширяемость Marked, позволяя добавлять новые элементы без изменения ядра библиотеки.