diff --git a/package-lock.json b/package-lock.json index fbf9a64..259d2d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@athenna/artisan", - "version": "4.28.0", + "version": "4.29.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@athenna/artisan", - "version": "4.28.0", + "version": "4.29.0", "license": "MIT", "dependencies": { "chalk-rainbow": "^1.0.0", diff --git a/package.json b/package.json index 4a4205f..7bb082d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@athenna/artisan", - "version": "4.28.0", + "version": "4.29.0", "description": "The Athenna CLI application. Built on top of commander and inspired in @adonisjs/ace.", "license": "MIT", "author": "João Lenon ", diff --git a/src/commands/MakeCommandCommand.ts b/src/commands/MakeCommandCommand.ts index f8eea56..9580469 100644 --- a/src/commands/MakeCommandCommand.ts +++ b/src/commands/MakeCommandCommand.ts @@ -7,9 +7,8 @@ * file that was distributed with this source code. */ +import { Path } from '@athenna/common' import { BaseCommand, Argument } from '#src' -import { Path, String } from '@athenna/common' -import { sep, resolve, isAbsolute } from 'node:path' export class MakeCommandCommand extends BaseCommand { @Argument({ @@ -28,8 +27,14 @@ export class MakeCommandCommand extends BaseCommand { public async handle(): Promise { this.logger.simple('({bold,green} [ MAKING COMMAND ])\n') + const destination = Config.get( + 'rc.commands.make:command.destination', + Path.commands() + ) + const file = await this.generator - .path(this.getFilePath()) + .fileName(this.name) + .destination(destination) .template('command') .setNameProperties(true) .make() @@ -38,8 +43,8 @@ export class MakeCommandCommand extends BaseCommand { `Command ({yellow} "${file.name}") successfully created.` ) - const signature = String.toCamelCase(file.name) - const importPath = this.getImportPath(file.name) + const signature = this.generator.getSignature() + const importPath = this.generator.getImportPath() await this.rc.setTo('commands', signature, importPath).save() @@ -47,39 +52,4 @@ export class MakeCommandCommand extends BaseCommand { `Athenna RC updated: ({dim,yellow} { commands += "${signature}": "${importPath}" })` ) } - - /** - * Get the file path where it will be generated. - */ - private getFilePath(): string { - return this.getDestinationPath().concat(`${sep}${this.name}.${Path.ext()}`) - } - - /** - * Get the destination path for the file that will be generated. - */ - private getDestinationPath(): string { - let destination = Config.get( - 'rc.commands.make:command.destination', - Path.commands() - ) - - if (!isAbsolute(destination)) { - destination = resolve(Path.pwd(), destination) - } - - return destination - } - - /** - * Get the import path that should be registered in RC file. - */ - private getImportPath(fileName: string): string { - const destination = this.getDestinationPath() - - return `${destination - .replace(Path.pwd(), '') - .replace(/\\/g, '/') - .replace('/', '#')}/${fileName}` - } } diff --git a/src/helpers/command/Generator.ts b/src/helpers/command/Generator.ts index c36f6dd..885ed87 100644 --- a/src/helpers/command/Generator.ts +++ b/src/helpers/command/Generator.ts @@ -9,11 +9,14 @@ import { View } from '@athenna/view' import { File, String } from '@athenna/common' -import { resolve, isAbsolute } from 'node:path' +import { sep, resolve, isAbsolute } from 'node:path' import { AlreadyExistFileException } from '#src/exceptions/AlreadyExistFileException' export class Generator { private _path: string + private _fileName: string + private _dest: string + private _ext = Path.ext() private _template: string private _properties: any private _setNameProperties = true @@ -22,6 +25,9 @@ export class Generator { * Set the file path where the file will be generated. * Remember that the file name in the path will be used * to define the name properties. + * + * If defining a path, `fileName()`, `destination()` and + * `extension()` will be ignored. */ public path(path: string): Generator { this._path = path @@ -77,6 +83,98 @@ export class Generator { return this } + /** + * Set the name of the file that will be generated + * and use as reference to define the name properties. + */ + public fileName(name: string) { + this._fileName = name + + return this + } + + /** + * Set the destination where the file should be generated. + * + * @example + * ```ts + * this.generator + * .name('UserController') + * .destination(Path.controllers()) + * ``` + */ + public destination(dest: string) { + if (!isAbsolute(dest)) { + dest = resolve(Path.pwd(), dest) + } + + this._dest = dest + + return this + } + + /** + * Set the file extension. + * + * @example + * ```ts + * this.generator + * .name('UserController') + * .extension('ts') + * .destination(Path.controllers()) + * ``` + */ + public extension(ext: string) { + this._ext = ext + + return this + } + + /** + * Get the signature that will be used as key + * to be defined in `.athennarc.json` file. + * + * Only works when defining `fileName()`. + * + * @example + * ```ts + * this.generator.fileName('user.controller') + * + * const name = this.generator.getSignature() + * + * // userController + * ``` + */ + public getSignature(): string { + return String.toCamelCase(this._fileName) + } + + /** + * Create a sub path import path for the file + * from the destination defined and the + * file name. + * + * Only works when defining `destination()` and + * `name()`. + * + * @example + * ```ts + * this.generator + * .fileName('UserController') + * .destination(Path.controllers()) + * + * const importPath = this.generator.getImportPath() + * + * /// #app/http/controllers/UserController + * ``` + */ + public getImportPath(): string { + return `${this._dest + .replace(Path.pwd(), '') + .replace(/\\/g, '/') + .replace('/', '#')}/${this._fileName}` + } + /** * Make a new file using templates. * @@ -97,20 +195,32 @@ export class Generator { * ``` */ public async make(): Promise { + if (!this._path) { + this._path = this._dest.concat(`${sep}${this._fileName}.${this._ext}`) + } + const file = new File(this._path, '') if (file.fileExists) { throw new AlreadyExistFileException(this._path) } + if (!this._fileName) { + this._fileName = file.name + } + if (this._setNameProperties) { const timestamp = Date.now() - const nameUp = file.name.toUpperCase() - const nameCamel = String.toCamelCase(file.name) - const namePlural = String.pluralize(file.name) - const namePascal = String.toPascalCase(file.name) - const namePluralCamel = String.toCamelCase(String.pluralize(file.name)) - const namePluralPascal = String.toPascalCase(String.pluralize(file.name)) + const nameUp = this._fileName.toUpperCase() + const nameCamel = String.toCamelCase(this._fileName) + const namePlural = String.pluralize(this._fileName) + const namePascal = String.toPascalCase(this._fileName) + const namePluralCamel = String.toCamelCase( + String.pluralize(this._fileName) + ) + const namePluralPascal = String.toPascalCase( + String.pluralize(this._fileName) + ) this.properties({ nameUp, @@ -135,7 +245,11 @@ export class Generator { return file.setContent(content) } - let templatePath = Config.get(`rc.templates.${this._template}`) + const templates = Config.get('rc.templates') + + // Avoid problems when user define template + // key with dot notation. + let templatePath = templates[this._template] if (!isAbsolute(templatePath)) { templatePath = resolve(templatePath) diff --git a/tests/unit/helpers/command/GeneratorTest.ts b/tests/unit/helpers/command/GeneratorTest.ts index 456e76a..549247a 100644 --- a/tests/unit/helpers/command/GeneratorTest.ts +++ b/tests/unit/helpers/command/GeneratorTest.ts @@ -89,4 +89,26 @@ export default class GeneratorTest { assert.isTrue(await File.exists(path)) assert.isTrue(new File(path).getContentAsStringSync().includes('GeneratorTestCommandd')) } + + @Test() + public async shouldBeAbleToGenerateFilesFromTemplatesSettingFileNameDestinationAndExt({ assert }: Context) { + const path = Path.fixtures('tmp/GeneratorTestCommand.ts') + + View.createTemplate('command', await new File('../../../../templates/command.edge').getContentAsString()) + + assert.isTrue(View.hasTemplate('command')) + + await this.generator + .path(path) + .fileName('GeneratorTestCommand') + .extension('ts') + .destination(Path.fixtures('tmp')) + .properties({ namePascal: 'GeneratorTestCommandd' }) + .setNameProperties(true) + .template('command') + .make() + + assert.isTrue(await File.exists(path)) + assert.isTrue(new File(path).getContentAsStringSync().includes('GeneratorTestCommandd')) + } }