Хук moduleParsed

Фаза moduleParsed выполняется в момент, когда Rollup завершает разбор модуля в абстрактное синтаксическое дерево (AST) и сформировал базовую структуру информации о модуле. На этом этапе модуль уже загружен, пропущен через load и transform (если они были задействованы), и его содержимое приведено к финальному виду, который будет использоваться в графе зависимостей.

Хук вызывается для каждого модуля, включённого в граф сборки:

moduleParsed(id, moduleInfo)
  • id — абсолютный путь к модулю (или виртуальный идентификатор)
  • moduleInfo — объект с метаданными о модуле, формируемый Rollup

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

Ключевой момент: вызов происходит после парсинга AST, но до окончательной фиксации графа зависимостей и оптимизаций tree-shaking.

Роль в жизненном цикле модуля

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

  1. load
  2. transform
  3. парсинг AST
  4. moduleParsed
  5. построение и уточнение графа зависимостей
  6. оптимизации (tree-shaking, scope analysis)

Таким образом, moduleParsed является точкой, где:

  • AST уже доступен через moduleInfo.ast
  • зависимости уже извлечены
  • но граф ещё не финализирован

Это делает хук особенно ценным для анализа структуры модуля, но не для изменения исходного кода.

Доступные данные и ограничения

Объект moduleInfo содержит широкий набор данных:

  • code — итоговый код модуля после transform
  • ast — ESTree-совместимое дерево
  • imports — список импортов
  • exports — экспортируемые сущности
  • hasDefaultExport — наличие default export
  • isEntry — является ли модуль входной точкой
  • moduleSideEffects — информация о побочных эффектах

Однако важное ограничение заключается в том, что:

Изменение moduleInfo внутри moduleParsed не влияет на уже построенный AST или граф зависимостей.

Любые попытки модифицировать структуру модуля на этом этапе являются либо игнорируемыми, либо приводят к неочевидному поведению.

Типичные сценарии использования

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

Анализ AST и структуры

Наиболее распространённый сценарий — инспекция AST:

export default function myPlugin() {
  return {
    name: 'analyzer',
    moduleParsed(id, info) {
      if (info.ast && info.ast.body) {
        const functionDeclarations = info.ast.body.filter(
          node => node.type === 'FunctionDeclaration'
        );

        if (functionDeclarations.length > 5) {
          // логирование или метрики
        }
      }
    }
  };
}

Сбор метаданных

Хук часто используют для построения внешних индексов модулей:

  • карта всех экспортов
  • анализ циклических зависимостей
  • построение документации

Валидация кода

Возможна проверка архитектурных правил:

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

Отличие от transform и load

Ключевая ошибка при использовании Rollup-плагинов — путаница между transform и moduleParsed.

transform

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

moduleParsed

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

Ключевое различие:

transform влияет на входные данные, moduleParsed — наблюдает результат

AST и его доступность

Одним из ключевых преимуществ moduleParsed является доступ к AST:

moduleParsed(id, info) {
  const ast = info.ast;
}

AST в Rollup:

  • строится через Acorn
  • соответствует ESTree спецификации
  • содержит позиции, типы узлов, структуру выражений

Однако AST может быть null в случаях:

  • виртуальных модулей без парсинга
  • бинарных или нативных ресурсов
  • модулей, обработанных только load без JS-кода

Взаимодействие с module graph

На момент вызова moduleParsed:

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

Это даёт возможность использовать API:

this.getModuleInfo(id)

или анализировать связи через moduleInfo.importedIds.

Однако важно учитывать:

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

Асинхронные сценарии

Хотя хук чаще используется синхронно, поддержка Promise позволяет выполнять тяжёлые операции:

  • анализ AST через сторонние инструменты
  • запись метрик в базу данных
  • интеграция с линтерами
moduleParsed(id, info) {
  return someAsyncAnalysis(info.ast).then(result => {
    // сохранение результатов
  });
}

При этом Rollup не блокирует весь пайплайн, но учитывает завершение Promise перед финализацией сборки.

Ограничения и подводные камни

Отсутствие влияния на код

Любые попытки изменить info.code или info.ast не приводят к изменениям в сборке.

Потенциальная нагрузка

Так как хук вызывается для каждого модуля:

  • тяжёлые операции могут замедлить сборку
  • AST traversal в больших проектах требует оптимизации

Дублирование логики

Некоторые данные уже доступны в transform, поэтому дублирование анализа в moduleParsed часто избыточно.

Практическая архитектура плагина

Корректное использование обычно выглядит так:

export default function plugin() {
  const cache = new Map();

  return {
    name: 'module-analyzer',

    moduleParsed(id, info) {
      cache.set(id, {
        imports: info.imports,
        exports: info.exports,
        hasDefault: info.hasDefaultExport
      });
    },

    buildEnd() {
      // использование накопленных данных
    }
  };
}

Такой подход позволяет:

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

Роль в архитектуре Rollup

moduleParsed занимает промежуточное положение между:

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

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