Standard libraries that lift up your code.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

90 lines
3.3 KiB

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.
export class TemplateDirective extends Directive {
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)')
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.',
...( => {
return ` - ${}: ${template.description}`
async handle(): Promise<void> {
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
const template = this.cli.getTemplate(templateName)
if ( !template ) {
throw new ErrorWithContext(`Unable to find template supposedly registered with name: ${templateName}`, {
const name = destinationName.split(':').reverse()[0]
const path ='..', 'src', 'app', ...template.baseAppPath, ...(`${destinationName}${template.fileSuffix}`).split(':'))
if ( await path.exists() ) {
this.error(`File already exists: ${path}`)
process.exitCode = 1
// 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 ${} in ${path}`)