From a3108f86aba0521c9a2f9670135b32cd7b119e8a Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Thu, 20 Aug 2020 15:08:11 +0300 Subject: [PATCH] refactor: move generation rules to separate functions --- .../schematics/src/commands/api/index.ts | 189 ++++++++++++------ .../schematics/src/commands/proxy/index.ts | 23 ++- .../packages/schematics/src/models/service.ts | 10 + .../packages/schematics/src/utils/enum.ts | 10 +- .../packages/schematics/src/utils/model.ts | 22 +- .../packages/schematics/src/utils/service.ts | 27 +-- .../packages/schematics/src/utils/type.ts | 26 +-- .../schematics/src/utils/workspace.ts | 8 +- 8 files changed, 207 insertions(+), 108 deletions(-) diff --git a/npm/ng-packs/packages/schematics/src/commands/api/index.ts b/npm/ng-packs/packages/schematics/src/commands/api/index.ts index d6c01373e0..bf2922dc25 100644 --- a/npm/ng-packs/packages/schematics/src/commands/api/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/api/index.ts @@ -1,9 +1,33 @@ import { normalize, strings } from '@angular-devkit/core'; -import { applyTemplates, branchAndMerge, chain, move, SchematicContext, SchematicsException, Tree, url } from '@angular-devkit/schematics'; +import { + applyTemplates, + branchAndMerge, + chain, + move, + Rule, + SchematicContext, + SchematicsException, + Tree, + url, +} from '@angular-devkit/schematics'; import { Exception } from '../../enums'; -import { applyWithOverwrite, buildDefaultPath, createApiDefinitionReader, createControllerToServiceMapper, createImportRefsToModelMapper, createImportRefToEnumMapper, getEnumNamesFromImports, interpolate, resolveProject, serializeParameters } from '../../utils'; +import { ServiceGeneratorParams } from '../../models'; +import { + applyWithOverwrite, + buildDefaultPath, + createApiDefinitionReader, + createControllerToServiceMapper, + createImportRefsToModelMapper, + createImportRefToEnumMapper, + EnumGeneratorParams, + getEnumNamesFromImports, + interpolate, + ModelGeneratorParams, + resolveProject, + serializeParameters, +} from '../../utils'; import * as cases from '../../utils/text'; -import type { Schema as GenerateProxySchema } from './schema'; +import { Schema as GenerateProxySchema } from './schema'; export default function(params: GenerateProxySchema) { const solution = params.solution; @@ -13,79 +37,124 @@ export default function(params: GenerateProxySchema) { async (tree: Tree, _context: SchematicContext) => { const target = await resolveProject(tree, params.target!); const targetPath = buildDefaultPath(target.definition); - const readApiDefinition = createApiDefinitionReader(`${targetPath}/shared/api-definition.json`); + const readApiDefinition = createApiDefinitionReader( + `${targetPath}/shared/api-definition.json`, + ); const data = readApiDefinition(tree); const types = data.types; const definition = data.modules[moduleName]; - if (!definition) throw new SchematicsException(interpolate(Exception.InvalidModule, moduleName)); + if (!definition) + throw new SchematicsException(interpolate(Exception.InvalidModule, moduleName)); - const mapControllerToService = createControllerToServiceMapper(solution, types, definition.remoteServiceName); + const apiName = definition.remoteServiceName; const controllers = Object.values(definition.controllers || {}); const serviceImports: Record = {}; + const generateServices = createServiceGenerator({ + targetPath, + solution, + types, + apiName, + controllers, + serviceImports, + }); - const createServiceFiles = chain( - controllers.map(controller => { - const service = mapControllerToService(controller); - service.imports.forEach(({refs, path}) => refs.forEach(ref => { - if (path === '@abp/ng.core') return; - if (!serviceImports[path]) return (serviceImports[path] = [ref]); - serviceImports[path] = [...new Set([...serviceImports[path], ref])]; - })); + const modelImports: Record = {}; + const generateModels = createModelGenerator({ + targetPath, + solution, + types, + serviceImports, + modelImports, + }); - return applyWithOverwrite(url('./files-service'), [ - applyTemplates({ - ...cases, - serializeParameters, - ...service, - }), - move(normalize(targetPath)), - ]); - } - ), - ); + const generateEnums = createEnumGenerator({ + targetPath, + solution, + types, + serviceImports, + modelImports, + }); - const mapImportRefsToModel = createImportRefsToModelMapper(solution, types); - const modelImports: Record = {}; + return branchAndMerge(chain([generateServices, generateModels, generateEnums])); + }, + ]); +} - const createModelFiles = chain( - Object.values(serviceImports).map(refs => { - const model = mapImportRefsToModel(refs); - model.imports.forEach(({refs, path}) => refs.forEach(ref => { - if (path === '@abp/ng.core') return; - if (!modelImports[path]) return (modelImports[path] = [ref]); - modelImports[path] = [...new Set([...modelImports[path], ref])]; - })); +function createEnumGenerator(params: EnumGeneratorParams) { + const { targetPath, serviceImports, modelImports } = params; + const mapImportRefToEnum = createImportRefToEnumMapper(params); + const enumRefs = [ + ...new Set([ + ...getEnumNamesFromImports(serviceImports), + ...getEnumNamesFromImports(modelImports), + ]), + ]; - return applyWithOverwrite(url('./files-model'), [ - applyTemplates({ - ...cases, - ...model, - }), - move(normalize(targetPath)), - ]); + return chain( + enumRefs.map(ref => { + return applyWithOverwrite(url('./files-enum'), [ + applyTemplates({ + ...cases, + ...mapImportRefToEnum(ref), }), - ); + move(normalize(targetPath)), + ]); + }), + ); +} - const mapImportRefToEnum = createImportRefToEnumMapper(solution, types); - const enumRefs = [...new Set([ - ...getEnumNamesFromImports(serviceImports), - ...getEnumNamesFromImports(modelImports), - ])]; +function createModelGenerator(params: ModelGeneratorParams) { + const { targetPath, serviceImports, modelImports } = params; + const mapImportRefsToModel = createImportRefsToModelMapper(params); - const createEnumFiles = chain( - enumRefs.map(ref => { - return applyWithOverwrite(url('./files-enum'), [ - applyTemplates({ - ...cases, - ...mapImportRefToEnum(ref), - }), - move(normalize(targetPath)), - ]); + return chain( + Object.values(serviceImports).reduce((rules: Rule[], refs) => { + const model = mapImportRefsToModel(refs); + model.imports.forEach(({ refs, path }) => + refs.forEach(ref => { + if (path === '@abp/ng.core') return; + if (!modelImports[path]) return (modelImports[path] = [ref]); + modelImports[path] = [...new Set([...modelImports[path], ref])]; }), ); - return branchAndMerge(chain([createServiceFiles, createModelFiles, createEnumFiles])); - }, - ]); + const rule = applyWithOverwrite(url('./files-model'), [ + applyTemplates({ + ...cases, + ...model, + }), + move(normalize(targetPath)), + ]); + rules.push(rule); + + return rules; + }, []), + ); } +function createServiceGenerator(params: ServiceGeneratorParams) { + const { targetPath, controllers, serviceImports } = params; + const mapControllerToService = createControllerToServiceMapper(params); + + return chain( + controllers.map(controller => { + const service = mapControllerToService(controller); + service.imports.forEach(({ refs, path }) => + refs.forEach(ref => { + if (path === '@abp/ng.core') return; + if (!serviceImports[path]) return (serviceImports[path] = [ref]); + serviceImports[path] = [...new Set([...serviceImports[path], ref])]; + }), + ); + + return applyWithOverwrite(url('./files-service'), [ + applyTemplates({ + ...cases, + serializeParameters, + ...service, + }), + move(normalize(targetPath)), + ]); + }), + ); +} diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy/index.ts b/npm/ng-packs/packages/schematics/src/commands/proxy/index.ts index 24fcb91d24..d597f720cd 100644 --- a/npm/ng-packs/packages/schematics/src/commands/proxy/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/proxy/index.ts @@ -1,9 +1,21 @@ import { strings } from '@angular-devkit/core'; -import { branchAndMerge, chain, schematic, SchematicContext, Tree } from '@angular-devkit/schematics'; +import { + branchAndMerge, + chain, + schematic, + SchematicContext, + Tree, +} from '@angular-devkit/schematics'; import { API_DEFINITION_ENDPOINT } from '../../constants'; import { ApiDefinition } from '../../models'; -import { buildDefaultPath, createApiDefinitionSaver, getApiDefinition, getSourceUrl, resolveProject } from '../../utils'; -import type { Schema as GenerateProxySchema } from './schema'; +import { + buildDefaultPath, + createApiDefinitionSaver, + getApiDefinition, + getSourceUrl, + resolveProject, +} from '../../utils'; +import { Schema as GenerateProxySchema } from './schema'; export default function(params: GenerateProxySchema) { const moduleName = strings.camelize(params.module || 'app'); @@ -16,7 +28,10 @@ export default function(params: GenerateProxySchema) { const targetPath = buildDefaultPath(target.definition); const data: ApiDefinition = await getApiDefinition(sourceUrl + API_DEFINITION_ENDPOINT); - const saveApiDefinition = createApiDefinitionSaver(data, `${targetPath}/shared/api-definition.json`) + const saveApiDefinition = createApiDefinitionSaver( + data, + `${targetPath}/shared/api-definition.json`, + ); const createApi = schematic('api', params); return branchAndMerge(chain([saveApiDefinition, createApi])); diff --git a/npm/ng-packs/packages/schematics/src/models/service.ts b/npm/ng-packs/packages/schematics/src/models/service.ts index c9ef05eca4..98a18bddd8 100644 --- a/npm/ng-packs/packages/schematics/src/models/service.ts +++ b/npm/ng-packs/packages/schematics/src/models/service.ts @@ -1,7 +1,17 @@ +import { Controller, Type } from './api-definition'; import { Import } from './import'; import { Method } from './method'; import { Omissible } from './util'; +export interface ServiceGeneratorParams { + targetPath: string; + solution: string; + types: Record; + apiName: string; + controllers: Controller[]; + serviceImports: Record; +} + export class Service { apiName: string; imports: Import[] = []; diff --git a/npm/ng-packs/packages/schematics/src/utils/enum.ts b/npm/ng-packs/packages/schematics/src/utils/enum.ts index d58ed23a36..3b6de0134a 100644 --- a/npm/ng-packs/packages/schematics/src/utils/enum.ts +++ b/npm/ng-packs/packages/schematics/src/utils/enum.ts @@ -4,6 +4,14 @@ import { Type } from '../models'; import { interpolate } from './common'; import { parseNamespace } from './namespace'; +export interface EnumGeneratorParams { + targetPath: string; + solution: string; + types: Record; + serviceImports: Record; + modelImports: Record; +} + export function getEnumNamesFromImports(serviceImports: Record) { return Object.keys(serviceImports) .filter(path => path.includes('/enums/')) @@ -13,7 +21,7 @@ export function getEnumNamesFromImports(serviceImports: Record }, []); } -export function createImportRefToEnumMapper(solution: string, types: Record) { +export function createImportRefToEnumMapper({ solution, types }: EnumGeneratorParams) { return (ref: string) => { const { enumNames, enumValues } = types[ref]; if (!enumNames || !enumValues) diff --git a/npm/ng-packs/packages/schematics/src/utils/model.ts b/npm/ng-packs/packages/schematics/src/utils/model.ts index ec454aad6f..89de6e3888 100644 --- a/npm/ng-packs/packages/schematics/src/utils/model.ts +++ b/npm/ng-packs/packages/schematics/src/utils/model.ts @@ -9,10 +9,19 @@ import { createTypesToImportsReducer, flattenUnionTypes, normalizeTypeAnnotations, + removeTypeModifiers, } from './type'; -export function createImportRefsToModelMapper(solution: string, types: Record) { - const mapImportRefToInterface = createImportRefToInterfaceMapper(solution, types); +export interface ModelGeneratorParams { + targetPath: string; + solution: string; + types: Record; + serviceImports: Record; + modelImports: Record; +} + +export function createImportRefsToModelMapper({ solution, types }: ModelGeneratorParams) { + const mapImportRefToInterface = createImportRefToInterfaceMapper(types); const createImportRefToImportReducer = createImportRefToImportReducerCreator(solution, types); return (importRefs: string[]) => { @@ -53,8 +62,9 @@ function sortInterfaces(interfaces: Interface[]) { interfaces.sort((a, b) => (a.identifier > b.identifier ? 1 : -1)); } -export function createImportRefToInterfaceMapper(solution: string, types: Record) { - const simplifyType = createTypeSimplifier(solution); +export function createImportRefToInterfaceMapper(types: Record) { + const simplifyType = createTypeSimplifier(); + const getIdentifier = (type: string) => removeTypeModifiers(simplifyType(type)); return (ref: string) => { const typeDef = types[ref]; @@ -62,10 +72,10 @@ export function createImportRefToInterfaceMapper(solution: string, types: Record const identifier = (typeDef.genericArguments ?? []).reduce( (acc, t, i) => acc.replace(`T${i}`, t), - simplifyType(ref), + getIdentifier(ref), ); - const base = typeDef.baseType ? simplifyType(typeDef.baseType) : null; + const base = typeDef.baseType ? getIdentifier(typeDef.baseType) : null; const _interface = new Interface({ identifier, base, ref }); typeDef.properties?.forEach(({ name, typeSimple }) => { diff --git a/npm/ng-packs/packages/schematics/src/utils/service.ts b/npm/ng-packs/packages/schematics/src/utils/service.ts index 57d8c97e0c..085b57c172 100644 --- a/npm/ng-packs/packages/schematics/src/utils/service.ts +++ b/npm/ng-packs/packages/schematics/src/utils/service.ts @@ -6,6 +6,7 @@ import { Method, Property, Service, + ServiceGeneratorParams, Signature, Type, TypeWithEnum, @@ -19,12 +20,12 @@ export function serializeParameters(parameters: Property[]) { return parameters.map(p => p.name + p.optional + ': ' + p.type + p.default, '').join(', '); } -export function createControllerToServiceMapper( - solution: string, - types: Record, - apiName: string, -) { - const mapActionToMethod = createActionToMethodMapper(solution); +export function createControllerToServiceMapper({ + solution, + types, + apiName, +}: ServiceGeneratorParams) { + const mapActionToMethod = createActionToMethodMapper(); return (controller: Controller) => { const name = controller.controllerName; @@ -44,9 +45,9 @@ function sortMethods(methods: Method[]) { methods.sort((a, b) => (a.signature.name > b.signature.name ? 1 : -1)); } -export function createActionToMethodMapper(solution: string) { - const mapActionToBody = createActionToBodyMapper(solution); - const mapActionToSignature = createActionToSignatureMapper(solution); +export function createActionToMethodMapper() { + const mapActionToBody = createActionToBodyMapper(); + const mapActionToSignature = createActionToSignatureMapper(); return (action: Action) => { const body = mapActionToBody(action); @@ -55,8 +56,8 @@ export function createActionToMethodMapper(solution: string) { }; } -export function createActionToBodyMapper(solution: string) { - const adaptType = createTypeAdapter(solution); +export function createActionToBodyMapper() { + const adaptType = createTypeAdapter(); return ({ httpMethod, parameters, returnValue, url }: Action) => { const responseType = adaptType(returnValue.typeSimple); @@ -68,8 +69,8 @@ export function createActionToBodyMapper(solution: string) { }; } -export function createActionToSignatureMapper(solution: string) { - const adaptType = createTypeAdapter(solution); +export function createActionToSignatureMapper() { + const adaptType = createTypeAdapter(); return (action: Action) => { const signature = new Signature({ name: getMethodNameFromAction(action) }); diff --git a/npm/ng-packs/packages/schematics/src/utils/type.ts b/npm/ng-packs/packages/schematics/src/utils/type.ts index 6157275fc2..3a48fd9018 100644 --- a/npm/ng-packs/packages/schematics/src/utils/type.ts +++ b/npm/ng-packs/packages/schematics/src/utils/type.ts @@ -6,25 +6,15 @@ import { parseNamespace } from './namespace'; import { relativePathToEnum, relativePathToModel } from './path'; import { parseGenerics } from './tree'; -export function createTypeSimplifier(solution: string) { - const solutionRegex = new RegExp(solution.replace(/\./g, `\.`) + `\.`); - const voloRegex = /^Volo\.(Abp\.?)(Application|ObjectExtending\.?)/; - - return createTypeParser( - type => - type - .replace(voloRegex, '') - .replace(solutionRegex, '') - .split('.') - .pop()!, - ); +export function createTypeSimplifier() { + return createTypeParser(type => type.split('.').pop()!); } export function createTypeParser(replacerFn = (t: string) => t) { return (originalType: string) => flattenUnionTypes([], originalType) .map(type => { - type = removeTypeModifiers(normalizeTypeAnnotations(type)); + type = normalizeTypeAnnotations(type); type = type.replace( /System\.([0-9A-Za-z]+)/g, (_, match) => SYSTEM_TYPES.get(match) ?? strings.camelize(match), @@ -78,15 +68,15 @@ export function createTypesToImportsReducer(solution: string, namespace: string) } export function createTypeToImportMapper(solution: string, namespace: string) { - const adaptType = createTypeAdapter(solution); - const simplifyType = createTypeSimplifier(solution); + const adaptType = createTypeAdapter(); + const simplifyType = createTypeSimplifier(); return (type: string, isEnum: boolean) => { if (!type || type.startsWith('System')) return; const modelNamespace = parseNamespace(solution, type); const refs = [removeTypeModifiers(type)]; - const specifiers = [adaptType(simplifyType(type).split('<')[0])]; + const specifiers = [adaptType(simplifyType(refs[0]).split('<')[0])]; const path = /^Volo\.Abp\.(Application\.Dtos|ObjectExtending)/.test(type) ? '@abp/ng.core' : isEnum @@ -97,7 +87,7 @@ export function createTypeToImportMapper(solution: string, namespace: string) { }; } -export function createTypeAdapter(solution: string) { - const simplifyType = createTypeSimplifier(solution); +export function createTypeAdapter() { + const simplifyType = createTypeSimplifier(); return (type: string) => parseGenerics(type, node => simplifyType(node.data)).toString(); } diff --git a/npm/ng-packs/packages/schematics/src/utils/workspace.ts b/npm/ng-packs/packages/schematics/src/utils/workspace.ts index b05a7549e8..56c0411493 100644 --- a/npm/ng-packs/packages/schematics/src/utils/workspace.ts +++ b/npm/ng-packs/packages/schematics/src/utils/workspace.ts @@ -1,6 +1,5 @@ import { experimental, strings, workspaces } from '@angular-devkit/core'; -import { SchematicsException } from '@angular-devkit/schematics'; -import type { Tree } from '@angular-devkit/schematics'; +import { SchematicsException, Tree } from '@angular-devkit/schematics'; import { Exception } from '../enums'; import { Project } from '../models'; import { getWorkspace, ProjectType } from './angular'; @@ -35,10 +34,7 @@ export function readWorkspaceSchema(tree: Tree) { return workspaceSchema; } -export async function resolveProject( - tree: Tree, - name: string, -): Promise { +export async function resolveProject(tree: Tree, name: string): Promise { name = name || readWorkspaceSchema(tree).defaultProject!; const workspace = await getWorkspace(tree); let definition: Project['definition'] | undefined;