/**
* @module libflitter/config/ConfigUnit
*/
const CanonicalUnit = require('../canon/CanonicalUnit')
const path = require('path')
const error_context = require('../errors/error_context.fn')
// Private helper functions
// Some of these are duplicated in UtilityUnit,
// however ConfigUnit starts before UtilityUnit
// so they must be included here.
const priv = {
/**
* Grabs an environment variable by name and tries to infer its type.
* @param {string} name
* @param default_value
* @return {string|null|boolean|number}
*/
env(name, default_value){
const val = process.env[name]
if ( !val ) return typeof default_value !== "undefined" ? default_value : null
else return this.infer(val)
},
/**
* Attempt to infer the variable type of a string's data.
* @param {string} val
* @return {boolean|null|*|number|undefined}
*/
infer(val){
try {
if (!val) return null
else if (val.toLowerCase() === 'true') return true
else if (val.toLowerCase() === 'false') return false
else if (!isNaN(val)) return +val
else if (this.is_json(val)) return JSON.parse(val)
else if (val.toLowerCase() === 'null') return null
else if (val.toLowerCase() === 'undefined') return undefined
else return val
} catch (e) {
throw error_context(e, {
inference_value: val,
})
}
},
/**
* Checks if a string is valid JSON.
* @param string
* @return {boolean} - true if the string is valid JSON
*/
is_json(string){
try {
JSON.parse(string)
return true
}
catch (e) {
return false
}
}
}
/**
* Unit to load and manage config files.
* @extends module:libflitter/canon/CanonicalUnit~CanonicalUnit
*/
class ConfigUnit extends CanonicalUnit {
/**
* Name of the service provided by this unit: 'configs'
* @returns {string} - 'configs'
*/
static get name() {
return 'configs'
}
/**
* Instantiate the unit.
* @param {string} [base_directory = './config'] - the base directory
*/
constructor(base_directory = './config') {
super(base_directory)
/**
* The canonical name of the item.
* @type {string} = 'config'
*/
this.canonical_item = 'config'
/**
* The file extension of the canonical item files.
* @type {string} - '.config.js'
*/
this.suffix = '.config.js'
}
/**
* Initialize the unit. Load the dotenv configuration.
* @param app
* @returns {Promise<void>}
*/
async go(app) {
require('dotenv').config()
global.env = priv.env.bind(priv)
await super.go(app)
delete global.env
}
/**
* A helper function that calls {@link module:libflitter/config/ConfigUnit~ConfigUnit#get}, but
* guarantees an object is returned, even if no config is found for the given accessor.
* @param {string} accessor - period-delineated access string
* @param {Object} [merge = {}] - default values to override
* @return {Object}
*/
guarantee(accessor, merge = {}) {
const config = this.get(accessor)
const grant = config ? config : {}
return {...merge, ...grant}
}
/**
* Get the templates provided by this unit.
* Currently, "config" provided by {@link module:libflitter/templates/config}
* @returns {{config: {template: (config|(function(string): string)|*), extension: string, directory: string}}}
*/
templates() {
return {
config: {
template: require('../templates/config'),
directory: this.directory,
extension: '.config.js'
}
}
}
/**
* Get the fully-qualified path to the migrations provided by this unit.
* @returns {string}
*/
migrations(){
return path.resolve(__dirname, 'migrations')
}
}
module.exports = exports = ConfigUnit