config 4.0.0 → 4.1.0

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