UNPKG

dotenv-extended

Version:

A module for loading .env files and optionally loading defaults and a schema for validating all values are present.

311 lines (301 loc) 10.4 kB
#!/usr/bin/env node var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // src/index.js var import_dotenv2 = __toESM(require("dotenv")); // src/utils/parse-primitive.js var parsePrimitive = (value) => { if (value === null || typeof value === "undefined") { return value; } if (typeof value === "number" || typeof value === "boolean") { return value; } if (typeof value !== "string") { return value; } let normalized = value.trim().replace(/(^"|"$)|(^'|'$)/g, "").toLowerCase(); if (normalized === "" || normalized === "undefined") { return void 0; } if (normalized === "null") { return null; } if (normalized === "nan") { return NaN; } if (normalized === "true" || normalized === "1") { return true; } if (normalized === "false" || normalized === "0") { return false; } const numeric = Number(normalized); if (!Number.isNaN(numeric)) { return numeric; } return value; }; var parse_primitive_default = parsePrimitive; // src/utils/normalize-option-key.js var normalizedWithSeparators = (key) => key.toLowerCase().replace(/[_-\s]+([a-z0-9])/g, (_, next) => next.toUpperCase()); var normalizeOptionKey = (key) => { const trimmed = key.trim(); if (!trimmed) { return ""; } if (/[_-\s]/.test(trimmed)) { return normalizedWithSeparators(trimmed); } if (trimmed === trimmed.toUpperCase()) { return trimmed.toLowerCase(); } return trimmed.charAt(0).toLowerCase() + trimmed.slice(1); }; var normalize_option_key_default = normalizeOptionKey; // src/utils/config-from-env.js var getConfigFromEnv = (env) => { let config2 = {}; Object.keys(env).forEach((key) => { const curr = key.split("DOTENV_CONFIG_"); if (curr.length === 2 && curr[0] === "" && curr[1].length) { config2[normalize_option_key_default(curr[1])] = parse_primitive_default(env[key]); } }); return config2; }; var config_from_env_default = getConfigFromEnv; // src/utils/load-environment-file.js var import_fs = __toESM(require("fs")); var import_dotenv = __toESM(require("dotenv")); var loadEnvironmentFile = (path, encoding, silent, errorOnMissingFiles = false) => { try { const data = import_fs.default.readFileSync(path, encoding); return import_dotenv.default.parse(data); } catch (err) { if (errorOnMissingFiles && err && err.code === "ENOENT") { throw new Error(`MISSING CONFIG FILE: ${path}`); } if (!silent) { console.error(err.message); } return {}; } }; var load_environment_file_default = loadEnvironmentFile; // src/index.js var normalizeLayeredFiles = (value) => { if (!value) { return []; } if (Array.isArray(value)) { return value.filter(Boolean); } if (typeof value === "string") { return value.split(",").map((item) => item.trim()).filter(Boolean); } return []; }; var loadLayeredFiles = (files, options) => normalizeLayeredFiles(files).reduce((acc, filePath) => { const fileData = load_environment_file_default( filePath, options.encoding, options.silent, options.errorOnMissingFiles ); return Object.assign(acc, fileData); }, {}); var parse = import_dotenv2.default.parse.bind(import_dotenv2.default); var config = (options) => { let defaultsData, environmentData, defaultOptions = { encoding: "utf8", silent: true, path: ".env", defaults: ".env.defaults", schema: ".env.schema", schemaExtends: void 0, errorOnMissing: false, errorOnExtra: false, errorOnRegex: false, errorOnMissingFiles: false, returnSchemaOnly: false, includeProcessEnv: false, assignToProcessEnv: true, overrideProcessEnv: false }, processEnvOptions = config_from_env_default(process.env); options = Object.assign({}, defaultOptions, processEnvOptions, options); defaultsData = loadLayeredFiles(options.defaults, options); environmentData = loadLayeredFiles(options.path, options); let configData = Object.assign({}, defaultsData, environmentData); const config2 = options.includeProcessEnv ? Object.assign({}, configData, process.env) : configData; const configOnlyKeys = Object.keys(configData); const configKeys = Object.keys(config2); let schemaKeys = null; if (options.errorOnMissing || options.errorOnExtra || options.errorOnRegex || options.returnSchemaOnly) { const baseSchema = load_environment_file_default( options.schema, options.encoding, options.silent, options.errorOnMissingFiles ); const schema = normalizeLayeredFiles(options.schemaExtends).reduce( (acc, schemaPath) => { const schemaLayer = load_environment_file_default( schemaPath, options.encoding, options.silent, options.errorOnMissingFiles ); return Object.assign(acc, schemaLayer); }, Object.assign({}, baseSchema) ); schemaKeys = Object.keys(schema); let missingKeys = schemaKeys.filter(function(key) { return configKeys.indexOf(key) < 0; }); let extraKeys = configOnlyKeys.filter(function(key) { return schemaKeys.indexOf(key) < 0; }); if (options.errorOnMissing && missingKeys.length) { throw new Error("MISSING CONFIG VALUES: " + missingKeys.join(", ")); } if (options.errorOnExtra && extraKeys.length) { throw new Error("EXTRA CONFIG VALUES: " + extraKeys.join(", ")); } if (options.errorOnRegex) { const regexMismatchKeys = schemaKeys.filter(function(key) { if (schema[key]) { return !new RegExp(schema[key]).test( typeof config2[key] === "string" ? config2[key] : "" ); } }); if (regexMismatchKeys.length) { throw new Error("REGEX MISMATCH: " + regexMismatchKeys.join(", ")); } } } if (options.includeProcessEnv && !options.overrideProcessEnv) { for (let i = 0; i < configKeys.length; i++) { if (typeof process.env[configKeys[i]] !== "undefined") configData[configKeys[i]] = process.env[configKeys[i]]; } } if (options.returnSchemaOnly && schemaKeys) { configData = schemaKeys.reduce((acc, key) => { if (typeof config2[key] !== "undefined") { acc[key] = config2[key]; } return acc; }, {}); } if (options.assignToProcessEnv) { if (options.overrideProcessEnv) { Object.assign(process.env, configData); } else { const tmp = Object.assign({}, configData, process.env); Object.assign(process.env, tmp); } } return configData; }; // src/utils/parse-command.js var dotEnvFlagRegex = /^--(.+)=(.+)/; var dotEnvBooleanFlagRegex = /^--(.+)$/; var parseCommand = (args) => { const config2 = {}; let command = null; let commandArgs = []; for (let i = 0; i < args.length; i++) { const match = dotEnvFlagRegex.exec(args[i]); if (match) { config2[normalize_option_key_default(match[1])] = parse_primitive_default(match[2]); continue; } const booleanFlagMatch = dotEnvBooleanFlagRegex.exec(args[i]); if (booleanFlagMatch && normalize_option_key_default(booleanFlagMatch[1]) === "print") { config2.print = true; } else { command = args[i]; commandArgs = args.slice(i + 1); break; } } return [config2, command, commandArgs]; }; var parse_command_default = parseCommand; // src/bin/index.js var import_node_child_process = require("child_process"); var spawnCommand = (command, commandArgs, options) => (0, import_node_child_process.spawn)(command, commandArgs, options); var writeStdout = (value) => process.stdout.write(value); var writeStderr = (value) => process.stderr.write(value); var toDotenvString = (values) => Object.keys(values).map((key) => `${key}=${values[key]}`).join("\n"); function loadAndExecute(args, dependencies = {}) { const { spawnCommandFn = spawnCommand, processOn = process.on.bind(process), processExit = process.exit, writeStdoutFn = writeStdout, writeStderrFn = writeStderr } = dependencies; const [dotEnvConfig, command, commandArgs] = parse_command_default(args); const { print, ...configOptions } = dotEnvConfig; if (print && command) { writeStderrFn("dotenv-extended: --print mode cannot be combined with command execution.\n"); processExit(1); return; } if (print) { const mergedConfig = config({ ...configOptions, assignToProcessEnv: false, includeProcessEnv: false }); const format = typeof print === "string" ? print.toLowerCase() : "json"; if (format === "dotenv") { writeStdoutFn(`${toDotenvString(mergedConfig)} `); return mergedConfig; } writeStdoutFn(`${JSON.stringify(mergedConfig, null, 2)} `); return mergedConfig; } if (command) { config(configOptions); const proc = spawnCommandFn(command, commandArgs, { stdio: "inherit", shell: true, env: process.env }); processOn("SIGTERM", () => proc.kill("SIGTERM")); processOn("SIGINT", () => proc.kill("SIGINT")); processOn("SIGBREAK", () => proc.kill("SIGBREAK")); processOn("SIGHUP", () => proc.kill("SIGHUP")); proc.on("exit", processExit); return proc; } } // src/bin/cli.js loadAndExecute(process.argv.slice(2));