You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

133 lines
4.2 KiB

'use strict';
var _ = require('lodash');
var fs = require('fs');
var paths = require('./paths');
var env = module.exports = {
getConfigFromFile: getConfigFromFile
};
var converters = {
string: { // Any object that implements `toString`
parse: function (x) { return x != null && x.toString(); },
validate: function (x) { return _.isString(x); }
},
number: { // Numbers and strings that can be parsed into numbers
parse: parseFloat,
validate: function (x) { return !isNaN(x); }
},
boolean: { // `true`, `false`, `'true'` and `'false'`
parse: function (x) {
if (typeof x === 'boolean') return x;
if (x === 'true') return true;
if (x === 'false') return false;
},
validate: _.isBoolean
},
date: { // Any string for which `new Date()` will return a valid date
parse: function (x) { return new Date(x); },
validate: _.isDate
},
object: { // Parse JSON objects
parse: JSON.parse,
validate: _.isObject
},
function: { // Any valid JavaScript expression; `this` refers to `env`
parse: function (x) { return eval(x); } // jshint ignore: line
}
};
// Add environment variables from `process.env`
_.extend(env, process.env);
// Parse environment variables from `env.json`
var config = getConfigFromFile(paths.CONFIG);
_.each(config, parseAndSetEnvironmentVariableOptions);
function getConfigFromFile(fileName) {
var configFilePath = paths.get(fileName);
var configFileContent;
try {
configFileContent = fs.readFileSync(configFilePath, 'utf8');
}
catch (error) {
if (error.code === 'ENOENT') {
console.error('File not found: \'' + configFilePath + '\'! Continuing...');
}
else {
throw error;
}
}
var parsedConfig;
if (configFileContent) {
try {
parsedConfig = JSON.parse(configFileContent);
}
catch (error) {
console.error(error && error.message || error);
}
}
if (!parsedConfig) {
parsedConfig = {};
}
return parsedConfig;
}
function parseAndSetEnvironmentVariableOptions(options, key) {
if (!_.isObject(options)) options = { default: options }; // Shorthand notation
var processValueIsDefined = process.env[key] !== undefined;
var defaultValueIsDefined = options.default !== undefined;
var environmentValueIsDefined = options[env.NODE_ENV] !== undefined;
if (processValueIsDefined || defaultValueIsDefined || environmentValueIsDefined) {
var value;
var defaultValueKey = environmentValueIsDefined ? env.NODE_ENV : 'default'; // Use corresponding key to get default value
if (processValueIsDefined) { // Values from `process.env` take precedence over `env.json`
if (options.type === undefined) {
var defaultValue = options[defaultValueKey];
if (defaultValue !== undefined) { // Infer type from default value
options.type = typeof defaultValue;
}
else { // `process.env` can only contain strings anyway
options.type = 'string';
}
}
value = process.env[key];
}
else if (defaultValueIsDefined || environmentValueIsDefined) { // The condition is just for clarity, it's always true
value = options[defaultValueKey];
if (value === undefined && !options.required) return; // Don't enforce convertion
_.defaults(options, { type: typeof value }); // Infer type from default value if necessary
process.env[key] = value; // Put the value into `process.env`
}
var converter = converters[options.type];
if (!converter) {
console.error('Unsupported type for environment variable `' + key + '`: ' + options.type);
process.exit(1);
}
var parsedValue = _.isString(value) ?
converter.parse.call(env, value) : // Call with `env` to allow for variable interdependency
value; // Only parse the value if it's a string that could've come from `process.env`
if (converter.validate && !converter.validate(parsedValue)) {
console.error('Invalid value for environment variable `' + key + '`: ' + value);
process.exit(1);
}
env[key] = parsedValue;
}
else if (options.required) {
console.error('Required environment variable `' + key + '`');
process.exit(1);
}
}