Конфигурация сборщика редко остаётся статической. В разных режимах работы требуется изменять:
Жёстко прописанные значения быстро превращают конфигурацию в трудно поддерживаемый файл. Переменные окружения позволяют адаптировать конфигурацию под различные сценарии запуска без изменения исходного кода.
Наиболее распространённые переменные:
NODE_ENV=development
NODE_ENV=production
BUILD_TARGET=modern
ENABLE_ANALYTICS=true
API_URL=https://example.com
Конфигурация Rollup выполняется внутри Node.js, поэтому все
переменные окружения доступны через объект process.env.
Пример:
console.log(process.env.NODE_ENV);
Внутри rollup.config.js:
export default {
output: {
sourcemap: process.env.NODE_ENV !== 'production'
}
};
Если переменная не существует, значение будет
undefined.
Самый простой способ — передать переменную во время запуска.
NODE_ENV=production rollup -c
set NODE_ENV=production && rollup -c
$env:NODE_ENV="production"; rollup -c
Проблема такого подхода — различия между платформами.
Пакет cross-env обеспечивает единый синтаксис для всех
операционных систем.
Установка:
npm install --save-dev cross-env
Пример package.json:
{
"scripts": {
"build": "cross-env NODE_ENV=production rollup -c",
"dev": "cross-env NODE_ENV=development rollup -c -w"
}
}
Теперь команды работают одинаково на Linux, macOS и Windows.
const isProduction = process.env.NODE_ENV === 'production';
export default {
output: {
sourcemap: !isProduction
}
};
const isProduction = process.env.NODE_ENV === 'production';
export default {
output: {
file: isProduction
? 'dist/app.min.js'
: 'dist/app.js'
}
};
import terser from '@rollup/plugin-terser';
const isProduction = process.env.NODE_ENV === 'production';
export default {
plugins: [
isProduction && terser()
]
};
Поскольку Rollup не любит false внутри массива плагинов,
обычно используется фильтрация:
plugins: [
isProduction && terser()
].filter(Boolean)
.envПри большом количестве переменных передавать их вручную неудобно. Для
этого используются .env файлы.
Пример:
NODE_ENV=production
API_URL=https://api.example.com
ENABLE_ANALYTICS=true
Пакет dotenv загружает переменные из .env в
process.env.
Установка:
npm install --save-dev dotenv
.env в Rollupimport dotenv from 'dotenv';
dotenv.config();
console.log(process.env.API_URL);
export default {
// конфигурация
};
После вызова config() все значения становятся доступны
через process.env.
Обычно используется несколько файлов окружения:
.env
.env.development
.env.production
import dotenv from 'dotenv';
const envFile =
process.env.NODE_ENV === 'production'
? '.env.production'
: '.env.development';
dotenv.config({
path: envFile
});
export default {};
Иногда переменная может отсутствовать.
Небезопасный вариант:
const apiUrl = process.env.API_URL;
Безопасный вариант:
const apiUrl =
process.env.API_URL ||
'http://localhost:3000';
Все значения из process.env являются строками.
Даже если указано:
ENABLE_CACHE=true
PORT=3000
Node.js получит:
process.env.ENABLE_CACHE === 'true'
process.env.PORT === '3000'
const enableCache =
process.env.ENABLE_CACHE === 'true';
const port = Number(process.env.PORT);
const port = Number(process.env.PORT);
if (Number.isNaN(port)) {
throw new Error('Некорректный PORT');
}
Переменные окружения внутри rollup.config.js доступны
только во время сборки. Браузер не имеет доступа к
process.env.
Чтобы передать значение в клиентский бандл, используется подмена констант.
Установка:
npm install --save-dev @rollup/plugin-replace
import replace from '@rollup/plugin-replace';
export default {
plugins: [
replace({
preventAssignment: true,
'process.env.NODE_ENV': JSON.stringify(
process.env.NODE_ENV
)
})
]
};
Исходный код:
if (process.env.NODE_ENV === 'production') {
enableAnalytics();
}
После сборки:
if ('production' === 'production') {
enableAnalytics();
}
После этого минификатор может удалить лишние ветки.
Современные версии плагина требуют:
preventAssignment: true
Это предотвращает ошибочные замены в выражениях присваивания.
Например:
process.env.NODE_ENV = 'test';
replace({
preventAssignment: true,
__API_URL__: JSON.stringify(
process.env.API_URL
)
})
Код приложения:
fetch(__API_URL__ + '/users');
После сборки:
fetch("https://api.example.com/users");
Иногда удобнее заменить сразу набор значений.
replace({
preventAssignment: true,
__ENV__: JSON.stringify({
API_URL: process.env.API_URL,
VERSION: process.env.VERSION
})
})
Использование:
console.log(__ENV__.API_URL);
Некоторые инструменты используют синтаксис:
import.meta.env.MODE
В чистом Rollup такой механизм отсутствует по умолчанию, но его можно реализовать через replace:
replace({
preventAssignment: true,
'import.meta.env.MODE': JSON.stringify(
process.env.NODE_ENV
)
})
Переменные окружения часто применяются для выбора конфигурации.
const outputDir =
process.env.NODE_ENV === 'production'
? 'dist/prod'
: 'dist/dev';
export default {
output: {
dir: outputDir
}
};
const format =
process.env.BUILD_FORMAT || 'esm';
export default {
output: {
format
}
};
const input =
process.env.TARGET === 'admin'
? 'src/admin.js'
: 'src/main.js';
export default {
input
};
Rollup позволяет экспортировать функцию вместо объекта.
Это особенно удобно при работе с переменными окружения.
export default () => {
const isProduction =
process.env.NODE_ENV === 'production';
return {
output: {
sourcemap: !isProduction
}
};
};
Rollup передаёт аргументы функции конфигурации.
export default commandLineArgs => {
console.log(commandLineArgs);
return {};
};
Запуск:
rollup -c --environment TARGET:mobile
Rollup умеет передавать переменные без сторонних инструментов.
rollup -c --environment NODE_ENV:production
Несколько значений:
rollup -c --environment NODE_ENV:production,MINIFY:true
export default () => {
console.log(process.env.NODE_ENV);
console.log(process.env.MINIFY);
return {};
};
Rollup автоматически добавляет значения в
process.env.
CLI-параметры могут переопределять .env.
import dotenv from 'dotenv';
dotenv.config();
const mode =
process.env.NODE_ENV || 'development';
Запуск:
rollup -c --environment NODE_ENV:production
В этом случае значение из CLI станет приоритетным.
При отсутствии критически важной переменной сборка должна завершаться ошибкой.
if (!process.env.API_URL) {
throw new Error(
'Переменная API_URL обязательна'
);
}
При большом количестве переменных удобно создать отдельный модуль.
import dotenv from 'dotenv';
dotenv.config();
export const config = {
isProduction:
process.env.NODE_ENV === 'production',
apiUrl:
process.env.API_URL ||
'http://localhost:3000',
enableAnalytics:
process.env.ENABLE_ANALYTICS === 'true'
};
import { config } from './env.js';
export default {
output: {
sourcemap: !config.isProduction
}
};
Нельзя передавать в клиентский код:
Опасный пример:
replace({
preventAssignment: true,
__SECRET_KEY__: JSON.stringify(
process.env.SECRET_KEY
)
})
После сборки значение окажется внутри JavaScript-файла браузера.
Распространённая практика — разделять переменные по префиксам.
Пример:
PUBLIC_API_URL=https://example.com
SECRET_KEY=123456
В клиент передаются только значения с PUBLIC_.
const publicEnv = Object.fromEntries(
Object.entries(process.env).filter(
([key]) => key.startsWith('PUBLIC_')
)
);
replace({
preventAssignment: true,
__PUBLIC_ENV__: JSON.stringify(publicEnv)
})
Разные окружения могут формировать полностью разные сборки.
const isProduction =
process.env.NODE_ENV === 'production';
const config = isProduction
? productionConfig
: developmentConfig;
export default config;
Иногда значения приходят из внешнего источника.
export default async () => {
const response = await fetchConfig();
return {
plugins: [
replace({
preventAssignment: true,
__API_URL__: JSON.stringify(
response.apiUrl
)
})
]
};
};
Некоторые проекты предпочитают JSON-конфигурации.
{
"apiUrl": "https://example.com",
"debug": false
}
import config from './config.json';
export default {
plugins: [
replace({
preventAssignment: true,
__CONFIG__: JSON.stringify(config)
})
]
};
Неправильно:
replace({
'process.env.NODE_ENV': 'production'
})
Результат:
if (production === 'production')
Правильно:
replace({
'process.env.NODE_ENV': JSON.stringify(
'production'
)
})
Исходный код:
console.log(process.env.NODE_ENV);
В браузере:
ReferenceError: process is not defined
Неправильно:
const mode = process.env.NODE_ENV;
dotenv.config();
Правильно:
dotenv.config();
const mode = process.env.NODE_ENV;
Файлы .env часто добавляют в
.gitignore.
.env
.env.production
project/
├── src/
├── dist/
├── .env
├── .env.production
├── .env.development
├── env.js
├── package.json
└── rollup.config.js
import dotenv from 'dotenv';
import replace from '@rollup/plugin-replace';
import terser from '@rollup/plugin-terser';
dotenv.config({
path:
process.env.NODE_ENV === 'production'
? '.env.production'
: '.env.development'
});
const isProduction =
process.env.NODE_ENV === 'production';
const publicEnv = Object.fromEntries(
Object.entries(process.env).filter(
([key]) => key.startsWith('PUBLIC_')
)
);
export default {
input: 'src/main.js',
output: {
file: isProduction
? 'dist/app.min.js'
: 'dist/app.js',
format: 'esm',
sourcemap: !isProduction
},
plugins: [
replace({
preventAssignment: true,
'process.env.NODE_ENV':
JSON.stringify(
process.env.NODE_ENV
),
__PUBLIC_ENV__:
JSON.stringify(publicEnv)
}),
isProduction && terser()
].filter(Boolean)
};