import {Directive, OptionDefinition} from '../Directive' import {PositionalOption} from './options/PositionalOption' import {CommandLine} from '../service' import {Inject, Injectable} from '../../di' import {ErrorWithContext} from '../../util' /** * Create a new file based on a template registered with the CommandLine service. */ @Injectable() export class TemplateDirective extends Directive { @Inject() protected readonly cli!: CommandLine getKeywords(): string | string[] { return ['new', 'make'] } getDescription(): string { return 'create a new file from a registered template' } getOptions(): OptionDefinition[] { const registeredTemplates = this.cli.getTemplates() const template = new PositionalOption('template_name', 'the template to base the new file on (e.g. model, controller)') template.whitelist(...registeredTemplates.pluck('name').all()) const destination = new PositionalOption('file_name', 'canonical name of the file to create (e.g. auth:Group, dash:Activity)') return [template, destination] } getHelpText(): string { const registeredTemplates = this.cli.getTemplates() return [ 'Modules in Extollo register templates that can be used to quickly create common file types.', '', 'For example, you can create a new model from @extollo/orm using the "model" template:', '', './ex new model auth:Group', '', 'This would create a new Group model in the ./src/app/models/auth/Group.model.ts file.', '', 'AVAILABLE TEMPLATES:', '', ...(registeredTemplates.map(template => { return ` - ${template.name}: ${template.description}` }).all()), ].join('\n') } async handle(): Promise { const templateName: string = this.option('template_name') const destinationName: string = this.option('file_name') if ( destinationName.includes('/') || destinationName.includes('\\') ) { this.error(`The destination should be a canonical name, not a file path.`) this.error(`Reference sub-directories using the : character instead.`) this.error(`Did you mean ${destinationName.replace(/\/|\\/g, ':')}?`) process.exitCode = 1 return } const template = this.cli.getTemplate(templateName) if ( !template ) { throw new ErrorWithContext(`Unable to find template supposedly registered with name: ${templateName}`, { templateName, destinationName, }) } const name = destinationName.split(':').reverse()[0] const path = this.app().path('..', 'src', 'app', ...template.baseAppPath, ...(`${destinationName}${template.fileSuffix}`).split(':')) if ( await path.exists() ) { this.error(`File already exists: ${path}`) process.exitCode = 1 return } // Make sure the parent direction exists await path.concat('..').mkdir() const contents = await template.render(name, destinationName, path.clone()) await path.write(contents) this.success(`Created new ${template.name} in ${path}`) } }