env-lift
Version:
Simple namespaced environment variable configuration management solution
130 lines (116 loc) • 4.25 kB
JavaScript
var E = '',
TRUE = 'true',
FALSE = 'false',
/**
* Default separator while retrieving values of multi-level keys.
* @const
* @type {string}
*/
DEFAULT_SEPARATOR = '_', // default separator value constant
/**
* Accepts a string and replaces all non alphabetic characters with default separator and converts characters to
* uppercase.
*
* @param {object} key
* @param {string=} [separator=}
* @returns {string}
*/
rekey = function (key, separator) {
return key.toUpperCase().replace(/[^A-Z0-9]/g, separator || E);
},
/**
* Stores the set of typecast functions to be applied when retrieving values from environment.
*
* @enum
* @type {Objeck<function>}
*/
typecasts = {
'string': String,
'number': Number,
'undefined': String,
'boolean': function (value) {
value = (value && value.toString) ? value.toString().trim().toLowerCase() : value;
if (!isNaN(value)) {
return Number(value);
}
if (value === FALSE) {
return false;
}
if (value === TRUE) {
return true;
}
}
},
// main function that parses objects
lift;
/**
* Recursively traverses through objects and whenever native value types are encountered, replaces it with corresponding
* data from environment variables.
*
* @param {object} obj
* @param {string=} [namespace=] - Ensure that the namespace is pre-transformed. This improves performance.
* @param {string=} [separator=_]
* @param {function} transformer - The key name to pick data is forwarded as parameter and the value returned by the
* transformer function is used to lookup environment variables.
*/
lift = function (obj, namespace, separator, transformer, _debug) {
var key,
cast,
eKey;
// iterate on each item in this object and operate on their keys.
for (key in obj) {
// only process keys that are owned by this object
if (!obj.hasOwnProperty(key)) {
continue;
}
// determine the type of the value and extract the casting function.
cast = typecasts[typeof obj[key]];
!cast && (obj[key] === null) && (cast = typecasts.undefined); // specially handle null
// generate the equivalent environment key for the namespace
eKey = (namespace || E) + separator + (transformer ? transformer(key, separator) : key);
// debug log
_debug && (process.env[eKey] !== undefined) && console.log('env-lift: %s=%s', eKey, process.env[eKey],
cast ? ('[' + cast(process.env[eKey]) + ']') : E) ;
// if a cast function is there, it implies that native value is detected and we need to lookup environment
if (cast) {
// if environment has a defined variable, we replace the original value with this.
if (process.env[eKey] !== undefined) {
obj[key] = cast(process.env[eKey]);
}
}
// otherwise, we recurse into the value of the property
else {
lift(obj[key], eKey, separator, transformer, _debug);
}
}
return obj;
};
module.exports = /** @lends module:env-lift */ {
/**
* Replaces values in `reference` object with equivalent definition of environment variables prefixed with
* `namespace` and object's key path.
*
* @note This function mutes the original reference object.
*
* @param {string} namespace
* @param {object} reference
*
* @example
* require('env-lift').load('test', {
* port: 123,
* name: 'Sample Text'
* });
*
* // if there is an environment variable TEST_PORT=8080
* // The above would return: {
* // port: 8080,
* // name: 'Sample Text'
* // }
*/
load: function (namespace, reference) {
var debug = !!process.env.ENV_LIFT_DEBUG,
nsKey = rekey(namespace || E, DEFAULT_SEPARATOR);
debug && console.log('env-lift: debugging %s', nsKey);
return lift(reference, nsKey, DEFAULT_SEPARATOR, rekey, debug);
}
};