import { copyFile, readFile } from "fs/promises"; import * as handlebars from 'handlebars'; import { join } from 'path'; import { Project } from "ts-morph"; import { Logger } from 'winston'; import { analyzeFiles } from "./analyzeTypescriptFiles"; import { createFile, createPath } from "./fileHelpers"; /** * Generate the Client Templates. * @param options */ export async function generateClientTemplate(options: { pathToClientTemplate: string, pathToInterfaceTemplate: string, outputDir: string, inputDir: string, tsConfigFilePath: string, // A Logger. logger?: Logger }) { if (options.logger) { options.logger.info('Templates will be stored in ' + options.outputDir); } const filesToCopy: { src: string[], name: string, path: string[] }[] = [ { name: 'nopeDispatcher.ts', path: ['dispatcher'], src: ['lib', 'dispatcher'] }, { name: 'nopeObservable.ts', path: ['observables'], src: ['lib', 'observables'] }, { name: 'nopeRemoteObservable.ts', path: ['observables'], src: ['lib', 'observables'] } ]; for (const file of filesToCopy) { // Create the Output dir (if it doenst exists) await createPath(join(options.outputDir, ...file.path)); // Copy the File. await copyFile( join(__dirname, '..', '..', '..', ...file.src, file.name), join(options.outputDir, ...file.path, file.name) ); if (options.logger) { options.logger.info('Copied -> ' + file.name); } } // Function to Determine new project files. const project = new Project({ tsConfigFilePath: options.tsConfigFilePath, addFilesFromTsConfig: false, }); project.addSourceFilesAtPaths(options.inputDir); // Readin the Source-Files. const sourceFiles = project.getSourceFiles(); // Generate the Files const result = analyzeFiles(sourceFiles); // load the File. const clientTemplate = await readFile(options.pathToClientTemplate, { encoding: 'utf-8' }) // Renderfuncting const renderClient = handlebars.compile(clientTemplate); // Generate the Files. const files: { name: string, content: string, }[] = result.map(item => { // Extract the Class uri const classUri = item.classDecorator.decoratorSettings.exportsElementsToDispatcher ? item.classDecorator.decoratorSettings.exportsElementsToDispatcher.uri || item.className : item.className; (item as any).orginalName = item.className; item.className = item.className + 'ClientInterface'; item.methods = item.methods.map(m => { const methodUri = m.decoratorSettings.exportMethodToDispatcher ? m.decoratorSettings.exportMethodToDispatcher.uri || m.name : m.name; (m as any).uri = classUri + '_' + methodUri; return m; }); item.properties = item.properties.map(p => { const methodUri = p.decoratorSettings.exportPropertyToDispatcher ? p.decoratorSettings.exportPropertyToDispatcher.uri || p.name : p.name; (p as any).uri = classUri + '_' + methodUri; return p; }); return { name: item.className + '.ts', content: renderClient(item) } }); // load the File. const interfaceTemplate = await readFile(options.pathToInterfaceTemplate, { encoding: 'utf-8' }) // Renderfuncting const renderInterface = handlebars.compile(interfaceTemplate); await createFile( // Generate the Path. join(options.outputDir, 'BackendInterface.ts'), renderInterface({ classes: result }) ); if (options.logger) { options.logger.info('Generated -> BackendInterface.ts'); } for (const file of files) { // Define the File Name: const fileName = join(options.outputDir, 'clients', file.name); await createFile( // Generate the Path. join(options.outputDir, 'clients', file.name), file.content ); if (options.logger) { options.logger.info('Generated -> ' + join('clients', fileName)); } // Function to Determine new project files. const project = new Project({ tsConfigFilePath: options.tsConfigFilePath, addFilesFromTsConfig: false, }); project.addSourceFileAtPath(fileName); // Readin the Source-Files. const sourceFiles = project.getSourceFiles(); for (const file of sourceFiles) { file.formatText(); await file.save(); } } // Compile the Template and parse the Code. return true; }