diff --git a/packages/schematics/collection.json b/packages/schematics/collection.json index 6def1d0..2a4b9f5 100644 --- a/packages/schematics/collection.json +++ b/packages/schematics/collection.json @@ -20,6 +20,11 @@ "factory": "./dist/lib/service/service.factory#main", "description": "Create a @rhtml service.", "schema": "./dist/lib/service/schema.json" + }, + "controller": { + "factory": "./dist/lib/controller/controller.factory#main", + "description": "Create a @rhtml controller.", + "schema": "./dist/lib/controller/schema.json" } } -} +} \ No newline at end of file diff --git a/packages/schematics/src/lib/controller/controller.factory.ts b/packages/schematics/src/lib/controller/controller.factory.ts new file mode 100644 index 0000000..11cf5a9 --- /dev/null +++ b/packages/schematics/src/lib/controller/controller.factory.ts @@ -0,0 +1,92 @@ +import { join, Path, strings } from '@angular-devkit/core'; +import { + apply, + branchAndMerge, + chain, + filter, + mergeWith, + move, + noop, + Rule, + SchematicContext, + template, + Tree, + url, +} from '@angular-devkit/schematics'; + +import { + DeclarationOptions, + ModuleDeclarator, +} from '../../utils/module.declarator'; +import { ModuleFinder } from '../../utils/module.finder'; +import { Location, NameParser } from '../../utils/name.parser'; +import { mergeSourceRoot } from '../../utils/source-root.helpers'; +import { DEFAULT_LANGUAGE } from '../defaults'; +import { ControllerOptions } from './controller.schema'; + +const ELEMENT_METADATA = 'bootstrap'; +const ELEMENT_TYPE = 'controller'; + +export function main(options: ControllerOptions): Rule { + options = transform(options); + return (tree: Tree, context: SchematicContext) => { + return branchAndMerge( + chain([ + mergeSourceRoot(options), + mergeWith(generate(options)), + addDeclarationToModule(options), + ]) + )(tree, context); + }; +} + +function transform(source: ControllerOptions): ControllerOptions { + const target: ControllerOptions = Object.assign({}, source); + target.metadata = ELEMENT_METADATA; + target.type = ELEMENT_TYPE; + + const location: Location = new NameParser().parse(target); + target.name = strings.dasherize(location.name); + target.path = strings.dasherize(location.path); + target.language = + target.language !== undefined ? target.language : DEFAULT_LANGUAGE; + + target.path = target.flat + ? target.path + : join(target.path as Path, target.name); + return target; +} + +function generate(options: ControllerOptions) { + return (context: SchematicContext) => + apply(url(join('./files' as Path, options.language)), [ + options.spec ? noop() : filter((path) => !path.endsWith('.spec.ts')), + template({ + ...strings, + ...options, + }), + move(options.path), + ])(context); +} + +function addDeclarationToModule(options: ControllerOptions): Rule { + return (tree: Tree) => { + if (options.skipImport !== undefined && options.skipImport) { + return tree; + } + options.module = new ModuleFinder(tree).find({ + name: options.name, + path: options.path as Path, + }); + if (!options.module) { + return tree; + } + const content = tree.read(options.module).toString(); + const declarator: ModuleDeclarator = new ModuleDeclarator(); + tree.overwrite( + options.module, + declarator.declare(content, options as DeclarationOptions) + ); + return tree; + }; +} diff --git a/packages/schematics/src/lib/controller/files/ts/__name__.controller.spec.ts b/packages/schematics/src/lib/controller/files/ts/__name__.controller.spec.ts new file mode 100644 index 0000000..2883d56 --- /dev/null +++ b/packages/schematics/src/lib/controller/files/ts/__name__.controller.spec.ts @@ -0,0 +1,33 @@ +import { Bootstrap, get, Module } from '@rhtml/di' +import { FastifyModule } from '@rhtml/fastify' +import fastify from 'fastify' + +import { <%= classify(name) %>Controller } from './<%= name %>.controller'; + +describe('<%= classify(name) %> Controller', () => { + let <%= decamelize(name) %>Controller: <%= classify(name) %>Controller + + beforeAll(async () => { + @Module({ + imports: [ + FastifyModule.forRoot(fastify, { + logger: true, + }), + ], + bootstrap: [<%= classify(name) %>Controller], + }) + class AppModule {} + + await Bootstrap(AppModule) + <%= decamelize(name) %>Controller = get(<%= classify(name) %>Controller) + + }); + + it('e2e: get => (<%= classify(name) %>) : Should sucessfully return expected result', async () => { + const response = { + hello: 'world', + } + const result = await <%= decamelize(name) %>Controller.get<%= classify(name) %>() + expect(result).toEqual(response) + }); +}); diff --git a/packages/schematics/src/lib/controller/files/ts/__name__.controller.ts b/packages/schematics/src/lib/controller/files/ts/__name__.controller.ts new file mode 100644 index 0000000..5d44a83 --- /dev/null +++ b/packages/schematics/src/lib/controller/files/ts/__name__.controller.ts @@ -0,0 +1,16 @@ +import { Controller, Route } from '@rhtml/fastify' + +@Controller({ + route: '/<%= decamelize(name) %>', +}) +export class <%= classify(name) %>Controller { + + @Route({ + method: 'GET', + }) + get<%= classify(name) %>() { + return { + hello: 'world' + } + } +} diff --git a/packages/schematics/src/lib/controller/schema.json b/packages/schematics/src/lib/controller/schema.json new file mode 100644 index 0000000..6274bcd --- /dev/null +++ b/packages/schematics/src/lib/controller/schema.json @@ -0,0 +1,49 @@ +{ + "$schema": "http://json-schema.org/schema", + "id": "SchematicsRHTMLController", + "title": "RHTML Controller Options Schema", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the controller.", + "$default": { + "$source": "argv", + "index": 0 + }, + "x-prompt": "What name would you like to use for the controller?" + }, + "path": { + "type": "string", + "format": "path", + "description": "The path to create the controller." + }, + "language": { + "type": "string", + "description": "RHTML controller language (ts/js)." + }, + "sourceRoot": { + "type": "string", + "description": "RHTML controller source root directory." + }, + "skipImport": { + "description": "Flag to skip the module import.", + "default": false + }, + "module": { + "type": "string", + "description": "Allows specification of the declaring module." + }, + "flat": { + "default": false, + "description": "Flag to indicate if a directory is created." + }, + "spec": { + "default": true, + "description": "Specifies if a spec file is generated." + } + }, + "required": [ + "name" + ] +} \ No newline at end of file diff --git a/packages/schematics/src/lib/service/service.factory.ts b/packages/schematics/src/lib/service/service.factory.ts index e72e9e0..5b253f6 100644 --- a/packages/schematics/src/lib/service/service.factory.ts +++ b/packages/schematics/src/lib/service/service.factory.ts @@ -14,7 +14,6 @@ import { Tree, url, } from '@angular-devkit/schematics'; -import { isNullOrUndefined } from 'util'; import { DeclarationOptions, @@ -52,7 +51,7 @@ function transform(source: ServiceOptions): ServiceOptions { target.metadata = 'providers'; target.type = 'service'; - if (isNullOrUndefined(target.name)) { + if (!target.name) { throw new SchematicsException('Option (name) is required.'); } const location: Location = new NameParser().parse(target);