config.js 47 KB

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