rollup.rollup(): сборка и получение объекта bundle

rollup.rollup() является центральной точкой программного API Rollup и запускает процесс построения бандла. На этом этапе происходит анализ зависимостей, построение графа модулей, применение плагинов и подготовка внутреннего представления будущего результата сборки.

import { rollup } from 'rollup';

const bundle = await rollup({
  input: 'src/index.js'
});

Возвращаемое значение — объект bundle, который представляет собой результат фазы построения (build phase), но ещё не содержит финального кода. Это промежуточная сущность, позволяющая отдельно управлять генерацией и записью выходных файлов.


Этап построения графа модулей

При вызове rollup.rollup() выполняется последовательность шагов:

  1. Разбор входной точки (input)
  2. Рекурсивный обход импортов
  3. Применение плагинов на этапе загрузки и трансформации
  4. Построение графа зависимостей
  5. Определение оптимальной структуры чанков

Граф модулей в Rollup — это ориентированный граф, в котором каждый узел соответствует модулю, а ребра представляют зависимости через import.

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


Роль плагинов в процессе rollup.rollup()

Плагины активно участвуют уже на этапе rollup.rollup(). Основные хуки, влияющие на сборку:

  • options — модификация входных параметров
  • buildStart — начало процесса сборки
  • resolveId — разрешение путей модулей
  • load — загрузка содержимого модуля
  • transform — трансформация кода
  • moduleParsed — реакция на разобранный модуль

Каждый модуль проходит через цепочку плагинов, что позволяет изменять код до того, как он попадёт в граф.

Ключевой момент: на этапе rollup() код ещё не генерируется, но уже полностью формируется структура бандла.


Объект bundle: промежуточный результат сборки

Результат вызова rollup.rollup() — это объект bundle, содержащий методы:

  • generate(outputOptions)
  • write(outputOptions)
  • close()

Этот объект инкапсулирует весь построенный граф и внутренние оптимизации.

Пример:

const bundle = await rollup({
  input: 'src/index.js'
});

const result = await bundle.generate({
  format: 'esm'
});

Метод bundle.generate()

Метод generate() запускает процесс генерации выходного кода без записи на диск.

На этом этапе происходят:

  • разделение на чанки (code splitting)
  • tree-shaking (удаление неиспользуемого кода)
  • генерация итогового синтаксиса (ESM, CJS, IIFE и др.)
  • применение output-плагинов

Результат:

{
  output: [
    {
      code: '...',
      map: {...},
      fileName: 'chunk.js',
      format: 'esm'
    }
  ]
}

Важно, что generate() возвращает структуру результата в памяти и не взаимодействует с файловой системой.


Метод bundle.write()

write() выполняет те же операции, что и generate(), но дополнительно записывает результат в файловую систему.

await bundle.write({
  dir: 'dist',
  format: 'esm'
});

Функционально процесс включает:

  • генерацию чанков
  • вычисление имен файлов
  • применение output.plugins
  • запись файлов через встроенный файловый эмиттер

Различие между generate() и write() заключается исключительно в финальном этапе I/O.


Структура outputOptions

Объект outputOptions управляет тем, как будет формироваться финальный бандл:

  • format — формат вывода (esm, cjs, iife, umd, system)
  • dir или file — место записи
  • entryFileNames — шаблон имен входных чанков
  • chunkFileNames — шаблон имен дополнительных чанков
  • assetFileNames — ресурсы (CSS, изображения)
  • sourcemap — генерация source maps

Эти параметры влияют только на фазу генерации, но не на построение графа в rollup().


Внутренний результат bundle: чанки и модули

После выполнения rollup.rollup() формируется внутреннее представление:

  • Modules — обработанные исходные файлы
  • Chunks — группы модулей, объединённые в один файл
  • Assets — статические ресурсы

Rollup стремится минимизировать количество чанков, сохраняя оптимальную структуру зависимостей.

Пример логики формирования чанков:

  • entry module → основной чанк
  • динамический import() → отдельный чанк
  • общие зависимости → выделяются в shared chunk

Асинхронная модель выполнения

rollup.rollup() всегда возвращает Promise, так как процесс включает:

  • асинхронную загрузку модулей
  • выполнение плагинов
  • возможные операции чтения файлов
const bundle = await rollup(inputOptions);

Асинхронность распространяется дальше на generate() и write(), что позволяет строить сложные пайплайны сборки.


Влияние tree-shaking на этапе rollup()

Tree-shaking начинается уже после построения графа, но его основы закладываются в rollup.rollup():

  • анализ import/export
  • определение side effects
  • пометка используемых узлов графа

Каждый модуль получает мета-информацию:

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

Это делает Rollup особенно эффективным для библиотек и ESM-кода.


Работа с несколькими входными точками

Если в input передан массив или объект, rollup.rollup() строит несколько entry-узлов:

await rollup({
  input: {
    main: 'src/main.js',
    admin: 'src/admin.js'
  }
});

В этом случае формируется несколько независимых деревьев, которые могут:

  • разделять общие зависимости
  • генерировать несколько entry chunks
  • использовать shared chunks для оптимизации

Контроль жизненного цикла bundle

Объект bundle требует явного закрытия при использовании в долгоживущих процессах:

await bundle.close();

Это освобождает:

  • файловые дескрипторы
  • кеши модулей
  • ресурсы плагинов

Особенно важно при использовании Rollup в watch-режиме или серверных сборщиках.


Связь rollup.rollup() с архитектурой сборки

rollup.rollup() можно рассматривать как слой компиляции:

  • вход: конфигурация и исходный код
  • выход: структурированный граф модулей

Дальнейшие методы generate() и write() являются этапами сериализации этого графа в конкретные форматы.

Такое разделение позволяет:

  • повторно использовать один bundle для разных форматов
  • генерировать несколько output без повторной сборки
  • оптимизировать CI/CD процессы

Переиспользование bundle для мульти-вывода

Один и тот же bundle может использоваться несколько раз:

const bundle = await rollup(inputOptions);

await bundle.write({ dir: 'dist/esm', format: 'esm' });
await bundle.write({ dir: 'dist/cjs', format: 'cjs' });

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