Comparing version 3.1.0 to 3.2.0
19
defer.js
// Create a deferredConfig prototype so that we can check for it when reviewing the configs later. | ||
function DeferredConfig () { | ||
} | ||
DeferredConfig.prototype.resolve = function (config, original) {}; | ||
function DeferredConfig() {} | ||
DeferredConfig.prototype.prepare = function() {}; | ||
DeferredConfig.prototype.resolve = function() {}; | ||
// Accept a function that we'll use to resolve this value later and return a 'deferred' configuration value to resolve it later. | ||
function deferConfig (func) { | ||
function deferConfig(func) { | ||
var obj = Object.create(DeferredConfig.prototype); | ||
obj.resolve = func; | ||
obj.prepare = function(config, prop, property) { | ||
var original = prop[property]._original; | ||
obj.resolve = function() { | ||
var value = func.call(config, config, original); | ||
Object.defineProperty(prop, property, {value: value}); | ||
return value; | ||
}; | ||
Object.defineProperty(prop, property, {get: function() { return obj.resolve(); }}); | ||
return obj; | ||
}; | ||
return obj; | ||
@@ -11,0 +20,0 @@ } |
@@ -0,1 +1,8 @@ | ||
3.2.0 / 2019-07-11 | ||
================== | ||
* Asynchronous configs - iMoses | ||
* Multiple config directories - @iMoses | ||
* Improved parser support - @iMoses | ||
3.1.0 / 2019-04-07 | ||
@@ -2,0 +9,0 @@ ================== |
@@ -7,15 +7,6 @@ // config.js (c) 2010-2015 Loren West and other contributors | ||
// Dependencies | ||
var Yaml = null, // External libraries are lazy-loaded | ||
VisionmediaYaml = null, // only if these file types exist. | ||
Coffee = null, | ||
Iced = null, | ||
CSON = null, | ||
PPARSER = null, | ||
JSON5 = null, | ||
TOML = null, | ||
HJSON = null, | ||
XML = null, | ||
deferConfig = require('../defer').deferConfig, | ||
var deferConfig = require('../defer').deferConfig, | ||
DeferredConfig = require('../defer').DeferredConfig, | ||
RawConfig = require('../raw').RawConfig, | ||
Parser = require('../parser'), | ||
Utils = require('util'), | ||
@@ -29,2 +20,3 @@ Path = require('path'), | ||
HOST, HOSTNAME, ALLOW_CONFIG_MUTATIONS, CONFIG_SKIP_GITCRYPT, | ||
NODE_CONFIG_PARSER, | ||
env = {}, | ||
@@ -37,16 +29,2 @@ privateUtil = {}, | ||
// Define soft dependencies so transpilers don't include everything | ||
var COFFEE_2_DEP = "coffeescript", | ||
COFFEE_DEP = "coffee-script", | ||
ICED_DEP = "iced-coffee-script", | ||
JS_YAML_DEP = "js-yaml", | ||
YAML_DEP = "yaml", | ||
JSON5_DEP = "json5", | ||
HJSON_DEP = "hjson", | ||
TOML_DEP = "toml", | ||
CSON_DEP = "cson", | ||
PPARSER_DEP = "properties", | ||
XML_DEP = "x2js", | ||
TS_DEP = "ts-node"; | ||
/** | ||
@@ -135,3 +113,5 @@ * <p>Application Configurations</p> | ||
for (var fnName in util) { | ||
util[fnName] = util[fnName].bind(t); | ||
if (typeof util[fnName] === 'function') { | ||
util[fnName] = util[fnName].bind(t); | ||
} | ||
} | ||
@@ -158,3 +138,3 @@ | ||
* @param object {object} - Object to get the property for | ||
* @param property {string | array[string]} - The property name to get (as an array or '.' delimited string) | ||
* @param property {string|string[]} - The property name to get (as an array or '.' delimited string) | ||
* @return value {*} - Property value, including undefined if not defined. | ||
@@ -177,3 +157,2 @@ */ | ||
/** | ||
@@ -562,2 +541,16 @@ * <p>Get a configuration value</p> | ||
NODE_CONFIG_PARSER = util.initParam('NODE_CONFIG_PARSER'); | ||
if (NODE_CONFIG_PARSER) { | ||
try { | ||
var parserModule = Path.isAbsolute(NODE_CONFIG_PARSER) | ||
? NODE_CONFIG_PARSER | ||
: Path.join(CONFIG_DIR, NODE_CONFIG_PARSER); | ||
Parser = require(parserModule); | ||
} | ||
catch (e) { | ||
console.warn('Failed to load config parser from ' + NODE_CONFIG_PARSER); | ||
console.log(e); | ||
} | ||
} | ||
// Determine the host name from the OS module, $HOST, or $HOSTNAME | ||
@@ -583,3 +576,3 @@ // Remove any . appendages, and default to null if not set | ||
// #236: Also add full hostname when they are different. | ||
if ( hostName ) { | ||
if (hostName) { | ||
var firstDomain = hostName.split('.')[0]; | ||
@@ -592,3 +585,3 @@ | ||
// Add full hostname when it is not the same | ||
if ( hostName != firstDomain ) { | ||
if (hostName !== firstDomain) { | ||
baseNames.push(hostName, hostName + '-' + env); | ||
@@ -603,20 +596,10 @@ } | ||
var extNames = ['js', 'ts', 'json', 'json5', 'hjson', 'toml', 'coffee', 'iced', 'yaml', 'yml', 'cson', 'properties', 'xml']; | ||
var allowedFiles = {}; | ||
var resolutionIndex = 1; | ||
var extNames = Parser.getFilesOrder(); | ||
baseNames.forEach(function(baseName) { | ||
extNames.forEach(function(extName) { | ||
// Try merging the config object into this object | ||
var fullFilename = Path.join(CONFIG_DIR , baseName + '.' + extName); | ||
var configObj = util.parseFile(fullFilename); | ||
if (configObj) { | ||
util.extendDeep(config, configObj); | ||
} | ||
// See if the application instance file is available | ||
allowedFiles[baseName + '.' + extName] = resolutionIndex++; | ||
if (APP_INSTANCE) { | ||
fullFilename = Path.join(CONFIG_DIR, baseName + '-' + APP_INSTANCE + '.' + extName); | ||
configObj = util.parseFile(fullFilename); | ||
if (configObj) { | ||
util.extendDeep(config, configObj); | ||
} | ||
allowedFiles[baseName + '-' + APP_INSTANCE + '.' + extName] = resolutionIndex++; | ||
} | ||
@@ -626,2 +609,10 @@ }); | ||
var locatedFiles = util.locateMatchingFiles(CONFIG_DIR, allowedFiles); | ||
locatedFiles.forEach(function(fullFilename) { | ||
var configObj = util.parseFile(fullFilename); | ||
if (configObj) { | ||
util.extendDeep(config, configObj); | ||
} | ||
}); | ||
// Override configurations from the $NODE_CONFIG environment variable | ||
@@ -677,7 +668,36 @@ // NODE_CONFIG only applies to the base config | ||
/** | ||
* Return a list of fullFilenames who exists in allowedFiles | ||
* Ordered according to allowedFiles argument specifications | ||
* | ||
* @protected | ||
* @method locateMatchingFiles | ||
* @param configDirs {string} the config dir, or multiple dirs separated by a column (:) | ||
* @param allowedFiles {object} an object. keys and supported filenames | ||
* and values are the position in the resolution order | ||
* @returns {string[]} fullFilenames - path + filename | ||
*/ | ||
util.locateMatchingFiles = function(configDirs, allowedFiles) { | ||
return configDirs.split(':') | ||
.reduce(function(files, configDir) { | ||
if (configDir) { | ||
try { | ||
FileSystem.readdirSync(configDir).forEach(function(file) { | ||
if (allowedFiles[file]) { | ||
files.push([allowedFiles[file], Path.join(configDir, file)]); | ||
} | ||
}); | ||
} | ||
catch(e) {} | ||
return files; | ||
} | ||
}, []) | ||
.sort(function(a, b) { return a[0] - b[0]; }) | ||
.map(function(file) { return file[1]; }); | ||
}; | ||
// Using basic recursion pattern, find all the deferred values and resolve them. | ||
util.resolveDeferredConfigs = function (config) { | ||
var completeConfig = config; | ||
var deferred = []; | ||
function _iterate (prop) { | ||
@@ -697,15 +717,18 @@ | ||
propsToSort.sort().forEach(function (property) { | ||
if (prop[property].constructor == Object) { | ||
if (prop[property].constructor === Object) { | ||
_iterate(prop[property]); | ||
} else if (prop[property].constructor == Array) { | ||
} else if (prop[property].constructor === Array) { | ||
for (var i = 0; i < prop[property].length; i++) { | ||
_iterate(prop[property][i]); | ||
if (prop[property][i] instanceof DeferredConfig) { | ||
deferred.push(prop[property][i].prepare(config, prop[property], i)); | ||
} | ||
else { | ||
_iterate(prop[property][i]); | ||
} | ||
} | ||
} else { | ||
if (prop[property] instanceof DeferredConfig ) { | ||
prop[property]= prop[property].resolve.call(completeConfig,completeConfig, prop[property]._original); | ||
if (prop[property] instanceof DeferredConfig) { | ||
deferred.push(prop[property].prepare(config, prop, property)); | ||
} | ||
else { | ||
// Nothing to do. Keep the property how it is. | ||
} | ||
// else: Nothing to do. Keep the property how it is. | ||
} | ||
@@ -715,5 +738,7 @@ }); | ||
_iterate(config); | ||
} | ||
_iterate(config); | ||
deferred.forEach(function (defer) { defer.resolve(); }); | ||
}; | ||
/** | ||
@@ -742,9 +767,6 @@ * Parse and return the specified configuration file. | ||
* @param fullFilename {string} The full file path and name | ||
* @return {configObject} The configuration object parsed from the file | ||
* @return configObject {object|null} The configuration object parsed from the file | ||
*/ | ||
util.parseFile = function(fullFilename) { | ||
// Initialize | ||
var t = this, | ||
extension = fullFilename.substr(fullFilename.lastIndexOf('.') + 1), | ||
var t = this, // Initialize | ||
configObject = null, | ||
@@ -754,21 +776,15 @@ fileContent = null, | ||
// Return null if the file doesn't exist. | ||
// Note that all methods here are the Sync versions. This is appropriate during | ||
// module loading (which is a synchronous operation), but not thereafter. | ||
try { | ||
stat = FileSystem.statSync(fullFilename); | ||
if (!stat || stat.size < 1) { | ||
return null; | ||
} | ||
} catch (e1) { | ||
return null | ||
} | ||
// Try loading the file. | ||
try { | ||
fileContent = FileSystem.readFileSync(fullFilename, 'UTF-8'); | ||
// Try loading the file. | ||
fileContent = FileSystem.readFileSync(fullFilename, 'utf-8'); | ||
fileContent = fileContent.replace(/^\uFEFF/, ''); | ||
} | ||
catch (e2) { | ||
throw new Error('Config file ' + fullFilename + ' cannot be read'); | ||
if (e2.code !== 'ENOENT') { | ||
throw new Error('Config file ' + fullFilename + ' cannot be read'); | ||
} | ||
return null; // file doesn't exists | ||
} | ||
@@ -787,70 +803,3 @@ | ||
if (extension === 'js') { | ||
// Use the built-in parser for .js files | ||
configObject = require(fullFilename); | ||
} | ||
else if (extension === 'ts') { | ||
if (!require.extensions['.ts']) { | ||
require(TS_DEP).register({ | ||
lazy: true, | ||
compilerOptions: { | ||
allowJs: true, | ||
} | ||
}); | ||
} | ||
// Imports config if it is exported via module.exports = ... | ||
// See https://github.com/lorenwest/node-config/issues/524 | ||
configObject = require(fullFilename) | ||
// Because of ES6 modules usage, `default` is treated as named export (like any other) | ||
// Therefore config is a value of `default` key. | ||
if (configObject.default) { | ||
configObject = configObject.default | ||
} | ||
} | ||
else if (extension === 'coffee') { | ||
// .coffee files can be loaded with either coffee-script or iced-coffee-script. | ||
// Prefer iced-coffee-script, if it exists. | ||
// Lazy load the appropriate extension | ||
if (!Coffee) { | ||
Coffee = {}; | ||
// The following enables iced-coffee-script on .coffee files, if iced-coffee-script is available. | ||
// This is commented as per a decision on a pull request. | ||
//try { | ||
// Coffee = require("iced-coffee-script"); | ||
//} | ||
//catch (e) { | ||
// Coffee = require("coffee-script"); | ||
//} | ||
try { | ||
// Try to load coffeescript | ||
Coffee = require(COFFEE_2_DEP); | ||
} | ||
catch (e) { | ||
// If it doesn't exist, try to load it using the deprecated module name | ||
Coffee = require(COFFEE_DEP); | ||
} | ||
// coffee-script >= 1.7.0 requires explicit registration for require() to work | ||
if (Coffee.register) { | ||
Coffee.register(); | ||
} | ||
} | ||
// Use the built-in parser for .coffee files with coffee-script | ||
configObject = require(fullFilename); | ||
} | ||
else if (extension === 'iced') { | ||
Iced = require(ICED_DEP); | ||
// coffee-script >= 1.7.0 requires explicit registration for require() to work | ||
if (Iced.register) { | ||
Iced.register(); | ||
} | ||
} | ||
else { | ||
configObject = util.parseString(fileContent, extension); | ||
} | ||
configObject = Parser.parse(fullFilename, fileContent); | ||
} | ||
@@ -903,108 +852,6 @@ catch (e3) { | ||
util.parseString = function (content, format) { | ||
// Initialize | ||
var configObject = null; | ||
// Parse the file based on extension | ||
if (format === 'yaml' || format === 'yml') { | ||
if (!Yaml && !VisionmediaYaml) { | ||
// Lazy loading | ||
try { | ||
// Try to load the better js-yaml module | ||
Yaml = require(JS_YAML_DEP); | ||
} | ||
catch (e) { | ||
try { | ||
// If it doesn't exist, load the fallback visionmedia yaml module. | ||
VisionmediaYaml = require(YAML_DEP); | ||
} | ||
catch (e) { } | ||
} | ||
} | ||
if (Yaml) { | ||
configObject = Yaml.load(content); | ||
} | ||
else if (VisionmediaYaml) { | ||
// The yaml library doesn't like strings that have newlines but don't | ||
// end in a newline: https://github.com/visionmedia/js-yaml/issues/issue/13 | ||
content += '\n'; | ||
configObject = VisionmediaYaml.eval(util.stripYamlComments(content)); | ||
} | ||
else { | ||
console.error("No YAML parser loaded. Suggest adding js-yaml dependency to your package.json file.") | ||
} | ||
var parser = Parser.getParser(format); | ||
if (typeof parser === 'function') { | ||
return parser(null, content); | ||
} | ||
else if (format === 'json') { | ||
try { | ||
configObject = JSON.parse(content); | ||
} | ||
catch (e) { | ||
// All JS Style comments will begin with /, so all JSON parse errors that | ||
// encountered a syntax error will complain about this character. | ||
if (e.name !== 'SyntaxError' || e.message.indexOf('Unexpected token /') !== 0) { | ||
throw e; | ||
} | ||
if (!JSON5) { | ||
JSON5 = require(JSON5_DEP); | ||
} | ||
configObject = JSON5.parse(content); | ||
} | ||
} | ||
else if (format === 'json5') { | ||
if (!JSON5) { | ||
JSON5 = require(JSON5_DEP); | ||
} | ||
configObject = JSON5.parse(content); | ||
} else if (format === 'hjson') { | ||
if (!HJSON) { | ||
HJSON = require(HJSON_DEP); | ||
} | ||
configObject = HJSON.parse(content); | ||
} else if (format === 'toml') { | ||
if(!TOML) { | ||
TOML = require(TOML_DEP); | ||
} | ||
configObject = TOML.parse(content); | ||
} | ||
else if (format === 'cson') { | ||
if (!CSON) { | ||
CSON = require(CSON_DEP); | ||
} | ||
// Allow comments in CSON files | ||
if (typeof CSON.parseSync === 'function') { | ||
configObject = CSON.parseSync(util.stripComments(content)); | ||
} else { | ||
configObject = CSON.parse(util.stripComments(content)); | ||
} | ||
} | ||
else if (format === 'properties') { | ||
if (!PPARSER) { | ||
PPARSER = require(PPARSER_DEP); | ||
} | ||
configObject = PPARSER.parse(content, { namespaces: true, variables: true, sections: true }); | ||
} else if (format === 'xml') { | ||
if (!XML) { | ||
XML = require(XML_DEP); | ||
} | ||
var x2js = new XML(); | ||
configObject = x2js.xml2js(content); | ||
var rootKeys = Object.keys(configObject); | ||
if(rootKeys.length == 1) { | ||
configObject = configObject[rootKeys[0]]; | ||
} | ||
} | ||
return configObject; | ||
}; | ||
@@ -1146,2 +993,4 @@ | ||
Object.defineProperty(child,i,propDescriptor); | ||
} else if (util.isPromise(parent[i])) { | ||
child[i] = parent[i]; | ||
} else { | ||
@@ -1412,3 +1261,5 @@ child[i] = _clone(parent[i], 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) | ||
@@ -1434,84 +1285,2 @@ else if (mergeFrom[prop] && typeof mergeFrom[prop] === 'object') { | ||
/** | ||
* Strip YAML comments from the string | ||
* | ||
* The 2.0 yaml parser doesn't allow comment-only or blank lines. Strip them. | ||
* | ||
* @protected | ||
* @method stripYamlComments | ||
* @param fileString {string} The string to strip comments from | ||
* @return {string} The string with comments stripped. | ||
*/ | ||
util.stripYamlComments = function(fileStr) { | ||
// First replace removes comment-only lines | ||
// Second replace removes blank lines | ||
return fileStr.replace(/^\s*#.*/mg,'').replace(/^\s*[\n|\r]+/mg,''); | ||
} | ||
/** | ||
* Strip all Javascript type comments from the string. | ||
* | ||
* The string is usually a file loaded from the O/S, containing | ||
* newlines and javascript type comments. | ||
* | ||
* Thanks to James Padolsey, and all who contributed to this implementation. | ||
* http://james.padolsey.com/javascript/javascript-comment-removal-revisted/ | ||
* | ||
* @protected | ||
* @method stripComments | ||
* @param fileString {string} The string to strip comments from | ||
* @param stringRegex {RegExp} Optional regular expression to match strings that | ||
* make up the config file | ||
* @return {string} The string with comments stripped. | ||
*/ | ||
util.stripComments = function(fileStr, stringRegex) { | ||
stringRegex = stringRegex || /(['"])(\\\1|.)+?\1/g; | ||
var uid = '_' + +new Date(), | ||
primitives = [], | ||
primIndex = 0; | ||
return ( | ||
fileStr | ||
/* Remove strings */ | ||
.replace(stringRegex, function(match){ | ||
primitives[primIndex] = match; | ||
return (uid + '') + primIndex++; | ||
}) | ||
/* Remove Regexes */ | ||
.replace(/([^\/])(\/(?!\*|\/)(\\\/|.)+?\/[gim]{0,3})/g, function(match, $1, $2){ | ||
primitives[primIndex] = $2; | ||
return $1 + (uid + '') + primIndex++; | ||
}) | ||
/* | ||
- Remove single-line comments that contain would-be multi-line delimiters | ||
E.g. // Comment /* <-- | ||
- Remove multi-line comments that contain would be single-line delimiters | ||
E.g. /* // <-- | ||
*/ | ||
.replace(/\/\/.*?\/?\*.+?(?=\n|\r|$)|\/\*[\s\S]*?\/\/[\s\S]*?\*\//g, '') | ||
/* | ||
Remove single and multi-line comments, | ||
no consideration of inner-contents | ||
*/ | ||
.replace(/\/\/.+?(?=\n|\r|$)|\/\*[\s\S]+?\*\//g, '') | ||
/* | ||
Remove multi-line comments that have a replaced ending (string/regex) | ||
Greedy, so no inner strings/regexes will stop it. | ||
*/ | ||
.replace(RegExp('\\/\\*[\\s\\S]+' + uid + '\\d+', 'g'), '') | ||
/* Bring back strings & regexes */ | ||
.replace(RegExp(uid + '(\\d+)', 'g'), function(match, n){ | ||
return primitives[n]; | ||
}) | ||
); | ||
}; | ||
/** | ||
* Is the specified argument a regular javascript object? | ||
@@ -1523,3 +1292,3 @@ * | ||
* @method isObject | ||
* @param arg {*} An argument of any type. | ||
* @param obj {*} An argument of any type. | ||
* @return {boolean} TRUE if the arg is an object, FALSE if not | ||
@@ -1532,2 +1301,14 @@ */ | ||
/** | ||
* Is the specified argument a javascript promise? | ||
* | ||
* @protected | ||
* @method isPromise | ||
* @param obj {*} An argument of any type. | ||
* @returns {boolean} | ||
*/ | ||
util.isPromise = function(obj) { | ||
return util.isObject(obj) && obj.toString() === '[object Promise]'; | ||
}; | ||
/** | ||
* <p>Initialize a parameter from the command line or process environment</p> | ||
@@ -1669,3 +1450,3 @@ * | ||
} | ||
} | ||
}; | ||
@@ -1675,2 +1456,6 @@ // Instantiate and export the configuration | ||
// copy methods to util for backwards compatibility | ||
util.stripComments = Parser.stripComments; | ||
util.stripYamlComments = Parser.stripYamlComments; | ||
// Produce warnings if the configuration is empty | ||
@@ -1677,0 +1462,0 @@ var showWarnings = !(util.initParam('SUPPRESS_NO_CONFIG_WARNING')); |
{ | ||
"name": "config", | ||
"version": "3.1.0", | ||
"version": "3.2.0", | ||
"main": "./lib/config.js", | ||
@@ -5,0 +5,0 @@ "description": "Configuration control for production node deployments", |
@@ -149,3 +149,3 @@ Configure your Node.js Applications | ||
<td><img src=https://avatars0.githubusercontent.com/u/842998?v=4><a href="https://github.com/nsabovic">nsabovic</a></td> | ||
</tr><tr><td><img src=https://avatars0.githubusercontent.com/u/5138570?v=4><a href="https://github.com/ScionOfBytes">ScionOfBytes</a></td> | ||
</tr><tr><td><img src=https://avatars0.githubusercontent.com/u/5138570?v=4><a href="https://github.com/BadgerBadgerBadgerBadger">BadgerBadgerBadgerBadger</a></td> | ||
<td><img src=https://avatars2.githubusercontent.com/u/2529835?v=4><a href="https://github.com/simon-scherzinger">simon-scherzinger</a></td> | ||
@@ -152,0 +152,0 @@ <td><img src=https://avatars1.githubusercontent.com/u/175627?v=4><a href="https://github.com/axelhzf">axelhzf</a></td> |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
87283
9
1691
23