sails
Version:
API-driven framework for building realtime apps, using MVC conventions (based on Express and Socket.io)
203 lines (167 loc) • 5.79 kB
JavaScript
// Note: This is an inlined version of the rc npm package.
/*!
*
* @description Recursive object extending
* @author Viacheslav Lotsmanov <lotsmanov89@gmail.com>
* @license MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2013-2018 Viacheslav Lotsmanov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* Module dependencies
*/
;
var fs = require('fs');
var ini = require('ini');
var path = require('path');
var etc = '/etc';
var win = process.platform === 'win32';
var home = win ? process.env.USERPROFILE : process.env.HOME;
var deepExtend = require('./deep-extend.js');
module.exports = function rc(name, defaults, argv, parse) {
var parseFn = function (content) {
//if it ends in .json or starts with { then it must be json.
//must be done this way, because ini accepts everything.
//can't just try and parse it and let it throw if it's not ini.
//everything is ini. even json with a syntax error.
if(/^\s*{/.test(content)) {
return JSON.parse(content);
}
return ini.parse(content);
};
var fileFn = function () {
var args = [].slice.call(arguments).filter(function (arg) { return arg !== null;});
//path.join breaks if it's a not a string, so just skip this.
for(var i in args) {
if('string' !== typeof args[i]) {
return;
}
}
var file = path.join.apply(null, args);
try {
return fs.readFileSync(file,'utf-8');
} catch (unusedErr) {
return;
}
};
var jsonFn = function () {
var content = fileFn.apply(null, arguments);
return content ? parseFn(content) : null;
};
var envFn = function (prefix, env) {
env = env || process.env;
var obj = {};
var l = prefix.length;
for(var k in env) {
if(k.toLowerCase().indexOf(prefix.toLowerCase()) === 0) {
var keypath = k.substring(l).split('__');
// Trim empty strings from keypath array
var _emptyStringIndex;
while ((_emptyStringIndex=keypath.indexOf('')) > -1) {
keypath.splice(_emptyStringIndex, 1);
}
var cursor = obj;
keypath.forEach(function _buildSubObj(_subkey,i){
// (check for _subkey first so we ignore empty strings)
// (check for cursor to avoid assignment to primitive objects)
if (!_subkey || typeof cursor !== 'object') {
return;
}
// If this is the last key, just stuff the value in there
// Assigns actual value from env variable to final key
// (unless it's just an empty string- in that case use the last valid key)
if (i === keypath.length-1) {
cursor[_subkey] = env[k];
}
// Build sub-object if nothing already exists at the keypath
if (cursor[_subkey] === undefined) {
cursor[_subkey] = {};
}
// Increment cursor used to track the object at the current depth
cursor = cursor[_subkey];
});
}
}
return obj;
};
var findFn = function () {
var rel = path.join.apply(null, [].slice.call(arguments));
function find(start, rel) {
var file = path.join(start, rel);
try {
fs.statSync(file);
return file;
} catch (unusedErr) {
if(path.dirname(start) !== start) {// root
return find(path.dirname(start), rel);
}
}
}
return find(process.cwd(), rel);
};
if('string' !== typeof name) {
throw new Error('rc(name): name *must* be string');
}
if(!argv) {
argv = require('minimist')(process.argv.slice(2));
}
defaults = (
'string' === typeof defaults
? jsonFn(defaults) : defaults
) || {};
parse = parse || parseFn;
var env = envFn(name + '_');
var configs = [defaults];
var configFiles = [];
function addConfigFile (file) {
if (configFiles.indexOf(file) >= 0) {return;}
var fileConfig = fileFn(file);
if (fileConfig) {
configs.push(parse(fileConfig));
configFiles.push(file);
}
}
// which files do we look at?
if (!win) {
[path.join(etc, name, 'config'), path.join(etc, name + 'rc')].forEach(addConfigFile);
}
if (home) {
[
path.join(home, '.config', name, 'config'),
path.join(home, '.config', name),
path.join(home, '.' + name, 'config'),
path.join(home, '.' + name + 'rc')
].forEach(addConfigFile);
addConfigFile(findFn('.'+name+'rc'));
}
if (env.config) {
addConfigFile(env.config);
}
if (argv.config) {
addConfigFile(argv.config);
}
return deepExtend.apply(null, configs.concat([
env,
argv,
configFiles.length ? {configs: configFiles, config: configFiles[configFiles.length - 1]} : undefined,
]));
};