From 5418c837d308cd3bd0be3a7ac382df152aa1232d Mon Sep 17 00:00:00 2001 From: Martin Karkowski Date: Mon, 24 Aug 2020 09:37:48 +0200 Subject: [PATCH] creating functionality to write the results into files. --- lib/helpers/analyzeTypescriptFiles.ts | 49 ++++---- lib/helpers/generateTemplate.ts | 166 ++++++++++---------------- 2 files changed, 88 insertions(+), 127 deletions(-) diff --git a/lib/helpers/analyzeTypescriptFiles.ts b/lib/helpers/analyzeTypescriptFiles.ts index 2d502e0..e8d407a 100644 --- a/lib/helpers/analyzeTypescriptFiles.ts +++ b/lib/helpers/analyzeTypescriptFiles.ts @@ -37,6 +37,7 @@ export type MethodInformation = { isImplementation: boolean; returnType: TypeInformation; head: string; + authorDescription: string; } export type DecoratorInformation = { @@ -46,6 +47,25 @@ export type DecoratorInformation = { decoratorSettings: { [index: string]: { [index: string]: any }} } +/** + * Interface for the Analyzing Result + */ +export interface IAnalyzeResult { + // Name of the Class + className: string, + // Decorators of the Class + classDecorator: DecoratorInformation, + // Methods of the Class + methods: (MethodInformation & DecoratorInformation)[], + // Properties of the Class + properties: (PropertyInformation & DecoratorInformation)[], + // Imports of the Class (contians external Files) + imports: { + content: string, + required: boolean, + } +} + export type PropertyInformation = ModifierInformation & TypeInformation & { name: string; declaration: PropertyDeclaration; }; /** @@ -363,7 +383,12 @@ export function getDescription(declaration: PropertyDeclaration | InterfaceDecla // Use the Regex to remove the Imports from the Head const regex = /import\(.+?\)./g - const head = declaration.getType().getText().replace(regex, ''); + // Define the Head + let head = declaration.getType().getText().replace(regex, ''); + head = head.slice(0, head.length - ('=> '+ returnType.simplifiedType).length) + + // Extract the Description of the Author. + const authorDescription = declaration.getLeadingCommentRanges().map(comment => comment.getText()).join('\n'); return { declaration, @@ -374,7 +399,8 @@ export function getDescription(declaration: PropertyDeclaration | InterfaceDecla isAsync, isGenerator, isImplementation, - returnType + returnType, + authorDescription } } } @@ -717,25 +743,6 @@ export function getDeclarationByName(files: { [index: string]: SourceFile }, fil throw Error('Declaration "' + filePath + '" not found'); } -/** - * Interface for the Analyzing Result - */ -export interface IAnalyzeResult { - // Name of the Class - className: string, - // Decorators of the Class - classDecorator: DecoratorInformation, - // Methods of the Class - methods: (MethodInformation & DecoratorInformation)[], - // Properties of the Class - properties: (PropertyInformation & DecoratorInformation)[], - // Imports of the Class (contians external Files) - imports: { - content: string, - required: boolean, - } -} - /** * Function, that will create the imported Type classes based on source-Files. * @param sourceFiles diff --git a/lib/helpers/generateTemplate.ts b/lib/helpers/generateTemplate.ts index 1a68cbd..2b80bc0 100644 --- a/lib/helpers/generateTemplate.ts +++ b/lib/helpers/generateTemplate.ts @@ -1,120 +1,74 @@ -import { SourceFile } from "ts-morph"; -import { createFileMapping, analyzeClasses, getDeclarationByName } from "./analyzeTypescriptFiles"; +import { Project } from "ts-morph"; +import { analyzeFiles } from "./analyzeTypescriptFiles"; +import * as handlebars from 'handlebars'; +import { readFile, copyFile } from "fs/promises"; +import { join } from 'path'; +import { createFile, createPath } from "./fileHelpers"; /** - * Function, that will create the imported Type classes based on source-Files. - * @param sourceFiles - * @param options Options, to Controll the generation. + * Generate the Client Templates. + * @param options */ -export function transformClasses(sourceFiles: SourceFile[], options: { - classDecorator: string, - classInterface: string, - methodDecorator: string, - propertyType: string, - propertyDecorator: string, -} = { - classDecorator: 'exportsElementsToDispatcher', - classInterface: '', - methodDecorator: 'exportMethodToDispatcher', - propertyType: 'nopeObservable', - propertyDecorator: 'exportPropertyToDispatcher' -}){ - const fileMapping = createFileMapping(sourceFiles); - const classes = analyzeClasses(sourceFiles, options); +export async function generateClientTemplate(options: { + pathToTemplate: string, + outputDir: string, + inputDir: string, + tsConfigFilePath: string, +}) { - + // Create the Output dir (if it doenst exists) + await createPath(options.outputDir); - // Iterate over the Classes - for (const relevantClass of classes){ + // Firstly copy the nopeDispatcher + await copyFile( + join(__dirname,'..','..','..','lib','dispatcher','nopeDispatcher.ts'), + join(options.outputDir,'nopeDispatcher.ts') + ); - const requiredImports = new Set(); - const mappingTypeToImport: { - [index: string]: Set - } = {} + // Function to Determine new project files. + const project = new Project({ + tsConfigFilePath: options.tsConfigFilePath, + addFilesFromTsConfig: false, + }); - // Iterate over the Properties - for (const prop of relevantClass.properties){ - if (!prop.isBaseType){ - for (const {identifier: type, path} of (prop.typeImports || [])){ + project.addSourceFilesAtPaths(options.inputDir); - // Only if the import isnt the Base Type, add it to the List. - if (type !== options.propertyType) { - // Check if the Imported Type has been Adden multiple Times. - if (mappingTypeToImport[type] === undefined) { - mappingTypeToImport[type] = new Set(); - } + // Readin the Source-Files. + const sourceFiles = project.getSourceFiles(); - mappingTypeToImport[type].add(path); - requiredImports.add(type); - } - } - } + // Generate the Files + const result = analyzeFiles(sourceFiles); + + // load the File. + const template = await readFile(options.pathToTemplate, { + encoding: 'utf-8' + }) + + // Renderfuncting + const render = handlebars.compile(template); + + // Generate the Files. + const files: { + name: string, + content: string, + }[] = result.map(item => { + + item.className = item.className + 'ClientInterface' + + return { + name: item.className + '.ts', + content: render(item) } + }); - // Iterate over the Methods - for (const method of relevantClass.methods){ - if (!method.returnType.isBaseType) { - for (const {identifier, path} of (method.returnType.typeImports || [])){ - - // Only if the import isnt the Base Type, add it to the List. - if (identifier !== options.propertyType) { - // Check if the Imported Type has been Adden multiple Times. - if (mappingTypeToImport[identifier] === undefined) { - mappingTypeToImport[identifier] = new Set(); - } - - mappingTypeToImport[identifier].add(path); - requiredImports.add(identifier); - } - } - } - - for (const parm of method.params){ - if (!parm.isBaseType){ - for (const {identifier, path} of (parm.typeImports || [])){ - - // Only if the import isnt the Base Type, add it to the List. - if (identifier !== options.propertyType) { - // Check if the Imported Type has been Adden multiple Times. - if (mappingTypeToImport[identifier] === undefined) { - mappingTypeToImport[identifier] = new Set(); - } - - mappingTypeToImport[identifier].add(path); - requiredImports.add(identifier); - } - } - } - } - } - - let fileWithTypesForClass = `// Automatic extracted Interfaces for the Class "` + relevantClass.className + `"\n// Generated on: ` + (new Date()).toISOString(); - - // List containing the already used Types - const importedBaseTypes = new Set(); - - // Iterate over the Imports - for (const reqType of requiredImports) { - if (mappingTypeToImport[reqType] && mappingTypeToImport[reqType].size === 1) { - // Extract the Path of the File - const pathToFile = Array.from(mappingTypeToImport[reqType].values())[0] + '.ts'; - - const declarations = getDeclarationByName(fileMapping, pathToFile, reqType).filter(item => !importedBaseTypes.has(item.baseType)); - declarations.map(item => importedBaseTypes.add(item.baseType)); - - fileWithTypesForClass += declarations.map(item => - item.originalCode - ).reduce( - (prev, current, idx) => prev + '\n\n' + current, '' - ); - - } else if (mappingTypeToImport[reqType].size > 1) { - // Multiple Items are using the Same Name. - } - } - - console.log(fileWithTypesForClass) + for (const file of files){ + await createFile( + // Generate the Path. + join(options.outputDir,'clients',file.name), + file.content + ) } - + // Compile the Template and parse the Code. + return true; } \ No newline at end of file