@calvear/env
    TypeScript icon, indicating that this package has built-in type declarations

    2.0.1 • Public • Published
    logo

    env

    ¡Environment variables made easy!


    version   typescript   nodejs engine   npm engine   license


    📖 About

    Eases NodeJS environment variable handling, like env-cmd or dotenv, but with powerfull features and extensibility for adding custom providers (as plugins) for load, pull and push the variables from different stores.

    (back to top)

    📌 Requirements

    First, download and install NodeJS. Version 16 or higher is required.

    Validate installed versions of node and npm with:

    > node -v
    v16.14.2
    
    > npm -v
    8.3.0

    You can initialize a new npm project using:

    > npm init

    (back to top)

    ⚡️ Quick start

    🔔 Make sure that you have NodeJS 14+ installed on your computer.

    • Installs the package:
    > npm install @calvear/env
    
      added 1 packages, and audited 1 packages in 1s
    
      found 0 vulnerabilities
    
    > _
    • Executes binary directly:
    > node_modules/.bin/env --help
    
      Usage: env [command] [options..] [: subcmd [:]] [options..]
    
      Commands:
        env [options..] [: <subcmd> :]
        env pull [options..]
        env push [options..]
        env schema [options..]
    
    > _
    • Or add desired commands in your npm script in package.json:
    {
      ...,
      "scripts": {
        // starts project injecting "dev" environment variables and debug log level
        "start:dev": "env -e dev -m debug : node dist/main.js : --log debug",
        // starts project injecting "prod" environment variables
        "start:prod": "env -e prod -m debug : node dist/main.js",
        ...,
        // builds project injecting "prod" environment variables
        "build:prod": "env -e prod -m build : tsc",
        ...,
        "env:schema": "env schema -e dev --ci",
        // uploads environment "dev" variables
        "env:push:dev": "env push -e dev",
        // downloads environment "dev" variables
        "env:pull:dev": "env pull -e dev"
      },
      ...
    }
    • Execs your command:

    file: dist/main.js

    console.log(`My environment loaded is: ${process.env.ENV}`);
    > npm run start:dev
    
      13:31:59.865 INFO  loading dev environment in debug mode
      13:31:59.911 DEBUG using package-json provider
      13:31:59.912 DEBUG using app-settings provider
      13:31:59.914 DEBUG using secrets provider
      13:32:00.109 DEBUG environment loaded:
      {
        NODE_ENV: 'development',
        ENV: 'dev',
        VERSION: '1.0.0',
        NAME: '@my-app',
        VAR1: true,
        VAR2: true,
        GROUP1__VAR1: 'G1V2',
        ARR1: '1,val,true',
        SECRET: '***'
      }
      13:32:00.116 INFO  executing command > node dist/main.js
      My environment loaded is: dev
      13:32:00.232 INFO  process finished successfully
    
    > _

    (back to top)

    Structure

    ├── src/
    │   ├── commands/ # lib commands handlers
    │   │   ├── env.command.ts
    │   │   ├── export.command.ts
    │   │   ├── pull.command.ts
    │   │   ├── push.command.ts
    │   │   └── schema.command.ts
    │   ├── interfaces/ # provider interfaces
    │   ├── providers/ # integrated providers
    │   │   ├── package-json.provider.ts
    │   │   ├── app-settings.provider.ts
    │   │   ├── secrets.provider.ts
    │   │   └── local.provider.ts
    │   ├── utils/
    │   │   ├── command.util.ts
    │   │   ├── interpolate.util.ts
    │   │   ├── json.util.ts
    │   │   ├── normalize.util.ts
    │   │   ├── schema.util.ts
    │   │   └── logger.ts
    │   ├── arguments.ts # global arguments
    │   ├── exec.ts # initialization logic (load config, commands, etc.)
    │   └── main.ts
    ├── tests/ # integration tests
    ├── .eslintrc.json
    ├── jest.config.json
    ├── tsconfig.build.json
    └── tsconfig.json

    (back to top)

    ⚙️ Commands & Options

    Options handling has the ability of replace arguments itself, using [[ and ]] as delimiters. So, in example for define your config file path, you must use your root argument, supposing root has the value of "config", this definition [[root]]/any-config-file.json will be config/any-config-file.json, or if your env argument is "dev", this definition [[root]]/config-file.[[env]].json will be config/config-file.dev.json.

    Options

    Global options

    Option Description Type Default Required?
    --help Shows help boolean No
    --e, --env Environment for load string Yes
    -m, --modes Execution modes string[] [] No
    --nd, --nestingDelimiter Nesting level delimiter for flatten string __ No
    --arrDesc, --arrayDescomposition Whether serialize or break down arrays boolean false No
    -x, --expand Interpolates environment variables using itself boolean false No
    -ci Continuous Integration mode boolean false No

    Workspace options

    Option Description Type Default Required?
    --root Default environment folder path string env No
    -c, --configFile Config JSON file path string [[root]]/settings/settings.json No
    -s, --schemaFile Environment Schema JSON file path string [[root]]/settings/schema.json No

    JSON Schema options

    Option Description Type Default Required?
    -r, --resolve Whether merges new schema or override merge, override merge No
    --null, --nullable Whether variables are nullable by default boolean true No
    --df, --detectFormat Whether format of strings variables are included in schema boolean true No

    Logger options

    Option Description Type Default Required?
    --log, --logLevel Log level silly, trace, debug, info, warn, error info No
    Commands
    • env

    Inject your environment variables into process.env and executes a command.

    env -e [env] [options..] [: subcmd [:]] [options..]

    Examples:

    > env -e dev -m test unit : npm test
    > env -e dev -m debug : npm start : -c [[root]]/[[env]].settings.json
    > env -e prod -m build optimize : npm build
    • pull

    Pulls environment variables from providers stores.

    env pull -e [env] [options..]
    Option Description Type Default Required?
    -o, --overwrite Overwrite local variables boolean false No

    Examples:

    > env pull -e dev
    • push

    Pushes environment variables to providers stores.

    env push -e [env] [options..]
    Option Description Type Default Required?
    -f, --force Force push for secrets (replace all) boolean false No

    Examples:

    > env push -e dev
    • schema

    Generates validation schema from providers output variables.

    env schema -e [env] -m [modes] [options..]

    Examples:

    > env schema -e dev -m build
    • export

    Export unified environment variables to a file from providers.

    env export -e [env] -m [modes] [options..]
    Option Description Type Default Required?
    -u, -p, --uri Uri for export file with variables string .env No
    -f, --format Format for export variables string dotenv No

    Examples:

    > env export -e dev -m build -f json --uri [[env]].env.json

    (back to top)

    📡 Providers

    Main feature of this library is using providers for get and set environment variables. So, you can define your own provider, but lib came with 3 integrated providers:

    • package-json

    Load some info from your project package.json.

    Info read is:

    {
        "version": "1.0.0",
        "project": "project-name",
        "name": "@package-name",
        "title": "app-name",
        "description": "any description"
    }
    Option Description Type Default Required?
    --vp, --varPrefix Prefix for loaded variables string "" No

    Examples:

    > env -e dev -m build : react-script build : --vp REACT_APP_
    • app-settings

    Non secrets loader for appsettings.json.

    appsettings.json file has the format below:

    {
        "|DEFAULT|": {},
        "|MODE|": {},
        "|ENV|": {}
    }

    In example:

    {
        "|DEFAULT|": {
            "VAR1": "v1_default"
        },
        "|MODE|": {
            "build": {
                "NODE_ENV": "production"
            },
            "debug": {
                "NODE_ENV": "development"
            },
            "test": {
                "NODE_ENV": "test"
            }
        },
        "|ENV|": {
            "dev": {
                "C1": "V1",
                "C2": "V2",
                "C3": 3,
                "GROUP1": {
                    "VAR1": null,
                    "VAR2": "G1V2",
                    "VAR3": true,
                    "GROUP2": {
                        "VAR1": "G1G2V1"
                    }
                },
                "C4": "23"
            }
        }
    }
    Option Description Type Default Required?
    --ef, --envFile Environment variables file path string [[root]]/appsettings.json No
    --sp, --sectionPrefix Prefix for env and modes in env file string `` No
    • secrets

    Secrets loader for env/[[env]].env.json.

    Option Description Type Default Required?
    --sf, --secretsFile Secret variables file path string [[root]]/[[env]].env.json No
    • local

    Local variables loader for env/[[env]].local.env.json.

    Option Description Type Default Required?
    --lf, --localFile Local variables file path string [[root]]/[[env]].local.env.json No
    • package-json

    Load some info from your project package.json.

    Info read is:

    {
        "version": "1.0.0",
        "project": "project-name",
        "name": "@package-name",
        "title": "app-name",
        "description": "any description"
    }
    Option Description Type Default Required?
    --vp, --varPrefix Prefix for loaded variables string "" No

    Examples:

    > env -e dev -m build : react-script build : --vp REACT_APP_

    (back to top)

    Creating Custom Providers

    You can create your custom providers, in two ways:

    • Local Script: you must create a JavaScript file (.js), exporting by default your "provider" following standard interface exported by this lib.
    • NPM Package: you must create your custom NPM library and export by default your "provider" using standard interface exported by this lib.

    How to load your provider is shown in Config Section.

    In example, a provider exported by your NPM package written in TypeScript should be like:

    import { CommandArguments, EnvProvider } from '@calvear/env';
    import { logger, readJson, writeJson } from '@calvear/env/utils';
    
    const KEY = 'my-unique-provider-key';
    
    interface MyProviderCommandArguments extends CommandArguments {
        anyExtraOption: boolean;
    }
    
    export const MyProvider: EnvProvider<MyProviderCommandArguments> = {
        // unique identifier for provider
        key: KEY,
    
        // (optional) allows to provider adds new arguments/options
        // to commands using yargs for builder
        builder: (builder) => {
            builder.options({
                anyExtraOption: {
                    group: KEY,
                    alias: ['a', 'aeo'],
                    type: 'boolean',
                    default: false,
                    describe: 'Any option description',
                },
            });
        },
    
        // call on environment variables loading,
        // may be a Promise
        load: ({ env, modes, ...options }) => {
            if (env === 'dev')
                return {
                    NODE_ENV: 'development',
                };
    
            // you can return a list of JSON environment variables for merge
            return [
                {
                    NODE_ENV: 'production',
                    ANY_VAR: 'ABC', // will be replaced by value below
                },
                {
                    ANY_VAR: 'ANY_VALUE',
                    ANY_GROUP: {
                        INNER_VAR: 12,
                    },
                },
            ];
        },
    
        // (optional) call on pulling variables from provider store,
        // config may pass in your config file
        pull: ({ env, modes, ...options }, config) => {
            // anyway you want for pulling variables to cache
        },
    
        // (optional) call on pushing/updating variables to provider store,
        // config may pass in your config file
        push: ({ env, modes, ...options }, config) => {
            // anyway you should do for pushing or updating your variables
        },
    };

    (back to top)

    📥 Config

    You can configure any config argument inside you config file, but commonly providers are designed for this purpose.

    {
        "log": "silly",
        // will hide values of keys SECRET and MY_API_KEY in logging
        "logMaskValuesOfKeys": ["SECRET", "MY_API_KEY"],
        // integrated providers and custom providers together
        "providers": [
            {
                "path": "package-json",
                "type": "integrated"
            },
            {
                "path": "app-settings",
                "type": "integrated"
            },
            {
                "path": "secrets",
                "type": "integrated"
            },
            {
                "path": "local",
                "type": "integrated"
            },
            {
                // custom NPM package
                "path": "@npm-package",
                "type": "module",
                "config": {
                    "any-config": "any value"
                }
            },
            {
                // custom script inside project
                "path": "scripts/custom-loader.js",
                "type": "script"
            }
        ]
    }

    📑 Roadmap

    • [x] Environment injection handling
    • [x] Customizable variables store providers
    • [x] Commands
      • [x] push executes a pushing action over every providers
      • [x] pull executes a pulling action over every providers
      • [x] schema regenerates JSON schema using providers output
      • [x] export exports environment variables in json or dotenv format
      • [ ] prepare prepares environment (creates folder and files required)
    • [ ] Improve documentation
    • [ ] Providers pull history
    • [ ] Providers pull and push difference calc and prompts
    • [ ] Providers dependsOn option
    • [ ] Programatic module

    (back to top)

    🧿 Linting

    Project uses ESLint, for code formatting and code styling normalizing.

    • eslint: linter integrated with TypeScript.

    For correct interpretation of linters, is recommended to use Visual Studio Code as IDE and install the plugins in .vscode folder at 'extensions.json', as well as use the config provided in 'settings.json'

    (back to top)

    📋 Changelog

    For last changes see CHANGELOG.md file for details.

    (back to top)

    🛠️ Built with

    (back to top)

    📄 License

    This project is licensed under the MIT License - see LICENSE.md file for details.

    (back to top)


    by Alvear Candia, Cristopher Alejandro

    Install

    npm i @calvear/env

    DownloadsWeekly Downloads

    1

    Version

    2.0.1

    License

    MIT

    Unpacked Size

    156 kB

    Total Files

    101

    Last publish

    Collaborators

    • calvear