bundle.close(): освобождение ресурсов

Метод bundle.close() относится к завершающей стадии работы с экземпляром бандла, возвращаемого функцией rollup.rollup(). Его основная задача — корректное освобождение ресурсов, которые могли быть заняты в процессе сборки, особенно в режимах наблюдения (watch) или при использовании плагинов, создающих долгоживущие соединения, файловые дескрипторы или кеши.

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


Жизненный цикл объекта bundle

После вызова:

const bundle = await rollup.rollup(inputOptions);

создаётся объект, содержащий скомпилированный граф модулей и внутреннее состояние сборщика. Далее обычно выполняется один из методов:

await bundle.generate(outputOptions);
await bundle.write(outputOptions);

или многократные вызовы в режиме watch.

На этом этапе Rollup может удерживать:

  • файловые дескрипторы (через плагины)
  • кеши модулей
  • подписки на изменения файлов
  • watcher-инстансы (например, chokidar)
  • внутренние структуры графа зависимостей

Метод bundle.close() завершает этот жизненный цикл.


Что именно делает bundle.close()

bundle.close() инициирует процедуру очистки всех ресурсов, связанных с конкретным экземпляром сборки.

Ключевые действия:

Освобождение watch-режима

Если сборка была создана через rollup.watch(), либо плагины зарегистрировали наблюдатели файлов, bundle.close() завершает:

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

Очистка ресурсов плагинов

Rollup-плагины могут создавать долгоживущие ресурсы:

  • HTTP-соединения (в редких случаях, например, dev-серверы)
  • файловые кэши
  • worker-процессы
  • подключённые базы данных или индексаторы

bundle.close() даёт плагинам возможность корректно завершить работу через соответствующие lifecycle hooks (если они реализованы).


Освобождение памяти

Хотя JavaScript использует сборщик мусора, Rollup может удерживать:

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

После вызова bundle.close() эти структуры становятся недоступными и могут быть освобождены GC.


Завершение внутренних очередей

Rollup может иметь асинхронные задачи:

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

close() завершает или корректно дожидается завершения таких операций.


Когда необходимо вызывать bundle.close()

Однократная сборка без watch

В простом сценарии:

const bundle = await rollup.rollup(inputOptions);

await bundle.write(outputOptions);

await bundle.close();

Даже если сборка разовая, вызов полезен при наличии плагинов, создающих внешние ресурсы.


CI/CD и скрипты сборки

В автоматизированных процессах:

  • сборка выполняется многократно
  • процесс Node.js может не завершаться мгновенно
  • плагины могут удерживать watchers

В таких случаях отсутствие close() может приводить к зависанию процесса.


Долгоживущие Node.js процессы

Если Rollup встроен в:

  • кастомный build-tool
  • dev-server
  • SSR-сервер

то bundle.close() используется для:

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

Поведение при отсутствии bundle.close()

Игнорирование закрытия бандла может приводить к следующим последствиям:

Утечки памяти

Особенно при частых пересборках:

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

Зависшие watchers

Если используется watch-режим:

  • файловые наблюдатели продолжают работать
  • процесс Node.js не завершается
  • возможны дублирующиеся события изменений

Накопление ресурсов плагинов

Некоторые плагины не освобождают ресурсы автоматически:

  • временные файлы остаются на диске
  • worker-процессы продолжают выполняться
  • сетевые соединения остаются открытыми

Взаимодействие с rollup.watch()

Важно различать:

  • bundle.close()
  • watcher.close() (возвращается rollup.watch())

В watch-режиме:

const watcher = rollup.watch(inputOptions);

watcher.close();

Но внутри watcher создаёт bundle-экземпляры. В зависимости от версии Rollup и плагинов, bundle.close() может вызываться автоматически или требоваться вручную в пользовательских сценариях.


Асинхронная природа bundle.close()

Метод возвращает Promise:

await bundle.close();

Это означает, что закрытие включает:

  • завершение асинхронных plugin hooks
  • ожидание завершения фоновых задач
  • корректное освобождение файловых ресурсов

Игнорирование await может привести к незавершённой очистке.


Пример корректного жизненного цикла

import { rollup } from 'rollup';

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

  try {
    await bundle.write({
      file: 'dist/bundle.js',
      format: 'esm'
    });
  } finally {
    await bundle.close();
  }
}

build();

Использование finally гарантирует закрытие даже при ошибках сборки.


Влияние на плагины Rollup

Плагины могут реагировать на закрытие через собственные механизмы очистки. Хотя прямого универсального hook close в API плагинов нет, закрытие бандла влияет на:

  • завершение всех незавершённых buildEnd
  • остановку файловых наблюдателей
  • деинициализацию кешей, привязанных к инстансу сборки

Некоторые плагины реализуют собственные cleanup-методы через внешние API, и bundle.close() становится точкой их завершения.


Особенности использования в сложных сборочных системах

В интеграциях Rollup как библиотеки:

  • Vite-подобные системы
  • кастомные bundler orchestration tools
  • микросервисные сборочные пайплайны

bundle.close() становится частью управления жизненным циклом инстансов сборки.

Типичная модель:

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

Типичные ошибки при работе с bundle.close()

Игнорирование await

bundle.close(); // ошибка архитектуры

В этом случае очистка может не завершиться.


Многократное закрытие одного bundle

Повторный вызов:

await bundle.close();
await bundle.close();

может приводить к предупреждениям или no-op поведению, но в зависимости от версии и плагинов — к нестабильности.


Закрытие до завершения write/generate

Неправильный порядок:

await bundle.close();
await bundle.write(outputOptions);

прерывает процесс сборки и может оставить незавершённые задачи.


Роль bundle.close() в архитектуре Rollup API

Rollup API строится вокруг чёткого разделения стадий:

  1. создание графа (rollup.rollup)
  2. генерация/запись (generate / write)
  3. завершение жизненного цикла (close)

bundle.close() формально завершает инстанс сборки, переводя его из активного состояния в завершённое.

Это делает API предсказуемым в долгоживущих процессах и предотвращает накопление ресурсов при повторных сборках.