cli/CliUnit.js

/**
 * @module flitter-cli/CliUnit
 */

const ShellDirective = require('./directives/ShellDirective')
const TemplateDirective = require('./directives/TemplateDirective')
const TestDirective = require('./directives/TestDirective')
const DeployDirective = require('./directives/DeployDirective')
const UsageDirective = require('./directives/UsageDirective')

const Unit = require('libflitter/Unit')
const Context = require('libflitter/Context')

/**
 * Unit that provides the functionality associated with flitter-cli.
 * 
 * @extends module:libflitter/Unit~Unit
 */
class CliUnit extends Unit {

    /**
     * Loads the module. If any registered unit has deployments/templates/directives, register them with flitter-cli.
     * Register the help messages to be used by ./flitter help. Then, bind them all to the context.
     * @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){
        this.app = app
        
        let directives = {}
        let templates = {}
        let deployments = {}
        
        for( let unit_name in app.units ){
            const unit = app.units[unit_name]
            
            // load any directives
            if ( typeof unit.directives === 'function' ){
                directives = {...directives, ...unit.directives()}
            }
            
            // load any templates
            if ( typeof unit.templates === 'function' ){
                templates = {...templates, ...unit.templates()}
            }
            
            // load any deployments
            if ( typeof unit.deploy === 'function' ){
                const deploy_name = unit.name() ? unit.name() : unit_name
                deployments[deploy_name] = unit.deploy
            }
            
        }
        
        let usage = {}
        
        for ( let directive_name in directives ){
            const directive = directives[directive_name]
            
            if ( directive.help() !== null ){
                usage[directive.name()] = directive.help()
            }
        }
        
        context.bind('directives', directives)
        context.bind('templates', templates)
        context.bind('deployments', deployments)
        context.bind('usage', usage)
        context.bind('invoke', this.invoke)
        
    }

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

    /**
     * Get the directive classes provided by this unit. Should be key-value pairs such that the key is the name of
     * the class, and the value is an instance of {@link module:flitter-cli/Directive~Directive}.
     * @returns {Object}
     */
    directives(){
        return {
            ShellDirective,
            TemplateDirective,
            TestDirective,
            DeployDirective,
            UsageDirective
        }
    }

    /**
     * Invoke a command, programmatically. Replaces console.log with a pseudo function that collects messages.
     * @param {string} directive - the directive to be called
     * @param {string[]} argv - array of command line arguments
     * @returns {Promise<Array>} - array of items that were sent to console.log during the command's execution
     */
    async invoke(directive, argv = []){
        const cli = new (require('./CliAppUnit'))
        
        // create a fake console.log to collect messages
        const old_log = console.log
        function pseudo_log(message){
            this.push(message)
        }
        
        console.flitter_cli_messages = []
        console.log = pseudo_log.bind(console.flitter_cli_messages)
        
        // format argv
        let args = [directive].concat(argv)
        
        // run the CLI
        await cli.go(this, (new Context(this)), args)
        
        // cleanup the changes we made
        const msg = console.flitter_cli_messages
        delete console.flitter_cli_messages
        
        console.log = old_log
        
        return msg
    }
    
    
}

module.exports = exports = CliUnit