Найти в Дзене

Правила wb-rules и dotenv: переменные окружения

Переменные окружения - это все те строковые константы, которые раньше были зашиты прямо в код. Сегодня мы вынесем их в отдельный текстовый файл и оформим построчно в виде пар «ключ-значение». Чтобы стало ещё интереснее, научимся собирать проект в вариантах development и production. Первый используется для отладки и экспериментов, а второй - серьёзный, компактный и максимально эффективный. Разумеется, разные варианты сборки потребуют переключения между разными наборами строковых значений. И обновлённая система умеет делать это буквально по щелчку пальцев. Прежде чем открыть очередную тайну профессиональной разработки, следует сделать небольшую оговорку - переменные окружения работают только совместно со сборщиком модулей. Если вы пропустили недавний рассказ о работе с Rollup, придётся наверстать: После успешного освоения юнит-тестов, мы и дальше будем налегать на их применение. В частности, сегодня напишем пару-тройку тестов для того, чтобы посмотреть, как функционируют переменные окруж
Оглавление

Переменные окружения - это все те строковые константы, которые раньше были зашиты прямо в код. Сегодня мы вынесем их в отдельный текстовый файл и оформим построчно в виде пар «ключ-значение».

Чтобы стало ещё интереснее, научимся собирать проект в вариантах development и production. Первый используется для отладки и экспериментов, а второй - серьёзный, компактный и максимально эффективный.

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

Прежде чем открыть очередную тайну профессиональной разработки, следует сделать небольшую оговорку - переменные окружения работают только совместно со сборщиком модулей. Если вы пропустили недавний рассказ о работе с Rollup, придётся наверстать:

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

Возможности программирования правил wb-rules в Visual Studio Code изложены по шагам, в строгом хронологическом порядке и для удобства собраны в специальную подборку. Последовательно выполнив все этапы, вы сможете понять каждую конструкцию, получите полностью рабочее решение:

Правила wb-rules | Умный дом с проводами | Дзен

Нужно всё и сразу? Не вопрос - в конце каждой статьи есть ссылка на проект GitHub, свободный от авторских прав. Он поделён на ветки, поэтому всегда соответствует рассмотренной теме. Качайте и используйте на своё усмотрение.

Суть переменных окружения

В исходном коде используются названия переменных (ключи), а в процессе сборки они меняются на свои значения.

Зачем оно нужно?

  1. Чтобы упростить процесс разработки - больше не требуется искать по всему проекту, где же там осталось ещё одно упоминание «того самого» топика MQTT или названия виртуального устройства.
  2. Чтобы обезопасить заказчика от потенциальной утечки данных - исходный код с отделённой от него чувствительной информацией можно показывать друзьям и коллегам, его можно спокойно заливать в систему контроля версий и публичные репозитории.
  3. Чтобы изымать по условию целые ветки кода - невостребованный функционал полностью «выпиливается» простым переключением переменной окружения, словно его там никогда и не было.

Особенности использования

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

С целью упрощения кода, инициализация модуля в правилах wb-rules сопровождается передачей ему объекта конфигурации, где в качестве значений свойств используются переменные окружения - так они остаются «на поверхности» и избавляют от необходимости вникать в детали внутренней реализации.

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

ℹ️ Примечание: всё это справедливо лишь для тех переменных, значения которых попадут в итоговый код.

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

Подготовка к работе

Установка и настройка dotenv-run

Откроем терминал VS Code и выполним в нём команду

yarn add --dev @dotenv-run/rollup

Менеджер пакетов установит плагин для Rollup, позволяющий работать с файлами формата dotenv.

Перейдём в файл rollup.config.mjs и добавим строчку импорта

import dotenv from '@dotenv-run/rollup'

В экспортируемом объекте конфигурации найдём перечисление используемых плагинов и добавим после вызова typescript следующую конструкцию:

dotenv({
prefix: '^APP_',
verbose: true
}),

Здесь мы фильтруем переменные окружения, оставляя лишь те, которые начинаются с указанного префикса - по умолчанию они берутся не только из созданных нами файлов, но также из командной строки и самой операционной системы. Директива verbose при каждом выполнении сборки выводит в консоль обнаруженные соответствия, что удобно при настройке плагина.

Конфигурация Rollup с добавленным в неё плагином dotenv-run
Конфигурация Rollup с добавленным в неё плагином dotenv-run

Настройка Jest

Для обеспечения возможности работы с переменными окружения в юнит-тестах, рекомендуют использовать модуль dotenv/config, но нам такой способ не подходит - решение не обеспечивает каскадное чтение .env файлов.

Перейдя в папку tests, создадим в ней файл dotenv-setup.ts со следующим содержимым:

import { env as dotenv } from '@dotenv-run/core'
dotenv({
prefix: '^APP_'
})

Конфигурация идентична той, что применялась при настройке плагина Rollup.

Откроем файл jest.config.ts и перейдём к секции setupFiles, добавив перед wb-engine-setup следующую конструкцию:

<rootDir>/tests/dotenv-setup.ts

Файл модуля dotenv-setup и конфигурация Jest
Файл модуля dotenv-setup и конфигурация Jest

Также потребуется скорректировать tests/tsconfig.json, поменяв секцию include:

"include": ["**/*.ts", "../types/*"]

Это включит обработку всех файлов с расширением *.ts во всех каталогах внутри папки tests. Чтобы убедиться в функционировании переменных окружения, позже напишем для них несколько тестов.

Скорректированная конфигурация tests/tsconfig.json
Скорректированная конфигурация tests/tsconfig.json

Файлы с наборами переменных окружения

