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')

/**
 * Unit that provides the functionality associated with flitter-cli.
 * @extends module:libflitter/Unit~Unit
 */
class CliUnit extends Unit {
    /**
     * Defines the services required by this unit.
     * @returns {Array<string>}
     */
    static get services() {
        return [...super.services, 'output', 'canon', 'app']
    }

    /**
     * Gets the name of the service provided by this unit: 'cli'
     * @returns {string} - 'cli'
     */
    static get name() {
        return 'cli'
    }

    /**
     * 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){
        this.app = app
        
        let directives = this.directives()
        let templates = {}
        let deployments = {}

        const services = app.di().container.proxy()
        for ( const unit_name of app.__unit_array ) {
            const unit = services[unit_name]

            // Load the unit's directives
            if ( typeof unit.directives === 'function' ) {
                const base_directives = unit.directives()
                const unit_directives = {}

                for ( const name in base_directives ) {
                    const D = base_directives[name]
                    this.app.di().inject(D)
                    unit_directives[name] = D
                }

                directives = {...directives, ...unit_directives}
            }

            // Load the unit's templates
            if ( typeof unit.templates === 'function' ) {
                templates = {...templates, ...unit.templates()}
            }

            // Load the unit's deployments
            if ( typeof unit.deploy === 'function' ) {
                const deploy_name = unit.name()
                deployments[deploy_name] = unit.deploy.bind(unit)
            }
        }
        
        let usage = {}
        for ( let directive_name in directives ){
            const directive = directives[directive_name]
            
            if ( directive.help() !== null ){
                usage[directive.name()] = directive.help()
            }
        }

        this.loaded_deployments = deployments
        this.loaded_templates = templates
        this.loaded_directives = directives
        this.usage = usage
        this._is_cli_invoked = this.app.di().has('CliApp')
    }

    is_cli() {
        return this._is_cli_invoked
    }

    /**
     * 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 {Array<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 CliAppUnit = require('./CliAppUnit')
        const cli = this.app.di().make(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.app, 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