В современных фронтенд-проектах сборка становится неотъемлемой частью автоматизированного жизненного цикла доставки кода. Rollup в этом контексте выступает как инструмент, обеспечивающий детерминированную компиляцию модулей, tree-shaking и генерацию оптимизированных бандлов для различных окружений.
В CI/CD-процессах Rollup обычно выполняется как отдельный этап после установки зависимостей и перед тестированием или деплоем. Его задача — преобразовать исходный модульный код в готовые артефакты, которые можно безопасно публиковать в staging или production.
Ключевая особенность интеграции Rollup в CI/CD заключается в том, что он должен работать без интерактивного ввода, быть воспроизводимым и одинаково выполняться в разных средах.
При использовании Rollup в автоматизированных пайплайнах необходимо учитывать ряд требований:
Особенно важно избегать ситуаций, когда результат сборки зависит от времени выполнения или скрытых системных переменных. CI должен всегда воспроизводить одинаковый бандл при одинаковом входном коде.
Обычно пайплайн включает следующие этапы:
Rollup занимает центральное место между анализом кода и тестированием, так как формирует финальную версию модулей.
Конфигурационный файл Rollup должен быть максимально предсказуемым. Пример базовой структуры:
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';
export default {
input: 'src/index.js',
output: [
{
file: 'dist/bundle.esm.js',
format: 'esm',
sourcemap: true
},
{
file: 'dist/bundle.cjs.js',
format: 'cjs',
sourcemap: true
}
],
plugins: [
resolve(),
commonjs(),
babel({ babelHelpers: 'bundled' })
]
};
В CI важно фиксировать:
Даже незначительное изменение порядка может привести к различиям в выходных артефактах.
CI/CD часто требует разных режимов сборки:
Rollup обычно управляет этим через process.env.NODE_ENV
или отдельные флаги.
Пример:
const production = process.env.NODE_ENV === 'production';
export default {
plugins: [
production && terser()
]
};
В CI важно явно задавать переменные окружения, а не полагаться на значения по умолчанию.
Один из ключевых способов ускорения сборки — кэширование зависимостей и промежуточных результатов.
В контексте Rollup можно кэшировать:
node_modules.rollup.cache (если используется кастомная
реализация)dist, если сборка инкрементальнаяОднако важно избегать кэширования, которое может привести к неконсистентным результатам. Особенно это касается плагинов, трансформирующих код на основе внешних факторов.
Типичный workflow для Rollup-сборки:
name: build
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
В данном случае npm run build должен запускать Rollup
без дополнительных интерактивных шагов.
Пример .gitlab-ci.yml:
stages:
- build
build:
image: node:20
stage: build
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
Особенность GitLab CI — удобная работа с артефактами. Rollup-выход обычно сохраняется как артефакт для последующих стадий (тестирование, деплой).
В CI/CD часто требуется связывать билд с конкретным коммитом или тегом.
Подходы:
Пример:
import { execSync } from 'child_process';
const commitHash = execSync('git rev-parse --short HEAD')
.toString()
.trim();
export default {
output: {
file: `dist/bundle.${commitHash}.js`,
format: 'esm'
}
};
Это позволяет точно идентифицировать, какой код был развернут.
Rollup сам по себе не является многопоточным сборщиком, но ускорение возможно через:
Пример стратегии:
В CI любая ошибка сборки должна приводить к остановке pipeline.
Типичные источники ошибок:
Важно, чтобы Rollup возвращал корректный exit code. В скриптах npm это достигается автоматически, но при кастомных Node-скриптах нужно явно пробрасывать ошибки.
Иногда Rollup запускается не через CLI, а через API:
import { rollup } from 'rollup';
import config from './rollup.config.js';
async function build() {
const bundle = await rollup(config);
await Promise.all(
config.output.map(output =>
bundle.write(output)
)
);
}
build().catch(err => {
console.error(err);
process.exit(1);
});
Такой подход удобен в сложных CI-сценариях, где требуется динамическая сборка конфигурации.
Часто один pipeline должен собирать несколько окружений:
Rollup позволяет формировать разные конфигурации:
export default [
{
input: 'src/index.js',
output: {
file: 'dist/app.esm.js',
format: 'esm'
}
},
{
input: 'src/index.js',
output: {
file: 'dist/app.legacy.js',
format: 'iife'
}
}
];
CI может запускать их последовательно или параллельно.
В CI важно контролировать:
Rollup в этом контексте выступает как конечный этап проверки целостности цепочки зависимостей.
После успешной сборки Rollup-артефакты обычно:
Ключевой принцип — отделение сборки от деплоя. CI генерирует неизменяемый результат, CD только распространяет его.
В зрелых CI/CD системах отслеживаются:
Рост размера бандла может сигнализировать о проблемах tree-shaking или утечках зависимостей.
На практике часто встречаются следующие проблемы:
Решение обычно сводится к фиксации окружения и упрощению конфигурации Rollup.
В крупных монорепозиториях Rollup используется:
Часто добавляются инструменты orchestration, которые решают, какие пакеты нужно пересобирать при изменении кода.
Rollup-сборка может быть частью проверок качества:
Эти проверки выполняются после сборки и могут блокировать деплой.