В корне проекта создадим простой текстовый файл .env - именно так, с точкой в начале названия. Информация хранится в нём в виде строк с парами ключ=значение.

Для наглядности, внесём в него две ключевые пары:

APP_NAME=wb-default
APP_SECRET=strong-secret

Рядом создадим файл .env.test, который будет расширять набор переменных при работе в режиме юнит-тестов (Jest автоматически устанавливает NODE_ENV=test, если иное не определено командой запуска).

Внесём в этот новый файл переменную APP_NAME и присвоим ей значение wb-test. Расширенный под конкретный режим набор переменных обладает более высоким приоритетом, поэтому значения совпадающих ключей будут взяты из него.

Файлы с переменными окружения
Файлы с переменными окружения

Если нужно переопределить какие-то значения для сборки в режимах development и production, создают файлы .env.development и .env.production соответственно.

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

.env.{mode}.local
.env.{mode}
.env.local
.env

ℹ️ Примечание: файл .env.local не обрабатывается, когда используется окружение test.

Добавление поддержки IntelliSense

Чтобы TypeScript мог подсвечивать определённые нами env-переменные, понадобится создать в папке types файл env.d.ts и поместить в него следующую конструкцию:

declare global {
namespace NodeJS {
interface ProcessEnv {
/** Название приложения */
APP_NAME: string,
/** Какой-то секрет приложения */
APP_SECRET: string,
}}}
export { }

Здесь мы используем комментарии в формате JSDoc - если в коде навести курсор на название переменной окружения, появится ещё и подсказка с текстом комментария.

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

Проверка функционирования

Перейдя в каталог tests, создадим внутри папку env, а в ней файл env.test.ts - здесь мы будем тестировать работу с переменными окружения.

В созданный файл поместим конструкцию

test('expect NODE_ENV is set to "test"', () => {
expect(process.env.NODE_ENV).toBe('test')
})

Этим мы подтвердим свою теорию о том, что Jest работает в окружении test. Обращение к переменным происходит через process.env.* - как это принято в NodeJs.

Далее прочитаем переменную APP_NAME, которая была переопределена файлом .env.test

test('expect APP_NAME from ".env.test" file', () => {
expect(process.env.APP_NAME).toBe('wb-test')
})

Наконец, прочитаем переменную APP_SECRET - поскольку её нигде не переопределяли, значение будет взято из файла .env

test('expect APP_SECRET from ".env" file', () => {
expect(process.env.APP_SECRET).toBe('strong-secret')
})

Проверка функционирования переменных окружения в тестовой среде
Проверка функционирования переменных окружения в тестовой среде

Разделение команды сборки

Откроем терминал VS Code и добавим ещё один инструмент:

yarn add --dev cross-env

Дело в том, что подходы к заданию переменных окружения через командную строку различаются для Windows и Linux. Чтобы не переделывать команды под разные системы, используем cross-env.

Перейдя в секцию scripts файла package.json, отредактируем команду build:

eslint && yarn test && cross-env NODE_ENV=production rollup -c && tsc-alias && babel build -d dist

Теперь эта команда готова собирать проект в режиме production - компактном и эффективном, пригодном к установке на оборудование заказчика.

Слишком длинная строчка, верно? Пришло время узнать о т.н. хуках - на каждый скрипт можно создать два действия: одно автоматически выполняется до, а второе после вызова основной команды.

Разделим команду build, чтобы она приняла следующий вид:

"prebuild": "eslint && yarn test",
"build": "cross-env NODE_ENV=production rollup -c",
"postbuild": "tsc-alias && babel build -d dist"

Теперь, запуская сборку привычной командой yarn build, мы фактически выполняем три действия: сначала стартует prebuild, если ошибок не возникло - наступает черёд основной команды, а затем отрабатывает postbuild.

Добавим рядом ещё одну команду build:dev, скопировав предыдущую и поменяв production на development:

"prebuild:dev": "yarn prebuild",
"build:dev": "cross-env NODE_ENV=development rollup -c",
"postbuild:dev": "yarn postbuild"

Чтобы не дублировать одни и те же конструкции, в хуках для build:dev мы ссылаемся на уже существующие команды.

Для сборки в режиме разработчика, будем выполнять в терминале команду

yarn build:dev


ℹ️
Примечание: режим development понадобится нам после выхода wb-rules 3.0, для использования точек останова при отладке. Будет генерировать дополнительную информацию для дебаггера.

Разделённые команды сборки
Разделённые команды сборки

Работа с кодом

Откроем модуль src/wb-rules-modules/example-module.ts и добавим туда вот такую функцию, которая принимает на вход объект с настройками и возвращает другой объект, использующий эти настройки:

export const useExample = (options: { name: string; secret: string }) => ({
getName: () => options.name
})

Затем перейдём в файл правил src/wb-rules/example.ts и запишем туда

import { useExample } from '@wbm/example-module'
const example = useExample({
name: process.env.APP_NAME,
secret: process.env.APP_SECRET
})
log(example.getName())

Здесь на основе переданных в модуль значений создаётся экземпляр объекта Example, а дальше мы обращаемся к его методу getName, чтобы записать значение в лог.

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

Как видно на скриншоте, после сборки Rollup удаляет весь незадействованный код и даже лишние свойства из объекта конфигурации. Вместе с тем, переменная окружения APP_NAME заменяется на своё значение.

Шаблон проекта на GitHub

Готовый проект по материалам статьи можно скачать из репозитория GitHub:

GitHub - wihome-dev/wb-rules-typescript at babel-stable

Наука
7 млн интересуются