dotenv-mono
Version:
This package permit to have a centralized dotenv on a monorepo. It also includes some extra features such as manipulation and saving of changes to the dotenv file, a default centralized file, and a file loader with ordering and priorities.
481 lines • 17 kB
JavaScript
"use strict";
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _Dotenv__cwd, _Dotenv__debug, _Dotenv__defaults, _Dotenv__depth, _Dotenv__encoding, _Dotenv__expand, _Dotenv__extension, _Dotenv__override, _Dotenv__path, _Dotenv__priorities;
Object.defineProperty(exports, "__esModule", { value: true });
exports.config = exports.load = exports.Dotenv = void 0;
exports.dotenvLoad = dotenvLoad;
exports.dotenvConfig = dotenvConfig;
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const dotenv_1 = __importDefault(require("dotenv"));
const dotenv_expand_1 = __importDefault(require("dotenv-expand"));
/**
* Dotenv controller.
*/
class Dotenv {
/**
* Constructor.
* @param cwd - current Working Directory
* @param debug - turn on/off debugging
* @param depth - max walking up depth
* @param encoding - file encoding
* @param expand - turn on/off dotenv-expand plugin
* @param extension - add dotenv extension
* @param override - override process variables
* @param path - dotenv path
* @param priorities - priorities
*/
constructor({ cwd, debug, defaults, depth, encoding, expand, extension, override, path, priorities, } = {}) {
// Public config properties
this.config = {};
this.env = {};
this.plain = "";
// Accessor properties
_Dotenv__cwd.set(this, "");
_Dotenv__debug.set(this, false);
_Dotenv__defaults.set(this, ".env.defaults");
_Dotenv__depth.set(this, 4);
_Dotenv__encoding.set(this, "utf8");
_Dotenv__expand.set(this, true);
_Dotenv__extension.set(this, "");
_Dotenv__override.set(this, false);
_Dotenv__path.set(this, "");
_Dotenv__priorities.set(this, {});
this.cwd = cwd;
this.debug = debug;
this.defaults = defaults;
this.depth = depth;
this.encoding = encoding;
this.expand = expand;
this.extension = extension;
this.override = override;
this.path = path;
this.priorities = priorities;
// Auto-bind matchers
this.dotenvDefaultsMatcher = this.dotenvDefaultsMatcher.bind(this);
this.dotenvMatcher = this.dotenvMatcher.bind(this);
}
/**
* Get debugging.
*/
get debug() {
return __classPrivateFieldGet(this, _Dotenv__debug, "f");
}
/**
* Set debugging.
* @param value
*/
set debug(value) {
if (value != null)
__classPrivateFieldSet(this, _Dotenv__debug, value, "f");
}
/**
* Get defaults filename.
*/
get defaults() {
return __classPrivateFieldGet(this, _Dotenv__defaults, "f");
}
/**
* Set defaults filename.
* @param value
*/
set defaults(value) {
if (value != null)
__classPrivateFieldSet(this, _Dotenv__defaults, value, "f");
}
/**
* Get encoding.
*/
get encoding() {
return __classPrivateFieldGet(this, _Dotenv__encoding, "f");
}
/**
* Set encoding.
* @param value
*/
set encoding(value) {
if (value != null)
__classPrivateFieldSet(this, _Dotenv__encoding, value, "f");
}
/**
* Get dotenv-expand plugin enabling.
*/
get expand() {
return __classPrivateFieldGet(this, _Dotenv__expand, "f");
}
/**
* Turn on/off dotenv-expand plugin.
*/
set expand(value) {
if (value != null)
__classPrivateFieldSet(this, _Dotenv__expand, value, "f");
}
/**
* Get extension.
*/
get extension() {
return __classPrivateFieldGet(this, _Dotenv__extension, "f");
}
/**
* Set extension.
*/
set extension(value) {
if (value != null)
__classPrivateFieldSet(this, _Dotenv__extension, value.replace(/^\.+/, "").replace(/\.+$/, ""), "f");
}
/**
* Get current working directory.
*/
get cwd() {
var _a;
if (!__classPrivateFieldGet(this, _Dotenv__cwd, "f"))
return (_a = process.cwd()) !== null && _a !== void 0 ? _a : "";
return __classPrivateFieldGet(this, _Dotenv__cwd, "f");
}
/**
* Set current working directory.
* @param value
*/
set cwd(value) {
__classPrivateFieldSet(this, _Dotenv__cwd, value !== null && value !== void 0 ? value : "", "f");
}
/**
* Get depth.
*/
get depth() {
return __classPrivateFieldGet(this, _Dotenv__depth, "f");
}
/**
* Set depth.
* @param value
*/
set depth(value) {
if (value != null)
__classPrivateFieldSet(this, _Dotenv__depth, value, "f");
}
/**
* Get override.
*/
get override() {
return __classPrivateFieldGet(this, _Dotenv__override, "f");
}
/**
* Set override.
* @param value
*/
set override(value) {
if (value != null)
__classPrivateFieldSet(this, _Dotenv__override, value, "f");
}
/**
* Get path.
*/
get path() {
return __classPrivateFieldGet(this, _Dotenv__path, "f");
}
/**
* Set path.
*/
set path(value) {
if (value != null)
__classPrivateFieldSet(this, _Dotenv__path, value, "f");
}
/**
* Get priorities.
*/
get priorities() {
var _a;
const nodeEnv = (_a = process.env.NODE_ENV) !== null && _a !== void 0 ? _a : "development";
const ext = this.extension ? `.${this.extension}` : "";
const priorities = Object.assign({
[`.env${ext}.${nodeEnv}.local`]: 75,
[`.env${ext}.local`]: 50,
[`.env${ext}.${nodeEnv}`]: 25,
[`.env${ext}`]: 1,
}, __classPrivateFieldGet(this, _Dotenv__priorities, "f"));
return priorities;
}
/**
* Merge priorities specified with default and check NODE_ENV.
* @param value
*/
set priorities(value) {
if (value != null)
__classPrivateFieldSet(this, _Dotenv__priorities, value, "f");
}
/**
* Parses a string or buffer in the .env file format into an object.
* @see https://docs.dotenv.org
* @returns an object with keys and values based on `src`. example: `{ DB_HOST : 'localhost' }`
*/
parse(src) {
return dotenv_1.default.parse(src);
}
/**
* Loads `.env` and default file contents.
* @param loadOnProcess - load contents inside process
* @returns current instance
*/
load(loadOnProcess = true) {
// Reset
this.env = {};
this.config = {};
// Load dotenv source file
const file = this.path || this.find();
this.loadDotenv(file, loadOnProcess);
// Load default without override the source file
const defaultFile = this.find(this.dotenvDefaultsMatcher);
this.loadDotenv(defaultFile, loadOnProcess, true);
return this;
}
/**
* Load with dotenv package and set parsed and plain content into the instance.
* @private
* @param file - path to dotenv
* @param loadOnProcess - load contents inside process
* @param defaults - is the default dotenv
*/
loadDotenv(file, loadOnProcess, defaults = false) {
if (!file || !fs_1.default.existsSync(file))
return;
try {
const plain = fs_1.default.readFileSync(file, { encoding: this.encoding, flag: "r" });
const config = loadOnProcess
? dotenv_1.default.config({
path: file,
debug: this.debug,
encoding: this.encoding,
override: !defaults && this.override,
})
: {
parsed: this.parse(plain),
processEnv: {},
};
if (this.expand)
dotenv_expand_1.default.expand(config);
this.mergeDotenvConfig(config);
if (!defaults)
this.plain = plain;
}
catch (error) {
if (this.debug) {
console.error(`Error loading dotenv file: ${file}`, error);
}
}
}
/**
* Merge dotenv package configs.
* @private
* @param config - dotenv config
*/
mergeDotenvConfig(config) {
var _a, _b, _c, _d, _e;
this.config = {
parsed: Object.assign(Object.assign({}, ((_a = this.config.parsed) !== null && _a !== void 0 ? _a : {})), ((_b = config.parsed) !== null && _b !== void 0 ? _b : {})),
error: (_d = (_c = this.config.error) !== null && _c !== void 0 ? _c : config.error) !== null && _d !== void 0 ? _d : undefined,
};
this.env = Object.assign(Object.assign({}, this.env), ((_e = this.config.parsed) !== null && _e !== void 0 ? _e : {}));
}
/**
* Loads `.env` file contents.
* @returns current instance
*/
loadFile() {
this.load(false);
return this;
}
/**
* Find first `.env` file walking up from cwd directory based on priority criteria.
* @returns file matched with higher priority
*/
find(matcher) {
if (!matcher)
matcher = this.dotenvMatcher;
let dotenv = null;
let directory = path_1.default.resolve(this.cwd);
const { root } = path_1.default.parse(directory);
let depth = 0;
while (depth < this.depth) {
depth++;
const { foundPath, foundDotenv } = matcher(dotenv, directory);
dotenv = foundDotenv;
if (foundPath || directory === root) {
break;
}
directory = path_1.default.dirname(directory);
}
return dotenv;
}
/**
* Dotenv matcher.
* @private
* @param dotenv - dotenv result
* @param cwd - current working directory
* @returns paths found
*/
dotenvMatcher(dotenv, cwd) {
let priority = -1;
Object.keys(this.priorities).forEach((fileName) => {
if (this.priorities[fileName] > priority && fs_1.default.existsSync(path_1.default.join(cwd, fileName))) {
dotenv = path_1.default.join(cwd, fileName);
priority = this.priorities[fileName];
}
});
const foundPath = dotenv != null ? cwd : null;
if (typeof foundPath === "string") {
try {
const stat = fs_1.default.statSync(path_1.default.resolve(cwd, foundPath));
if (stat.isDirectory())
return { foundPath, foundDotenv: dotenv };
}
catch (_a) { }
}
return { foundPath, foundDotenv: dotenv };
}
/**
* Defaults dotenv matcher.
* @private
* @param dotenv - dotenv result
* @param cwd - current working directory
* @returns paths found
*/
dotenvDefaultsMatcher(dotenv, cwd) {
if (fs_1.default.existsSync(path_1.default.join(cwd, this.defaults))) {
dotenv = path_1.default.join(cwd, this.defaults);
}
const foundPath = dotenv != null ? cwd : null;
if (typeof foundPath === "string") {
try {
const stat = fs_1.default.statSync(path_1.default.resolve(cwd, foundPath));
if (stat.isDirectory())
return { foundPath, foundDotenv: dotenv };
}
catch (_a) { }
}
return { foundPath, foundDotenv: dotenv };
}
/**
* Save `.env` file contents.
* @param changes - data to change on the dotenv
* @returns current instance
*/
save(changes) {
const file = this.path || this.find();
if (!file || !fs_1.default.existsSync(file))
return this;
// https://github.com/stevenvachon/edit-dotenv
const EOL = os_1.default.EOL;
const breakPattern = /\n/g;
const breakReplacement = "\\n";
const flags = "gm";
const groupPattern = /\$/g;
const groupReplacement = "$$$";
const h = "[^\\S\\r\\n]";
const returnPattern = /\r/g;
const returnReplacement = "\\r";
const endsWithEOL = (string) => string.endsWith("\n") || string.endsWith("\r\n");
let hasAppended = false;
const data = Object.keys(changes).reduce((result, variable) => {
const rawValue = changes[variable];
if (rawValue == null)
return result;
const value = String(rawValue)
.replace(breakPattern, breakReplacement)
.replace(returnPattern, returnReplacement)
.trim();
const safeName = this.escapeRegExp(variable);
// Match all between equal and eol
const varPattern = new RegExp(`^(${h}*${safeName}${h}*=${h}*).*?(${h}*)$`, flags);
if (varPattern.test(result)) {
const safeValue = value.replace(groupPattern, groupReplacement);
return result.replace(varPattern, `$1${safeValue}$2`);
}
else if (result === "") {
hasAppended = true;
return `${variable}=${value}${EOL}`;
}
else if (!endsWithEOL(result) && !hasAppended) {
hasAppended = true;
// Add an extra break between previously defined and newly appended variable
return `${result}${EOL}${EOL}${variable}=${value}`;
}
else if (!endsWithEOL(result)) {
// Add break for appended variable
return `${result}${EOL}${variable}=${value}`;
}
else if (endsWithEOL(result) && !hasAppended) {
hasAppended = true;
// Add an extra break between previously defined and newly appended variable
return `${result}${EOL}${variable}=${value}${EOL}`;
}
else {
// Add break for appended variable
return `${result}${variable}=${value}${EOL}`;
}
}, this.plain);
fs_1.default.writeFileSync(file, data, {
encoding: this.encoding,
});
this.plain = data;
// Update env with new changes
Object.keys(changes).forEach((key) => {
const value = changes[key];
if (value !== undefined && value !== null) {
this.env[key] = String(value);
}
});
return this;
}
/**
* Escape regex.
* @param string - string to escape
* @returns escaped string
*/
escapeRegExp(string) {
return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
}
}
exports.Dotenv = Dotenv;
_Dotenv__cwd = new WeakMap(), _Dotenv__debug = new WeakMap(), _Dotenv__defaults = new WeakMap(), _Dotenv__depth = new WeakMap(), _Dotenv__encoding = new WeakMap(), _Dotenv__expand = new WeakMap(), _Dotenv__extension = new WeakMap(), _Dotenv__override = new WeakMap(), _Dotenv__path = new WeakMap(), _Dotenv__priorities = new WeakMap();
/**
* Load dotenv on process and return instance of Dotenv.
* @param props - configuration
* @returns Dotenv instance
*/
function dotenvLoad(props) {
const dotenv = new Dotenv(props);
return dotenv.load();
}
/**
* @see dotenvLoad
*/
exports.load = dotenvLoad;
/**
* Load dotenv on process and return the dotenv output.
* @param props - configuration
* @returns DotenvConfigOutput
*/
function dotenvConfig(props) {
const dotenv = new Dotenv(props);
return dotenv.load().config;
}
/**
* @see dotenvConfig
*/
exports.config = dotenvConfig;
exports.default = Dotenv;
//# sourceMappingURL=index.js.map