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
JavaScript
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));