mikro-config
Version:
tiny node config wrapper for plain JS configuration files
133 lines (132 loc) • 5.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = require("lodash");
const fs_1 = require("fs");
const CONFIG = {};
/**
* Simple config helper, loads configuration in this order:
* - /config/default.js
* - /config/*.js (all files in config folder excluding local configs)
* - /config/env/$NODE_ENV.js
* - /config/env/$NODE_ENV.local.js
* - /config/local.js
*/
class MikroConfig {
constructor(configDir = process.env.NODE_CONFIG_DIR || process.cwd() + '/config') {
this.configDir = configDir;
const env = this.getEnvironment();
this.buildConfig(this.configDir, env);
}
get(key, defaultValue = undefined) {
return lodash_1.get(CONFIG, key, defaultValue);
}
has(key) {
return lodash_1.has(CONFIG, key);
}
/**
* options can be either object or path to config file to merge (checks for existence synchronously)
*/
addOptions(options, optional = false) {
if (typeof options === 'string') {
if (!options.endsWith('.js') && !options.endsWith('.json') && !options.endsWith('.ts')) {
if (fs_1.existsSync(options + '.json')) {
options += '.json';
}
else if (fs_1.existsSync(options + '.js')) {
options += '.js';
}
else if (fs_1.existsSync(options + '.ts')) {
options += '.ts';
}
else {
options += '.js';
}
}
if (fs_1.existsSync(options)) {
options = require(options);
}
else if (optional) {
options = {};
}
else {
throw new Error(`Configuration file '${options}' not found!`);
}
}
lodash_1.merge(CONFIG, options);
}
getEnvironment() {
return process.env.NODE_ENV || 'development';
}
buildConfig(configDir, environment) {
// load default config file
this.addOptions(configDir + '/default', true);
// load additional config files
if (fs_1.existsSync(configDir)) {
fs_1.readdirSync(configDir)
.filter((file) => {
const isConfigFile = file.endsWith('.js') || (file.endsWith('.ts') && !file.endsWith('.d.ts')) || file.endsWith('.json');
const isIgnored = ['default.js', 'default.json', 'local.js', 'local.json'].includes(file);
return isConfigFile && !isIgnored;
})
.forEach((file) => this.addOptions(`${configDir}/${file}`));
}
// load environment config
this.addOptions(`${configDir}/env/${environment}`, true);
// load local environment config
this.addOptions(`${configDir}/env/${environment}.local`, true);
// load local config
this.addOptions(`${configDir}/local`, true);
// override with environment config
this.addEnvOptions();
// propagate referenced config values (e.g. `HttpClient($[backend.api])` => `HttpClient('https://api.io/...')`)
this.propagateReferences(CONFIG);
// support `config.foo.bar` syntax (instead of `config.get('foo.bar')`)
lodash_1.merge(this, CONFIG);
}
addEnvOptions() {
if (!process.env.MIKRO_CONFIG_PREFIX) {
return;
}
const prefix = process.env.MIKRO_CONFIG_PREFIX;
Object.keys(process.env).forEach(key => {
if (!key.startsWith(prefix)) {
return;
}
// convert PREFIX_VAR_NAME to varName
let _key = key.substr(prefix.length);
_key = _key.replace(/___/g, '.');
_key = _key.toLowerCase().replace(/_(\w)/g, (m, w) => w.toUpperCase());
lodash_1.set(CONFIG, _key, process.env[key]);
});
}
propagateReferences(config) {
Object.keys(config).forEach(key => {
const value = config[key];
if (typeof value === 'string' && value.includes('$[')) {
this.propagateSingleReference(config, key);
}
else if (typeof value === 'object' && value) {
this.propagateReferences(value);
}
});
}
propagateSingleReference(config, key) {
const value = config[key];
const referencedProperty = config[key].match(/\$\[([\w.]+)]/)[1];
let newValue = this.get(referencedProperty);
// value is `$[...]`
if (value.match(/^\$\[(.*)]$/)) {
return config[key] = newValue;
}
// value is part of string (e.g. `ServiceName($[...])`)
// wrap string values in apostrophes
if (typeof newValue === 'string') {
newValue = `'${newValue}'`;
}
config[key] = value.replace(/\$\[([\w.]+)]/, newValue);
if (config[key].match(/\$\[([\w.]+)]/)) {
this.propagateSingleReference(config, key);
}
}
}
exports.MikroConfig = MikroConfig;