Хук generateBundle относится к завершающей стадии сборки
в Rollup, когда основной граф модулей уже преобразован в набор выходных
чанков и ассетов, но результат ещё не записан на диск или в файловую
систему. Это точка расширения, позволяющая вмешаться в итоговую
структуру бандла, изменить содержимое выходных файлов, добавить новые
ассеты или удалить уже сформированные элементы.
Фаза генерации бандла следует за этапом renderChunk, где
каждый чанк уже получает финальный вид как текстовый JavaScript-код.
После этого Rollup формирует единый объект результата, который
передаётся в generateBundle.
Жизненный цикл Rollup можно условно разделить на несколько этапов:
options, buildStart)resolveId, load,
transform, moduleParsed)renderStart,
renderChunk)generateBundle)writeBundle)generateBundle находится между формированием итогового
представления бандла и его записью. Важно понимать, что на этом
этапе:
Именно это делает хук критически важным для постобработки результата.
Хук вызывается один раз на каждую генерацию вывода:
generateBundle(options, bundle, isWrite)
Объект конфигурации выходного формата:
esm, cjs, iife
и др.)Этот объект позволяет адаптировать поведение плагина под конкретный тип сборки.
Ключевой параметр хука — объект, содержащий все сгенерированные сущности бандла.
Структура:
ключи объекта — имена файлов (fileName)
значения — объекты типа:
chunk (JavaScript-чанк)asset (не-JS ресурсы)Пример:
{
"main.js": {
type: "chunk",
code: "..."
},
"style.css": {
type: "asset",
source: "body { margin: 0; }"
}
}
Через bundle можно:
Булев флаг:
true — результат будет записан на диск
(rollup -o)false — используется только
rollup.rollup() без записиЭтот флаг важен для плагинов, которые должны вести себя по-разному в режиме генерации и записи.
Каждый chunk имеет поле code, которое можно
изменять:
generateBundle(options, bundle) {
for (const fileName of Object.keys(bundle)) {
const file = bundle[fileName];
if (file.type === 'chunk') {
file.code += '\nconsole.log("postprocessed");';
}
}
}
Такой подход применяется для:
Важно учитывать, что изменения происходят после всех этапов оптимизации.
В generateBundle можно создавать новые файлы:
generateBundle(options, bundle) {
this.emitFile({
type: 'asset',
fileName: 'meta.json',
source: JSON.stringify({ build: Date.now() })
});
}
Особенности:
Можно удалить ненужные элементы:
generateBundle(options, bundle) {
for (const fileName of Object.keys(bundle)) {
if (fileName.includes('debug')) {
delete bundle[fileName];
}
}
}
Удаление применяется до записи и полностью исключает файл из результата.
Хотя прямого API для переименования нет, можно изменить
fileName через модификацию объекта:
generateBundle(options, bundle) {
const entry = bundle['main.js'];
if (entry) {
delete bundle['main.js'];
entry.fileName = 'app.js';
bundle['app.js'] = entry;
}
}
Это требует аккуратности, так как ссылки между чанками могут зависеть от имен.
В проектах с code splitting bundle содержит несколько
типов чанков:
Хук позволяет:
Пример анализа:
generateBundle(options, bundle) {
for (const file of Object.values(bundle)) {
if (file.type === 'chunk') {
console.log(file.fileName, file.imports, file.exports);
}
}
}
Это используется для:
Несмотря на широкие возможности, существуют ограничения:
На этом этапе недоступны:
Доступен только финальный bundle.
Изменение code — это строковая операция. Нет доступа к
AST или синтаксическим структурам.
Некорректные изменения могут привести к:
Используется для:
generateBundle(options, bundle) {
const meta = Object.keys(bundle).map(name => ({
name,
size: bundle[name].type === 'chunk'
? bundle[name].code.length
: bundle[name].source.length
}));
this.emitFile({
type: 'asset',
fileName: 'bundle-meta.json',
source: JSON.stringify(meta, null, 2)
});
}
Хук часто используется для:
Ключевое различие:
generateBundle — модификация результата до записиwriteBundle — постобработка уже записанных файловgenerateBundle предпочтителен, когда требуется:
writeBundle используется, когда нужно:
Внутри generateBundle доступен контекст плагина:
this.emitFilethis.getFileNamethis.errorПример генерации связанного ассета:
generateBundle() {
const fileRef = this.emitFile({
type: 'asset',
name: 'runtime-info',
source: 'init'
});
const fileName = this.getFileName(fileRef);
}
Это позволяет создавать взаимосвязанные сущности внутри бандла.
При большом количестве чанков важно учитывать:
bundle может содержать сотни файловОптимизация:
chunk /
asset)В архитектуре Rollup-плагинов generateBundle часто
выступает как слой агрегации:
Типичные роли:
generateBundle тесно связан с механизмом
emitFile:
emitFile регистрирует новый файл в сборкеbundleОсобенность: порядок вызовов может влиять на итоговую структуру,
поэтому добавленные файлы становятся частью текущего объекта
bundle в рамках одного вызова хука.
При использовании watch-режима:
generateBundle вызывается при каждой пересборкеbundle каждый раз пересоздаётсяПлагины должны быть идемпотентными, иначе возможны дублирующиеся ассеты или неконсистентные изменения.
generateBundle является финальной точкой программного
вмешательства в структуру результата сборки, где:
Это делает хук инструментом уровня «финальной сборки продукта», а не этапом трансформации кода.