UNPKG

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
"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