Переменные окружения и их влияние на сборку

Переменные окружения в процессе сборки JavaScript-приложений выступают механизмом конфигурации, позволяющим изменять поведение кода и инструмента сборки без изменения исходного кода. В системе сборки esbuild они используются не как самостоятельный API, а как источник данных, который может быть встроен в результат бандлинга через механизмы подстановки и плагинов.

Важное различие заключается в том, что переменные окружения в сборщике существуют на двух уровнях: во время выполнения сборки и внутри собранного кода после компиляции.


Два уровня работы переменных окружения

Переменные во время выполнения сборки

На этапе запуска esbuild доступен стандартный механизм окружения операционной системы и среды Node.js:

  • process.env
  • переменные CI/CD
  • локальные .env файлы (через внешние инструменты)

На этом уровне переменные используются для настройки самого процесса сборки: выбора режима, включения плагинов, изменения входных точек.


Переменные внутри бандла

После компиляции переменные окружения не сохраняются автоматически. Вместо этого используется подстановка значений в код через механизм define.

Пример:

define: {
  "process.env.NODE_ENV": '"production"'
}

После сборки выражение:

if (process.env.NODE_ENV === "production") {
  console.log("prod mode");
}

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

if ("production" === "production") {
  console.log("prod mode");
}

и затем упрощается до:

console.log("prod mode");

Механизм define и его роль

define является ключевым инструментом эмуляции переменных окружения в esbuild.

Он работает как статическая замена текста в AST на этапе сборки.

Поддерживаемые формы:

define: {
  "process.env.API_URL": '"https://api.example.com"',
  "global.IS_BROWSER": "true"
}

Особенности:

  • значения подставляются как сырой код
  • строки требуют двойного экранирования кавычек
  • поддерживаются любые идентификаторы, не только process.env

Влияние переменных окружения на tree shaking

Подстановка значений через define напрямую влияет на удаление мёртвого кода.

Пример:

if (process.env.DEBUG) {
  console.log("debug");
}

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

define: {
  "process.env.DEBUG": "false"
}

код превращается в:

if (false) {
  console.log("debug");
}

и затем полностью удаляется.

Это позволяет использовать переменные окружения как механизм управления включением функциональности на этапе сборки.


Различие между process.env и define

Механизм Когда применяется Где существует Итог
process.env runtime Node.js сервер/среда выполнения динамическое значение
define build time внутри esbuild статическая подстановка

Важно, что esbuild не эмулирует process.env автоматически. Без define обращение к нему в браузерном коде приведёт к ошибке выполнения.


Режимы development и production

Частая практика — использование переменной NODE_ENV.

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

define: {
  "process.env.NODE_ENV": '"development"'
}

или:

define: {
  "process.env.NODE_ENV": '"production"'
}

Влияние:

  • включение/отключение логирования
  • изменение поведения библиотек (React, Vue, etc.)
  • активация оптимизаций

В связке с minify: true и treeShaking значение production позволяет агрессивно удалять код.


Использование dotenv и внешних источников

Файлы .env не поддерживаются напрямую esbuild. Обычно используется промежуточная загрузка через сторонние библиотеки:

import dotenv from "dotenv";
dotenv.config();

Далее значения передаются в define:

define: {
  "process.env.API_URL": JSON.stringify(process.env.API_URL)
}

Такой подход обеспечивает:

  • централизованное управление конфигурацией
  • разделение секретов и публичных значений
  • совместимость с CI/CD

Влияние переменных окружения на плагины

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

  • подключение разных API
  • выбор стратегии кэширования
  • переключение mock-данных

Пример плагина:

const envPlugin = {
  name: "env-plugin",
  setup(build) {
    const isProd = process.env.NODE_ENV === "production";

    build.initialOptions.define = {
      ...build.initialOptions.define,
      "process.env.IS_PROD": JSON.stringify(isProd)
    };
  }
};

Инкрементальная сборка и изменение окружения

esbuild использует кэширование для ускорения пересборки.

Изменение переменных окружения влияет на:

  • необходимость полной пересборки
  • инвалидацию кэша
  • пересчёт define-замен

Если значение переменной меняется, но входные файлы не изменяются, esbuild всё равно пересобирает бандл, поскольку результат компиляции зависит от конфигурации.


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

Переменные окружения часто содержат секреты, однако при использовании define они попадают в клиентский бандл.

Ключевое ограничение:

  • всё, что передано через define, становится частью публичного JavaScript-кода

Следствия:

  • API-ключи, переданные через define, доступны в браузере
  • секреты должны оставаться на серверной стороне Node.js

Условная компиляция через env-переменные

Механизм define используется для имитации условной компиляции:

if (process.env.FEATURE_X === "enabled") {
  enableFeatureX();
}

Сборка:

define: {
  "process.env.FEATURE_X": '"enabled"'
}

Результат:

enableFeatureX();

При этом альтернативная ветка удаляется полностью.


Платформенные различия

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

define: {
  "process.env.PLATFORM": '"browser"'
}

или

define: {
  "process.env.PLATFORM": '"node"'
}

Это влияет на:

  • выбор API (fetch vs fs)
  • подключение полифиллов
  • использование разных entry points

CI/CD и переменные окружения

В системах непрерывной интеграции переменные окружения становятся источником конфигурации сборки:

  • CI=true
  • BUILD_ID
  • BRANCH_NAME

Они часто транслируются в define:

define: {
  "process.env.BUILD_ID": JSON.stringify(process.env.BUILD_ID)
}

Это позволяет:

  • трассировать сборки
  • включать диагностические режимы
  • управлять флагами релиза

Переопределение и приоритеты

При формировании конфигурации esbuild действует правило последнего значения:

define: {
  "process.env.NODE_ENV": '"development"',
  "process.env.NODE_ENV": '"production"'
}

Итоговое значение будет production.

При использовании плагинов порядок их выполнения влияет на финальную карту переменных.


Типовые ошибки при работе с env

  • отсутствие кавычек в строках:

    • некорректно: "production"
    • корректно: '"production"'
  • ожидание runtime-значений от define

  • смешивание process.env без подстановки

  • утечка секретов в клиентский код

  • дублирование переменных в нескольких плагинах


Оптимизационные эффекты

Использование переменных окружения через define позволяет:

  • удалять условные блоки кода
  • уменьшать размер бандла
  • ускорять выполнение за счёт устранения ветвлений
  • улучшать работу minifier’а

В связке с возможностями esbuild это становится одним из основных механизмов конфигурации сборки без runtime-накладных расходов.