Directive нотация

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


Типы директив

Директивы бывают трёх основных типов:

  1. Leaf directives (листовые директивы) Представляют собой одиночные элементы без вложенного контента. Их можно использовать для вставки ссылок, кнопок, иконок или других небольших блоков.

    Пример синтаксиса:

    :icon[star]{color="gold"}

    Здесь icon — имя директивы, [star] — аргумент, а {color="gold"} — набор атрибутов.

  2. Container directives (контейнерные директивы) Используются для обрамления целых блоков контента, таких как карточки, предупреждения или секции с кодом.

    Пример синтаксиса:

    :::note{type="warning"}
    Текст предупреждения внутри контейнера.
    :::

    В AST это создаёт узел containerDirective, содержащий дочерние узлы Markdown.

  3. Leaf-Block directives (гибридные листо-контейнерные директивы) Могут содержать текстовый контент, но обычно применяются для блоков с минимальной структурой. Они полезны для блоков типа цитат с параметрами или интерактивных элементов.


Структура узла директивы в AST

При парсинге Markdown через Remark директивы преобразуются в узлы типа:

{
  type: 'leafDirective' | 'containerDirective' | 'textDirective',
  name: 'имя_директивы',
  attributes: {
    ключ: значение
  },
  children: [ /* дочерние узлы Markdown */ ],
  data: { hName, hProperties } // для Rehype, используется при трансформации в HTML
}
  • type — тип директивы (leaf, container, text).
  • name — идентификатор, используемый для определения логики рендеринга.
  • attributes — объект с ключами и значениями, переданными через {}.
  • children — массив дочерних узлов для контейнерных директив.
  • data.hName / data.hProperties — поля для Rehype, позволяющие задать HTML-теги и их свойства.

Обработка директив в Remark

Для работы с директивами в Remark используется плагин remark-directive. Он расширяет стандартный парсер, добавляя поддержку синтаксиса:

import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkDirective from 'remark-directive';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';

const processor = unified()
  .use(remarkParse)
  .use(remarkDirective)
  .use(transformDirectives)
  .use(remarkRehype)
  .use(rehypeStringify);

function transformDirectives() {
  return (tree) => {
    visit(tree, (node) => {
      if (node.type === 'leafDirective' || node.type === 'containerDirective') {
        node.data = node.data || {};
        node.data.hName = node.name; // пример преобразования в HTML-тег с именем директивы
        node.data.hProperties = node.attributes || {};
      }
    });
  };
}

Функция transformDirectives демонстрирует стандартный способ преобразования директив в HTML-структуру через Rehype.


Атрибуты директив

Атрибуты задаются через фигурные скобки {} и могут включать:

  • Простые строки: {color="red"}
  • Булевы значения: {collapsed=true}
  • Массивы через запятую: {tags="js,remark,retext"}

При трансформации в HTML они обычно конвертируются в свойства элементов: hProperties.


Применение в практике

  • Обогащение контента: директивы позволяют создавать семантически значимые блоки, которые затем легко стилизовать через CSS.
  • Интерактивные компоненты: контейнерные директивы могут содержать код или виджеты, которые потом обрабатываются JavaScript.
  • Документация: в технических текстах директивы помогают структурировать подсказки, предупреждения и примеры кода.

Взаимодействие Remark и Rehype

Remark создаёт AST на основе Markdown с поддержкой директив. Rehype берёт этот AST и конвертирует его в HTML. Поля data.hName и data.hProperties обеспечивают точное соответствие тегов и атрибутов:

  • LeafDirective → <имя атрибуты/>
  • ContainerDirective → <имя атрибуты>...контент...

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


Ограничения и нюансы

  • Директивы не поддерживаются в стандартном Markdown, поэтому их обработка требует плагина remark-directive.
  • Вложенные контейнерные директивы должны корректно закрываться (:::), иначе AST будет неполным.
  • При использовании Rehype важно правильно задавать hName и hProperties, иначе HTML-теги могут генерироваться некорректно.

Примеры сложных конструкций

Комбинация контейнерной и листовой директивы:

:::card{title="Пример карточки"}
Текст внутри карточки.

:icon[star]{color="gold"}
:::

В AST это создаёт контейнер card с дочерним текстом и листовой директивой icon. При трансформации в HTML можно получить:

Текст внутри карточки.

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