cli/directives/TemplateDirective.js

/**
 * @module flitter-cli/directives/TemplateDirective
 */

const Directive = require('../Directive')
const fs = require('fs').promises
const path = require('path')

const PositionalOption = require('../options/PositionalOption')

/**
 * Directive to create new files from registered templates by name.
 * 
 * @extends module:flitter-cli/Directive~Directive
 */
class TemplateDirective extends Directive {

    /**
     * Get the name of the directive. Used by ./flitter.
     * @static
     * @returns {string} "new"
     */
    static name(){
        return "new"
    }

    /**
     * Get the usage information for the directive. Used by ./flitter help.
     * @returns {string}
     */
    static help(){
        return "create a new file from a registered template: <template name> <file name>"
    }

    /**
     * Get the CLI flag options provided by this directive.
     * @returns {Array<module:flitter-cli/options/Option~Option>}
     */
    static options() {
        const templates = this.prototype.cli.loaded_templates
        const template_option = new PositionalOption('resource name', 'type of resource to be created')
        template_option.whitelist(...Object.keys(templates))

        return [
            template_option,
            '{file name} | canonical name of the resource to create',
        ]
    }

    /**
     * Handles an invocation of the directive. Creates a new template with the specified class name.
     * This directive is sub-directory aware and accepts Flitter canonical names.
     * @param {module:libflitter/app/FlitterApp~FlitterApp} app - the Flitter app
     * @param {Array} argv - the CLI arguments passed after the directive
     * @returns {Promise<void>}
     */
    async handle(app, argv){
        const templates = this.cli.loaded_templates
        let found = false
        let names = "\t"
        
        for ( let template_name in templates ){
            names += template_name+", "

            if ( template_name === this.option('resource name') ){
                const template = templates[template_name]
                
                if ( !this.option('file name') ){
                    found = true
                    this.error(`Please provide a canonical name for the new ${template_name}.`)
                    break
                }
                
                const file_name = this.option('file name').replace(/:/g, '/')+template.extension
                
                let subdir = ""
                if ( file_name.lastIndexOf("/") > -1 ){
                    subdir = file_name.substr(0, file_name.lastIndexOf("/"))
                }                
                
                // mkdir -p the template destination
                await fs.mkdir(path.resolve(template.directory, subdir), { recursive: true })
                
                // get the full path to the file
                const file_path = path.resolve(template.directory, file_name)
                const class_name = this.option('file name').split(":").slice(-1)[0]

                // build some context to pass along
                const ctx = {
                    app,
                    services: app.di().service(),
                }
                
                await fs.writeFile(file_path, template.template(class_name, this.option('file name'), ctx))

                this.success(`Created new ${template_name} as ${this.option('file name')}.`)
                found = true
                break
            }
        }

        if ( !found ){
            this.error('Please enter a valid template name.')
            this.info('The following templates are registered with Flitter:')
            this.info(`\t${names.slice(0, -2)}`)
        }
    }
    
}

module.exports = exports = TemplateDirective