Skip to content

Commit

Permalink
feat: add middleware command and lint option
Browse files Browse the repository at this point in the history
  • Loading branch information
jlenon7 committed Apr 6, 2022
1 parent ce2fd6c commit a3e54f0
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 36 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"build": "tsc --project tsconfig.json && tscpaths -p tsconfig.json -s . -o .",
"test": "npm run lint:fix && cross-env NODE_TS=true cross-env NODE_ENV=testing jest --verbose",
"test:debug": "npm run lint:fix && cross-env NODE_TS=true cross-env NODE_ENV=testing cross-env DEBUG=api:* jest --verbose",
"lint:fix": "eslint \"{src,tests}/**/*.ts\" --fix"
"eslint": "eslint",
"lint:fix": "npm run eslint \"{src,tests}/**/!(*.d)*.ts\" --fix"
},
"keywords": [
"nodejs",
Expand Down Expand Up @@ -133,6 +134,7 @@
"dot-notation": "off",
"camelcase": "off",
"no-undef": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-var-requires": "off",
"no-useless-constructor": "off",
"@typescript-eslint/no-useless-constructor": "off",
Expand Down
17 changes: 17 additions & 0 deletions src/Cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,30 @@ export class Cli {
'Current extension available: ts, js',
'ts',
)
.option('--no-lint', 'Do not run eslint in the controller')
.description(
'Make a new controller file inside app/Http/Controllers directory',
)
.action(makeCommand.controller.bind(makeCommand))
.showHelpAfterError()
.createHelp()

this.program
.command('make:middleware')
.argument('<name>', 'Your middleware name')
.option(
'-e, --extension <extension>',
'Current extension available: ts, js',
'ts',
)
.option('--no-lint', 'Do not run eslint in the middleware', false)
.description(
'Make a new middleware file inside app/Http/Middlewares directory',
)
.action(makeCommand.middleware.bind(makeCommand))
.showHelpAfterError()
.createHelp()

await this.program.parseAsync(process.argv)
}
}
73 changes: 68 additions & 5 deletions src/Commands/Make.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import chalk from 'chalk'
import { parse } from 'path'
import { existsSync } from 'fs'
import { Logger } from '@athenna/logger'
import { runCommand } from '../Utils/runCommand'
import { File, Folder, Path, String } from '@secjs/utils'

