Структура проекта при использовании Rollup строится вокруг принципа явного определения входной точки и прозрачного контроля над графом модулей. В отличие от монолитных сборок, где исходный код рассматривается как единое целое, Rollup опирается на модульную модель ES Modules и выстраивает граф зависимостей от указанного entry.
Типичный проект на Rollup минимально включает следующие элементы:
project/
src/
index.js
dist/
rollup.config.js
package.json
Ключевым является каталог src, в котором располагается
исходный код, и файл конфигурации, определяющий поведение сборщика.
Файл src/index.js традиционно выступает входной
точкой:
import { sum } from './utils/sum.js';
import { multiply } from './utils/multiply.js';
export function calculate(a, b) {
return sum(a, b) + multiply(a, b);
}
Rollup начинает построение графа именно с этого файла. Все
import становятся ребрами графа зависимостей, а
export определяют публичный API модуля.
В Rollup входная точка задаётся явно через параметр
input:
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'esm'
}
};
Здесь важно, что input не просто строка, а начало обхода
графа модулей. Rollup не ищет зависимости автоматически в папках — он
строго следует указанному пути.
Допустимы различные формы задания:
input: './src/index.js'
или с использованием Node-резолвинга:
input: require.resolve('./src/index.js')
В больших проектах предпочтение часто отдают абсолютным путям, чтобы исключить неоднозначность при запуске из разных окружений.
Rollup поддерживает мульти-entry конфигурации, что важно для библиотек, предоставляющих несколько независимых API.
export default {
input: ['src/index.js', 'src/admin.js'],
output: {
dir: 'dist',
format: 'esm'
}
};
В этом случае каждый вход формирует отдельный chunk.
Более контролируемый вариант:
export default {
input: {
main: 'src/index.js',
admin: 'src/admin.js'
},
output: {
dir: 'dist',
format: 'esm'
}
};
Результат:
dist/
main.js
admin.js
Такой подход позволяет явно управлять именами выходных файлов.
Внутренне Rollup строит directed graph, где:
Входная точка является корневым узлом обхода. Пример:
index.js
├── utils/sum.js
├── utils/multiply.js
└── constants.js
Rollup анализирует только те модули, которые достижимы из entry. Всё остальное игнорируется, что напрямую связано с tree-shaking.
Выбор entry напрямую влияет на структуру выходного бандла:
output: {
format: 'esm'
}
Каждый входной модуль сохраняет структуру импортов. Entry становится корнем ESM-графа.
output: {
format: 'iife',
name: 'MyLibrary'
}
Входной файл превращается в точку инициализации IIFE-функции:
var MyLibrary = (function () {
// содержимое entry и зависимостей
})();
output: {
format: 'cjs'
}
Entry становится модулем, экспортируемым через
module.exports.
В библиотеках часто используют отдельный entry для публичного API:
src/
index.js // публичный API
internal/
helpers.js
export { calculate } from './core/calculate.js';
В приложениях entry обычно содержит побочные эффекты:
import './styles.css';
import { initApp } from './app/init.js';
initApp();
Динамические импорты влияют на граф следующим образом:
export async function loadModule() {
const mod = await import('./heavy-module.js');
return mod.run();
}
Rollup разделяет такие модули в отдельные чанки, но они остаются связанными с основным entry через async edge.
При необходимости сохранить структуру проекта используется:
export default {
input: 'src/index.js',
output: {
dir: 'dist',
format: 'esm',
preserveModules: true
}
};
В этом режиме entry перестаёт быть точкой объединения, а превращается в корень дерева файлов.
Структура сохраняется:
dist/
index.js
utils/
sum.js
multiply.js
При нескольких entry Rollup автоматически выполняет дедупликацию зависимостей:
input: {
app: 'src/app.js',
admin: 'src/admin.js'
}
Если оба entry используют один модуль:
import { log } from './shared/log.js';
то Rollup выносит его в отдельный shared chunk.
Хотя Rollup не зависит напрямую от package.json, в
библиотечных проектах часто согласуют entry с полями:
{
"main": "dist/index.cjs",
"module": "dist/index.js",
"exports": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
}
Это не влияет на процесс сборки, но определяет потребление результата.
Некоторые плагины могут модифицировать entry-граф:
virtual:)Пример виртуального entry:
import virtual from '@rollup/plugin-virtual';
export default {
input: 'entry',
plugins: [
virtual({
entry: 'export default 42;'
})
]
};
Здесь entry существует только в памяти.
В монорепозиториях часто используется схема:
packages/
core/
src/index.js
ui/
src/index.js
Каждый пакет имеет собственный entry:
input: 'packages/core/src/index.js'
или через отдельные конфиги Rollup для каждого пакета.
Entry определяет границы анализа:
Чем точнее entry, тем эффективнее tree-shaking.
Типичные проблемы:
Каждая из этих ситуаций влияет на корректность построения графа и результат сборки.