dotenv-manipulator
Version:
Load, add, update or remove variables from both your .env file and process.env at runtime
189 lines (188 loc) • 6.82 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const fs = require("fs");
const path = require("path");
/**
* dotenv Manipulator :
* - Load environment variables from a `.env` to `process.env`
* - Add, update, or remove variables from both your `.env` file and `process.env` <b>at runtime</b>
* @example
* // require
* const Manipulator = require('dotenv-manipulator').default
* @example
* // import
* import Manipulator from 'dotenv-manipulator'
*/
class Manipulator {
/**
* Starts dotenv Manipulator
* @param envPath Path to `.env`, default is `process.cwd()`.
* @param throwable If set to `true` functions throw errors whenever there's an incorrect input.
* @param encoding Specify file encoding, default is `utf8`.
* @example
* // basic setup
* const Manipulator = require('dotenv-manipulator')
* const dotenvM = new Manipulator()
* @example
* // complete setup
* const Manipulator = require('dotenv-manipulator')
* const dotenvM = new Manipulator('/path/to/project', false, 'utf-8')
* @example
* // undefined = default value
* const Manipulator = require('dotenv-manipulator')
* const dotenvM = new Manipulator(undefined, true, 'latin1')
*/
constructor(envPath = path.normalize(process.cwd()), throwable = false, encoding = 'utf-8') {
this.encoding = 'utf-8';
this.encoding = encoding;
this.throwable = throwable;
this.env_path = path.join(envPath, '.env');
if (!fs.existsSync(this.env_path)) {
fs.closeSync(fs.openSync(this.env_path, 'w'));
}
this.env = this.__readSync();
this.__sort();
}
__readSync() {
const OBJ = {};
const file = fs.readFileSync(this.env_path, { encoding: this.encoding }).toString().split(/\r?\n/);
if (file.length) {
for (const line of file) {
if (line !== '') {
const keyvalue = line.split('=');
if (keyvalue.length === 2) {
OBJ[keyvalue[0]] = keyvalue[1];
process.env[keyvalue[0]] = keyvalue[1];
}
}
}
}
return OBJ;
}
/**
* Add object keys and values to `.env` file and `process.env`
* @param obj Object to add to the environment
* @param force If you try to add a key/value pair that is already in the environment : `add(obj, true)` would update said variables and `add(obj)` would just ignore your input
* @example
* const obj = { REMOTE: '95.81.123.228', PORT: 3000 }
* dotenvM.add(obj)
* @example
* // without force argument
* console.log(process.env.REMOTE_PORT) //=> '3000'
* dotenvM.add({ REMOTE_PORT: '2000' })
* console.log(process.env.REMOTE_PORT) //=> '3000'
*
* // with force argument set to true
* dotenvM.add({ REMOTE_PORT: '2000' }, true)
* console.log(process.env.REMOTE_PORT) //=> '2000'
* @example
* // DEBUGGING #1
* dotenvM.throwable = false
*
* let debug = dotenvM.add(['wrong', 'type', 'of', 'data'])
* let debug_1 = dotenvM.add({ GOOOD: 'type', OF: 'data' })
* console.log(debug.message) //=> [ADD_ERROR]: object must be [object Object] but received [object Array]
* console.log(debug_1) //=> undefined
*
* // DEBUGGING #2
* dotenvM.throwable = true
*
* try {
* dotenvM.add(['wrong', 'type', 'of', 'data'])
* } catch(e) {
* console.log(e.message) //=> [ADD_ERROR]: object must be [object Object] but received [object Array]
* }
*/
add(obj, force = false) {
if (Object.prototype.toString.call(obj) !== '[object Object]') {
const errorMessage = new TypeError(`[ADD_ERROR]: object must be [object Object] but received ${Object.prototype.toString.call(obj)}`);
if (this.throwable) {
throw errorMessage;
}
else {
return errorMessage;
}
}
for (const res of Object.entries(obj)) {
const key = res[0];
const val = res[1];
if (!this.env[key]) {
this.env[key] = val;
process.env[key] = val;
}
else if (force) {
this.env[key] = val;
process.env[key] = val;
}
}
this.__save();
}
/**
* Remove variable from `.env` file and `process.env`
* @param obj Key(s) you want to remove
* @example
* // #1
* dotenvM.remove('REMOTE')
* dotenvM.remove('PORT')
*
* // #2
* dotenvM.remove( { REMOTE: 'value that dotenvM wont read', PORT: 'no.. really it doesnt care' } )
*
* // #3
* dotenvM.remove(['REMOTE', 'PORT'])
*/
remove(obj) {
if (typeof obj === 'string') {
delete process.env[obj];
delete this.env[obj];
}
else if (Object.prototype.toString.call(obj) === '[object Object]') {
for (const key of Object.keys(obj)) {
delete process.env[key];
delete this.env[key];
}
}
else if (Array.isArray(obj)) {
for (const key of obj) {
delete process.env[key];
delete this.env[key];
}
}
else {
const errorMessage = new TypeError(`[REMOVE_ERROR]: expected [object Object] or [object Array] or [String] but received ${Object.prototype.toString.call(obj)}`);
if (this.throwable) {
throw errorMessage;
}
else {
return errorMessage;
}
}
this.__save();
}
__sort() {
const order = {};
for (const key of Object.keys(this.env).sort()) {
order[key] = this.env[key];
}
this.env = order;
}
__save() {
this.__sort();
let string = '';
for (const key in this.env) {
if (Object.prototype.hasOwnProperty.call(this.env, key)) {
const val = this.env[key];
string += key + '=' + val + '\n';
}
}
if (string.length) {
fs.writeFileSync(this.env_path, string, { encoding: this.encoding });
}
else {
if (Object.keys(this.env).length === 0) {
fs.writeFileSync(this.env_path, string, { encoding: this.encoding });
}
}
}
}
exports.default = Manipulator;