Передача мета-информации между плагинами

В системе плагинов Rollup мета-информация выступает дополнительным каналом обмена данными между этапами сборки, позволяя плагинам координировать поведение без прямых зависимостей друг от друга. В отличие от стандартных параметров хуков, мета-данные не входят в публичный API модуля и используются как внутренний механизм расширения контекста сборки.

Основная задача мета-информации заключается в передаче сведений, которые:

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

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

Контекст плагинов и ограниченность стандартных данных

В Rollup каждый хук получает ограниченный набор аргументов: имя модуля, его содержимое, идентификаторы зависимостей, карту импорта. Однако этих данных недостаточно для сложных сценариев, например:

  • условной трансформации модулей в зависимости от других плагинов
  • координации нескольких этапов оптимизации
  • накопления статистики по проекту
  • передачи промежуточных результатов между фазами генерации кода

Для решения этих задач используется механизм meta, доступный через объект контекста плагина.

Контекстный объект this и роль meta

Во время выполнения хуков Rollup предоставляет плагинам объект контекста, в котором доступен набор служебных методов и свойств. Одним из ключевых элементов этого контекста является this.meta.

Структура meta-объекта

meta представляет собой произвольное хранилище данных, связанное с текущим процессом сборки. Его структура не фиксирована на уровне API и определяется плагинами самостоятельно.

Типично он используется как:

  • объект для хранения промежуточных результатов
  • реестр флагов состояния сборки
  • контейнер для межплагинного взаимодействия

Пример концептуального использования:

export default function pluginA() {
  return {
    name: 'plugin-a',
    buildStart() {
      this.meta = this.meta || {};
      this.meta.startTime = Date.now();
    }
  };
}

Модель передачи данных между плагинами

Rollup выполняет плагины последовательно, но не изолированно. Каждый следующий плагин имеет доступ к тому же контексту сборки. Это означает, что мета-данные становятся общим состоянием, доступным всем участникам цепочки.

Принцип разделяемого состояния

Мета-информация в Rollup фактически является shared state:

  • один плагин записывает данные
  • другой плагин читает их
  • третий плагин может модифицировать результат

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

Порядок выполнения плагинов

Порядок влияет на то, какие данные окажутся доступны в конкретный момент:

  1. buildStart
  2. resolveId
  3. load
  4. transform
  5. generateBundle
  6. writeBundle

Если плагин записывает мета-информацию в buildStart, она будет доступна в transform, но недоступна до момента инициализации сборки.

Практическая организация meta-данных

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

Для предотвращения конфликтов ключей мета-данные обычно структурируются по пространствам имен:

this.meta = {
  myPlugin: {
    cache: new Map(),
    flags: {
      optimized: false
    }
  }
};

Такой подход снижает риск перезаписи данных другим плагином.

Хранение кэшированных результатов

Один из частых сценариев — кэширование результатов тяжелых операций:

export default function pluginCache() {
  return {
    name: 'plugin-cache',
    transform(code, id) {
      this.meta = this.meta || {};
      this.meta.cache = this.meta.cache || new Map();

      if (this.meta.cache.has(id)) {
        return this.meta.cache.get(id);
      }

      const result = code.replace(/console\.log/g, '');
      this.meta.cache.set(id, result);

      return result;
    }
  };
}

Здесь meta используется как разделяемый кэш, который может быть доступен другим плагинам.

Передача сигналов между фазами сборки

Мета-информация часто применяется для передачи сигналов между фазами жизненного цикла сборки.

Флаги состояния

Плагин может устанавливать глобальные флаги:

buildStart() {
  this.meta ||= {};
  this.meta.hasPolyfills = true;
}

Другой плагин в фазе transform может использовать этот флаг:

transform(code) {
  if (this.meta?.hasPolyfills) {
    return code + '\n// polyfills enabled';
  }
  return code;
}

Таким образом реализуется условная логика без жесткой связности плагинов.

Аггрегация информации

Некоторые плагины собирают статистику:

transform(code, id) {
  this.meta ||= {};
  this.meta.stats ||= {
    files: 0,
    totalSize: 0
  };

  this.meta.stats.files += 1;
  this.meta.stats.totalSize += code.length;

  return code;
}

В конце сборки другой хук может использовать эти данные:

generateBundle() {
  const stats = this.meta.stats;
  console.log(stats);
}

Ограничения и проблемы модели meta

Отсутствие изоляции

Главная проблема заключается в том, что meta является общим состоянием. Это приводит к рискам:

  • перезапись данных другим плагином
  • неочевидные зависимости между плагинами
  • трудности отладки

Непредсказуемость порядка

Хотя Rollup определяет порядок хуков, порядок плагинов может быть изменен конфигурацией. Это влияет на содержимое meta в любой момент времени.

Потенциальные утечки состояния

Если meta содержит ссылки на большие структуры данных (AST, карты модулей), это может привести к увеличению потребления памяти.

Расширенные паттерны использования meta

Ленивая инициализация

Часто meta инициализируется только при необходимости:

function getMeta(ctx) {
  ctx.meta ||= {};
  return ctx.meta;
}

Это позволяет унифицировать доступ к данным и уменьшить дублирование кода.

Интеграция нескольких плагинов

Meta используется как координационный слой между плагинами оптимизации:

  • плагин анализа собирает данные
  • плагин трансформации использует их
  • плагин вывода учитывает финальные результаты

Пример координации:

// analyzer
transform(code) {
  this.meta.analysis ||= {};
  this.meta.analysis.hasDynamicImports = code.includes('import(');
  return code;
}

// optimizer
transform(code) {
  if (this.meta.analysis?.hasDynamicImports) {
    return optimizeAsync(code);
  }
  return optimizeSync(code);
}

Передача промежуточных AST

В сложных сборках meta используется для передачи структур AST между плагинами, чтобы избежать повторного парсинга:

parse(code) {
  this.meta ||= {};
  this.meta.ast = parseCode(code);
}

Дальнейшие плагины могут использовать уже готовое дерево.

Архитектурные рекомендации

При использовании meta в Rollup важно учитывать несколько принципов:

  • минимизация глобального состояния
  • разделение данных по логическим зонам
  • контроль жизненного цикла мета-информации
  • очистка временных данных после использования

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

Использование meta в watch-режиме

В режиме наблюдения за файлами meta может сохраняться между перезапусками сборки. Это позволяет реализовать:

  • инкрементальные кэши
  • ускоренные пересборки
  • повторное использование анализа модулей

Однако это также создает риск устаревших данных, если плагин не сбрасывает состояние корректно:

buildStart() {
  this.meta = {};
}

Взаимодействие meta и plugin context API

Meta не является официально стандартизированным API уровня Rollup как, например, this.emitFile или this.resolve. Это делает его более гибким, но менее предсказуемым.

В отличие от строго определенных методов контекста:

  • emitFile управляет ассетами
  • resolveId управляет модулями
  • load управляет загрузкой исходников

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

Моделирование сложных пайплайнов через meta

В крупных проектах meta становится основой для построения внутренних пайплайнов внутри Rollup:

  • этап анализа
  • этап нормализации
  • этап оптимизации
  • этап генерации

Каждый этап записывает промежуточные результаты в meta, формируя единый конвейер обработки.

Такой подход позволяет реализовать сложные трансформации без модификации ядра Rollup и без введения дополнительных внешних систем координации.