rollup.rollup() и bundle.generate() / bundle.write()

rollup.rollup() запускает процесс сборки и возвращает объект сборки (bundle), который является центральной точкой дальнейшего взаимодействия с результатом компиляции. В отличие от привычных одношаговых инструментов, Rollup разделяет этапы анализа графа модулей и этапы генерации выходного кода, что позволяет гибко управлять выводом: получать результат в памяти или записывать его в файловую систему.

Функция rollup.rollup(inputOptions) принимает объект конфигурации, который описывает входные точки, плагины и поведение резолвинга.

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

Типичная форма вызова:

import { rollup } from 'rollup';

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

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

  • анализ входной точки (input)
  • обход импортов и построение графа зависимостей
  • применение resolve и transform плагинов
  • формирование промежуточного представления модулей
  • подготовка к генерации кода

Важно, что на данном шаге ещё не создаются финальные файлы. Все операции работают в памяти.

Если в процессе сборки возникает ошибка (например, неразрешённый импорт), она будет выброшена именно на этапе rollup().


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

Результатом вызова rollup() является объект bundle. Он содержит методы генерации и записи, а также внутреннее состояние графа.

Основные методы:

  • bundle.generate(outputOptions)
  • bundle.write(outputOptions)
  • bundle.close()

Этот объект можно рассматривать как «собранный проект», который можно многократно использовать для разных целей вывода.


bundle.generate(): генерация результата в памяти

Метод bundle.generate(outputOptions) используется для получения итогового кода без записи на диск. Это ключевой инструмент для:

  • серверного рендеринга
  • интеграции с API
  • тестирования бандла
  • кастомной обработки output

Пример:

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

console.log(result.output);

На выходе возвращается объект следующей структуры:

{
  output: [
    {
      type: 'chunk',
      code: '...',
      map: {...},
      fileName: 'index.js'
    }
  ]
}

Особенности generate()

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

Типичный сценарий — генерация нескольких форматов:

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

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


bundle.write(): запись результата на диск

Метод bundle.write(outputOptions) выполняет генерацию кода и запись файлов в файловую систему. По сути, это комбинация generate + emit файлов.

Пример:

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

Основное отличие от generate заключается в том, что результат не только формируется, но и сохраняется физически.

Поведение write()

  • создаёт директорию (если указана dir)
  • записывает один или несколько файлов
  • применяет naming-конвенции (fileName, entryFileNames, chunkFileNames)
  • учитывает asset emission (из плагинов)
  • выполняет финализацию sourcemap

Различия между generate() и write()

Несмотря на схожесть внутренней логики, методы решают разные задачи.

generate()

  • работает только в памяти
  • возвращает объект с output[]
  • не создаёт файлов
  • используется для кастомной обработки

write()

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

Ключевая идея: generate — это «виртуальная сборка», write — «физическая сборка».


Внутренний процесс формирования output

И generate, и write используют единый механизм генерации:

  1. traversal графа модулей
  2. tree-shaking (удаление неиспользуемого кода)
  3. генерация chunks
  4. применение output plugins
  5. генерация финального кода
  6. (для write) эмиссия файлов

Tree-shaking играет критическую роль: Rollup анализирует статические импорты и удаляет неиспользуемые экспорты, формируя минимальный результат.


Повторное использование bundle

Объект bundle может быть использован несколько раз до вызова close():

const bundle = await rollup(config);

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

Это позволяет:

  • избегать повторного парсинга исходников
  • ускорять multi-target сборку
  • разделять этапы генерации и записи

outputOptions и влияние на результат

Output options определяют поведение генерации:

  • format (esm, cjs, iife, umd)
  • dir / file
  • sourcemap
  • entryFileNames
  • chunkFileNames
  • assetFileNames
  • plugins (output plugins)

Эти параметры применяются уже после построения графа, на этапе генерации кода.


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

И rollup(), и методы bundle работают асинхронно, что связано с:

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

После завершения работы рекомендуется вызывать:

await bundle.close();

Это освобождает ресурсы плагинов (например, watcher, соединения, кеши).


Ошибки и отладка на уровне bundle

Ошибки могут возникать:

  • на этапе rollup() (graph building)
  • на этапе generate/write (code generation)
  • в output plugins

Особенность модели Rollup — строгая детерминированность графа: если зависимость не может быть разрешена, генерация останавливается до bundle.


Практическое значение разделения generate и write

Разделение на два метода позволяет строить сложные системы сборки:

  • CI/CD пайплайны, где generate используется для анализа
  • серверные инструменты, где нужен только код в памяти
  • multi-build системы с разными форматами
  • кастомные бандлеры поверх Rollup API

Такая архитектура делает Rollup не просто CLI-инструментом, а программируемым компилятором модульного графа JavaScript.