UNPKG

48.5 kBJavaScriptView Raw
1// config.js (c) 2010-2022 Loren West and other contributors
2// May be freely distributed under the MIT license.
3// For further details and documentation:
4// http://lorenwest.github.com/node-config
5
6// Dependencies
7var DeferredConfig = require('../defer').DeferredConfig,
8 RawConfig = require('../raw').RawConfig,
9 Parser = require('../parser'),
10 Utils = require('util'),
11 Path = require('path'),
12 FileSystem = require('fs');
13
14// Static members
15var DEFAULT_CLONE_DEPTH = 20,
16 CONFIG_DIR, NODE_ENV, APP_INSTANCE,
17 CONFIG_SKIP_GITCRYPT, NODE_ENV_VAR_NAME,
18 NODE_CONFIG_PARSER,
19 env = {},
20 configSources = [], // Configuration sources - array of {name, original, parsed}
21 checkMutability = true, // Check for mutability/immutability on first get
22 gitCryptTestRegex = /^.GITCRYPT/; // regular expression to test for gitcrypt files.
23
24/**
25 * <p>Application Configurations</p>
26 *
27 * <p>
28 * The config module exports a singleton object representing all
29 * configurations for this application deployment.
30 * </p>
31 *
32 * <p>
33 * Application configurations are stored in files within the config directory
34 * of your application. The default configuration file is loaded, followed
35 * by files specific to the deployment type (development, testing, staging,
36 * production, etc.).
37 * </p>
38 *
39 * <p>
40 * For example, with the following config/default.yaml file:
41 * </p>
42 *
43 * <pre>
44 * ...
45 * customer:
46 * &nbsp;&nbsp;initialCredit: 500
47 * &nbsp;&nbsp;db:
48 * &nbsp;&nbsp;&nbsp;&nbsp;name: customer
49 * &nbsp;&nbsp;&nbsp;&nbsp;port: 5984
50 * ...
51 * </pre>
52 *
53 * <p>
54 * The following code loads the customer section into the CONFIG variable:
55 * <p>
56 *
57 * <pre>
58 * var CONFIG = require('config').customer;
59 * ...
60 * newCustomer.creditLimit = CONFIG.initialCredit;
61 * database.open(CONFIG.db.name, CONFIG.db.port);
62 * ...
63 * </pre>
64 *
65 * @module config
66 * @class Config
67 */
68
69/**
70 * <p>Get the configuration object.</p>
71 *
72 * <p>
73 * The configuration object is a shared singleton object within the application,
74 * attained by calling require('config').
75 * </p>
76 *
77 * <p>
78 * Usually you'll specify a CONFIG variable at the top of your .js file
79 * for file/module scope. If you want the root of the object, you can do this:
80 * </p>
81 * <pre>
82 * var CONFIG = require('config');
83 * </pre>
84 *
85 * <p>
86 * Sometimes you only care about a specific sub-object within the CONFIG
87 * object. In that case you could do this at the top of your file:
88 * </p>
89 * <pre>
90 * var CONFIG = require('config').customer;
91 * or
92 * var CUSTOMER_CONFIG = require('config').customer;
93 * </pre>
94 *
95 * <script type="text/javascript">
96 * document.getElementById("showProtected").style.display = "block";
97 * </script>
98 *
99 * @method constructor
100 * @return CONFIG {object} - The top level configuration object
101 */
102var Config = function() {
103 var t = this;
104
105 // Bind all utility functions to this
106 for (var fnName in util) {
107 if (typeof util[fnName] === 'function') {
108 util[fnName] = util[fnName].bind(t);
109 }
110 }
111
112 // Merge configurations into this
113 util.extendDeep(t, util.loadFileConfigs());
114 util.attachProtoDeep(t);
115
116 // Perform strictness checks and possibly throw an exception.
117 util.runStrictnessChecks(t);
118};
119
120/**
121 * Utilities are under the util namespace vs. at the top level
122 */
123var util = Config.prototype.util = {};
124
125/**
126 * Underlying get mechanism
127 *
128 * @private
129 * @method getImpl
130 * @param object {object} - Object to get the property for
131 * @param property {string|string[]} - The property name to get (as an array or '.' delimited string)
132 * @return value {*} - Property value, including undefined if not defined.
133 */
134var getImpl= function(object, property) {
135 var t = this,
136 elems = Array.isArray(property) ? property : property.split('.'),
137 name = elems[0],
138 value = object[name];
139 if (elems.length <= 1) {
140 return value;
141 }
142 // Note that typeof null === 'object'
143 if (value === null || typeof value !== 'object') {
144 return undefined;
145 }
146 return getImpl(value, elems.slice(1));
147};
148
149/**
150 * <p>Get a configuration value</p>
151 *
152 * <p>
153 * This will return the specified property value, throwing an exception if the
154 * configuration isn't defined. It is used to assure configurations are defined
155 * before being used, and to prevent typos.
156 * </p>
157 *
158 * @method get
159 * @param property {string} - The configuration property to get. Can include '.' sub-properties.
160 * @return value {*} - The property value
161 */
162Config.prototype.get = function(property) {
163 if(property === null || property === undefined){
164 throw new Error("Calling config.get with null or undefined argument");
165 }
166
167 // Make configurations immutable after first get (unless disabled)
168 if (checkMutability) {
169 if (!util.initParam('ALLOW_CONFIG_MUTATIONS', false)) {
170 util.makeImmutable(config);
171 }
172 checkMutability = false;
173 }
174 var t = this,
175 value = getImpl(t, property);
176
177 // Produce an exception if the property doesn't exist
178 if (value === undefined) {
179 throw new Error('Configuration property "' + property + '" is not defined');
180 }
181
182 // Return the value
183 return value;
184};
185
186/**
187 * Test that a configuration parameter exists
188 *
189 * <pre>
190 * var config = require('config');
191 * if (config.has('customer.dbName')) {
192 * console.log('Customer database name: ' + config.customer.dbName);
193 * }
194 * </pre>
195 *
196 * @method has
197 * @param property {string} - The configuration property to test. Can include '.' sub-properties.
198 * @return isPresent {boolean} - True if the property is defined, false if not defined.
199 */
200Config.prototype.has = function(property) {
201 // While get() throws an exception for undefined input, has() is designed to test validity, so false is appropriate
202 if(property === null || property === undefined){
203 return false;
204 }
205 var t = this;
206 return (getImpl(t, property) !== undefined);
207};
208
209/**
210 * <p>
211 * Set default configurations for a node.js module.
212 * </p>
213 *
214 * <p>
215 * This allows module developers to attach their configurations onto the
216 * default configuration object so they can be configured by the consumers
217 * of the module.
218 * </p>
219 *
220 * <p>Using the function within your module:</p>
221 * <pre>
222 * var CONFIG = require("config");
223 * CONFIG.util.setModuleDefaults("MyModule", {
224 * &nbsp;&nbsp;templateName: "t-50",
225 * &nbsp;&nbsp;colorScheme: "green"
226 * });
227 * <br>
228 * // Template name may be overridden by application config files
229 * console.log("Template: " + CONFIG.MyModule.templateName);
230 * </pre>
231 *
232 * <p>
233 * The above example results in a "MyModule" element of the configuration
234 * object, containing an object with the specified default values.
235 * </p>
236 *
237 * @method setModuleDefaults
238 * @param moduleName {string} - Name of your module.
239 * @param defaultProperties {object} - The default module configuration.
240 * @return moduleConfig {object} - The module level configuration object.
241 */
242util.setModuleDefaults = function (moduleName, defaultProperties) {
243
244 // Copy the properties into a new object
245 var t = this,
246 moduleConfig = util.cloneDeep(defaultProperties);
247
248 // Set module defaults into the first sources element
249 if (configSources.length === 0 || configSources[0].name !== 'Module Defaults') {
250 configSources.splice(0, 0, {
251 name: 'Module Defaults',
252 parsed: {}
253 });
254 }
255 util.setPath(configSources[0].parsed, moduleName.split('.'), {});
256 util.extendDeep(getImpl(configSources[0].parsed, moduleName), defaultProperties);
257
258 // Create a top level config for this module if it doesn't exist
259 util.setPath(t, moduleName.split('.'), getImpl(t, moduleName) || {});
260
261 // Extend local configurations into the module config
262 util.extendDeep(moduleConfig, getImpl(t, moduleName));
263
264 // Merge the extended configs without replacing the original
265 util.extendDeep(getImpl(t, moduleName), moduleConfig);
266
267 // reset the mutability check for "config.get" method.
268 // we are not making t[moduleName] immutable immediately,
269 // since there might be more modifications before the first config.get
270 if (!util.initParam('ALLOW_CONFIG_MUTATIONS', false)) {
271 checkMutability = true;
272 }
273
274 // Attach handlers & watchers onto the module config object
275 return util.attachProtoDeep(getImpl(t, moduleName));
276};
277
278/**
279 * <p>Make a configuration property hidden so it doesn't appear when enumerating
280 * elements of the object.</p>
281 *
282 * <p>
283 * The property still exists and can be read from and written to, but it won't
284 * show up in for ... in loops, Object.keys(), or JSON.stringify() type methods.
285 * </p>
286 *
287 * <p>
288 * If the property already exists, it will be made hidden. Otherwise it will
289 * be created as a hidden property with the specified value.
290 * </p>
291 *
292 * <p><i>
293 * This method was built for hiding configuration values, but it can be applied
294 * to <u>any</u> javascript object.
295 * </i></p>
296 *
297 * <p>Example:</p>
298 * <pre>
299 * var CONFIG = require('config');
300 * ...
301 *
302 * // Hide the Amazon S3 credentials
303 * CONFIG.util.makeHidden(CONFIG.amazonS3, 'access_id');
304 * CONFIG.util.makeHidden(CONFIG.amazonS3, 'secret_key');
305 * </pre>
306 *
307 * @method makeHidden
308 * @param object {object} - The object to make a hidden property into.
309 * @param property {string} - The name of the property to make hidden.
310 * @param value {*} - (optional) Set the property value to this (otherwise leave alone)
311 * @return object {object} - The original object is returned - for chaining.
312 */
313util.makeHidden = function(object, property, value) {
314
315 // If the new value isn't specified, just mark the property as hidden
316 if (typeof value === 'undefined') {
317 Object.defineProperty(object, property, {
318 enumerable : false
319 });
320 }
321 // Otherwise set the value and mark it as hidden
322 else {
323 Object.defineProperty(object, property, {
324 value : value,
325 enumerable : false
326 });
327 }
328
329 return object;
330}
331
332/**
333 * <p>Make a javascript object property immutable (assuring it cannot be changed
334 * from the current value).</p>
335 * <p>
336 * If the specified property is an object, all attributes of that object are
337 * made immutable, including properties of contained objects, recursively.
338 * If a property name isn't supplied, all properties of the object are made
339 * immutable.
340 * </p>
341 * <p>
342 *
343 * </p>
344 * <p>
345 * New properties can be added to the object and those properties will not be
346 * immutable unless this method is called on those new properties.
347 * </p>
348 * <p>
349 * This operation cannot be undone.
350 * </p>
351 *
352 * <p>Example:</p>
353 * <pre>
354 * var config = require('config');
355 * var myObject = {hello:'world'};
356 * config.util.makeImmutable(myObject);
357 * </pre>
358 *
359 * @method makeImmutable
360 * @param object {object} - The object to specify immutable properties for
361 * @param [property] {string | [string]} - The name of the property (or array of names) to make immutable.
362 * If not provided, all owned properties of the object are made immutable.
363 * @param [value] {* | [*]} - Property value (or array of values) to set
364 * the property to before making immutable. Only used when setting a single
365 * property. Retained for backward compatibility.
366 * @return object {object} - The original object is returned - for chaining.
367 */
368util.makeImmutable = function(object, property, value) {
369 if (Buffer.isBuffer(object)) {
370 return object;
371 }
372 var properties = null;
373
374 // Backwards compatibility mode where property/value can be specified
375 if (typeof property === 'string') {
376 return Object.defineProperty(object, property, {
377 value : (typeof value === 'undefined') ? object[property] : value,
378 writable : false,
379 configurable: false
380 });
381 }
382
383 // Get the list of properties to work with
384 if (Array.isArray(property)) {
385 properties = property;
386 }
387 else {
388 properties = Object.keys(object);
389 }
390
391 // Process each property
392 for (var i = 0; i < properties.length; i++) {
393 var propertyName = properties[i],
394 value = object[propertyName];
395
396 if (value instanceof RawConfig) {
397 Object.defineProperty(object, propertyName, {
398 value: value.resolve(),
399 writable: false,
400 configurable: false
401 });
402 } else if (Array.isArray(value)) {
403 // Ensure object items of this array are also immutable.
404 value.forEach((item, index) => { if (util.isObject(item) || Array.isArray(item)) util.makeImmutable(item) })
405
406 Object.defineProperty(object, propertyName, {
407 value: Object.freeze(value)
408 });
409 } else {
410 // Call recursively if an object.
411 if (util.isObject(value)) {
412 // Create a proxy, to capture user updates of configuration options, and throw an exception for awareness, as per:
413 // https://github.com/lorenwest/node-config/issues/514
414 value = new Proxy(util.makeImmutable(value), {
415 set (target, name) {
416 const message = (Reflect.has(target, name) ? 'update' : 'add');
417 // Notify the user.
418 throw Error(`Can not ${message} runtime configuration property: "${name}". Configuration objects are immutable unless ALLOW_CONFIG_MUTATIONS is set.`)
419 }
420 })
421 }
422
423 Object.defineProperty(object, propertyName, {
424 value: value,
425 writable : false,
426 configurable: false
427 });
428
429 // Ensure new properties can not be added, as per:
430 // https://github.com/lorenwest/node-config/issues/505
431 Object.preventExtensions(object[propertyName])
432 }
433 }
434
435 return object;
436};
437
438/**
439 * Return the sources for the configurations
440 *
441 * <p>
442 * All sources for configurations are stored in an array of objects containing
443 * the source name (usually the filename), the original source (as a string),
444 * and the parsed source as an object.
445 * </p>
446 *
447 * @method getConfigSources
448 * @return configSources {Array[Object]} - An array of objects containing
449 * name, original, and parsed elements
450 */
451util.getConfigSources = function() {
452 var t = this;
453 return configSources.slice(0);
454};
455
456/**
457 * Looks into an options object for a specific attribute
458 *
459 * <p>
460 * This method looks into the options object, and if an attribute is defined, returns it,
461 * and if not, returns the default value
462 * </p>
463 *
464 * @method getOption
465 * @param options {Object | undefined} the options object
466 * @param optionName {string} the attribute name to look for
467 * @param defaultValue { any } the default in case the options object is empty, or the attribute does not exist.
468 * @return options[optionName] if defined, defaultValue if not.
469 */
470util.getOption = function(options, optionName, defaultValue) {
471 if (options !== undefined && options[optionName] !== undefined){
472 return options[optionName];
473 } else {
474 return defaultValue;
475 }
476};
477
478
479/**
480 * Load the individual file configurations.
481 *
482 * <p>
483 * This method builds a map of filename to the configuration object defined
484 * by the file. The search order is:
485 * </p>
486 *
487 * <pre>
488 * default.EXT
489 * (deployment).EXT
490 * (hostname).EXT
491 * (hostname)-(deployment).EXT
492 * local.EXT
493 * local-(deployment).EXT
494 * runtime.json
495 * </pre>
496 *
497 * <p>
498 * EXT can be yml, yaml, coffee, iced, json, cson or js signifying the file type.
499 * yaml (and yml) is in YAML format, coffee is a coffee-script, iced is iced-coffee-script,
500 * json is in JSON format, cson is in CSON format, properties is in .properties format
501 * (http://en.wikipedia.org/wiki/.properties), and js is a javascript executable file that is
502 * require()'d with module.exports being the config object.
503 * </p>
504 *
505 * <p>
506 * hostname is the $HOST environment variable (or --HOST command line parameter)
507 * if set, otherwise the $HOSTNAME environment variable (or --HOSTNAME command
508 * line parameter) if set, otherwise the hostname found from
509 * require('os').hostname().
510 * </p>
511 *
512 * <p>
513 * Once a hostname is found, everything from the first period ('.') onwards
514 * is removed. For example, abc.example.com becomes abc
515 * </p>
516 *
517 * <p>
518 * (deployment) is the deployment type, found in the $NODE_ENV environment
519 * variable (which can be overridden by using $NODE_CONFIG_ENV
520 * environment variable). Defaults to 'development'.
521 * </p>
522 *
523 * <p>
524 * The runtime.json file contains configuration changes made at runtime either
525 * manually, or by the application setting a configuration value.
526 * </p>
527 *
528 * <p>
529 * If the $NODE_APP_INSTANCE environment variable (or --NODE_APP_INSTANCE
530 * command line parameter) is set, then files with this appendage will be loaded.
531 * See the Multiple Application Instances section of the main documentation page
532 * for more information.
533 * </p>
534 *
535 * @protected
536 * @method loadFileConfigs
537 * @param configDir { string | null } the path to the directory containing the configurations to load
538 * @param options { object | undefined } parsing options. Current supported option: skipConfigSources: true|false
539 * @return config {Object} The configuration object
540 */
541util.loadFileConfigs = function(configDir, options) {
542
543 // Initialize
544 var t = this,
545 config = {};
546
547 // Specify variables that can be used to define the environment
548 var node_env_var_names = ['NODE_CONFIG_ENV', 'NODE_ENV'];
549
550 // Loop through the variables to try and set environment
551 for (const node_env_var_name of node_env_var_names) {
552 NODE_ENV = util.initParam(node_env_var_name);
553 if (!!NODE_ENV) {
554 NODE_ENV_VAR_NAME = node_env_var_name;
555 break;
556 }
557 }
558
559 // If we haven't successfully set the environment using the variables, we'll default it
560 if (!NODE_ENV) {
561 NODE_ENV = 'development';
562 }
563
564 node_env_var_names.forEach(node_env_var_name => {
565 env[node_env_var_name] = NODE_ENV;
566 });
567
568 // Split files name, for loading multiple files.
569 NODE_ENV = NODE_ENV.split(',');
570
571 var dir = configDir || util.initParam('NODE_CONFIG_DIR', Path.join( process.cwd(), 'config') );
572 dir = _toAbsolutePath(dir);
573
574 APP_INSTANCE = util.initParam('NODE_APP_INSTANCE');
575 CONFIG_SKIP_GITCRYPT = util.initParam('CONFIG_SKIP_GITCRYPT');
576
577 // This is for backward compatibility
578 var runtimeFilename = util.initParam('NODE_CONFIG_RUNTIME_JSON', Path.join(dir , 'runtime.json') );
579
580 NODE_CONFIG_PARSER = util.initParam('NODE_CONFIG_PARSER');
581 if (NODE_CONFIG_PARSER) {
582 try {
583 var parserModule = Path.isAbsolute(NODE_CONFIG_PARSER)
584 ? NODE_CONFIG_PARSER
585 : Path.join(dir, NODE_CONFIG_PARSER);
586 Parser = require(parserModule);
587 }
588 catch (e) {
589 console.warn('Failed to load config parser from ' + NODE_CONFIG_PARSER);
590 console.log(e);
591 }
592 }
593
594 var HOST = util.initParam('HOST');
595 var HOSTNAME = util.initParam('HOSTNAME');
596
597 // Determine the host name from the OS module, $HOST, or $HOSTNAME
598 // Remove any . appendages, and default to null if not set
599 try {
600 var hostName = HOST || HOSTNAME;
601
602 if (!hostName) {
603 var OS = require('os');
604 hostName = OS.hostname();
605 }
606 } catch (e) {
607 hostName = '';
608 }
609
610 // Store the hostname that won.
611 env.HOSTNAME = hostName;
612
613 // Read each file in turn
614 var baseNames = ['default'].concat(NODE_ENV);
615
616 // #236: Also add full hostname when they are different.
617 if (hostName) {
618 var firstDomain = hostName.split('.')[0];
619
620 NODE_ENV.forEach(function(env) {
621 // Backward compatibility
622 baseNames.push(firstDomain, firstDomain + '-' + env);
623
624 // Add full hostname when it is not the same
625 if (hostName !== firstDomain) {
626 baseNames.push(hostName, hostName + '-' + env);
627 }
628 });
629 }
630
631 NODE_ENV.forEach(function(env) {
632 baseNames.push('local', 'local-' + env);
633 });
634
635 var allowedFiles = {};
636 var resolutionIndex = 1;
637 var extNames = Parser.getFilesOrder();
638 baseNames.forEach(function(baseName) {
639 extNames.forEach(function(extName) {
640 allowedFiles[baseName + '.' + extName] = resolutionIndex++;
641 if (APP_INSTANCE) {
642 allowedFiles[baseName + '-' + APP_INSTANCE + '.' + extName] = resolutionIndex++;
643 }
644 });
645 });
646
647 var locatedFiles = util.locateMatchingFiles(dir, allowedFiles);
648 locatedFiles.forEach(function(fullFilename) {
649 var configObj = util.parseFile(fullFilename, options);
650 if (configObj) {
651 util.extendDeep(config, configObj);
652 }
653 });
654
655 // Override configurations from the $NODE_CONFIG environment variable
656 // NODE_CONFIG only applies to the base config
657 if (!configDir) {
658 var envConfig = {};
659
660 CONFIG_DIR = dir;
661
662 if (process.env.NODE_CONFIG) {
663 try {
664 envConfig = JSON.parse(process.env.NODE_CONFIG);
665 } catch(e) {
666 console.error('The $NODE_CONFIG environment variable is malformed JSON');
667 }
668 util.extendDeep(config, envConfig);
669 var skipConfigSources = util.getOption(options,'skipConfigSources', false);
670 if (!skipConfigSources){
671 configSources.push({
672 name: "$NODE_CONFIG",
673 parsed: envConfig,
674 });
675 }
676 }
677
678 // Override configurations from the --NODE_CONFIG command line
679 var cmdLineConfig = util.getCmdLineArg('NODE_CONFIG');
680 if (cmdLineConfig) {
681 try {
682 cmdLineConfig = JSON.parse(cmdLineConfig);
683 } catch(e) {
684 console.error('The --NODE_CONFIG={json} command line argument is malformed JSON');
685 }
686 util.extendDeep(config, cmdLineConfig);
687 var skipConfigSources = util.getOption(options,'skipConfigSources', false);
688 if (!skipConfigSources){
689 configSources.push({
690 name: "--NODE_CONFIG argument",
691 parsed: cmdLineConfig,
692 });
693 }
694 }
695
696 // Place the mixed NODE_CONFIG into the environment
697 env['NODE_CONFIG'] = JSON.stringify(util.extendDeep(envConfig, cmdLineConfig, {}));
698 }
699
700 // Override with environment variables if there is a custom-environment-variables.EXT mapping file
701 var customEnvVars = util.getCustomEnvVars(dir, extNames);
702 util.extendDeep(config, customEnvVars);
703
704 // Extend the original config with the contents of runtime.json (backwards compatibility)
705 var runtimeJson = util.parseFile(runtimeFilename) || {};
706 util.extendDeep(config, runtimeJson);
707
708 util.resolveDeferredConfigs(config);
709
710 // Return the configuration object
711 return config;
712};
713
714/**
715 * Return a list of fullFilenames who exists in allowedFiles
716 * Ordered according to allowedFiles argument specifications
717 *
718 * @protected
719 * @method locateMatchingFiles
720 * @param configDirs {string} the config dir, or multiple dirs separated by a column (:)
721 * @param allowedFiles {object} an object. keys and supported filenames
722 * and values are the position in the resolution order
723 * @returns {string[]} fullFilenames - path + filename
724 */
725util.locateMatchingFiles = function(configDirs, allowedFiles) {
726 return configDirs.split(Path.delimiter)
727 .reduce(function(files, configDir) {
728 if (configDir) {
729 configDir = _toAbsolutePath(configDir);
730 try {
731 FileSystem.readdirSync(configDir).forEach(function(file) {
732 if (allowedFiles[file]) {
733 files.push([allowedFiles[file], Path.join(configDir, file)]);
734 }
735 });
736 }
737 catch(e) {}
738 return files;
739 }
740 }, [])
741 .sort(function(a, b) { return a[0] - b[0]; })
742 .map(function(file) { return file[1]; });
743};
744
745// Using basic recursion pattern, find all the deferred values and resolve them.
746util.resolveDeferredConfigs = function (config) {
747 var deferred = [];
748
749 function _iterate (prop) {
750
751 // We put the properties we are going to look it in an array to keep the order predictable
752 var propsToSort = [];
753
754 // First step is to put the properties of interest in an array
755 for (var property in prop) {
756 if (Object.hasOwnProperty.call(prop, property) && prop[property] != null) {
757 propsToSort.push(property);
758 }
759 }
760
761 // Second step is to iterate of the elements in a predictable (sorted) order
762 propsToSort.sort().forEach(function (property) {
763 if (prop[property].constructor === Object) {
764 _iterate(prop[property]);
765 } else if (prop[property].constructor === Array) {
766 for (var i = 0; i < prop[property].length; i++) {
767 if (prop[property][i] instanceof DeferredConfig) {
768 deferred.push(prop[property][i].prepare(config, prop[property], i));
769 }
770 else {
771 _iterate(prop[property][i]);
772 }
773 }
774 } else {
775 if (prop[property] instanceof DeferredConfig) {
776 deferred.push(prop[property].prepare(config, prop, property));
777 }
778 // else: Nothing to do. Keep the property how it is.
779 }
780 });
781 }
782
783 _iterate(config);
784
785 deferred.forEach(function (defer) { defer.resolve(); });
786};
787
788/**
789 * Parse and return the specified configuration file.
790 *
791 * If the file exists in the application config directory, it will
792 * parse and return it as a JavaScript object.
793 *
794 * The file extension determines the parser to use.
795 *
796 * .js = File to run that has a module.exports containing the config object
797 * .coffee = File to run that has a module.exports with coffee-script containing the config object
798 * .iced = File to run that has a module.exports with iced-coffee-script containing the config object
799 * All other supported file types (yaml, toml, json, cson, hjson, json5, properties, xml)
800 * are parsed with util.parseString.
801 *
802 * If the file doesn't exist, a null will be returned. If the file can't be
803 * parsed, an exception will be thrown.
804 *
805 * This method performs synchronous file operations, and should not be called
806 * after synchronous module loading.
807 *
808 * @protected
809 * @method parseFile
810 * @param fullFilename {string} The full file path and name
811 * @param options { object | undefined } parsing options. Current supported option: skipConfigSources: true|false
812 * @return configObject {object|null} The configuration object parsed from the file
813 */
814util.parseFile = function(fullFilename, options) {
815 var t = this, // Initialize
816 configObject = null,
817 fileContent = null,
818 stat = null;
819
820 // Note that all methods here are the Sync versions. This is appropriate during
821 // module loading (which is a synchronous operation), but not thereafter.
822
823 try {
824 // Try loading the file.
825 fileContent = FileSystem.readFileSync(fullFilename, 'utf-8');
826 fileContent = fileContent.replace(/^\uFEFF/, '');
827 }
828 catch (e2) {
829 if (e2.code !== 'ENOENT') {
830 throw new Error('Config file ' + fullFilename + ' cannot be read. Error code is: '+e2.code
831 +'. Error message is: '+e2.message);
832 }
833 return null; // file doesn't exists
834 }
835
836 // Parse the file based on extension
837 try {
838
839 // skip if it's a gitcrypt file and CONFIG_SKIP_GITCRYPT is true
840 if (CONFIG_SKIP_GITCRYPT) {
841 if (gitCryptTestRegex.test(fileContent)) {
842 console.error('WARNING: ' + fullFilename + ' is a git-crypt file and CONFIG_SKIP_GITCRYPT is set. skipping.');
843 return null;
844 }
845 }
846
847 configObject = Parser.parse(fullFilename, fileContent);
848 }
849 catch (e3) {
850 if (gitCryptTestRegex.test(fileContent)) {
851 console.error('ERROR: ' + fullFilename + ' is a git-crypt file and CONFIG_SKIP_GITCRYPT is not set.');
852 }
853 throw new Error("Cannot parse config file: '" + fullFilename + "': " + e3);
854 }
855
856 // Keep track of this configuration sources, including empty ones, unless the skipConfigSources flag is set to true in the options
857 var skipConfigSources = util.getOption(options,'skipConfigSources', false);
858 if (typeof configObject === 'object' && !skipConfigSources) {
859 configSources.push({
860 name: fullFilename,
861 original: fileContent,
862 parsed: configObject,
863 });
864 }
865
866 return configObject;
867};
868
869/**
870 * Parse and return the specified string with the specified format.
871 *
872 * The format determines the parser to use.
873 *
874 * json = File is parsed using JSON.parse()
875 * yaml (or yml) = Parsed with a YAML parser
876 * toml = Parsed with a TOML parser
877 * cson = Parsed with a CSON parser
878 * hjson = Parsed with a HJSON parser
879 * json5 = Parsed with a JSON5 parser
880 * properties = Parsed with the 'properties' node package
881 * xml = Parsed with a XML parser
882 *
883 * If the file doesn't exist, a null will be returned. If the file can't be
884 * parsed, an exception will be thrown.
885 *
886 * This method performs synchronous file operations, and should not be called
887 * after synchronous module loading.
888 *
889 * @protected
890 * @method parseString
891 * @param content {string} The full content
892 * @param format {string} The format to be parsed
893 * @return {configObject} The configuration object parsed from the string
894 */
895util.parseString = function (content, format) {
896 var parser = Parser.getParser(format);
897 if (typeof parser === 'function') {
898 return parser(null, content);
899 }
900};
901
902/**
903 * Attach the Config class prototype to all config objects recursively.
904 *
905 * <p>
906 * This allows you to do anything with CONFIG sub-objects as you can do with
907 * the top-level CONFIG object. It's so you can do this:
908 * </p>
909 *
910 * <pre>
911 * var CUST_CONFIG = require('config').Customer;
912 * CUST_CONFIG.get(...)
913 * </pre>
914 *
915 * @protected
916 * @method attachProtoDeep
917 * @param toObject
918 * @param depth
919 * @return toObject
920 */
921util.attachProtoDeep = function(toObject, depth) {
922 if (toObject instanceof RawConfig) {
923 return toObject;
924 }
925
926 // Recursion detection
927 var t = this;
928 depth = (depth === null ? DEFAULT_CLONE_DEPTH : depth);
929 if (depth < 0) {
930 return toObject;
931 }
932
933 // Adding Config.prototype methods directly to toObject as hidden properties
934 // because adding to toObject.__proto__ exposes the function in toObject
935 for (var fnName in Config.prototype) {
936 if (!toObject[fnName]) {
937 util.makeHidden(toObject, fnName, Config.prototype[fnName]);
938 }
939 }
940
941 // Add prototypes to sub-objects
942 for (var prop in toObject) {
943 if (util.isObject(toObject[prop])) {
944 util.attachProtoDeep(toObject[prop], depth - 1);
945 }
946 }
947
948 // Return the original object
949 return toObject;
950};
951
952/**
953 * Return a deep copy of the specified object.
954 *
955 * This returns a new object with all elements copied from the specified
956 * object. Deep copies are made of objects and arrays so you can do anything
957 * with the returned object without affecting the input object.
958 *
959 * @protected
960 * @method cloneDeep
961 * @param parent {object} The original object to copy from
962 * @param [depth=20] {Integer} Maximum depth (default 20)
963 * @return {object} A new object with the elements copied from the copyFrom object
964 *
965 * This method is copied from https://github.com/pvorb/node-clone/blob/17eea36140d61d97a9954c53417d0e04a00525d9/clone.js
966 *
967 * Copyright © 2011-2014 Paul Vorbach and contributors.
968 * Permission is hereby granted, free of charge, to any person obtaining a copy
969 * of this software and associated documentation files (the “Software”), to deal
970 * in the Software without restriction, including without limitation the rights
971 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
972 * of the Software, and to permit persons to whom the Software is furnished to do so,
973 * subject to the following conditions: The above copyright notice and this permission
974 * notice shall be included in all copies or substantial portions of the Software.
975 */
976util.cloneDeep = function cloneDeep(parent, depth, circular, prototype) {
977 // maintain two arrays for circular references, where corresponding parents
978 // and children have the same index
979 var allParents = [];
980 var allChildren = [];
981
982 var useBuffer = typeof Buffer != 'undefined';
983
984 if (typeof circular === 'undefined')
985 circular = true;
986
987 if (typeof depth === 'undefined')
988 depth = 20;
989
990 // recurse this function so we don't reset allParents and allChildren
991 function _clone(parent, depth) {
992 // cloning null always returns null
993 if (parent === null)
994 return null;
995
996 if (depth === 0)
997 return parent;
998
999 var child;
1000 if (typeof parent != 'object') {
1001 return parent;
1002 }
1003
1004 if (Utils.isArray(parent)) {
1005 child = [];
1006 } else if (Utils.isRegExp(parent)) {
1007 child = new RegExp(parent.source, util.getRegExpFlags(parent));
1008 if (parent.lastIndex) child.lastIndex = parent.lastIndex;
1009 } else if (Utils.isDate(parent)) {
1010 child = new Date(parent.getTime());
1011 } else if (useBuffer && Buffer.isBuffer(parent)) {
1012 child = Buffer.alloc(parent.length);
1013 parent.copy(child);
1014 return child;
1015 } else {
1016 if (typeof prototype === 'undefined') child = Object.create(Object.getPrototypeOf(parent));
1017 else child = Object.create(prototype);
1018 }
1019
1020 if (circular) {
1021 var index = allParents.indexOf(parent);
1022
1023 if (index != -1) {
1024 return allChildren[index];
1025 }
1026 allParents.push(parent);
1027 allChildren.push(child);
1028 }
1029
1030 for (var i in parent) {
1031 var propDescriptor = Object.getOwnPropertyDescriptor(parent,i);
1032 var hasGetter = ((propDescriptor !== undefined) && (propDescriptor.get !== undefined));
1033
1034 if (hasGetter){
1035 Object.defineProperty(child,i,propDescriptor);
1036 } else if (util.isPromise(parent[i])) {
1037 child[i] = parent[i];
1038 } else {
1039 child[i] = _clone(parent[i], depth - 1);
1040 }
1041 }
1042
1043 return child;
1044 }
1045
1046 return _clone(parent, depth);
1047};
1048
1049/**
1050 * Set objects given a path as a string list
1051 *
1052 * @protected
1053 * @method setPath
1054 * @param object {object} - Object to set the property on
1055 * @param path {array[string]} - Array path to the property
1056 * @param value {*} - value to set, ignoring null
1057 */
1058util.setPath = function (object, path, value) {
1059 var nextKey = null;
1060 if (value === null || path.length === 0) {
1061 return;
1062 }
1063 else if (path.length === 1) { // no more keys to make, so set the value
1064 object[path.shift()] = value;
1065 }
1066 else {
1067 nextKey = path.shift();
1068 if (!Object.hasOwnProperty.call(object, nextKey)) {
1069 object[nextKey] = {};
1070 }
1071 util.setPath(object[nextKey], path, value);
1072 }
1073};
1074
1075/**
1076 * Create a new object patterned after substitutionMap, where:
1077 * 1. Terminal string values in substitutionMap are used as keys
1078 * 2. To look up values in a key-value store, variables
1079 * 3. And parent keys are created as necessary to retain the structure of substitutionMap.
1080 *
1081 * @protected
1082 * @method substituteDeep
1083 * @param substitutionMap {object} - an object whose terminal (non-subobject) values are strings
1084 * @param variables {object[string:value]} - usually process.env, a flat object used to transform
1085 * terminal values in a copy of substitutionMap.
1086 * @returns {object} - deep copy of substitutionMap with only those paths whose terminal values
1087 * corresponded to a key in `variables`
1088 */
1089util.substituteDeep = function (substitutionMap, variables) {
1090 var result = {};
1091
1092 function _substituteVars(map, vars, pathTo) {
1093 for (var prop in map) {
1094 var value = map[prop];
1095 if (typeof(value) === 'string') { // We found a leaf variable name
1096 if (vars[value] !== undefined && vars[value] !== '') { // if the vars provide a value set the value in the result map
1097 util.setPath(result, pathTo.concat(prop), vars[value]);
1098 }
1099 }
1100 else if (util.isObject(value)) { // work on the subtree, giving it a clone of the pathTo
1101 if ('__name' in value && '__format' in value && vars[value.__name] !== undefined && vars[value.__name] !== '') {
1102 try {
1103 var parsedValue = util.parseString(vars[value.__name], value.__format);
1104 } catch(err) {
1105 err.message = '__format parser error in ' + value.__name + ': ' + err.message;
1106 throw err;
1107 }
1108 util.setPath(result, pathTo.concat(prop), parsedValue);
1109 } else {
1110 _substituteVars(value, vars, pathTo.concat(prop));
1111 }
1112 }
1113 else {
1114 msg = "Illegal key type for substitution map at " + pathTo.join('.') + ': ' + typeof(value);
1115 throw Error(msg);
1116 }
1117 }
1118 }
1119
1120 _substituteVars(substitutionMap, variables, []);
1121 return result;
1122
1123};
1124
1125/* Map environment variables into the configuration if a mapping file,
1126 * `custom-environment-variables.EXT` exists.
1127 *
1128 * @protected
1129 * @method getCustomEnvVars
1130 * @param configDir {string} - the passed configuration directory
1131 * @param extNames {Array[string]} - acceptable configuration file extension names.
1132 * @returns {object} - mapped environment variables or {} if there are none
1133 */
1134util.getCustomEnvVars = function (configDir, extNames) {
1135 var result = {};
1136 var resolutionIndex = 1;
1137 var allowedFiles = {};
1138 extNames.forEach(function (extName) {
1139 allowedFiles['custom-environment-variables' + '.' + extName] = resolutionIndex++;
1140 });
1141 var locatedFiles = util.locateMatchingFiles(configDir, allowedFiles);
1142 locatedFiles.forEach(function (fullFilename) {
1143 var configObj = util.parseFile(fullFilename);
1144 if (configObj) {
1145 var environmentSubstitutions = util.substituteDeep(configObj, process.env);
1146 util.extendDeep(result, environmentSubstitutions);
1147 }
1148 });
1149 return result;
1150};
1151
1152/**
1153 * Return true if two objects have equal contents.
1154 *
1155 * @protected
1156 * @method equalsDeep
1157 * @param object1 {object} The object to compare from
1158 * @param object2 {object} The object to compare with
1159 * @param depth {integer} An optional depth to prevent recursion. Default: 20.
1160 * @return {boolean} True if both objects have equivalent contents
1161 */
1162util.equalsDeep = function(object1, object2, depth) {
1163
1164 // Recursion detection
1165 var t = this;
1166 depth = (depth === null ? DEFAULT_CLONE_DEPTH : depth);
1167 if (depth < 0) {
1168 return {};
1169 }
1170
1171 // Fast comparisons
1172 if (!object1 || !object2) {
1173 return false;
1174 }
1175 if (object1 === object2) {
1176 return true;
1177 }
1178 if (typeof(object1) != 'object' || typeof(object2) != 'object') {
1179 return false;
1180 }
1181
1182 // They must have the same keys. If their length isn't the same
1183 // then they're not equal. If the keys aren't the same, the value
1184 // comparisons will fail.
1185 if (Object.keys(object1).length != Object.keys(object2).length) {
1186 return false;
1187 }
1188
1189 // Compare the values
1190 for (var prop in object1) {
1191
1192 // Call recursively if an object or array
1193 if (object1[prop] && typeof(object1[prop]) === 'object') {
1194 if (!util.equalsDeep(object1[prop], object2[prop], depth - 1)) {
1195 return false;
1196 }
1197 }
1198 else {
1199 if (object1[prop] !== object2[prop]) {
1200 return false;
1201 }
1202 }
1203 }
1204
1205 // Test passed.
1206 return true;
1207};
1208
1209/**
1210 * Returns an object containing all elements that differ between two objects.
1211 * <p>
1212 * This method was designed to be used to create the runtime.json file
1213 * contents, but can be used to get the diffs between any two Javascript objects.
1214 * </p>
1215 * <p>
1216 * It works best when object2 originated by deep copying object1, then
1217 * changes were made to object2, and you want an object that would give you
1218 * the changes made to object1 which resulted in object2.
1219 * </p>
1220 *
1221 * @protected
1222 * @method diffDeep
1223 * @param object1 {object} The base object to compare to
1224 * @param object2 {object} The object to compare with
1225 * @param depth {integer} An optional depth to prevent recursion. Default: 20.
1226 * @return {object} A differential object, which if extended onto object1 would
1227 * result in object2.
1228 */
1229util.diffDeep = function(object1, object2, depth) {
1230
1231 // Recursion detection
1232 var t = this, diff = {};
1233 depth = (depth === null ? DEFAULT_CLONE_DEPTH : depth);
1234 if (depth < 0) {
1235 return {};
1236 }
1237
1238 // Process each element from object2, adding any element that's different
1239 // from object 1.
1240 for (var parm in object2) {
1241 var value1 = object1[parm];
1242 var value2 = object2[parm];
1243 if (value1 && value2 && util.isObject(value2)) {
1244 if (!(util.equalsDeep(value1, value2))) {
1245 diff[parm] = util.diffDeep(value1, value2, depth - 1);
1246 }
1247 }
1248 else if (Array.isArray(value1) && Array.isArray(value2)) {
1249 if(!util.equalsDeep(value1, value2)) {
1250 diff[parm] = value2;
1251 }
1252 }
1253 else if (value1 !== value2){
1254 diff[parm] = value2;
1255 }
1256 }
1257
1258 // Return the diff object
1259 return diff;
1260
1261};
1262
1263/**
1264 * Extend an object, and any object it contains.
1265 *
1266 * This does not replace deep objects, but dives into them
1267 * replacing individual elements instead.
1268 *
1269 * @protected
1270 * @method extendDeep
1271 * @param mergeInto {object} The object to merge into
1272 * @param mergeFrom... {object...} - Any number of objects to merge from
1273 * @param depth {integer} An optional depth to prevent recursion. Default: 20.
1274 * @return {object} The altered mergeInto object is returned
1275 */
1276util.extendDeep = function(mergeInto) {
1277
1278 // Initialize
1279 var t = this;
1280 var vargs = Array.prototype.slice.call(arguments, 1);
1281 var depth = vargs.pop();
1282 if (typeof(depth) != 'number') {
1283 vargs.push(depth);
1284 depth = DEFAULT_CLONE_DEPTH;
1285 }
1286
1287 // Recursion detection
1288 if (depth < 0) {
1289 return mergeInto;
1290 }
1291
1292 // Cycle through each object to extend
1293 vargs.forEach(function(mergeFrom) {
1294
1295 // Cycle through each element of the object to merge from
1296 for (var prop in mergeFrom) {
1297
1298 // save original value in deferred elements
1299 var fromIsDeferredFunc = mergeFrom[prop] instanceof DeferredConfig;
1300 var isDeferredFunc = mergeInto[prop] instanceof DeferredConfig;
1301
1302 if (fromIsDeferredFunc && Object.hasOwnProperty.call(mergeInto, prop)) {
1303 mergeFrom[prop]._original = isDeferredFunc ? mergeInto[prop]._original : mergeInto[prop];
1304 }
1305 // Extend recursively if both elements are objects and target is not really a deferred function
1306 if (mergeFrom[prop] instanceof Date) {
1307 mergeInto[prop] = mergeFrom[prop];
1308 } if (mergeFrom[prop] instanceof RegExp) {
1309 mergeInto[prop] = mergeFrom[prop];
1310 } else if (util.isObject(mergeInto[prop]) && util.isObject(mergeFrom[prop]) && !isDeferredFunc) {
1311 util.extendDeep(mergeInto[prop], mergeFrom[prop], depth - 1);
1312 }
1313 else if (util.isPromise(mergeFrom[prop])) {
1314 mergeInto[prop] = mergeFrom[prop];
1315 }
1316 // Copy recursively if the mergeFrom element is an object (or array or fn)
1317 else if (mergeFrom[prop] && typeof mergeFrom[prop] === 'object') {
1318 mergeInto[prop] = util.cloneDeep(mergeFrom[prop], depth -1);
1319 }
1320
1321 // Copy property descriptor otherwise, preserving accessors
1322 else if (Object.getOwnPropertyDescriptor(Object(mergeFrom), prop)){
1323 Object.defineProperty(mergeInto, prop, Object.getOwnPropertyDescriptor(Object(mergeFrom), prop));
1324 } else {
1325 mergeInto[prop] = mergeFrom[prop];
1326 }
1327 }
1328 });
1329
1330 // Chain
1331 return mergeInto;
1332
1333};
1334
1335/**
1336 * Is the specified argument a regular javascript object?
1337 *
1338 * The argument is an object if it's a JS object, but not an array.
1339 *
1340 * @protected
1341 * @method isObject
1342 * @param obj {*} An argument of any type.
1343 * @return {boolean} TRUE if the arg is an object, FALSE if not
1344 */
1345util.isObject = function(obj) {
1346 return (obj !== null) && (typeof obj === 'object') && !(Array.isArray(obj));
1347};
1348
1349/**
1350 * Is the specified argument a javascript promise?
1351 *
1352 * @protected
1353 * @method isPromise
1354 * @param obj {*} An argument of any type.
1355 * @returns {boolean}
1356 */
1357util.isPromise = function(obj) {
1358 return Object.prototype.toString.call(obj) === '[object Promise]';
1359};
1360
1361/**
1362 * <p>Initialize a parameter from the command line or process environment</p>
1363 *
1364 * <p>
1365 * This method looks for the parameter from the command line in the format
1366 * --PARAMETER=VALUE, then from the process environment, then from the
1367 * default specified as an argument.
1368 * </p>
1369 *
1370 * @method initParam
1371 * @param paramName {String} Name of the parameter
1372 * @param [defaultValue] {Any} Default value of the parameter
1373 * @return {Any} The found value, or default value
1374 */
1375util.initParam = function (paramName, defaultValue) {
1376 var t = this;
1377
1378 // Record and return the value
1379 var value = util.getCmdLineArg(paramName) || process.env[paramName] || defaultValue;
1380 env[paramName] = value;
1381 return value;
1382}
1383
1384/**
1385 * <p>Get Command Line Arguments</p>
1386 *
1387 * <p>
1388 * This method allows you to retrieve the value of the specified command line argument.
1389 * </p>
1390 *
1391 * <p>
1392 * The argument is case sensitive, and must be of the form '--ARG_NAME=value'
1393 * </p>
1394 *
1395 * @method getCmdLineArg
1396 * @param searchFor {String} The argument name to search for
1397 * @return {*} false if the argument was not found, the argument value if found
1398 */
1399util.getCmdLineArg = function (searchFor) {
1400 var cmdLineArgs = process.argv.slice(2, process.argv.length),
1401 argName = '--' + searchFor + '=';
1402
1403 for (var argvIt = 0; argvIt < cmdLineArgs.length; argvIt++) {
1404 if (cmdLineArgs[argvIt].indexOf(argName) === 0) {
1405 return cmdLineArgs[argvIt].substr(argName.length);
1406 }
1407 }
1408
1409 return false;
1410}
1411
1412/**
1413 * <p>Get a Config Environment Variable Value</p>
1414 *
1415 * <p>
1416 * This method returns the value of the specified config environment variable,
1417 * including any defaults or overrides.
1418 * </p>
1419 *
1420 * @method getEnv
1421 * @param varName {String} The environment variable name
1422 * @return {String} The value of the environment variable
1423 */
1424util.getEnv = function (varName) {
1425 return env[varName];
1426}
1427
1428
1429
1430/**
1431 * Returns a string of flags for regular expression `re`.
1432 *
1433 * @param {RegExp} re Regular expression
1434 * @returns {string} Flags
1435 */
1436util.getRegExpFlags = function (re) {
1437 var flags = '';
1438 re.global && (flags += 'g');
1439 re.ignoreCase && (flags += 'i');
1440 re.multiline && (flags += 'm');
1441 return flags;
1442};
1443
1444/**
1445 * Returns a new deep copy of the current config object, or any part of the config if provided.
1446 *
1447 * @param {Object} config The part of the config to copy and serialize. Omit this argument to return the entire config.
1448 * @returns {Object} The cloned config or part of the config
1449 */
1450util.toObject = function(config) {
1451 return JSON.parse(JSON.stringify(config || this));
1452};
1453
1454// Run strictness checks on NODE_ENV and NODE_APP_INSTANCE and throw an error if there's a problem.
1455util.runStrictnessChecks = function (config) {
1456 var sources = config.util.getConfigSources();
1457
1458 var sourceFilenames = sources.map(function (src) {
1459 return Path.basename(src.name);
1460 });
1461
1462 NODE_ENV.forEach(function(env) {
1463 // Throw an exception if there's no explicit config file for NODE_ENV
1464 var anyFilesMatchEnv = sourceFilenames.some(function (filename) {
1465 return filename.match(env);
1466 });
1467 // development is special-cased because it's the default value
1468 if (env && (env !== 'development') && !anyFilesMatchEnv) {
1469 _warnOrThrow(NODE_ENV_VAR_NAME+" value of '"+env+"' did not match any deployment config file names.");
1470 }
1471 // Throw if NODE_ENV matches' default' or 'local'
1472 if ((env === 'default') || (env === 'local')) {
1473 _warnOrThrow(NODE_ENV_VAR_NAME+" value of '"+env+"' is ambiguous.");
1474 }
1475 });
1476
1477 // Throw an exception if there's no explicit config file for NODE_APP_INSTANCE
1478 var anyFilesMatchInstance = sourceFilenames.some(function (filename) {
1479 return filename.match(APP_INSTANCE);
1480 });
1481 if (APP_INSTANCE && !anyFilesMatchInstance) {
1482 _warnOrThrow("NODE_APP_INSTANCE value of '"+APP_INSTANCE+"' did not match any instance config file names.");
1483 }
1484
1485 function _warnOrThrow (msg) {
1486 var beStrict = process.env.NODE_CONFIG_STRICT_MODE;
1487 var prefix = beStrict ? 'FATAL: ' : 'WARNING: ';
1488 var seeURL = 'See https://github.com/node-config/node-config/wiki/Strict-Mode';
1489
1490 console.error(prefix+msg);
1491 console.error(prefix+seeURL);
1492
1493 // Accept 1 and true as truthy values. When set via process.env, Node.js casts them to strings.
1494 if (["true", "1"].indexOf(beStrict) >= 0) {
1495 throw new Error(prefix+msg+' '+seeURL);
1496 }
1497 }
1498};
1499
1500// Helper functions shared accross object members
1501function _toAbsolutePath (configDir) {
1502 if (configDir.indexOf('.') === 0) {
1503 return Path.join(process.cwd(), configDir);
1504 }
1505
1506 return configDir;
1507}
1508
1509// Instantiate and export the configuration
1510var config = module.exports = new Config();
1511
1512// copy methods to util for backwards compatibility
1513util.stripComments = Parser.stripComments;
1514util.stripYamlComments = Parser.stripYamlComments;
1515
1516// Produce warnings if the configuration is empty
1517var showWarnings = !(util.initParam('SUPPRESS_NO_CONFIG_WARNING'));
1518if (showWarnings && Object.keys(config).length === 0) {
1519 console.error('WARNING: No configurations found in configuration directory:' +CONFIG_DIR);
1520 console.error('WARNING: To disable this warning set SUPPRESS_NO_CONFIG_WARNING in the environment.');
1521}