export class Make {
Expand Down Expand Up @@ -47,7 +48,8 @@ export class Make {

// TODO Resolve the path always looking to his project root
// TODO Maybe find for the package.json file in getConcretePath
const path = this.getConcretePath(name, template.base)
const normalizedName = this.normalizeName(name, template.base)
const path = this.getConcretePath(`app/Http/Controllers/${normalizedName}`)
const content = this.normalizeTemplate(name, template.getContentSync())

if (existsSync(path)) {
Expand All @@ -65,6 +67,69 @@ export class Make {
this.logger.success(
`Controller ({yellow} "${controller.name}") successfully created.`,
)

if (options.lint) {
await this.runEslintOnFile('Controller', controller.path)
}
}

async middleware(name: string, options: any): Promise<void> {
console.log(chalk.bold.green('[ MAKING MIDDLEWARE ]\n'))

if (name.includes('Middleware') || name.includes('Middlewares')) {
name = name.split('Middleware')[0]
}

const template = this.getTemplate('__name__Middleware', options)

if (!template) {
this.logger.error(
`Template for extension ({yellow} "${options.extension}") has not been found.`,
)

return
}

// TODO Resolve the path always looking to his project root
// TODO Maybe find for the package.json file in getConcretePath
const normalizedName = this.normalizeName(name, template.base)
const path = this.getConcretePath(`app/Http/Middlewares/${normalizedName}`)
const content = this.normalizeTemplate(name, template.getContentSync())

if (existsSync(path)) {
this.logger.error(
`The middleware ({yellow} "${
parse(path).name
}") already exists. Try using another name.`,
)

return
}

const middleware = await new File(path, content).create()

this.logger.success(
`Middleware ({yellow} "${middleware.name}") successfully created.`,
)

if (options.lint) {
await this.runEslintOnFile('Middleware', middleware.path)
}
}

private async runEslintOnFile(resource: string, filePath: string) {
const { name } = parse(filePath)

try {
await runCommand(`npm run eslint ${filePath} -- --fix --quiet`)
this.logger.success(
`${resource} ({yellow} "${name}") successfully linted.`,
)
} catch (error) {
this.logger.error(
`Failed to lint ${resource} ({yellow} "${name}"). Please check your eslint configurations.`,
)
}
}

private getTemplate(templateName: string, options: any): File {
Expand All @@ -73,10 +138,8 @@ export class Make {
return this.templatesFolder.files.find(f => f.base === templateName) as File
}

private getConcretePath(name: string, baseTemplateName: string) {
const normalizedName = this.normalizeName(name, baseTemplateName)

return `${this.clientFolder}/app/Http/Controllers/${normalizedName}`
private getConcretePath(normalizedPath: string) {
return `${this.clientFolder}/${normalizedPath}`
}

private normalizeName(name: string, baseTemplateName: string): string {
Expand Down
34 changes: 5 additions & 29 deletions src/Commands/New.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,14 @@
* file that was distributed with this source code.
*/

import ora from 'ora'
import chalk from 'chalk'
import Table from 'cli-table'

import { sep } from 'path'
import { existsSync } from 'fs'
import { promisify } from 'util'
import { Logger } from '@athenna/logger'
import { Folder, Path } from '@secjs/utils'
import { NodeExecException } from '../Exceptions/NodeExecException'

const exec = promisify(require('child_process').exec)
import { runCommand } from '../Utils/runCommand'

export class New {
private readonly logger: Logger
Expand Down Expand Up @@ -68,18 +64,18 @@ export class New {
const rmGitAndCopyEnv = `${cdCommand} && rm -rf .git && rm -rf .github && cp .env.example .env`
const moveProjectCommand = `mv ${projectPath} ${concretePath}`

await this.runCommand(
await runCommand(
cloneCommand,
`Cloning scaffold project from ${this.repositoryUrl}`,
)

await this.runCommand(
await runCommand(
rmGitAndCopyEnv,
'Removing defaults and creating .env file from .env.example',
)

await this.runCommand(runNpmInstallCommand, 'Installing dependencies')
await this.runCommand(moveProjectCommand, 'Moving project to your path')
await runCommand(runNpmInstallCommand, 'Installing dependencies')
await runCommand(moveProjectCommand, 'Moving project to your path')

console.log('\n')
this.logger.success(
Expand All @@ -99,24 +95,4 @@ export class New {

console.log(`\n${table.toString()}`)
}

private async runCommand(command: string, log?: string) {
const spinner = ora(log)

if (log) {
spinner.color = 'yellow'

spinner.start()
}

try {
await exec(command)

spinner.succeed(log)
} catch (err) {
spinner.fail(log)

throw new NodeExecException(command)
}
}
}
35 changes: 35 additions & 0 deletions src/Utils/runCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* @athenna/cli
*
* (c) Victor Tesoura Júnior <txsoura@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import ora from 'ora'
import { promisify } from 'util'
import { NodeExecException } from '../Exceptions/NodeExecException'

const exec = promisify(require('child_process').exec)

export async function runCommand(command: string, log?: string) {
const spinner = ora(log)

if (log) {
spinner.color = 'yellow'

spinner.start()
}

try {
await exec(command)

if (log) spinner.succeed(log)
} catch (err) {
// console.log(err)
if (log) spinner.fail(log)

throw new NodeExecException(command)
}
}
43 changes: 43 additions & 0 deletions templates/__name__Middleware.js.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { MiddlewareContract } from '@athenna/http'

export class <%= namePascal %>Middleware implements MiddlewareContract {
/**
* Use the constructor to resolve any dependency of the Ioc container
*
* @param container
* @return <%= namePascal %>Middleware
*/
constructor(_container) {}

/**
* Handle method is executed before the request gets in your controller.
*
* @param ctx
* @return any
*/
async handle({ next }) {
next()
}

/**
* Intercept method is executed before the response has been sent.
*
* @param ctx
* @return any
*/
async intercept({ body }) {
body.intercepted = true

return body
}

/**
* Terminate method is executed after the response has been sent.
*
* @param ctx
* @return any
*/
async terminate({ next }) {
next()
}
}
50 changes: 50 additions & 0 deletions templates/__name__Middleware.ts.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
HandleContextContract,
InterceptContextContract,
TerminateContextContract,
MiddlewareContract,
} from '@athenna/http'

import { Container } from 'providers/Container'

export class <%= namePascal %>Middleware implements MiddlewareContract {
/**
* Use the constructor to resolve any dependency of the Ioc container
*
* @param container
* @return <%= namePascal %>Middleware
*/
constructor(_container: Container) {}

/**
* Handle method is executed before the request gets in your controller.
*
* @param ctx
* @return any
*/
async handle({ next }: HandleContextContract) {
next()
}

/**
* Intercept method is executed before the response has been sent.
*
* @param ctx
* @return any
*/
async intercept({ body }: InterceptContextContract) {
body.intercepted = true

return body
}

/**
* Terminate method is executed after the response has been sent.
*
* @param ctx
* @return any
*/
async terminate({ next }: TerminateContextContract) {
next()
}
}
8 changes: 7 additions & 1 deletion tests/Unit/Commands/MakeTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,18 @@ import { Folder, Path } from '@secjs/utils'
import { Make } from '../../../src/Commands/Make'

describe('\n MakeTest', () => {
it('should be able to create a new http project', async () => {
it('should be able to create a controller file', async () => {
await new Make(Path.storage()).controller('TestControllers', { extension: 'ts' })

expect(existsSync(Path.storage('app/Http/Controllers/TestController.ts'))).toBeTruthy()
}, 60000)

it('should be able to create a middleware file', async () => {
await new Make(Path.storage()).middleware('TestMiddlewares', { extension: 'ts' })

expect(existsSync(Path.storage('app/Http/Middlewares/TestMiddleware.ts'))).toBeTruthy()
}, 60000)

afterEach(async () => {
await Folder.safeRemove('storage/app')
})
Expand Down

0 comments on commit a3e54f0

Please sign in to comment.