From d31aee32b3e0bb92e45b45cd7f476b1b670843b5 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Wed, 19 Aug 2020 16:08:01 +0300 Subject: [PATCH] feat: handle model imports in model schematics --- .../__namespace@dir__/index.ts.template | 10 +++ .../schematics/src/commands/api/index.ts | 1 + .../packages/schematics/src/utils/model.ts | 80 ++++++++++++++++--- .../packages/schematics/src/utils/type.ts | 14 ++-- 4 files changed, 88 insertions(+), 17 deletions(-) diff --git a/npm/ng-packs/packages/schematics/src/commands/api/files-model/shared/models/__namespace@dir__/index.ts.template b/npm/ng-packs/packages/schematics/src/commands/api/files-model/shared/models/__namespace@dir__/index.ts.template index e69de29bb2..c5790647c5 100644 --- a/npm/ng-packs/packages/schematics/src/commands/api/files-model/shared/models/__namespace@dir__/index.ts.template +++ b/npm/ng-packs/packages/schematics/src/commands/api/files-model/shared/models/__namespace@dir__/index.ts.template @@ -0,0 +1,10 @@ +<% +for (const {keyword, specifiers, path} of imports) { +%><%= keyword %> { <%= specifiers.join(', ') %> } from '<%= path %>'; +<% } +for (let {base, identifier, properties} of interfaces) { %> +export interface <%= identifier %> <%= base ? `extends ${base} ` : '' %>{<% + for (let {name, optional, type} of properties) { %> + <%= name + optional %>: <%= type %>;<% } %> +} +<% } %> \ No newline at end of file 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 f89bcc2ae9..fa599adc71 100644 --- a/npm/ng-packs/packages/schematics/src/commands/api/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/api/index.ts @@ -26,6 +26,7 @@ export default function(params: GenerateProxySchema) { 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])]; })); diff --git a/npm/ng-packs/packages/schematics/src/utils/model.ts b/npm/ng-packs/packages/schematics/src/utils/model.ts index f76133552b..ceb0603f2d 100644 --- a/npm/ng-packs/packages/schematics/src/utils/model.ts +++ b/npm/ng-packs/packages/schematics/src/utils/model.ts @@ -1,18 +1,30 @@ import { strings } from '@angular-devkit/core'; -import { Interface, Model, Property, Type } from '../models'; +import { Import, Interface, Model, Property, Type } from '../models'; +import { sortImports } from './import'; import { parseNamespace } from './namespace'; -import { createTypeSimplifier } from './type'; +import { relativePathToModel } from './path'; +import { parseGenerics } from './tree'; +import { createTypeSimplifier, createTypesToImportsReducer } from './type'; export function createImportRefsToModelMapper(solution: string, types: Record) { const mapImportRefToInterface = createImportRefToInterfaceMapper(solution, types); + const createImportRefToImportReducer = createImportRefToImportReducerCreator(solution, types); return (importRefs: string[]) => { - const model = new Model({ - namespace: parseNamespace(solution, importRefs[0]), - }); + const namespace = parseNamespace(solution, importRefs[0]); + const model = new Model({ namespace }); - importRefs.forEach(ref => { + const reduceImportRefToImport = createImportRefToImportReducer(namespace); + const imports = importRefs.reduce((accumulatedImports, ref) => { model.interfaces.push(mapImportRefToInterface(ref)); + return reduceImportRefToImport(accumulatedImports, ref); + }, []); + + sortImports(imports); + const selfPath = relativePathToModel(namespace, namespace); + imports.forEach(i => { + if (i.path === selfPath) return; + model.imports.push(i); }); model.interfaces.sort((a, b) => (a.identifier > b.identifier ? 1 : -1)); @@ -26,10 +38,10 @@ export function createImportRefToInterfaceMapper(solution: string, types: Record return (ref: string) => { const typeDef = types[ref]; - let identifier = simplifyType(ref); - (typeDef.genericArguments ?? []).forEach((t, i) => { - identifier = identifier.replace(`T${i}`, t); - }); + const identifier = (typeDef.genericArguments ?? []).reduce( + (acc, t, i) => acc.replace(`T${i}`, t), + simplifyType(ref), + ); const base = typeDef.baseType ? simplifyType(typeDef.baseType) : null; const _interface = new Interface({ identifier, base }); @@ -45,3 +57,51 @@ export function createImportRefToInterfaceMapper(solution: string, types: Record return _interface; }; } + +export function createImportRefToImportReducerCreator( + solution: string, + types: Record, +) { + return (namespace: string) => { + const reduceTypesToImport = createTypesToImportsReducer(solution, namespace); + + return (imports: Import[], importRef: string) => + reduceTypesToImport( + imports, + mergeBaseTypeWithProperties(types[importRef]).reduce((typeNames: string[], { type }) => { + parseGenerics(type) + .toGenerics() + .forEach(t => typeNames.push(t)); + + return typeNames; + }, []), + ); + }; +} + +export function mergeBaseTypeWithProperties({ baseType, genericArguments, properties }: Type) { + const removeGenerics = createGenericRemover(genericArguments); + const baseTypes = baseType ? [{ type: baseType }] : []; + const propTypes = (properties ?? []).map(({ type }) => ({ type })); + + return [...baseTypes, ...propTypes].map(removeGenerics); +} + +export function createGenericRemover(genericArguments: string[] | null) { + if (!genericArguments) return (def: SimpleTypeDef) => def; + + return ({ type }: SimpleTypeDef) => ({ + type: genericArguments.includes(type) + ? '' + : type.replace(/<([^<>]+)>/, (_, match) => { + return match + .split(/,\s*/) + .filter((t: string) => !genericArguments.includes(t)) + .join(','); + }), + }); +} + +interface SimpleTypeDef { + type: string; +} diff --git a/npm/ng-packs/packages/schematics/src/utils/type.ts b/npm/ng-packs/packages/schematics/src/utils/type.ts index 18726d0334..81dc412f01 100644 --- a/npm/ng-packs/packages/schematics/src/utils/type.ts +++ b/npm/ng-packs/packages/schematics/src/utils/type.ts @@ -30,17 +30,17 @@ export function createTypesToImportsReducer(solution: string, namespace: string) return (imports: Import[], types: string[]) => { types.forEach(type => { - const def = mapTypeToImport(type); - if (!def) return; + const newImport = mapTypeToImport(type); + if (!newImport) return; const existingImport = imports.find( - ({ keyword, path }) => keyword === def.keyword && path === def.path, + ({ keyword, path }) => keyword === newImport.keyword && path === newImport.path, ); - if (!existingImport) return imports.push(def); + if (!existingImport) return imports.push(newImport); - existingImport.refs = [...new Set([...existingImport.refs, ...def.refs])]; + existingImport.refs = [...new Set([...existingImport.refs, ...newImport.refs])]; existingImport.specifiers = [ - ...new Set([...existingImport.specifiers, ...def.specifiers]), + ...new Set([...existingImport.specifiers, ...newImport.specifiers]), ].sort(); }); @@ -52,7 +52,7 @@ export function createTypeToImportMapper(solution: string, namespace: string) { const adaptType = createTypeAdapter(solution); return (type: string) => { - if (type.startsWith('System')) return; + if (!type || type.startsWith('System')) return; const modelNamespace = parseNamespace(solution, type); const path = type.startsWith('Volo.Abp.Application.Dtos')