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/LICENSE +1 -1
- package/README.md +22 -22
- package/lib/config.js +188 -682
- package/lib/util.js +1220 -0
- package/package.json +6 -8
- package/parser.js +3 -0
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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 (!
|
|
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 =
|
|
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
|
|
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
|
|
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 {
|
|
242
|
-
* @return moduleConfig {
|
|
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
|
|
213
|
+
const path = moduleName.split('.');
|
|
214
|
+
const moduleConfig = FIRST_LOAD.setModuleDefaults(moduleName, defaultProperties);
|
|
215
|
+
let existing = Util.getPath(t, path);
|
|
249
216
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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 (!
|
|
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(
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
558
|
-
* @return
|
|
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
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
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
|
-
|
|
584
|
-
|
|
585
|
-
});
|
|
513
|
+
return newLoad.config;
|
|
514
|
+
}
|
|
586
515
|
|
|
587
|
-
|
|
588
|
-
|
|
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
|
-
|
|
591
|
-
|
|
527
|
+
// Override configurations from the $NODE_CONFIG environment variable
|
|
528
|
+
let envConfig = {};
|
|
592
529
|
|
|
593
|
-
|
|
594
|
-
CONFIG_SKIP_GITCRYPT = util.initParam('CONFIG_SKIP_GITCRYPT');
|
|
530
|
+
load.setEnv("CONFIG_DIR", options.configDir);
|
|
595
531
|
|
|
596
|
-
|
|
597
|
-
if (NODE_CONFIG_PARSER) {
|
|
532
|
+
if (process.env.NODE_CONFIG) {
|
|
598
533
|
try {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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
|
-
|
|
715
|
-
env['NODE_CONFIG'] = JSON.stringify(util.extendDeep(envConfig, cmdLineConfig, {}));
|
|
551
|
+
additional.push({ name: "--NODE_CONFIG argument", config: cmdLineConfig });
|
|
716
552
|
}
|
|
717
553
|
|
|
718
|
-
//
|
|
719
|
-
|
|
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
|
-
|
|
725
|
-
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
825
|
-
let
|
|
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
|
-
|
|
846
|
-
try {
|
|
616
|
+
loadInfo.loadFile(fullFilename);
|
|
847
617
|
|
|
848
|
-
|
|
849
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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 {
|
|
972
|
-
* @param [depth=20] {
|
|
973
|
-
* @return {
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
763
|
+
* @returns {Object} - mapped environment variables or {} if there are none
|
|
1145
764
|
*/
|
|
1146
765
|
util.getCustomEnvVars = function (configDir, extNames) {
|
|
1147
|
-
|
|
1148
|
-
let
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
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 {
|
|
1170
|
-
* @param object2 {
|
|
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 {
|
|
1236
|
-
* @param object2 {
|
|
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 {
|
|
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 &&
|
|
1257
|
-
if (!(
|
|
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(!
|
|
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 {
|
|
1285
|
-
* @param mergeFrom... {object
|
|
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 {
|
|
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
|
|
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
|
|
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] {
|
|
1384
|
-
* @return {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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(
|
|
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(
|
|
994
|
+
_warnOrThrow(`${FIRST_LOAD.getEnv("nodeEnv")} value of '${env}' is ambiguous.`);
|
|
1488
995
|
}
|
|
1489
996
|
});
|
|
1490
997
|
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
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
|
-
|
|
1515
|
-
|
|
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 =
|
|
1033
|
+
util.stripYamlComments = FIRST_LOAD.parser.stripYamlComments;
|
|
1528
1034
|
|
|
1529
1035
|
// Produce warnings if the configuration is empty
|
|
1530
|
-
const showWarnings = !(
|
|
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:' +
|
|
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
|
}
|