Публикация собственных плагинов

Подготовка к публикации

Плагины для Remark и Rehype представляют собой функции, которые расширяют возможности обработки AST (Abstract Syntax Tree) Markdown или HTML. Перед публикацией необходимо убедиться, что плагин:

  • Корректно обрабатывает входные данные.
  • Поддерживает совместимость с текущими версиями remark или rehype.
  • Содержит документацию с описанием API и примерами использования.
  • Имеет тесты, покрывающие ключевую функциональность.

В качестве обязательного шага стоит настроить пакет как npm-модуль, включив package.json с полями:

{
  "name": "remark-example-plugin",
  "version": "1.0.0",
  "main": "index.js",
  "keywords": ["remark", "plugin", "markdown"],
  "author": "Имя Автора",
  "license": "MIT"
}

Особое внимание уделяется полю keywords: наличие remark или rehype критично для поиска плагина через экосистему unified.


Структура плагина

Стандартная структура плагина включает один основной файл и опционально вспомогательные модули:

remark-example-plugin/
├─ index.js
├─ package.json
├─ README.md
└─ test/
   └─ index.test.js

index.js экспортирует функцию плагина. Для Remark пример базового шаблона:

module.exports = function remarkExamplePlugin(options = {}) {
  return (tree, file) => {
    // Обход дерева Markdown
    const visit = require('unist-util-visit');
    visit(tree, 'text', (node) => {
      node.value = node.value.replace(/example/gi, options.replacement || 'demo');
    });
  };
};

Для Rehype структура аналогична, но обход применяется к HTML AST, например:

module.exports = function rehypeExamplePlugin(options = {}) {
  return (tree) => {
    const visit = require('unist-util-visit');
    visit(tree, 'element', (node) => {
      if (node.tagName === 'strong') {
        node.tagName = 'b';
      }
    });
  };
};

Ключевым моментом является корректная работа с unist-util-visit или unist-util-visit-parents, что позволяет гибко модифицировать дерево узлов.


Документирование плагина

Документация должна включать:

  • Описание функциональности.
  • Пример подключения и вызова:
const remark = require('remark');
const remarkExamplePlugin = require('remark-example-plugin');

remark()
  .use(remarkExamplePlugin, { replacement: 'тест' })
  .process('Это пример текста', (err, file) => {
    console.log(String(file));
  });
  • Список опций с их типами и значениями по умолчанию.
  • Совместимость с другими популярными плагинами, если требуется.

README.md желательно оформлять в стиле markdown с подзаголовками ## Установка, ## Использование, ## Опции.


Тестирование плагина

Тесты проверяют:

  • Корректную обработку входных данных.
  • Сохранение структуры AST.
  • Обработку крайних случаев (пустой текст, вложенные узлы, специальные символы).

Пример на Jest:

const remark = require('remark');
const remarkExamplePlugin = require('../index');

test('заменяет слово example на demo', async () => {
  const file = await remark()
    .use(remarkExamplePlugin, { replacement: 'demo' })
    .process('example text');
  expect(String(file)).toBe('demo text');
});

Рекомендуется также проверять совместимость с TypeScript через декларации типов:

declare module 'remark-example-plugin' {
  import { Plugin } from 'unified';
  const remarkExamplePlugin: Plugin<[options?: { replacement?: string }]>;
  export default remarkExamplePlugin;
}

Публикация в npm

  1. Вход в npm: npm login.
  2. Проверка пакета: npm pack для локальной проверки структуры.
  3. Публикация: npm publish --access public.

Рекомендуется заранее проверить наличие уникального имени пакета, используя npm search remark-example-plugin. После публикации плагин становится доступен для установки через npm install remark-example-plugin.


Совместимость и версионирование

Для поддержания стабильности плагина:

  • Использовать семантическое версионирование (semver): MAJOR.MINOR.PATCH.
  • Обновления должны сопровождаться CHANGELOG.md.
  • Указывать совместимые версии remark/rehype в peerDependencies:
"peerDependencies": {
  "remark": "^14.0.0"
}
  • Тестировать плагин при апгрейде зависимостей.

Продвинутые практики

  • Использовать TypeScript для безопасности типов AST.
  • Интегрировать с CI/CD для автоматического тестирования и линтинга.
  • Поддерживать опции для расширяемости: фильтры узлов, кастомные обработчики.
  • Добавлять поддержку Unicode и многоязычного текста.

Эти меры позволяют создавать плагины, которые не только корректно работают, но и легко интегрируются в сложные проекты с большим количеством Markdown/HTML-данных.