Настройка бандлера Rollup для веб-проекта
Rollup занимает специфическую нишу среди сборщиков: он оптимален там, где результат — переиспользуемая библиотека, а не приложение. npm-пакеты, design system, SDK, утилиты — именно здесь Rollup выигрывает у Webpack и Vite за счёт качества tree-shaking и поддержки нескольких форматов вывода из одного конфига.
Когда выбирать Rollup
Rollup не универсальный инструмент. Для SPA с hot reload и dev-сервером удобнее Vite (который сам использует Rollup внутри для production-сборки). Rollup выбирают, когда нужно:
- собрать библиотеку в форматах ESM + CJS + UMD одновременно
- получить максимально чистый output без лишних обёрток
- контролировать, какие зависимости включаются в бандл, а какие остаются
external - генерировать TypeScript-декларации рядом с собранными файлами
Установка и базовый конфиг
npm install --save-dev rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-typescript rollup-plugin-dts
rollup.config.ts для типичной TypeScript-библиотеки:
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import dts from 'rollup-plugin-dts';
import { defineConfig } from 'rollup';
import pkg from './package.json' assert { type: 'json' };
export default defineConfig([
// Основная сборка: ESM + CJS
{
input: 'src/index.ts',
external: Object.keys(pkg.peerDependencies ?? {}),
plugins: [
resolve({ extensions: ['.ts', '.tsx'] }),
commonjs(),
typescript({ tsconfig: './tsconfig.build.json' }),
],
output: [
{
file: pkg.module, // dist/index.esm.js
format: 'esm',
sourcemap: true,
},
{
file: pkg.main, // dist/index.cjs.js
format: 'cjs',
sourcemap: true,
exports: 'named',
},
],
},
// Декларации TypeScript
{
input: 'dist/types/index.d.ts',
output: { file: 'dist/index.d.ts', format: 'esm' },
plugins: [dts()],
},
]);
package.json: exports map
Современный package.json для библиотеки с правильным exports:
{
"name": "my-lib",
"version": "1.0.0",
"type": "module",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs.js",
"types": "./dist/index.d.ts"
}
},
"files": ["dist"]
}
Внешние зависимости и peer deps
Частая ошибка — включать React или lodash в бандл библиотеки. external должен перечислять все peer-зависимости:
// Автоматически из package.json
const external = [
...Object.keys(pkg.peerDependencies ?? {}),
...Object.keys(pkg.dependencies ?? {}),
];
Если нужна частичная экстернализация (например, включить только lodash/merge, но не весь lodash):
external: (id) => id.startsWith('react') || /^lodash/.test(id),
Работа с CSS и ассетами
Для библиотек с CSS-модулями:
npm install --save-dev rollup-plugin-postcss
import postcss from 'rollup-plugin-postcss';
plugins: [
postcss({
modules: true,
extract: 'dist/styles.css', // отдельный файл вместо inject
minimize: true,
}),
]
Ассеты (SVG, изображения):
import url from '@rollup/plugin-url';
plugins: [
url({
include: ['**/*.svg'],
limit: 8192, // inline base64 до 8KB, иначе копирует файл
destDir: 'dist/assets',
}),
]
Многовходовая сборка
Для design system, где каждый компонент импортируется отдельно (import Button from 'ui/Button'):
import { glob } from 'glob';
const entries = Object.fromEntries(
(await glob('src/components/**/*.tsx')).map((file) => [
file.replace('src/', '').replace(/\.tsx$/, ''),
file,
])
);
export default defineConfig({
input: entries,
output: {
dir: 'dist',
format: 'esm',
preserveModules: true,
preserveModulesRoot: 'src',
},
// ...
});
preserveModules: true не объединяет всё в один файл, а сохраняет структуру директорий — это позволяет tree-shaking на стороне потребителя работать на уровне файлов.
Анализ размера бандла
npm install --save-dev rollup-plugin-visualizer
import { visualizer } from 'rollup-plugin-visualizer';
plugins: [
visualizer({
filename: 'dist/stats.html',
gzipSize: true,
brotliSize: true,
}),
]
После сборки открыть dist/stats.html — интерактивное дерево зависимостей с реальными размерами после gzip.
Watch-режим и разработка
rollup -c --watch
Или через конфиг для более тонкого контроля:
watch: {
include: 'src/**',
exclude: 'node_modules/**',
clearScreen: false,
},
Для разработки библиотеки параллельно с приложением-потребителем — npm link или file: зависимость в package.json потребителя. В монорепозитории — workspace-ссылки.
Сроки
Базовая настройка Rollup для TypeScript-библиотеки (один вход, ESM+CJS, декларации): 2–4 часа. Сложная настройка с CSS-модулями, многовходовой сборкой, настройкой CI для автоматического релиза в npm: 1–2 дня.







