Внутренний механизм Rollup предусматривает возможность динамического
создания дополнительных файлов на этапе сборки. Для этого используется
контекстный метод плагина this.emitFile, который позволяет
добавлять как отдельные чанки, так и статические ассеты, не входящие
напрямую в граф исходных модулей. Этот механизм является ключевым для
генерации побочных артефактов: CSS-файлов, вспомогательных скриптов,
иконок, JSON-данных, воркеров и любых производных ресурсов.
В рамках архитектуры Rollup эмиссия файлов интегрирована в фазу генерации бандла и тесно связана с жизненным циклом плагинов.
Метод this.emitFile доступен только внутри плагинов и
только в определённых хуках:
buildStarttransformresolveId (в ограниченных сценариях)loadrenderChunkgenerateBundleВажно учитывать, что результат вызова emitFile не
является немедленным созданием файла на диске. Вместо этого Rollup
регистрирует виртуальную сущность в графе вывода, которая будет
материализована только на этапе генерации.
emitFile принимает объект с описанием типа создаваемого
ресурса:
this.emitFile({
type: 'asset',
name: 'styles.css',
source: 'body { margin: 0; }'
});
или
this.emitFile({
type: 'chunk',
id: './src/worker.js'
});
Результатом вызова является строковый идентификатор, который можно использовать для дальнейшего обращения к созданному файлу.
const fileId = this.emitFile({
type: 'asset',
name: 'data.json',
source: JSON.stringify({ a: 1 })
});
Тип asset используется для генерации статических файлов,
не входящих в модульный граф.
this.emitFile({
type: 'asset',
fileName: 'runtime-info.txt',
source: 'generated at build time'
});
Особенности:
Тип chunk создаёт новый модульный чанк, который
включается в граф зависимостей.
this.emitFile({
type: 'chunk',
id: './src/secondary-entry.js'
});
Особенности:
Созданный файл не имеет фиксированного имени в момент вызова
emitFile. Rollup назначает имя на этапе
generateBundle. Для получения итогового имени
используется:
const referenceId = this.emitFile({
type: 'asset',
name: 'config.json',
source: '{}'
});
const fileName = this.getFileName(referenceId);
getFileName доступен только на этапе генерации, например
в generateBundle или renderChunk.
Каждый вызов emitFile возвращает referenceId —
внутренний ключ, по которому Rollup связывает:
Пример хранения нескольких файлов:
const cssId = this.emitFile({
type: 'asset',
name: 'main.css',
source: 'body{}'
});
const jsonId = this.emitFile({
type: 'asset',
name: 'meta.json',
source: '{"version":1}'
});
Позже эти идентификаторы используются для построения финального манифеста.
Наиболее полный контроль над результатами эмиссии достигается в хуке
generateBundle.
generateBundle(options, bundle) {
const id = this.emitFile({
type: 'asset',
name: 'manifest.json',
source: JSON.stringify(bundle, null, 2)
});
const fileName = this.getFileName(id);
}
В этот момент уже доступна финальная структура бандла, включая все чанки и ассеты.
В renderChunk можно динамически создавать
вспомогательные ресурсы на основе конкретного чанка.
renderChunk(code, chunk) {
if (chunk.isEntry) {
this.emitFile({
type: 'asset',
name: `${chunk.name}-meta.json`,
source: JSON.stringify({
imports: chunk.imports,
exports: chunk.exports
})
});
}
}
Это позволяет генерировать метаинформацию для каждого входного модуля.
При использовании code splitting emitFile становится
инструментом создания дополнительных точек расширения:
Пример динамического создания вспомогательного чанка:
this.emitFile({
type: 'chunk',
id: './src/helper.js'
});
Такой подход часто используется для выделения:
emitFile часто применяется вместе с виртуальными
модулями, когда содержимое создаётся на лету:
resolveId(source) {
if (source === 'virtual:config') {
return source;
}
},
load(id) {
if (id === 'virtual:config') {
return `
export const file = import.meta.ROLLUP_FILE_URL_config;
`;
}
}
И далее:
this.emitFile({
type: 'asset',
name: 'config.json',
source: '{}'
});
Rollup автоматически связывает
import.meta.ROLLUP_FILE_URL_* с реальным файлом.
Rollup предоставляет механизм инъекции ссылок на созданные файлы через специальные шаблоны:
import.meta.ROLLUP_FILE_URL_<id>Пример:
const id = this.emitFile({
type: 'asset',
name: 'image.png',
source: buffer
});
В результате можно получить URL:
export const url = import.meta.ROLLUP_FILE_URL_image;
Это используется для:
При работе с emitFile существуют ограничения:
getFileName недоступен в buildStartsource должен быть строкой или Uint8Arraychunk должен ссылаться на существующий модульНекорректные вызовы приводят к ошибкам сборки или неконсистентному графу зависимостей.
generateBundle(_, bundle) {
const manifest = {};
for (const file of Object.keys(bundle)) {
manifest[file] = bundle[file].type;
}
this.emitFile({
type: 'asset',
name: 'manifest.json',
source: JSON.stringify(manifest)
});
}
this.emitFile({
type: 'asset',
name: 'runtime.js',
source: `console.log('runtime loaded');`
});
const ids = [
this.emitFile({ type: 'asset', name: 'a.txt', source: 'A' }),
this.emitFile({ type: 'asset', name: 'b.txt', source: 'B' })
];
Внутри системы плагинов Rollup emitFile выступает
как:
Его использование напрямую влияет на структуру итогового бандла и может менять характер сборки от статической к динамически формируемой.