Модель плагинов Rollup строится вокруг набора хуков, которые определяют этапы компиляции, трансформации и генерации бандла. Каждый хук имеет строгую семантику выполнения: синхронную, асинхронную, параллельную или последовательную. Эти различия влияют на порядок вызовов, производительность и детерминированность сборки.
Синхронные хуки выполняются немедленно и возвращают результат без ожидания Promise. Их поведение максимально предсказуемо: следующий шаг конвейера не начинается, пока текущий хук не завершён и не вернул значение.
Ключевая особенность синхронных хуков заключается в том, что они не допускают асинхронных операций. Любая попытка вернуть Promise будет интерпретироваться Rollup как ошибка или приведёт к некорректному поведению в зависимости от контекста исполнения.
Типичные синхронные хуки используются там, где не требуется I/O или задержки:
Пример поведения:
Синхронность важна для внутренних оптимизаций Rollup, особенно в местах, где результат нужен немедленно для принятия решений о включении модуля в граф зависимостей.
Асинхронные хуки возвращают Promise и позволяют выполнять операции, зависящие от внешних ресурсов: чтение файлов, сетевые запросы, обращение к кэшу или базе данных.
Асинхронная модель в Rollup полностью поддерживается через
async/await. При этом движок сборки ожидает завершения
Promise перед переходом к следующему этапу.
Поведение асинхронного хука:
Асинхронность особенно важна в хуках:
load — загрузка содержимого модулей из файловой
системыtransform — асинхронная трансформация кода (например,
Babel, TypeScript)resolveId — возможное разрешение путей через внешние
источникиАсинхронные хуки добавляют гибкость, но увеличивают стоимость сборки, особенно при большом количестве модулей, где каждый Promise добавляет overhead планирования микротасков.
Параллельные хуки выполняются одновременно для всех плагинов, без
строгой зависимости от порядка их объявления. Rollup инициирует вызовы
всех обработчиков и ожидает завершения всех Promise через
Promise.all.
Параллельность используется там, где результат каждого плагина не влияет напрямую на результат других плагинов.
К таким хукам относятся:
buildStartbuildEndcloseBundleМодель выполнения:
Важно, что параллельность не означает хаотичность. Порядок завершения может быть любым, но финальная агрегация результатов происходит детерминированно.
Параллельные хуки полезны для:
Основное ограничение: параллельные хуки не должны изменять общий поток данных, иначе возникает гонка состояний.
Последовательные хуки выполняются строго в порядке регистрации плагинов. Каждый следующий плагин получает результат предыдущего, формируя цепочку трансформаций.
Наиболее характерный пример — хук transform.
Модель выполнения:
Если один из плагинов возвращает null, модуль может быть
пропущен из дальнейшей обработки.
Последовательность особенно важна для:
transform — цепочка модификаций кодаload — последовательное предоставление содержимогоresolveId, где первый успешный ответ прерывает
цепочкуВ отличие от параллельных хуков, последовательные строго детерминированы и зависят от порядка подключения плагинов в конфигурации.
Эта модель создаёт эффект конвейера, где каждый плагин выступает как стадия обработки.
В реальной системе Rollup большинство хуков не относятся строго к одной категории. Один и тот же плагин может содержать:
resolveIdloadtransformbuildStartКомбинация моделей создаёт многоуровневую систему исполнения:
Особое внимание уделяется взаимодействию transform и
load, так как они формируют основной поток кода.
load предоставляет содержимое, а transform
последовательно модифицирует его.
Разделение на синхронные, асинхронные, параллельные и последовательные хуки напрямую влияет на:
1. Производительность
Параллельные хуки уменьшают общее время инициализации сборки за счёт одновременного выполнения. Асинхронные хуки могут как ускорять процесс (при I/O), так и замедлять его (из-за ожидания Promise).
2. Детерминированность
Последовательные хуки обеспечивают строгий порядок трансформаций. Параллельные требуют аккуратного обращения, чтобы избежать зависимости от порядка завершения.
3. Контроль потока данных
Синхронные и последовательные хуки дают полный контроль над тем, как данные проходят через систему плагинов. Асинхронные и параллельные вводят элементы конкурентности.
4. Расширяемость плагинов
Плагинная архитектура Rollup опирается на возможность комбинировать все типы хуков, позволяя строить сложные цепочки обработки кода без изменения ядра сборщика.
Один плагин может одновременно участвовать в разных типах исполнения. Например:
buildStart выполняется параллельноresolveId вызывается последовательно по цепочке
плагинов до первого результатаload может быть асинхроннымtransform применяется последовательно ко всем
плагинамЭто создаёт многоуровневую модель, где один и тот же плагин участвует в разных контекстах исполнения.
Важная деталь: даже если хук асинхронный, его участие в последовательной цепочке не нарушает порядок. Rollup ожидает завершения Promise перед передачей результата следующему плагину.
Разделение моделей выполнения приводит к необходимости учитывать:
transform)Эти правила формируют основу предсказуемой работы сборщика и позволяют комбинировать плагины разных авторов без конфликтов в логике исполнения.