libflitter/config/ConfigUnit.js

/**
 * @module libflitter/config/ConfigUnit
 */

const rra = require('recursive-readdir-async')
const path = require('path')
const Unit = require('../Unit')

/**
 * The config unit loads the configuration files from a specified
 * directory and, after merging them with values from a .env file,
 * loads them into the global accessor daemon, config().
 * 
 * @extends module:libflitter/Unit~Unit
 */
class ConfigUnit extends Unit {
    
    /**
     * Instantiate the unit. Resolve the path to the directory with the configuration files.
     * @param {string} [config_directory = './config'] - the path to the directory with the configuration files
     */
    constructor(config_directory = './config'){
        super()

        /**
         * The fully-qualified path to the directory with the configuration files.
         * @type {string}
         * @name ConfigUnit#directory
         */
        this.directory = path.resolve(config_directory)
    }

    /**
     * Loads the unit. Starts the 'dotenv' package and imports configurations from {@link module:libflitter/config/ConfigUnit~ConfigUnit#directory}.
     * Binds the configuration and helper functions to the appropriate contexts.
     * @param {module:libflitter/app/FlitterApp~FlitterApp} app - the Flitter app
     * @param {module:libflitter/Context~Context} context - the Unit's context
     * @returns {Promise<void>}
     */
    async go(app, context){

        /*
         * Load the .env file.
         */
        require('dotenv').config()
        
        const files = await rra.list(this.directory)
        
        const configurations = {}
        
        /*
         * Parse the name of the configuration from the file
         * name. e.g. "db.config.js" becomes "db".
         * Store the imported config under this name.
         */
        for (let key in files){
            if ( files[key].fullname.endsWith('.config.js') ) {
                let name = files[key].fullname.replace(this.directory + '/', '')
                    .replace(/.config.js/g, '')
                    .replace(/\//g, ':')

                configurations[name] = require(files[key].fullname)
            }
        }
        
        context.bind('daemon', configurations)
        context.bind('get', this.getconfig)
        app.global.bind('config', this.getconfig)
    }

    /**
     * A helper function to get configuration values by period-delineated name.
     * This is usually bound to the relevant {@link module:libflitter/app/FlitterApp~FlitterApp}.
     * @param {string} accessor - period-delineated access string
     * @returns {string|*}
     */
    getconfig(accessor){
        const keys = accessor.split('.')
        let descending_value = this.d.config.daemon
        for (let key in keys){
            descending_value = descending_value[keys[key]]
        }

        return descending_value
    }

    /**
     * Get the name of the unit.
     * @returns {string} "config"
     */
    name(){
        return "config"
    }

    /**
     * Get the directories provided by the unit.
     * Currently, "config" mapped to {@link module:libflitter/config/ConfigUnit~ConfigUnit#directory}
     * @returns {{config: string}}
     */
    directories(){
        return {
            "config": this.directory
        }
    }

    /**
     * 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('libflitter/templates/config'),
                directory: this.directory,
                extension: '.config.js'
            }
        }
    }
}

module.exports = ConfigUnit