Rework raw.js to function like the new defer mechanism. by jdmarshall · Pull Request #879 · node-config/node-config · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions lib/config.js
82 changes: 64 additions & 18 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,25 @@ const OS = require("os");
const DEFAULT_CONFIG_DIR = Path.join( process.cwd(), 'config');
const SEEN_ERRORS = {};

/**
* @typedef {Object} BootstrapCallbackContext
* @property {Util} util
* @property {deferConfig} defer
* @property {RawConfig.raw} raw
*/

/**
* A function for deferred configuration
*
* Executable config files can now be initialized by a callback which receives useful
* context in order to avoid the need for require() or import() calls to config while
* it is still initializing.
*
* @callback bootstrapCallback
* @param context {BootstrapCallbackContext} - utility functions for startup
* @returns {Object} - data for config properties
*/

/**
* A source in the configSources list
*
Expand Down Expand Up @@ -411,8 +430,6 @@ class Util {
const allChildren = [];
const util = this;

const useBuffer = typeof Buffer !== 'undefined';

if (typeof circular === 'undefined')
circular = true;

Expand Down Expand Up @@ -440,10 +457,12 @@ class Util {
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (parent instanceof Date) {
child = new Date(parent.getTime());
} else if (useBuffer && Buffer.isBuffer(parent)) {
} else if (Buffer.isBuffer(parent)) {
child = Buffer.alloc(parent.length);
parent.copy(child);
return child;
} else if (parent instanceof RawConfig) {
child = parent;
} else {
if (typeof prototype === 'undefined') child = Object.create(Object.getPrototypeOf(parent));
else child = Object.create(prototype);
Expand Down Expand Up @@ -570,7 +589,7 @@ class Util {
// They must have the same keys. If their length isn't the same
// then they're not equal. If the keys aren't the same, the value
// comparisons will fail.
if (Object.keys(object1).length != Object.keys(object2).length) {
if (Object.keys(object1).length !== Object.keys(object2).length) {
return false;
}

Expand Down Expand Up @@ -602,7 +621,7 @@ class Util {
* @method extendDeep
* @param mergeInto {Object} The object to merge into
* @param mergeFrom... {Object} - Any number of objects to merge from
* @param depth {integer} An optional depth to prevent recursion. Default: 20.
* @param [depth] {number} An optional depth to prevent recursion. Default: 20.
* @return {Object} The altered mergeInto object is returned
*/
static extendDeep(mergeInto, ...vargs) {
Expand Down Expand Up @@ -632,19 +651,19 @@ class Util {
if (mergeFrom[prop] instanceof Date) {
mergeInto[prop] = mergeFrom[prop];
}
if (mergeFrom[prop] instanceof RegExp) {

if ((mergeFrom[prop] instanceof RegExp) ||
(mergeFrom[prop] instanceof RawConfig)) {
mergeInto[prop] = mergeFrom[prop];
} else if (Util.isObject(mergeInto[prop]) && Util.isObject(mergeFrom[prop]) && !isDeferredFunc) {
Util.extendDeep(mergeInto[prop], mergeFrom[prop], depth - 1);
} else if (Util.isPromise(mergeFrom[prop])) {
mergeInto[prop] = mergeFrom[prop];
}
// Copy recursively if the mergeFrom element is an object (or array or fn)
else if (mergeFrom[prop] && typeof mergeFrom[prop] === 'object') {
} else if (mergeFrom[prop] && typeof mergeFrom[prop] === 'object') {
// Copy recursively if the mergeFrom element is an object (or array or fn)
mergeInto[prop] = Util.cloneDeep(mergeFrom[prop], depth - 1);
}
// Copy property descriptor otherwise, preserving accessors
else if (Object.getOwnPropertyDescriptor(Object(mergeFrom), prop)) {
} else if (Object.getOwnPropertyDescriptor(Object(mergeFrom), prop)) {
// Copy property descriptor otherwise, preserving accessors
Object.defineProperty(mergeInto, prop, Object.getOwnPropertyDescriptor(Object(mergeFrom), prop));
} else if (mergeInto[prop] !== mergeFrom[prop]) {
mergeInto[prop] = mergeFrom[prop];
Expand Down Expand Up @@ -843,7 +862,7 @@ class Load {
/**
* @constructor
* @param {LoadOptions=} options - defaults to reading from environment variables
* @param {Env=} env - an Env instance to use (for testing)
* @param {Env=} env - optional Env data, usually from fromEnvironment()
*/
constructor(options, env = new Env()) {
/** @type {Env} */
Expand Down Expand Up @@ -1019,9 +1038,10 @@ class Load {
}

if (typeof configObject == 'function') {
/** @type bootstrapCallback */
let fn = configObject;

configObject = fn({ defer: deferConfig, util: Util });
configObject = fn({ defer: deferConfig, util: Util, raw: RawConfig.raw });
}

if (convert) {
Expand Down Expand Up @@ -1057,8 +1077,8 @@ class Load {

/**
* Return the report of where the sources for this load operation came from
* @returns {ConfigSource[]}
*/
* @returns {ConfigSource[]}
*/
getSources() {
return (this.sources ?? []).slice();
}
Expand Down Expand Up @@ -1147,7 +1167,7 @@ class Load {
* @param {string} format The format to be parsed
* @return {object} configObject The configuration object parsed from the string
*/
parseString = function (content, format) {
parseString(content, format) {
const parser = this.parser.getParser(format);

if (typeof parser === 'function') {
Expand Down Expand Up @@ -1276,6 +1296,32 @@ class Load {
}
}

/**
* This is meant to wrap configuration objects that should be left as is,
* meaning that the object or its prototype will not be modified in any way
*/
class RawConfig {
#rawObject = undefined;

constructor(data) {
this.#rawObject = data;
}

resolve() {
return this.#rawObject;
}

/**
* create a RawConfig
* @param rawObject {Object} properties we don't want to have manipulated by node-config
* @returns {RawConfig}
*/
static raw(rawObject) {
return new RawConfig(rawObject);
}
}


// Helper functions shared across object members
function _toAbsolutePath (configDir) {
if (configDir.indexOf('.') === 0) {
Expand All @@ -1301,4 +1347,4 @@ function _loadParser(name, dir) {
}
}

module.exports = { Util, Load };
module.exports = { Util, Load, RawConfig };
16 changes: 6 additions & 10 deletions raw.js