Browse Source

refactor: move generation rules to separate functions

pull/5137/head
Arman Ozak 6 years ago
parent
commit
a3108f86ab
  1. 189
      npm/ng-packs/packages/schematics/src/commands/api/index.ts
  2. 23
      npm/ng-packs/packages/schematics/src/commands/proxy/index.ts
  3. 10
      npm/ng-packs/packages/schematics/src/models/service.ts
  4. 10
      npm/ng-packs/packages/schematics/src/utils/enum.ts
  5. 22
      npm/ng-packs/packages/schematics/src/utils/model.ts
  6. 27
      npm/ng-packs/packages/schematics/src/utils/service.ts
  7. 26
      npm/ng-packs/packages/schematics/src/utils/type.ts
  8. 8
      npm/ng-packs/packages/schematics/src/utils/workspace.ts

189
npm/ng-packs/packages/schematics/src/commands/api/index.ts

@ -1,9 +1,33 @@
import { normalize, strings } from '@angular-devkit/core'; 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 { 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 * as cases from '../../utils/text';
import type { Schema as GenerateProxySchema } from './schema'; import { Schema as GenerateProxySchema } from './schema';
export default function(params: GenerateProxySchema) { export default function(params: GenerateProxySchema) {
const solution = params.solution; const solution = params.solution;
@ -13,79 +37,124 @@ export default function(params: GenerateProxySchema) {
async (tree: Tree, _context: SchematicContext) => { async (tree: Tree, _context: SchematicContext) => {
const target = await resolveProject(tree, params.target!); const target = await resolveProject(tree, params.target!);
const targetPath = buildDefaultPath(target.definition); 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 data = readApiDefinition(tree);
const types = data.types; const types = data.types;
const definition = data.modules[moduleName]; 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 controllers = Object.values(definition.controllers || {});
const serviceImports: Record<string, string[]> = {}; const serviceImports: Record<string, string[]> = {};
const generateServices = createServiceGenerator({
targetPath,
solution,
types,
apiName,
controllers,
serviceImports,
});
const createServiceFiles = chain( const modelImports: Record<string, string[]> = {};
controllers.map(controller => { const generateModels = createModelGenerator({
const service = mapControllerToService(controller); targetPath,
service.imports.forEach(({refs, path}) => refs.forEach(ref => { solution,
if (path === '@abp/ng.core') return; types,
if (!serviceImports[path]) return (serviceImports[path] = [ref]); serviceImports,
serviceImports[path] = [...new Set([...serviceImports[path], ref])]; modelImports,
})); });
return applyWithOverwrite(url('./files-service'), [ const generateEnums = createEnumGenerator({
applyTemplates({ targetPath,
...cases, solution,
serializeParameters, types,
...service, serviceImports,
}), modelImports,
move(normalize(targetPath)), });
]);
}
),
);
const mapImportRefsToModel = createImportRefsToModelMapper(solution, types); return branchAndMerge(chain([generateServices, generateModels, generateEnums]));
const modelImports: Record<string, string[]> = {}; },
]);
}
const createModelFiles = chain( function createEnumGenerator(params: EnumGeneratorParams) {
Object.values(serviceImports).map(refs => { const { targetPath, serviceImports, modelImports } = params;
const model = mapImportRefsToModel(refs); const mapImportRefToEnum = createImportRefToEnumMapper(params);
model.imports.forEach(({refs, path}) => refs.forEach(ref => { const enumRefs = [
if (path === '@abp/ng.core') return; ...new Set([
if (!modelImports[path]) return (modelImports[path] = [ref]); ...getEnumNamesFromImports(serviceImports),
modelImports[path] = [...new Set([...modelImports[path], ref])]; ...getEnumNamesFromImports(modelImports),
})); ]),
];
return applyWithOverwrite(url('./files-model'), [ return chain(
applyTemplates({ enumRefs.map(ref => {
...cases, return applyWithOverwrite(url('./files-enum'), [
...model, applyTemplates({
}), ...cases,
move(normalize(targetPath)), ...mapImportRefToEnum(ref),
]);
}), }),
); move(normalize(targetPath)),
]);
}),
);
}
const mapImportRefToEnum = createImportRefToEnumMapper(solution, types); function createModelGenerator(params: ModelGeneratorParams) {
const enumRefs = [...new Set([ const { targetPath, serviceImports, modelImports } = params;
...getEnumNamesFromImports(serviceImports), const mapImportRefsToModel = createImportRefsToModelMapper(params);
...getEnumNamesFromImports(modelImports),
])];
const createEnumFiles = chain( return chain(
enumRefs.map(ref => { Object.values(serviceImports).reduce((rules: Rule[], refs) => {
return applyWithOverwrite(url('./files-enum'), [ const model = mapImportRefsToModel(refs);
applyTemplates({ model.imports.forEach(({ refs, path }) =>
...cases, refs.forEach(ref => {
...mapImportRefToEnum(ref), if (path === '@abp/ng.core') return;
}), if (!modelImports[path]) return (modelImports[path] = [ref]);
move(normalize(targetPath)), 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)),
]);
}),
);
}

23
npm/ng-packs/packages/schematics/src/commands/proxy/index.ts

@ -1,9 +1,21 @@
import { strings } from '@angular-devkit/core'; 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 { API_DEFINITION_ENDPOINT } from '../../constants';
import { ApiDefinition } from '../../models'; import { ApiDefinition } from '../../models';
import { buildDefaultPath, createApiDefinitionSaver, getApiDefinition, getSourceUrl, resolveProject } from '../../utils'; import {
import type { Schema as GenerateProxySchema } from './schema'; buildDefaultPath,
createApiDefinitionSaver,
getApiDefinition,
getSourceUrl,
resolveProject,
} from '../../utils';
import { Schema as GenerateProxySchema } from './schema';
export default function(params: GenerateProxySchema) { export default function(params: GenerateProxySchema) {
const moduleName = strings.camelize(params.module || 'app'); const moduleName = strings.camelize(params.module || 'app');
@ -16,7 +28,10 @@ export default function(params: GenerateProxySchema) {
const targetPath = buildDefaultPath(target.definition); const targetPath = buildDefaultPath(target.definition);
const data: ApiDefinition = await getApiDefinition(sourceUrl + API_DEFINITION_ENDPOINT); 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); const createApi = schematic('api', params);
return branchAndMerge(chain([saveApiDefinition, createApi])); return branchAndMerge(chain([saveApiDefinition, createApi]));

10
npm/ng-packs/packages/schematics/src/models/service.ts

@ -1,7 +1,17 @@
import { Controller, Type } from './api-definition';
import { Import } from './import'; import { Import } from './import';
import { Method } from './method'; import { Method } from './method';
import { Omissible } from './util'; import { Omissible } from './util';
export interface ServiceGeneratorParams {
targetPath: string;
solution: string;
types: Record<string, Type>;
apiName: string;
controllers: Controller[];
serviceImports: Record<string, string[]>;
}
export class Service { export class Service {
apiName: string; apiName: string;
imports: Import[] = []; imports: Import[] = [];

10
npm/ng-packs/packages/schematics/src/utils/enum.ts

@ -4,6 +4,14 @@ import { Type } from '../models';
import { interpolate } from './common'; import { interpolate } from './common';
import { parseNamespace } from './namespace'; import { parseNamespace } from './namespace';
export interface EnumGeneratorParams {
targetPath: string;
solution: string;
types: Record<string, Type>;
serviceImports: Record<string, string[]>;
modelImports: Record<string, string[]>;
}
export function getEnumNamesFromImports(serviceImports: Record<string, string[]>) { export function getEnumNamesFromImports(serviceImports: Record<string, string[]>) {
return Object.keys(serviceImports) return Object.keys(serviceImports)
.filter(path => path.includes('/enums/')) .filter(path => path.includes('/enums/'))
@ -13,7 +21,7 @@ export function getEnumNamesFromImports(serviceImports: Record<string, string[]>
}, []); }, []);
} }
export function createImportRefToEnumMapper(solution: string, types: Record<string, Type>) { export function createImportRefToEnumMapper({ solution, types }: EnumGeneratorParams) {
return (ref: string) => { return (ref: string) => {
const { enumNames, enumValues } = types[ref]; const { enumNames, enumValues } = types[ref];
if (!enumNames || !enumValues) if (!enumNames || !enumValues)

22
npm/ng-packs/packages/schematics/src/utils/model.ts

@ -9,10 +9,19 @@ import {
createTypesToImportsReducer, createTypesToImportsReducer,
flattenUnionTypes, flattenUnionTypes,
normalizeTypeAnnotations, normalizeTypeAnnotations,
removeTypeModifiers,
} from './type'; } from './type';
export function createImportRefsToModelMapper(solution: string, types: Record<string, Type>) { export interface ModelGeneratorParams {
const mapImportRefToInterface = createImportRefToInterfaceMapper(solution, types); targetPath: string;
solution: string;
types: Record<string, Type>;
serviceImports: Record<string, string[]>;
modelImports: Record<string, string[]>;
}
export function createImportRefsToModelMapper({ solution, types }: ModelGeneratorParams) {
const mapImportRefToInterface = createImportRefToInterfaceMapper(types);
const createImportRefToImportReducer = createImportRefToImportReducerCreator(solution, types); const createImportRefToImportReducer = createImportRefToImportReducerCreator(solution, types);
return (importRefs: string[]) => { return (importRefs: string[]) => {
@ -53,8 +62,9 @@ function sortInterfaces(interfaces: Interface[]) {
interfaces.sort((a, b) => (a.identifier > b.identifier ? 1 : -1)); interfaces.sort((a, b) => (a.identifier > b.identifier ? 1 : -1));
} }
export function createImportRefToInterfaceMapper(solution: string, types: Record<string, Type>) { export function createImportRefToInterfaceMapper(types: Record<string, Type>) {
const simplifyType = createTypeSimplifier(solution); const simplifyType = createTypeSimplifier();
const getIdentifier = (type: string) => removeTypeModifiers(simplifyType(type));
return (ref: string) => { return (ref: string) => {
const typeDef = types[ref]; const typeDef = types[ref];
@ -62,10 +72,10 @@ export function createImportRefToInterfaceMapper(solution: string, types: Record
const identifier = (typeDef.genericArguments ?? []).reduce( const identifier = (typeDef.genericArguments ?? []).reduce(
(acc, t, i) => acc.replace(`T${i}`, t), (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 }); const _interface = new Interface({ identifier, base, ref });
typeDef.properties?.forEach(({ name, typeSimple }) => { typeDef.properties?.forEach(({ name, typeSimple }) => {

27
npm/ng-packs/packages/schematics/src/utils/service.ts

@ -6,6 +6,7 @@ import {
Method, Method,
Property, Property,
Service, Service,
ServiceGeneratorParams,
Signature, Signature,
Type, Type,
TypeWithEnum, TypeWithEnum,
@ -19,12 +20,12 @@ export function serializeParameters(parameters: Property[]) {
return parameters.map(p => p.name + p.optional + ': ' + p.type + p.default, '').join(', '); return parameters.map(p => p.name + p.optional + ': ' + p.type + p.default, '').join(', ');
} }
export function createControllerToServiceMapper( export function createControllerToServiceMapper({
solution: string, solution,
types: Record<string, Type>, types,
apiName: string, apiName,
) { }: ServiceGeneratorParams) {
const mapActionToMethod = createActionToMethodMapper(solution); const mapActionToMethod = createActionToMethodMapper();
return (controller: Controller) => { return (controller: Controller) => {
const name = controller.controllerName; const name = controller.controllerName;
@ -44,9 +45,9 @@ function sortMethods(methods: Method[]) {
methods.sort((a, b) => (a.signature.name > b.signature.name ? 1 : -1)); methods.sort((a, b) => (a.signature.name > b.signature.name ? 1 : -1));
} }
export function createActionToMethodMapper(solution: string) { export function createActionToMethodMapper() {
const mapActionToBody = createActionToBodyMapper(solution); const mapActionToBody = createActionToBodyMapper();
const mapActionToSignature = createActionToSignatureMapper(solution); const mapActionToSignature = createActionToSignatureMapper();
return (action: Action) => { return (action: Action) => {
const body = mapActionToBody(action); const body = mapActionToBody(action);
@ -55,8 +56,8 @@ export function createActionToMethodMapper(solution: string) {
}; };
} }
export function createActionToBodyMapper(solution: string) { export function createActionToBodyMapper() {
const adaptType = createTypeAdapter(solution); const adaptType = createTypeAdapter();
return ({ httpMethod, parameters, returnValue, url }: Action) => { return ({ httpMethod, parameters, returnValue, url }: Action) => {
const responseType = adaptType(returnValue.typeSimple); const responseType = adaptType(returnValue.typeSimple);
@ -68,8 +69,8 @@ export function createActionToBodyMapper(solution: string) {
}; };
} }
export function createActionToSignatureMapper(solution: string) { export function createActionToSignatureMapper() {
const adaptType = createTypeAdapter(solution); const adaptType = createTypeAdapter();
return (action: Action) => { return (action: Action) => {
const signature = new Signature({ name: getMethodNameFromAction(action) }); const signature = new Signature({ name: getMethodNameFromAction(action) });

26
npm/ng-packs/packages/schematics/src/utils/type.ts

@ -6,25 +6,15 @@ import { parseNamespace } from './namespace';
import { relativePathToEnum, relativePathToModel } from './path'; import { relativePathToEnum, relativePathToModel } from './path';
import { parseGenerics } from './tree'; import { parseGenerics } from './tree';
export function createTypeSimplifier(solution: string) { export function createTypeSimplifier() {
const solutionRegex = new RegExp(solution.replace(/\./g, `\.`) + `\.`); return createTypeParser(type => type.split('.').pop()!);
const voloRegex = /^Volo\.(Abp\.?)(Application|ObjectExtending\.?)/;
return createTypeParser(
type =>
type
.replace(voloRegex, '')
.replace(solutionRegex, '')
.split('.')
.pop()!,
);
} }
export function createTypeParser(replacerFn = (t: string) => t) { export function createTypeParser(replacerFn = (t: string) => t) {
return (originalType: string) => return (originalType: string) =>
flattenUnionTypes([], originalType) flattenUnionTypes([], originalType)
.map(type => { .map(type => {
type = removeTypeModifiers(normalizeTypeAnnotations(type)); type = normalizeTypeAnnotations(type);
type = type.replace( type = type.replace(
/System\.([0-9A-Za-z]+)/g, /System\.([0-9A-Za-z]+)/g,
(_, match) => SYSTEM_TYPES.get(match) ?? strings.camelize(match), (_, 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) { export function createTypeToImportMapper(solution: string, namespace: string) {
const adaptType = createTypeAdapter(solution); const adaptType = createTypeAdapter();
const simplifyType = createTypeSimplifier(solution); const simplifyType = createTypeSimplifier();
return (type: string, isEnum: boolean) => { return (type: string, isEnum: boolean) => {
if (!type || type.startsWith('System')) return; if (!type || type.startsWith('System')) return;
const modelNamespace = parseNamespace(solution, type); const modelNamespace = parseNamespace(solution, type);
const refs = [removeTypeModifiers(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) const path = /^Volo\.Abp\.(Application\.Dtos|ObjectExtending)/.test(type)
? '@abp/ng.core' ? '@abp/ng.core'
: isEnum : isEnum
@ -97,7 +87,7 @@ export function createTypeToImportMapper(solution: string, namespace: string) {
}; };
} }
export function createTypeAdapter(solution: string) { export function createTypeAdapter() {
const simplifyType = createTypeSimplifier(solution); const simplifyType = createTypeSimplifier();
return (type: string) => parseGenerics(type, node => simplifyType(node.data)).toString(); return (type: string) => parseGenerics(type, node => simplifyType(node.data)).toString();
} }

8
npm/ng-packs/packages/schematics/src/utils/workspace.ts

@ -1,6 +1,5 @@
import { experimental, strings, workspaces } from '@angular-devkit/core'; import { experimental, strings, workspaces } from '@angular-devkit/core';
import { SchematicsException } from '@angular-devkit/schematics'; import { SchematicsException, Tree } from '@angular-devkit/schematics';
import type { Tree } from '@angular-devkit/schematics';
import { Exception } from '../enums'; import { Exception } from '../enums';
import { Project } from '../models'; import { Project } from '../models';
import { getWorkspace, ProjectType } from './angular'; import { getWorkspace, ProjectType } from './angular';
@ -35,10 +34,7 @@ export function readWorkspaceSchema(tree: Tree) {
return workspaceSchema; return workspaceSchema;
} }
export async function resolveProject( export async function resolveProject(tree: Tree, name: string): Promise<Project> {
tree: Tree,
name: string,
): Promise<Project> {
name = name || readWorkspaceSchema(tree).defaultProject!; name = name || readWorkspaceSchema(tree).defaultProject!;
const workspace = await getWorkspace(tree); const workspace = await getWorkspace(tree);
let definition: Project['definition'] | undefined; let definition: Project['definition'] | undefined;

Loading…
Cancel
Save