Совместимость со старыми плагинами

В экосистеме Remark и Rehype совместимость со старыми плагинами является критическим аспектом, обеспечивающим плавный переход на новые версии и позволяющим использовать существующие наработки без полной переработки кода. Основной механизм совместимости строится на строгом соблюдении стандартов AST (Abstract Syntax Tree) и унификации API плагинов.

Структура AST и её стабильность

Remark использует собственный формат AST, известный как MDAST (Markdown Abstract Syntax Tree). Для Rehype применяется HAST (Hypertext Abstract Syntax Tree). Ключевой принцип совместимости заключается в неизменности базовых узлов и их свойств:

  • Типы узлов (type) должны сохраняться. Например, heading, paragraph, text.
  • Структура дочерних элементов (children) обязана соответствовать ожидаемому формату. Любое нарушение приводит к ошибкам у старых плагинов.
  • Дополнительные свойства (data, position) добавляются только в новые версии узлов, не удаляя или изменяя существующие.

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

API плагинов и обратная совместимость

Remark и Rehype предоставляют плагинам единый интерфейс через функцию-обработчик. Совместимость обеспечивается следующими принципами:

  • Поддержка старых аргументов. Плагины, использующие сигнатуру (options?: object) => Transformer, продолжают работать, даже если в новой версии добавлены дополнительные необязательные параметры.
  • Возврат Transformer. Transformer должен оставаться функцией (tree, file) => void | Promise<void>. Любые изменения типа возвращаемого значения могут нарушить работу старого плагина.
  • Асинхронность. Старые плагины могли быть синхронными. Новые версии Remark/Rehype допускают как синхронные, так и асинхронные плагины. Для обратной совместимости синхронный плагин автоматически обрабатывается как Promise, если используется асинхронная обработка.

Поддержка старых плагинов через адаптеры

Для плагинов, разработанных под ранние версии Remark или Rehype, можно использовать адаптеры, которые обеспечивают преобразование AST и корректную работу API:

  • Преобразование MDAST/HAST. Если структура узлов изменилась, адаптер создаёт “мост” между старой и новой схемой узлов, сохраняя ключевые свойства для плагина.
  • Обёртка функций. Старые функции-обработчики могут быть обёрнуты в новую сигнатуру (tree, file, next) => { ... }, что обеспечивает совместимость с современным движком Remark/Rehype.
  • Полиморфная поддержка данных. Плагины, использующие устаревшие поля в узлах AST, получают их через адаптер, даже если новые версии библиотеки заменили эти поля на новые свойства.

Примеры совместимости

  1. Старый плагин обработки заголовков:
function oldHeadingPlugin() {
  return (tree) => {
    visit(tree, 'heading', (node) => {
      node.value = node.value.toUpperCase();
    });
  };
}

В новых версиях Remark node может иметь поле children вместо value. Адаптер преобразует структуру:

function headingAdapterPlugin(oldPlugin) {
  return (tree) => {
    const adaptedTree = adaptTreeForOldPlugin(tree);
    oldPlugin()(adaptedTree);
    mergeTreeChanges(tree, adaptedTree);
  };
}
  1. Асинхронная обработка Markdown:

Старые плагины были синхронными, но их можно использовать в remark().use(asyncPlugin), оборачивая старую функцию в Promise.resolve().

Тестирование совместимости

Для гарантии стабильной работы старых плагинов рекомендуется проводить тестирование:

  • Сравнение AST до и после обработки плагином.
  • Сквозные тесты Markdown → HTML, чтобы убедиться, что выходной код не изменился неожиданным образом.
  • Регрессионное тестирование с ключевыми плагинами экосистемы, включая remark-parse, remark-stringify, rehype-stringify.

Лучшие практики

  • Не модифицировать базовые типы узлов MDAST/HAST.
  • Добавлять новые свойства через data для совместимости с плагинами, не использующими новые поля.
  • Использовать адаптеры для интеграции старых плагинов с современным стеком Remark/Rehype.
  • Документировать изменения API, чтобы разработчики могли корректно обновлять плагины.

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