config 3.0.1 → 3.2.2
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/History.md +22 -0
- package/README.md +5 -5
- package/async.js +70 -0
- package/defer.js +14 -5
- package/lib/config.js +117 -326
- package/package.json +1 -1
- package/parser.js +331 -0
package/History.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
3.2.2 / 2019-07-20
|
|
2
|
+
==================
|
|
3
|
+
|
|
4
|
+
* Fixed delimiter bug in configDirs to match O/S delimiter - @iMoses
|
|
5
|
+
|
|
6
|
+
3.2.1 / 2019-07-18
|
|
7
|
+
==================
|
|
8
|
+
|
|
9
|
+
* Fixed TypeError: obj.toString is not a function - @leosuncin
|
|
10
|
+
|
|
11
|
+
3.2.0 / 2019-07-11
|
|
12
|
+
==================
|
|
13
|
+
|
|
14
|
+
* Asynchronous configs - @iMoses
|
|
15
|
+
* Multiple config directories - @iMoses
|
|
16
|
+
* Improved parser support - @iMoses
|
|
17
|
+
|
|
18
|
+
3.1.0 / 2019-04-07
|
|
19
|
+
==================
|
|
20
|
+
|
|
21
|
+
* Support of module.exports syntax for TS config files @keenondrums
|
|
22
|
+
|
|
1
23
|
3.0.1 / 2018-12-16
|
|
2
24
|
==================
|
|
3
25
|
|
package/README.md
CHANGED
|
@@ -80,13 +80,13 @@ $ vi config/default.json
|
|
|
80
80
|
**Use configs in your code:**
|
|
81
81
|
|
|
82
82
|
```js
|
|
83
|
-
|
|
83
|
+
const config = require('config');
|
|
84
84
|
//...
|
|
85
|
-
|
|
85
|
+
const dbConfig = config.get('Customer.dbConfig');
|
|
86
86
|
db.connect(dbConfig, ...);
|
|
87
87
|
|
|
88
88
|
if (config.has('optionalFeature.detail')) {
|
|
89
|
-
|
|
89
|
+
const detail = config.get('optionalFeature.detail');
|
|
90
90
|
//...
|
|
91
91
|
}
|
|
92
92
|
```
|
|
@@ -141,13 +141,13 @@ Contributors
|
|
|
141
141
|
<td><img src=https://avatars0.githubusercontent.com/u/66902?v=4><a href="https://github.com/leachiM2k">leachiM2k</a></td>
|
|
142
142
|
<td><img src=https://avatars1.githubusercontent.com/u/791137?v=4><a href="https://github.com/josx">josx</a></td>
|
|
143
143
|
</tr><tr><td><img src=https://avatars2.githubusercontent.com/u/133277?v=4><a href="https://github.com/enyo">enyo</a></td>
|
|
144
|
+
<td><img src=https://avatars1.githubusercontent.com/u/4307697?v=4><a href="https://github.com/leosuncin">leosuncin</a></td>
|
|
144
145
|
<td><img src=https://avatars3.githubusercontent.com/u/1077378?v=4><a href="https://github.com/arthanzel">arthanzel</a></td>
|
|
145
146
|
<td><img src=https://avatars2.githubusercontent.com/u/1656140?v=4><a href="https://github.com/eheikes">eheikes</a></td>
|
|
146
|
-
<td><img src=https://avatars0.githubusercontent.com/u/355800?v=4><a href="https://github.com/diversario">diversario</a></td>
|
|
147
147
|
<td><img src=https://avatars3.githubusercontent.com/u/138707?v=4><a href="https://github.com/th507">th507</a></td>
|
|
148
148
|
<td><img src=https://avatars2.githubusercontent.com/u/506460?v=4><a href="https://github.com/Osterjour">Osterjour</a></td>
|
|
149
149
|
</tr><tr><td><img src=https://avatars0.githubusercontent.com/u/842998?v=4><a href="https://github.com/nsabovic">nsabovic</a></td>
|
|
150
|
-
<td><img src=https://avatars0.githubusercontent.com/u/5138570?v=4><a href="https://github.com/
|
|
150
|
+
<td><img src=https://avatars0.githubusercontent.com/u/5138570?v=4><a href="https://github.com/BadgerBadgerBadgerBadger">BadgerBadgerBadgerBadger</a></td>
|
|
151
151
|
<td><img src=https://avatars2.githubusercontent.com/u/2529835?v=4><a href="https://github.com/simon-scherzinger">simon-scherzinger</a></td>
|
|
152
152
|
<td><img src=https://avatars1.githubusercontent.com/u/175627?v=4><a href="https://github.com/axelhzf">axelhzf</a></td>
|
|
153
153
|
<td><img src=https://avatars3.githubusercontent.com/u/7782055?v=4><a href="https://github.com/benkroeger">benkroeger</a></td>
|
package/async.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
var asyncSymbol = Symbol('asyncSymbol');
|
|
2
|
+
var deferConfig = require('./defer').deferConfig;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param promiseOrFunc the promise will determine a property's value once resolved
|
|
6
|
+
* can also be a function to defer which resolves to a promise
|
|
7
|
+
* @returns {Promise} a marked promise to be resolve later using `resolveAsyncConfigs`
|
|
8
|
+
*/
|
|
9
|
+
function asyncConfig(promiseOrFunc) {
|
|
10
|
+
if (typeof promiseOrFunc === 'function') { // also acts as deferConfig
|
|
11
|
+
return deferConfig(function (config, original) {
|
|
12
|
+
var release;
|
|
13
|
+
function registerRelease(resolve) { release = resolve; }
|
|
14
|
+
function callFunc() { return promiseOrFunc.call(config, config, original); }
|
|
15
|
+
var promise = asyncConfig(new Promise(registerRelease).then(callFunc));
|
|
16
|
+
promise.release = release;
|
|
17
|
+
return promise;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
var promise = promiseOrFunc;
|
|
21
|
+
promise.async = asyncSymbol;
|
|
22
|
+
promise.prepare = function(config, prop, property) {
|
|
23
|
+
if (promise.release) {
|
|
24
|
+
promise.release();
|
|
25
|
+
}
|
|
26
|
+
return function() {
|
|
27
|
+
return promise.then(function(value) {
|
|
28
|
+
Object.defineProperty(prop, property, {value: value});
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
return promise;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Do not use `config.get` before executing this method, it will freeze the config object
|
|
37
|
+
* @param config the main config object, returned from require('config')
|
|
38
|
+
* @returns {Promise<config>} once all promises are resolved, return the original config object
|
|
39
|
+
*/
|
|
40
|
+
function resolveAsyncConfigs(config) {
|
|
41
|
+
var promises = [];
|
|
42
|
+
var resolvers = [];
|
|
43
|
+
(function iterate(prop) {
|
|
44
|
+
var propsToSort = [];
|
|
45
|
+
for (var property in prop) {
|
|
46
|
+
if (prop.hasOwnProperty(property) && prop[property] != null) {
|
|
47
|
+
propsToSort.push(property);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
propsToSort.sort().forEach(function(property) {
|
|
51
|
+
if (prop[property].constructor === Object) {
|
|
52
|
+
iterate(prop[property]);
|
|
53
|
+
}
|
|
54
|
+
else if (prop[property].constructor === Array) {
|
|
55
|
+
prop[property].forEach(iterate);
|
|
56
|
+
}
|
|
57
|
+
else if (prop[property] && prop[property].async === asyncSymbol) {
|
|
58
|
+
resolvers.push(prop[property].prepare(config, prop, property));
|
|
59
|
+
promises.push(prop[property]);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
})(config);
|
|
63
|
+
return Promise.all(promises).then(function() {
|
|
64
|
+
resolvers.forEach(function(resolve) { resolve(); });
|
|
65
|
+
return config;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports.asyncConfig = asyncConfig;
|
|
70
|
+
module.exports.resolveAsyncConfigs = resolveAsyncConfigs;
|
package/defer.js
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
// Create a deferredConfig prototype so that we can check for it when reviewing the configs later.
|
|
2
|
-
function DeferredConfig
|
|
3
|
-
}
|
|
4
|
-
DeferredConfig.prototype.resolve = function
|
|
2
|
+
function DeferredConfig() {}
|
|
3
|
+
DeferredConfig.prototype.prepare = function() {};
|
|
4
|
+
DeferredConfig.prototype.resolve = function() {};
|
|
5
5
|
|
|
6
6
|
// Accept a function that we'll use to resolve this value later and return a 'deferred' configuration value to resolve it later.
|
|
7
|
-
function deferConfig
|
|
7
|
+
function deferConfig(func) {
|
|
8
8
|
var obj = Object.create(DeferredConfig.prototype);
|
|
9
|
-
obj.
|
|
9
|
+
obj.prepare = function(config, prop, property) {
|
|
10
|
+
var original = prop[property]._original;
|
|
11
|
+
obj.resolve = function() {
|
|
12
|
+
var value = func.call(config, config, original);
|
|
13
|
+
Object.defineProperty(prop, property, {value: value});
|
|
14
|
+
return value;
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(prop, property, {get: function() { return obj.resolve(); }});
|
|
17
|
+
return obj;
|
|
18
|
+
};
|
|
10
19
|
return obj;
|
|
11
20
|
}
|
|
12
21
|
|
package/lib/config.js
CHANGED
|
@@ -4,19 +4,10 @@
|
|
|
4
4
|
// http://lorenwest.github.com/node-config
|
|
5
5
|
|
|
6
6
|
// Dependencies
|
|
7
|
-
var
|
|
8
|
-
VisionmediaYaml = null, // only if these file types exist.
|
|
9
|
-
Coffee = null,
|
|
10
|
-
Iced = null,
|
|
11
|
-
CSON = null,
|
|
12
|
-
PPARSER = null,
|
|
13
|
-
JSON5 = null,
|
|
14
|
-
TOML = null,
|
|
15
|
-
HJSON = null,
|
|
16
|
-
XML = null,
|
|
17
|
-
deferConfig = require('../defer').deferConfig,
|
|
7
|
+
var deferConfig = require('../defer').deferConfig,
|
|
18
8
|
DeferredConfig = require('../defer').DeferredConfig,
|
|
19
9
|
RawConfig = require('../raw').RawConfig,
|
|
10
|
+
Parser = require('../parser'),
|
|
20
11
|
Utils = require('util'),
|
|
21
12
|
Path = require('path'),
|
|
22
13
|
FileSystem = require('fs');
|
|
@@ -25,6 +16,7 @@ var Yaml = null, // External libraries are lazy-loaded
|
|
|
25
16
|
var DEFAULT_CLONE_DEPTH = 20,
|
|
26
17
|
NODE_CONFIG, CONFIG_DIR, RUNTIME_JSON_FILENAME, NODE_ENV, APP_INSTANCE,
|
|
27
18
|
HOST, HOSTNAME, ALLOW_CONFIG_MUTATIONS, CONFIG_SKIP_GITCRYPT,
|
|
19
|
+
NODE_CONFIG_PARSER,
|
|
28
20
|
env = {},
|
|
29
21
|
privateUtil = {},
|
|
30
22
|
deprecationWarnings = {},
|
|
@@ -32,20 +24,6 @@ var DEFAULT_CLONE_DEPTH = 20,
|
|
|
32
24
|
checkMutability = true, // Check for mutability/immutability on first get
|
|
33
25
|
gitCryptTestRegex = /^.GITCRYPT/; // regular expression to test for gitcrypt files.
|
|
34
26
|
|
|
35
|
-
// Define soft dependencies so transpilers don't include everything
|
|
36
|
-
var COFFEE_2_DEP = "coffeescript",
|
|
37
|
-
COFFEE_DEP = "coffee-script",
|
|
38
|
-
ICED_DEP = "iced-coffee-script",
|
|
39
|
-
JS_YAML_DEP = "js-yaml",
|
|
40
|
-
YAML_DEP = "yaml",
|
|
41
|
-
JSON5_DEP = "json5",
|
|
42
|
-
HJSON_DEP = "hjson",
|
|
43
|
-
TOML_DEP = "toml",
|
|
44
|
-
CSON_DEP = "cson",
|
|
45
|
-
PPARSER_DEP = "properties",
|
|
46
|
-
XML_DEP = "x2js",
|
|
47
|
-
TS_DEP = "ts-node";
|
|
48
|
-
|
|
49
27
|
/**
|
|
50
28
|
* <p>Application Configurations</p>
|
|
51
29
|
*
|
|
@@ -129,7 +107,9 @@ var Config = function() {
|
|
|
129
107
|
|
|
130
108
|
// Bind all utility functions to this
|
|
131
109
|
for (var fnName in util) {
|
|
132
|
-
util[fnName]
|
|
110
|
+
if (typeof util[fnName] === 'function') {
|
|
111
|
+
util[fnName] = util[fnName].bind(t);
|
|
112
|
+
}
|
|
133
113
|
}
|
|
134
114
|
|
|
135
115
|
// Merge configurations into this
|
|
@@ -151,7 +131,7 @@ var util = Config.prototype.util = {};
|
|
|
151
131
|
* @private
|
|
152
132
|
* @method getImpl
|
|
153
133
|
* @param object {object} - Object to get the property for
|
|
154
|
-
* @param property {string
|
|
134
|
+
* @param property {string|string[]} - The property name to get (as an array or '.' delimited string)
|
|
155
135
|
* @return value {*} - Property value, including undefined if not defined.
|
|
156
136
|
*/
|
|
157
137
|
var getImpl= function(object, property) {
|
|
@@ -169,7 +149,6 @@ var getImpl= function(object, property) {
|
|
|
169
149
|
return getImpl(value, elems.slice(1));
|
|
170
150
|
};
|
|
171
151
|
|
|
172
|
-
|
|
173
152
|
/**
|
|
174
153
|
* <p>Get a configuration value</p>
|
|
175
154
|
*
|
|
@@ -553,6 +532,20 @@ util.loadFileConfigs = function(configDir) {
|
|
|
553
532
|
// This is for backward compatibility
|
|
554
533
|
RUNTIME_JSON_FILENAME = util.initParam('NODE_CONFIG_RUNTIME_JSON', Path.join(CONFIG_DIR , 'runtime.json') );
|
|
555
534
|
|
|
535
|
+
NODE_CONFIG_PARSER = util.initParam('NODE_CONFIG_PARSER');
|
|
536
|
+
if (NODE_CONFIG_PARSER) {
|
|
537
|
+
try {
|
|
538
|
+
var parserModule = Path.isAbsolute(NODE_CONFIG_PARSER)
|
|
539
|
+
? NODE_CONFIG_PARSER
|
|
540
|
+
: Path.join(CONFIG_DIR, NODE_CONFIG_PARSER);
|
|
541
|
+
Parser = require(parserModule);
|
|
542
|
+
}
|
|
543
|
+
catch (e) {
|
|
544
|
+
console.warn('Failed to load config parser from ' + NODE_CONFIG_PARSER);
|
|
545
|
+
console.log(e);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
556
549
|
// Determine the host name from the OS module, $HOST, or $HOSTNAME
|
|
557
550
|
// Remove any . appendages, and default to null if not set
|
|
558
551
|
try {
|
|
@@ -573,7 +566,7 @@ util.loadFileConfigs = function(configDir) {
|
|
|
573
566
|
var baseNames = ['default'].concat(NODE_ENV);
|
|
574
567
|
|
|
575
568
|
// #236: Also add full hostname when they are different.
|
|
576
|
-
if (
|
|
569
|
+
if (hostName) {
|
|
577
570
|
var firstDomain = hostName.split('.')[0];
|
|
578
571
|
|
|
579
572
|
NODE_ENV.forEach(function(env) {
|
|
@@ -581,7 +574,7 @@ util.loadFileConfigs = function(configDir) {
|
|
|
581
574
|
baseNames.push(firstDomain, firstDomain + '-' + env);
|
|
582
575
|
|
|
583
576
|
// Add full hostname when it is not the same
|
|
584
|
-
if (
|
|
577
|
+
if (hostName !== firstDomain) {
|
|
585
578
|
baseNames.push(hostName, hostName + '-' + env);
|
|
586
579
|
}
|
|
587
580
|
});
|
|
@@ -591,28 +584,26 @@ util.loadFileConfigs = function(configDir) {
|
|
|
591
584
|
baseNames.push('local', 'local-' + env);
|
|
592
585
|
});
|
|
593
586
|
|
|
594
|
-
var
|
|
587
|
+
var allowedFiles = {};
|
|
588
|
+
var resolutionIndex = 1;
|
|
589
|
+
var extNames = Parser.getFilesOrder();
|
|
595
590
|
baseNames.forEach(function(baseName) {
|
|
596
591
|
extNames.forEach(function(extName) {
|
|
597
|
-
|
|
598
|
-
// Try merging the config object into this object
|
|
599
|
-
var fullFilename = Path.join(CONFIG_DIR , baseName + '.' + extName);
|
|
600
|
-
var configObj = util.parseFile(fullFilename);
|
|
601
|
-
if (configObj) {
|
|
602
|
-
util.extendDeep(config, configObj);
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// See if the application instance file is available
|
|
592
|
+
allowedFiles[baseName + '.' + extName] = resolutionIndex++;
|
|
606
593
|
if (APP_INSTANCE) {
|
|
607
|
-
|
|
608
|
-
configObj = util.parseFile(fullFilename);
|
|
609
|
-
if (configObj) {
|
|
610
|
-
util.extendDeep(config, configObj);
|
|
611
|
-
}
|
|
594
|
+
allowedFiles[baseName + '-' + APP_INSTANCE + '.' + extName] = resolutionIndex++;
|
|
612
595
|
}
|
|
613
596
|
});
|
|
614
597
|
});
|
|
615
598
|
|
|
599
|
+
var locatedFiles = util.locateMatchingFiles(CONFIG_DIR, allowedFiles);
|
|
600
|
+
locatedFiles.forEach(function(fullFilename) {
|
|
601
|
+
var configObj = util.parseFile(fullFilename);
|
|
602
|
+
if (configObj) {
|
|
603
|
+
util.extendDeep(config, configObj);
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
|
|
616
607
|
// Override configurations from the $NODE_CONFIG environment variable
|
|
617
608
|
// NODE_CONFIG only applies to the base config
|
|
618
609
|
if (!configDir) {
|
|
@@ -663,10 +654,39 @@ util.loadFileConfigs = function(configDir) {
|
|
|
663
654
|
return config;
|
|
664
655
|
};
|
|
665
656
|
|
|
657
|
+
/**
|
|
658
|
+
* Return a list of fullFilenames who exists in allowedFiles
|
|
659
|
+
* Ordered according to allowedFiles argument specifications
|
|
660
|
+
*
|
|
661
|
+
* @protected
|
|
662
|
+
* @method locateMatchingFiles
|
|
663
|
+
* @param configDirs {string} the config dir, or multiple dirs separated by a column (:)
|
|
664
|
+
* @param allowedFiles {object} an object. keys and supported filenames
|
|
665
|
+
* and values are the position in the resolution order
|
|
666
|
+
* @returns {string[]} fullFilenames - path + filename
|
|
667
|
+
*/
|
|
668
|
+
util.locateMatchingFiles = function(configDirs, allowedFiles) {
|
|
669
|
+
return configDirs.split(Path.delimiter)
|
|
670
|
+
.reduce(function(files, configDir) {
|
|
671
|
+
if (configDir) {
|
|
672
|
+
try {
|
|
673
|
+
FileSystem.readdirSync(configDir).forEach(function(file) {
|
|
674
|
+
if (allowedFiles[file]) {
|
|
675
|
+
files.push([allowedFiles[file], Path.join(configDir, file)]);
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
catch(e) {}
|
|
680
|
+
return files;
|
|
681
|
+
}
|
|
682
|
+
}, [])
|
|
683
|
+
.sort(function(a, b) { return a[0] - b[0]; })
|
|
684
|
+
.map(function(file) { return file[1]; });
|
|
685
|
+
};
|
|
686
|
+
|
|
666
687
|
// Using basic recursion pattern, find all the deferred values and resolve them.
|
|
667
688
|
util.resolveDeferredConfigs = function (config) {
|
|
668
|
-
var
|
|
669
|
-
|
|
689
|
+
var deferred = [];
|
|
670
690
|
|
|
671
691
|
function _iterate (prop) {
|
|
672
692
|
|
|
@@ -682,25 +702,30 @@ util.resolveDeferredConfigs = function (config) {
|
|
|
682
702
|
|
|
683
703
|
// Second step is to iterate of the elements in a predictable (sorted) order
|
|
684
704
|
propsToSort.sort().forEach(function (property) {
|
|
685
|
-
if (prop[property].constructor
|
|
705
|
+
if (prop[property].constructor === Object) {
|
|
686
706
|
_iterate(prop[property]);
|
|
687
|
-
} else if (prop[property].constructor
|
|
707
|
+
} else if (prop[property].constructor === Array) {
|
|
688
708
|
for (var i = 0; i < prop[property].length; i++) {
|
|
689
|
-
|
|
709
|
+
if (prop[property][i] instanceof DeferredConfig) {
|
|
710
|
+
deferred.push(prop[property][i].prepare(config, prop[property], i));
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
_iterate(prop[property][i]);
|
|
714
|
+
}
|
|
690
715
|
}
|
|
691
716
|
} else {
|
|
692
|
-
if (prop[property] instanceof DeferredConfig
|
|
693
|
-
prop[property]
|
|
694
|
-
}
|
|
695
|
-
else {
|
|
696
|
-
// Nothing to do. Keep the property how it is.
|
|
717
|
+
if (prop[property] instanceof DeferredConfig) {
|
|
718
|
+
deferred.push(prop[property].prepare(config, prop, property));
|
|
697
719
|
}
|
|
720
|
+
// else: Nothing to do. Keep the property how it is.
|
|
698
721
|
}
|
|
699
722
|
});
|
|
700
723
|
}
|
|
701
724
|
|
|
702
|
-
|
|
703
|
-
|
|
725
|
+
_iterate(config);
|
|
726
|
+
|
|
727
|
+
deferred.forEach(function (defer) { defer.resolve(); });
|
|
728
|
+
};
|
|
704
729
|
|
|
705
730
|
/**
|
|
706
731
|
* Parse and return the specified configuration file.
|
|
@@ -725,36 +750,27 @@ util.resolveDeferredConfigs = function (config) {
|
|
|
725
750
|
* @protected
|
|
726
751
|
* @method parseFile
|
|
727
752
|
* @param fullFilename {string} The full file path and name
|
|
728
|
-
* @return {
|
|
753
|
+
* @return configObject {object|null} The configuration object parsed from the file
|
|
729
754
|
*/
|
|
730
755
|
util.parseFile = function(fullFilename) {
|
|
731
|
-
|
|
732
|
-
// Initialize
|
|
733
|
-
var t = this,
|
|
734
|
-
extension = fullFilename.substr(fullFilename.lastIndexOf('.') + 1),
|
|
756
|
+
var t = this, // Initialize
|
|
735
757
|
configObject = null,
|
|
736
758
|
fileContent = null,
|
|
737
759
|
stat = null;
|
|
738
760
|
|
|
739
|
-
// Return null if the file doesn't exist.
|
|
740
761
|
// Note that all methods here are the Sync versions. This is appropriate during
|
|
741
762
|
// module loading (which is a synchronous operation), but not thereafter.
|
|
742
|
-
try {
|
|
743
|
-
stat = FileSystem.statSync(fullFilename);
|
|
744
|
-
if (!stat || stat.size < 1) {
|
|
745
|
-
return null;
|
|
746
|
-
}
|
|
747
|
-
} catch (e1) {
|
|
748
|
-
return null
|
|
749
|
-
}
|
|
750
763
|
|
|
751
|
-
// Try loading the file.
|
|
752
764
|
try {
|
|
753
|
-
|
|
765
|
+
// Try loading the file.
|
|
766
|
+
fileContent = FileSystem.readFileSync(fullFilename, 'utf-8');
|
|
754
767
|
fileContent = fileContent.replace(/^\uFEFF/, '');
|
|
755
768
|
}
|
|
756
769
|
catch (e2) {
|
|
757
|
-
|
|
770
|
+
if (e2.code !== 'ENOENT') {
|
|
771
|
+
throw new Error('Config file ' + fullFilename + ' cannot be read');
|
|
772
|
+
}
|
|
773
|
+
return null; // file doesn't exists
|
|
758
774
|
}
|
|
759
775
|
|
|
760
776
|
// Parse the file based on extension
|
|
@@ -768,68 +784,7 @@ util.parseFile = function(fullFilename) {
|
|
|
768
784
|
}
|
|
769
785
|
}
|
|
770
786
|
|
|
771
|
-
|
|
772
|
-
// Use the built-in parser for .js files
|
|
773
|
-
configObject = require(fullFilename);
|
|
774
|
-
}
|
|
775
|
-
else if (extension === 'ts') {
|
|
776
|
-
if (!require.extensions['.ts']) {
|
|
777
|
-
require(TS_DEP).register({
|
|
778
|
-
lazy: true,
|
|
779
|
-
compilerOptions: {
|
|
780
|
-
allowJs: true,
|
|
781
|
-
}
|
|
782
|
-
});
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
// Because of ES6 modules usage, `default` is treated as named export (like any other)
|
|
786
|
-
// Therefore config is a value of `default` key.
|
|
787
|
-
configObject = require(fullFilename).default;
|
|
788
|
-
}
|
|
789
|
-
else if (extension === 'coffee') {
|
|
790
|
-
// .coffee files can be loaded with either coffee-script or iced-coffee-script.
|
|
791
|
-
// Prefer iced-coffee-script, if it exists.
|
|
792
|
-
// Lazy load the appropriate extension
|
|
793
|
-
if (!Coffee) {
|
|
794
|
-
Coffee = {};
|
|
795
|
-
|
|
796
|
-
// The following enables iced-coffee-script on .coffee files, if iced-coffee-script is available.
|
|
797
|
-
// This is commented as per a decision on a pull request.
|
|
798
|
-
//try {
|
|
799
|
-
// Coffee = require("iced-coffee-script");
|
|
800
|
-
//}
|
|
801
|
-
//catch (e) {
|
|
802
|
-
// Coffee = require("coffee-script");
|
|
803
|
-
//}
|
|
804
|
-
|
|
805
|
-
try {
|
|
806
|
-
// Try to load coffeescript
|
|
807
|
-
Coffee = require(COFFEE_2_DEP);
|
|
808
|
-
}
|
|
809
|
-
catch (e) {
|
|
810
|
-
// If it doesn't exist, try to load it using the deprecated module name
|
|
811
|
-
Coffee = require(COFFEE_DEP);
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
// coffee-script >= 1.7.0 requires explicit registration for require() to work
|
|
815
|
-
if (Coffee.register) {
|
|
816
|
-
Coffee.register();
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
// Use the built-in parser for .coffee files with coffee-script
|
|
820
|
-
configObject = require(fullFilename);
|
|
821
|
-
}
|
|
822
|
-
else if (extension === 'iced') {
|
|
823
|
-
Iced = require(ICED_DEP);
|
|
824
|
-
|
|
825
|
-
// coffee-script >= 1.7.0 requires explicit registration for require() to work
|
|
826
|
-
if (Iced.register) {
|
|
827
|
-
Iced.register();
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
else {
|
|
831
|
-
configObject = util.parseString(fileContent, extension);
|
|
832
|
-
}
|
|
787
|
+
configObject = Parser.parse(fullFilename, fileContent);
|
|
833
788
|
}
|
|
834
789
|
catch (e3) {
|
|
835
790
|
if (gitCryptTestRegex.test(fileContent)) {
|
|
@@ -877,112 +832,10 @@ util.parseFile = function(fullFilename) {
|
|
|
877
832
|
* @return {configObject} The configuration object parsed from the string
|
|
878
833
|
*/
|
|
879
834
|
util.parseString = function (content, format) {
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
// Parse the file based on extension
|
|
884
|
-
if (format === 'yaml' || format === 'yml') {
|
|
885
|
-
if (!Yaml && !VisionmediaYaml) {
|
|
886
|
-
// Lazy loading
|
|
887
|
-
try {
|
|
888
|
-
// Try to load the better js-yaml module
|
|
889
|
-
Yaml = require(JS_YAML_DEP);
|
|
890
|
-
}
|
|
891
|
-
catch (e) {
|
|
892
|
-
try {
|
|
893
|
-
// If it doesn't exist, load the fallback visionmedia yaml module.
|
|
894
|
-
VisionmediaYaml = require(YAML_DEP);
|
|
895
|
-
}
|
|
896
|
-
catch (e) { }
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
if (Yaml) {
|
|
901
|
-
configObject = Yaml.load(content);
|
|
902
|
-
}
|
|
903
|
-
else if (VisionmediaYaml) {
|
|
904
|
-
// The yaml library doesn't like strings that have newlines but don't
|
|
905
|
-
// end in a newline: https://github.com/visionmedia/js-yaml/issues/issue/13
|
|
906
|
-
content += '\n';
|
|
907
|
-
configObject = VisionmediaYaml.eval(util.stripYamlComments(content));
|
|
908
|
-
}
|
|
909
|
-
else {
|
|
910
|
-
console.error("No YAML parser loaded. Suggest adding js-yaml dependency to your package.json file.")
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
else if (format === 'json') {
|
|
914
|
-
try {
|
|
915
|
-
configObject = JSON.parse(content);
|
|
916
|
-
}
|
|
917
|
-
catch (e) {
|
|
918
|
-
// All JS Style comments will begin with /, so all JSON parse errors that
|
|
919
|
-
// encountered a syntax error will complain about this character.
|
|
920
|
-
if (e.name !== 'SyntaxError' || e.message.indexOf('Unexpected token /') !== 0) {
|
|
921
|
-
throw e;
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
if (!JSON5) {
|
|
925
|
-
JSON5 = require(JSON5_DEP);
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
configObject = JSON5.parse(content);
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
else if (format === 'json5') {
|
|
932
|
-
|
|
933
|
-
if (!JSON5) {
|
|
934
|
-
JSON5 = require(JSON5_DEP);
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
configObject = JSON5.parse(content);
|
|
938
|
-
|
|
939
|
-
} else if (format === 'hjson') {
|
|
940
|
-
|
|
941
|
-
if (!HJSON) {
|
|
942
|
-
HJSON = require(HJSON_DEP);
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
configObject = HJSON.parse(content);
|
|
946
|
-
|
|
947
|
-
} else if (format === 'toml') {
|
|
948
|
-
|
|
949
|
-
if(!TOML) {
|
|
950
|
-
TOML = require(TOML_DEP);
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
configObject = TOML.parse(content);
|
|
954
|
-
}
|
|
955
|
-
else if (format === 'cson') {
|
|
956
|
-
if (!CSON) {
|
|
957
|
-
CSON = require(CSON_DEP);
|
|
958
|
-
}
|
|
959
|
-
// Allow comments in CSON files
|
|
960
|
-
if (typeof CSON.parseSync === 'function') {
|
|
961
|
-
configObject = CSON.parseSync(util.stripComments(content));
|
|
962
|
-
} else {
|
|
963
|
-
configObject = CSON.parse(util.stripComments(content));
|
|
964
|
-
}
|
|
835
|
+
var parser = Parser.getParser(format);
|
|
836
|
+
if (typeof parser === 'function') {
|
|
837
|
+
return parser(null, content);
|
|
965
838
|
}
|
|
966
|
-
else if (format === 'properties') {
|
|
967
|
-
if (!PPARSER) {
|
|
968
|
-
PPARSER = require(PPARSER_DEP);
|
|
969
|
-
}
|
|
970
|
-
configObject = PPARSER.parse(content, { namespaces: true, variables: true, sections: true });
|
|
971
|
-
} else if (format === 'xml') {
|
|
972
|
-
|
|
973
|
-
if (!XML) {
|
|
974
|
-
XML = require(XML_DEP);
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
var x2js = new XML();
|
|
978
|
-
configObject = x2js.xml2js(content);
|
|
979
|
-
var rootKeys = Object.keys(configObject);
|
|
980
|
-
if(rootKeys.length == 1) {
|
|
981
|
-
configObject = configObject[rootKeys[0]];
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
return configObject;
|
|
986
839
|
};
|
|
987
840
|
|
|
988
841
|
/**
|
|
@@ -1119,6 +972,8 @@ util.cloneDeep = function cloneDeep(parent, depth, circular, prototype) {
|
|
|
1119
972
|
|
|
1120
973
|
if (hasGetter){
|
|
1121
974
|
Object.defineProperty(child,i,propDescriptor);
|
|
975
|
+
} else if (util.isPromise(parent[i])) {
|
|
976
|
+
child[i] = parent[i];
|
|
1122
977
|
} else {
|
|
1123
978
|
child[i] = _clone(parent[i], depth - 1);
|
|
1124
979
|
}
|
|
@@ -1384,7 +1239,9 @@ util.extendDeep = function(mergeInto) {
|
|
|
1384
1239
|
} else if (util.isObject(mergeInto[prop]) && util.isObject(mergeFrom[prop]) && !isDeferredFunc) {
|
|
1385
1240
|
util.extendDeep(mergeInto[prop], mergeFrom[prop], depth - 1);
|
|
1386
1241
|
}
|
|
1387
|
-
|
|
1242
|
+
else if (util.isPromise(mergeFrom[prop])) {
|
|
1243
|
+
mergeInto[prop] = mergeFrom[prop];
|
|
1244
|
+
}
|
|
1388
1245
|
// Copy recursively if the mergeFrom element is an object (or array or fn)
|
|
1389
1246
|
else if (mergeFrom[prop] && typeof mergeFrom[prop] === 'object') {
|
|
1390
1247
|
mergeInto[prop] = util.cloneDeep(mergeFrom[prop], depth -1);
|
|
@@ -1404,88 +1261,6 @@ util.extendDeep = function(mergeInto) {
|
|
|
1404
1261
|
|
|
1405
1262
|
};
|
|
1406
1263
|
|
|
1407
|
-
/**
|
|
1408
|
-
* Strip YAML comments from the string
|
|
1409
|
-
*
|
|
1410
|
-
* The 2.0 yaml parser doesn't allow comment-only or blank lines. Strip them.
|
|
1411
|
-
*
|
|
1412
|
-
* @protected
|
|
1413
|
-
* @method stripYamlComments
|
|
1414
|
-
* @param fileString {string} The string to strip comments from
|
|
1415
|
-
* @return {string} The string with comments stripped.
|
|
1416
|
-
*/
|
|
1417
|
-
util.stripYamlComments = function(fileStr) {
|
|
1418
|
-
// First replace removes comment-only lines
|
|
1419
|
-
// Second replace removes blank lines
|
|
1420
|
-
return fileStr.replace(/^\s*#.*/mg,'').replace(/^\s*[\n|\r]+/mg,'');
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
/**
|
|
1424
|
-
* Strip all Javascript type comments from the string.
|
|
1425
|
-
*
|
|
1426
|
-
* The string is usually a file loaded from the O/S, containing
|
|
1427
|
-
* newlines and javascript type comments.
|
|
1428
|
-
*
|
|
1429
|
-
* Thanks to James Padolsey, and all who contributed to this implementation.
|
|
1430
|
-
* http://james.padolsey.com/javascript/javascript-comment-removal-revisted/
|
|
1431
|
-
*
|
|
1432
|
-
* @protected
|
|
1433
|
-
* @method stripComments
|
|
1434
|
-
* @param fileString {string} The string to strip comments from
|
|
1435
|
-
* @param stringRegex {RegExp} Optional regular expression to match strings that
|
|
1436
|
-
* make up the config file
|
|
1437
|
-
* @return {string} The string with comments stripped.
|
|
1438
|
-
*/
|
|
1439
|
-
util.stripComments = function(fileStr, stringRegex) {
|
|
1440
|
-
stringRegex = stringRegex || /(['"])(\\\1|.)+?\1/g;
|
|
1441
|
-
|
|
1442
|
-
var uid = '_' + +new Date(),
|
|
1443
|
-
primitives = [],
|
|
1444
|
-
primIndex = 0;
|
|
1445
|
-
|
|
1446
|
-
return (
|
|
1447
|
-
fileStr
|
|
1448
|
-
|
|
1449
|
-
/* Remove strings */
|
|
1450
|
-
.replace(stringRegex, function(match){
|
|
1451
|
-
primitives[primIndex] = match;
|
|
1452
|
-
return (uid + '') + primIndex++;
|
|
1453
|
-
})
|
|
1454
|
-
|
|
1455
|
-
/* Remove Regexes */
|
|
1456
|
-
.replace(/([^\/])(\/(?!\*|\/)(\\\/|.)+?\/[gim]{0,3})/g, function(match, $1, $2){
|
|
1457
|
-
primitives[primIndex] = $2;
|
|
1458
|
-
return $1 + (uid + '') + primIndex++;
|
|
1459
|
-
})
|
|
1460
|
-
|
|
1461
|
-
/*
|
|
1462
|
-
- Remove single-line comments that contain would-be multi-line delimiters
|
|
1463
|
-
E.g. // Comment /* <--
|
|
1464
|
-
- Remove multi-line comments that contain would be single-line delimiters
|
|
1465
|
-
E.g. /* // <--
|
|
1466
|
-
*/
|
|
1467
|
-
.replace(/\/\/.*?\/?\*.+?(?=\n|\r|$)|\/\*[\s\S]*?\/\/[\s\S]*?\*\//g, '')
|
|
1468
|
-
|
|
1469
|
-
/*
|
|
1470
|
-
Remove single and multi-line comments,
|
|
1471
|
-
no consideration of inner-contents
|
|
1472
|
-
*/
|
|
1473
|
-
.replace(/\/\/.+?(?=\n|\r|$)|\/\*[\s\S]+?\*\//g, '')
|
|
1474
|
-
|
|
1475
|
-
/*
|
|
1476
|
-
Remove multi-line comments that have a replaced ending (string/regex)
|
|
1477
|
-
Greedy, so no inner strings/regexes will stop it.
|
|
1478
|
-
*/
|
|
1479
|
-
.replace(RegExp('\\/\\*[\\s\\S]+' + uid + '\\d+', 'g'), '')
|
|
1480
|
-
|
|
1481
|
-
/* Bring back strings & regexes */
|
|
1482
|
-
.replace(RegExp(uid + '(\\d+)', 'g'), function(match, n){
|
|
1483
|
-
return primitives[n];
|
|
1484
|
-
})
|
|
1485
|
-
);
|
|
1486
|
-
|
|
1487
|
-
};
|
|
1488
|
-
|
|
1489
1264
|
/**
|
|
1490
1265
|
* Is the specified argument a regular javascript object?
|
|
1491
1266
|
*
|
|
@@ -1493,13 +1268,25 @@ util.stripComments = function(fileStr, stringRegex) {
|
|
|
1493
1268
|
*
|
|
1494
1269
|
* @protected
|
|
1495
1270
|
* @method isObject
|
|
1496
|
-
* @param
|
|
1271
|
+
* @param obj {*} An argument of any type.
|
|
1497
1272
|
* @return {boolean} TRUE if the arg is an object, FALSE if not
|
|
1498
1273
|
*/
|
|
1499
1274
|
util.isObject = function(obj) {
|
|
1500
1275
|
return (obj !== null) && (typeof obj === 'object') && !(Array.isArray(obj));
|
|
1501
1276
|
};
|
|
1502
1277
|
|
|
1278
|
+
/**
|
|
1279
|
+
* Is the specified argument a javascript promise?
|
|
1280
|
+
*
|
|
1281
|
+
* @protected
|
|
1282
|
+
* @method isPromise
|
|
1283
|
+
* @param obj {*} An argument of any type.
|
|
1284
|
+
* @returns {boolean}
|
|
1285
|
+
*/
|
|
1286
|
+
util.isPromise = function(obj) {
|
|
1287
|
+
return Object.prototype.toString.call(obj) === '[object Promise]';
|
|
1288
|
+
};
|
|
1289
|
+
|
|
1503
1290
|
/**
|
|
1504
1291
|
* <p>Initialize a parameter from the command line or process environment</p>
|
|
1505
1292
|
*
|
|
@@ -1637,11 +1424,15 @@ util.runStrictnessChecks = function (config) {
|
|
|
1637
1424
|
throw new Error(prefix+msg+' '+seeURL);
|
|
1638
1425
|
}
|
|
1639
1426
|
}
|
|
1640
|
-
}
|
|
1427
|
+
};
|
|
1641
1428
|
|
|
1642
1429
|
// Instantiate and export the configuration
|
|
1643
1430
|
var config = module.exports = new Config();
|
|
1644
1431
|
|
|
1432
|
+
// copy methods to util for backwards compatibility
|
|
1433
|
+
util.stripComments = Parser.stripComments;
|
|
1434
|
+
util.stripYamlComments = Parser.stripYamlComments;
|
|
1435
|
+
|
|
1645
1436
|
// Produce warnings if the configuration is empty
|
|
1646
1437
|
var showWarnings = !(util.initParam('SUPPRESS_NO_CONFIG_WARNING'));
|
|
1647
1438
|
if (showWarnings && Object.keys(config).length === 0) {
|
package/package.json
CHANGED
package/parser.js
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
// External libraries are lazy-loaded only if these file types exist.
|
|
2
|
+
var Yaml = null,
|
|
3
|
+
VisionmediaYaml = null,
|
|
4
|
+
Coffee = null,
|
|
5
|
+
Iced = null,
|
|
6
|
+
CSON = null,
|
|
7
|
+
PPARSER = null,
|
|
8
|
+
JSON5 = null,
|
|
9
|
+
TOML = null,
|
|
10
|
+
HJSON = null,
|
|
11
|
+
XML = null;
|
|
12
|
+
|
|
13
|
+
// Define soft dependencies so transpilers don't include everything
|
|
14
|
+
var COFFEE_2_DEP = 'coffeescript',
|
|
15
|
+
COFFEE_DEP = 'coffee-script',
|
|
16
|
+
ICED_DEP = 'iced-coffee-script',
|
|
17
|
+
JS_YAML_DEP = 'js-yaml',
|
|
18
|
+
YAML_DEP = 'yaml',
|
|
19
|
+
JSON5_DEP = 'json5',
|
|
20
|
+
HJSON_DEP = 'hjson',
|
|
21
|
+
TOML_DEP = 'toml',
|
|
22
|
+
CSON_DEP = 'cson',
|
|
23
|
+
PPARSER_DEP = 'properties',
|
|
24
|
+
XML_DEP = 'x2js',
|
|
25
|
+
TS_DEP = 'ts-node';
|
|
26
|
+
|
|
27
|
+
var Parser = module.exports;
|
|
28
|
+
|
|
29
|
+
Parser.parse = function(filename, content) {
|
|
30
|
+
var parserName = filename.substr(filename.lastIndexOf('.') +1); // file extension
|
|
31
|
+
if (typeof definitions[parserName] === 'function') {
|
|
32
|
+
return definitions[parserName](filename, content);
|
|
33
|
+
}
|
|
34
|
+
// TODO: decide what to do in case of a missing parser
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
Parser.xmlParser = function(filename, content) {
|
|
38
|
+
if (!XML) {
|
|
39
|
+
XML = require(XML_DEP);
|
|
40
|
+
}
|
|
41
|
+
var x2js = new XML();
|
|
42
|
+
var configObject = x2js.xml2js(content);
|
|
43
|
+
var rootKeys = Object.keys(configObject);
|
|
44
|
+
if(rootKeys.length === 1) {
|
|
45
|
+
return configObject[rootKeys[0]];
|
|
46
|
+
}
|
|
47
|
+
return configObject;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
Parser.jsParser = function(filename, content) {
|
|
51
|
+
return require(filename);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
Parser.tsParser = function(filename, content) {
|
|
55
|
+
if (!require.extensions['.ts']) {
|
|
56
|
+
require(TS_DEP).register({
|
|
57
|
+
lazy: true,
|
|
58
|
+
compilerOptions: {
|
|
59
|
+
allowJs: true,
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Imports config if it is exported via module.exports = ...
|
|
65
|
+
// See https://github.com/lorenwest/node-config/issues/524
|
|
66
|
+
var configObject = require(filename);
|
|
67
|
+
|
|
68
|
+
// Because of ES6 modules usage, `default` is treated as named export (like any other)
|
|
69
|
+
// Therefore config is a value of `default` key.
|
|
70
|
+
if (configObject.default) {
|
|
71
|
+
return configObject.default
|
|
72
|
+
}
|
|
73
|
+
return configObject;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
Parser.coffeeParser = function(filename, content) {
|
|
77
|
+
// .coffee files can be loaded with either coffee-script or iced-coffee-script.
|
|
78
|
+
// Prefer iced-coffee-script, if it exists.
|
|
79
|
+
// Lazy load the appropriate extension
|
|
80
|
+
if (!Coffee) {
|
|
81
|
+
Coffee = {};
|
|
82
|
+
|
|
83
|
+
// The following enables iced-coffee-script on .coffee files, if iced-coffee-script is available.
|
|
84
|
+
// This is commented as per a decision on a pull request.
|
|
85
|
+
//try {
|
|
86
|
+
// Coffee = require('iced-coffee-script');
|
|
87
|
+
//}
|
|
88
|
+
//catch (e) {
|
|
89
|
+
// Coffee = require('coffee-script');
|
|
90
|
+
//}
|
|
91
|
+
try {
|
|
92
|
+
// Try to load coffeescript
|
|
93
|
+
Coffee = require(COFFEE_2_DEP);
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
// If it doesn't exist, try to load it using the deprecated module name
|
|
97
|
+
Coffee = require(COFFEE_DEP);
|
|
98
|
+
}
|
|
99
|
+
// coffee-script >= 1.7.0 requires explicit registration for require() to work
|
|
100
|
+
if (Coffee.register) {
|
|
101
|
+
Coffee.register();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Use the built-in parser for .coffee files with coffee-script
|
|
105
|
+
return require(filename);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
Parser.icedParser = function(filename, content) {
|
|
109
|
+
Iced = require(ICED_DEP);
|
|
110
|
+
|
|
111
|
+
// coffee-script >= 1.7.0 requires explicit registration for require() to work
|
|
112
|
+
if (Iced.register) {
|
|
113
|
+
Iced.register();
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
Parser.yamlParser = function(filename, content) {
|
|
118
|
+
if (!Yaml && !VisionmediaYaml) {
|
|
119
|
+
// Lazy loading
|
|
120
|
+
try {
|
|
121
|
+
// Try to load the better js-yaml module
|
|
122
|
+
Yaml = require(JS_YAML_DEP);
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
try {
|
|
126
|
+
// If it doesn't exist, load the fallback visionmedia yaml module.
|
|
127
|
+
VisionmediaYaml = require(YAML_DEP);
|
|
128
|
+
}
|
|
129
|
+
catch (e) { }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (Yaml) {
|
|
133
|
+
return Yaml.load(content);
|
|
134
|
+
}
|
|
135
|
+
else if (VisionmediaYaml) {
|
|
136
|
+
// The yaml library doesn't like strings that have newlines but don't
|
|
137
|
+
// end in a newline: https://github.com/visionmedia/js-yaml/issues/issue/13
|
|
138
|
+
content += '\n';
|
|
139
|
+
return VisionmediaYaml.eval(Parser.stripYamlComments(content));
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
console.error('No YAML parser loaded. Suggest adding js-yaml dependency to your package.json file.')
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
Parser.jsonParser = function(filename, content) {
|
|
147
|
+
try {
|
|
148
|
+
return JSON.parse(content);
|
|
149
|
+
}
|
|
150
|
+
catch (e) {
|
|
151
|
+
// All JS Style comments will begin with /, so all JSON parse errors that
|
|
152
|
+
// encountered a syntax error will complain about this character.
|
|
153
|
+
if (e.name !== 'SyntaxError' || e.message.indexOf('Unexpected token /') !== 0) {
|
|
154
|
+
throw e;
|
|
155
|
+
}
|
|
156
|
+
if (!JSON5) {
|
|
157
|
+
JSON5 = require(JSON5_DEP);
|
|
158
|
+
}
|
|
159
|
+
return JSON5.parse(content);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
Parser.json5Parser = function(filename, content) {
|
|
164
|
+
if (!JSON5) {
|
|
165
|
+
JSON5 = require(JSON5_DEP);
|
|
166
|
+
}
|
|
167
|
+
return JSON5.parse(content);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
Parser.hjsonParser = function(filename, content) {
|
|
171
|
+
if (!HJSON) {
|
|
172
|
+
HJSON = require(HJSON_DEP);
|
|
173
|
+
}
|
|
174
|
+
return HJSON.parse(content);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
Parser.tomlParser = function(filename, content) {
|
|
178
|
+
if(!TOML) {
|
|
179
|
+
TOML = require(TOML_DEP);
|
|
180
|
+
}
|
|
181
|
+
return TOML.parse(content);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
Parser.csonParser = function(filename, content) {
|
|
185
|
+
if (!CSON) {
|
|
186
|
+
CSON = require(CSON_DEP);
|
|
187
|
+
}
|
|
188
|
+
// Allow comments in CSON files
|
|
189
|
+
if (typeof CSON.parseSync === 'function') {
|
|
190
|
+
return CSON.parseSync(Parser.stripComments(content));
|
|
191
|
+
}
|
|
192
|
+
return CSON.parse(Parser.stripComments(content));
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
Parser.propertiesParser = function(filename, content) {
|
|
196
|
+
if (!PPARSER) {
|
|
197
|
+
PPARSER = require(PPARSER_DEP);
|
|
198
|
+
}
|
|
199
|
+
return PPARSER.parse(content, { namespaces: true, variables: true, sections: true });
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Strip all Javascript type comments from the string.
|
|
204
|
+
*
|
|
205
|
+
* The string is usually a file loaded from the O/S, containing
|
|
206
|
+
* newlines and javascript type comments.
|
|
207
|
+
*
|
|
208
|
+
* Thanks to James Padolsey, and all who contributed to this implementation.
|
|
209
|
+
* http://james.padolsey.com/javascript/javascript-comment-removal-revisted/
|
|
210
|
+
*
|
|
211
|
+
* @protected
|
|
212
|
+
* @method stripComments
|
|
213
|
+
* @param fileStr {string} The string to strip comments from
|
|
214
|
+
* @param stringRegex {RegExp} Optional regular expression to match strings that
|
|
215
|
+
* make up the config file
|
|
216
|
+
* @return {string} The string with comments stripped.
|
|
217
|
+
*/
|
|
218
|
+
Parser.stripComments = function(fileStr, stringRegex) {
|
|
219
|
+
stringRegex = stringRegex || /(['"])(\\\1|.)+?\1/g;
|
|
220
|
+
|
|
221
|
+
var uid = '_' + +new Date(),
|
|
222
|
+
primitives = [],
|
|
223
|
+
primIndex = 0;
|
|
224
|
+
|
|
225
|
+
return (
|
|
226
|
+
fileStr
|
|
227
|
+
|
|
228
|
+
/* Remove strings */
|
|
229
|
+
.replace(stringRegex, function(match){
|
|
230
|
+
primitives[primIndex] = match;
|
|
231
|
+
return (uid + '') + primIndex++;
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
/* Remove Regexes */
|
|
235
|
+
.replace(/([^\/])(\/(?!\*|\/)(\\\/|.)+?\/[gim]{0,3})/g, function(match, $1, $2){
|
|
236
|
+
primitives[primIndex] = $2;
|
|
237
|
+
return $1 + (uid + '') + primIndex++;
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
/*
|
|
241
|
+
- Remove single-line comments that contain would-be multi-line delimiters
|
|
242
|
+
E.g. // Comment /* <--
|
|
243
|
+
- Remove multi-line comments that contain would be single-line delimiters
|
|
244
|
+
E.g. /* // <--
|
|
245
|
+
*/
|
|
246
|
+
.replace(/\/\/.*?\/?\*.+?(?=\n|\r|$)|\/\*[\s\S]*?\/\/[\s\S]*?\*\//g, '')
|
|
247
|
+
|
|
248
|
+
/*
|
|
249
|
+
Remove single and multi-line comments,
|
|
250
|
+
no consideration of inner-contents
|
|
251
|
+
*/
|
|
252
|
+
.replace(/\/\/.+?(?=\n|\r|$)|\/\*[\s\S]+?\*\//g, '')
|
|
253
|
+
|
|
254
|
+
/*
|
|
255
|
+
Remove multi-line comments that have a replaced ending (string/regex)
|
|
256
|
+
Greedy, so no inner strings/regexes will stop it.
|
|
257
|
+
*/
|
|
258
|
+
.replace(RegExp('\\/\\*[\\s\\S]+' + uid + '\\d+', 'g'), '')
|
|
259
|
+
|
|
260
|
+
/* Bring back strings & regexes */
|
|
261
|
+
.replace(RegExp(uid + '(\\d+)', 'g'), function(match, n){
|
|
262
|
+
return primitives[n];
|
|
263
|
+
})
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Strip YAML comments from the string
|
|
270
|
+
*
|
|
271
|
+
* The 2.0 yaml parser doesn't allow comment-only or blank lines. Strip them.
|
|
272
|
+
*
|
|
273
|
+
* @protected
|
|
274
|
+
* @method stripYamlComments
|
|
275
|
+
* @param fileStr {string} The string to strip comments from
|
|
276
|
+
* @return {string} The string with comments stripped.
|
|
277
|
+
*/
|
|
278
|
+
Parser.stripYamlComments = function(fileStr) {
|
|
279
|
+
// First replace removes comment-only lines
|
|
280
|
+
// Second replace removes blank lines
|
|
281
|
+
return fileStr.replace(/^\s*#.*/mg,'').replace(/^\s*[\n|\r]+/mg,'');
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
var order = ['js', 'ts', 'json', 'json5', 'hjson', 'toml', 'coffee', 'iced', 'yaml', 'yml', 'cson', 'properties', 'xml'];
|
|
285
|
+
var definitions = {
|
|
286
|
+
coffee: Parser.coffeeParser,
|
|
287
|
+
cson: Parser.csonParser,
|
|
288
|
+
hjson: Parser.hjsonParser,
|
|
289
|
+
iced: Parser.icedParser,
|
|
290
|
+
js: Parser.jsParser,
|
|
291
|
+
json: Parser.jsonParser,
|
|
292
|
+
json5: Parser.json5Parser,
|
|
293
|
+
properties: Parser.propertiesParser,
|
|
294
|
+
toml: Parser.tomlParser,
|
|
295
|
+
ts: Parser.tsParser,
|
|
296
|
+
xml: Parser.xmlParser,
|
|
297
|
+
yaml: Parser.yamlParser,
|
|
298
|
+
yml: Parser.yamlParser,
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
Parser.getParser = function(name) {
|
|
302
|
+
return definitions[name];
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
Parser.setParser = function(name, parser) {
|
|
306
|
+
definitions[name] = parser;
|
|
307
|
+
if (order.indexOf(name) === -1) {
|
|
308
|
+
order.push(name);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
Parser.getFilesOrder = function(name) {
|
|
313
|
+
if (name) {
|
|
314
|
+
return order.indexOf(name);
|
|
315
|
+
}
|
|
316
|
+
return order;
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
Parser.setFilesOrder = function(name, newIndex) {
|
|
320
|
+
if (Array.isArray(name)) {
|
|
321
|
+
return order = name;
|
|
322
|
+
}
|
|
323
|
+
if (typeof newIndex === 'number') {
|
|
324
|
+
var index = order.indexOf(name);
|
|
325
|
+
order.splice(newIndex, 0, name);
|
|
326
|
+
if (index > -1) {
|
|
327
|
+
order.splice(index >= newIndex ? index +1 : index, 1);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return order;
|
|
331
|
+
};
|