Конфигурация через переменные окружения

Механизм переменных окружения в Webpack используется для управления поведением сборки без изменения исходного кода конфигурации. Основная идея заключается в разделении конфигурации на среду выполнения и параметры, передаваемые извне: разработка, тестирование, продакшен-сборка, staging-окружения.

В основе работы лежат три ключевых уровня:

  • переменные окружения операционной системы (process.env)
  • передача параметров через CLI Webpack
  • обработка значений внутри webpack.config.js

Доступ к переменным окружения в Node.js контексте Webpack

Webpack выполняется в Node.js, поэтому доступ к окружению осуществляется через process.env.

console.log(process.env.NODE_ENV);
console.log(process.env.API_URL);

Однако прямое использование process.env в клиентском коде невозможно без дополнительной обработки, поскольку браузер не имеет доступа к переменным окружения Node.js.


NODE_ENV как базовая переменная среды

Наиболее распространённая переменная — NODE_ENV.

Типовые значения:

  • development — режим разработки
  • production — продакшен-сборка
  • test — тестовое окружение

Webpack использует mode, который по сути синхронизируется с NODE_ENV:

module.exports = {
  mode: 'production'
};

или через переменную окружения:

NODE_ENV=production webpack

Передача переменных через CLI Webpack

Webpack поддерживает передачу параметров через аргумент --env.

Пример вызова:

webpack --env mode=production --env apiUrl=https://api.example.com

Конфигурация:

module.exports = (env) => {
  return {
    mode: env.mode,
    output: {
      filename: 'bundle.js'
    }
  };
};

Структура env формируется автоматически:

env = {
  mode: 'production',
  apiUrl: 'https://api.example.com'
}

Использование функции-конфигурации вместо объекта

Webpack поддерживает экспорт функции вместо статического объекта. Это основной способ работы с переменными окружения.

module.exports = (env, argv) => {
  const isProd = argv.mode === 'production';

  return {
    mode: isProd ? 'production' : 'development',
    devtool: isProd ? false : 'source-map'
  };
};

Параметры:

  • env — пользовательские переменные через --env
  • argv — аргументы CLI Webpack (mode, watch, и др.)

DefinePlugin и проброс переменных в клиентский код

Переменные окружения Node.js не доступны в браузере. Для их передачи используется DefinePlugin.

const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
      'process.env.API_URL': JSON.stringify(process.env.API_URL)
    })
  ]
};

После сборки:

console.log(process.env.API_URL);

превратится в:

console.log("https://api.example.com");

Разница между DefinePlugin и runtime переменными

DefinePlugin выполняет подстановку на этапе сборки:

  • значения становятся статическими
  • отсутствует runtime-обращение к env

process.env без DefinePlugin:

  • работает только в Node.js (webpack config)
  • недоступен в браузере

Использование dotenv для локальных переменных

Для управления локальными переменными часто используется пакет dotenv.

Установка:

npm install dotenv

Файл .env:

API_URL=https://api.local
DEBUG=true

Подключение в webpack:

require('dotenv').config();

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env.API_URL': JSON.stringify(process.env.API_URL),
      'process.env.DEBUG': JSON.stringify(process.env.DEBUG)
    })
  ]
};

Разделение конфигураций по окружениям

Часто используется разбиение конфигурации:

webpack.common.js
webpack.dev.js
webpack.prod.js

Базовая конфигурация:

module.exports = {
  entry: './src/index.js'
};

Development:

module.exports = {
  mode: 'development',
  devtool: 'source-map'
};

Production:

module.exports = {
  mode: 'production',
  optimization: {
    minimize: true
  }
};

Слияние через webpack-merge:

const { merge } = require('webpack-merge');
const common = require('./webpack.common');

module.exports = merge(common, {
  mode: 'production'
});

Комбинация CLI env и конфигурационного файла

CLI:

webpack --env apiUrl=https://api.example.com --env debug=true

Конфигурация:

module.exports = (env) => {
  return {
    plugins: [
      new webpack.DefinePlugin({
        'process.env.API_URL': JSON.stringify(env.apiUrl),
        'process.env.DEBUG': JSON.stringify(env.debug)
      })
    ]
  };
};

Приведение типов переменных окружения

Переменные из CLI и .env всегда строковые.

Пример:

--env debug=false

В конфигурации:

const isDebug = env.debug === 'true';

Без явного преобразования значения остаются строками, что приводит к ошибкам логики.


Паттерн конфигурации через фабрику

Расширенный вариант конфигурации:

module.exports = (env = {}, argv = {}) => {
  const isProd = argv.mode === 'production';

  return {
    mode: isProd ? 'production' : 'development',

    plugins: [
      new webpack.DefinePlugin({
        'process.env.API_URL': JSON.stringify(
          env.apiUrl || 'http://localhost:3000'
        )
      })
    ]
  };
};

Такой подход позволяет:

  • задавать дефолтные значения
  • централизовать логику окружений
  • избегать дублирования конфигурации

Приоритет источников переменных

При одновременном использовании нескольких источников действует приоритет:

  1. CLI --env
  2. переменные process.env
  3. .env файл
  4. значения по умолчанию в конфигурации

Это важно при отладке неожиданных значений в сборке.


Безопасность переменных окружения

Все переменные, переданные через DefinePlugin, становятся частью клиентского бандла.

Следовательно:

  • нельзя передавать секреты (API keys с правами записи, токены админов)
  • допустимы только публичные конфигурационные значения
  • секреты должны оставаться на сервере или прокси-слое

Практика организации env-конфигурации

Часто используется структура:

.env
.env.development
.env.production

Загрузка вручную:

const envFile = `.env.${process.env.NODE_ENV}`;
require('dotenv').config({ path: envFile });

Использование cross-env для кроссплатформенности

В Windows и Unix синтаксис установки переменных различается. Решение — cross-env.

Установка:

npm install cross-env

Использование:

cross-env NODE_ENV=production webpack

Динамическая настройка сборки через env-флаги

Пример управления функциональностью:

module.exports = (env) => ({
  plugins: [
    new webpack.DefinePlugin({
      __FEATURE_X__: JSON.stringify(env.featureX === 'true')
    })
  ]
});

Использование в коде:

if (__FEATURE_X__) {
  console.log('Feature enabled');
}

Такой подход применяется для feature flags на этапе сборки.


Типизация переменных окружения в TypeScript проектах

Для предотвращения ошибок используется расширение типов:

declare namespace NodeJS {
  interface ProcessEnv {
    NODE_ENV: 'development' | 'production';
    API_URL: string;
  }
}

Это позволяет получать автодополнение и контроль значений на уровне компиляции.


Связка env и оптимизаций Webpack

Переменные окружения часто используются для управления оптимизациями:

const isProd = process.env.NODE_ENV === 'production';

module.exports = {
  optimization: {
    minimize: isProd,
    splitChunks: isProd
  }
};

Так формируется различное поведение сборки без изменения кода логики приложения.