import { readFile } from "fs/promises"; import * as handlebars from 'handlebars'; import { join } from 'path'; import { Project } from "ts-morph"; import * as TJS from "typescript-json-schema"; import { Logger } from 'winston'; import { IJsonSchema } from "../../types/IJSONSchema"; import { analyzeFiles, defaultClassFilter, defaultPropFilter } from "./analyzeTypescriptFiles"; import { createFile, createPath } from "./fileMethods"; import { flattenSchema } from "./jsonSchemaMethods"; /** * Generate the Client Templates. * @param options */ export async function generateSchemas(options: { pathToSchemaTemplate: string, tempDir: string, inputDir: string, tsConfigFilePath: string, logger?: Logger, }) { // Create the Output dir (if it doenst exists) await createPath(options.tempDir); // 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, { classDecorator: 'exportsElementsToOpenAPI', methodDecorator: 'exportMethodToOpenAPI', propertyDecorator: 'exportPropertyToOpenAPI', filterClasses: defaultClassFilter({ classDecorator: 'exportsElementsToOpenAPI', classInterface: '' }), filterMethods: (cl, method) => { return method.isImplementation && method.isAsync && method.params.filter(param => param.baseType === 'function').length === 0 }, filterProperties: defaultPropFilter({ propertyDecorator: 'exportPropertyToDispatcher', propertyType: 'nopeObservable' }), checkImport: type => type !== 'nopeObservable' } ); // load the File. const schemaTemplate = await readFile(options.pathToSchemaTemplate, { encoding: 'utf-8' }) // Renderfuncting const renderSchema = handlebars.compile(schemaTemplate); // Generate the Files. const files: { className: string, name: string, content: string, }[] = result.map(item => { return { className: item.className, name: item.className + 'Schema', content: renderSchema(item) } }); for (const file of files) { // Generate the Path. const fileName = join(options.tempDir, 'classes', file.name + '.ts'); await createFile( fileName, file.content ); if (options.logger) { options.logger.info('Generated -> ' + fileName); } } // After all files has been written => Generate the Schemas: const _settings: TJS.PartialArgs = { required: true, }; // Options for the TJS.Compiler; const compilerOptions: TJS.CompilerOptions = { strictNullChecks: true, skipLibCheck: true }; const schemaMapping: { [index: string]: IJsonSchema } = {}; for (const file of files) { const _program = TJS.getProgramFromFiles( [join(options.tempDir, 'classes', file.name + '.ts')], compilerOptions ); // We can either get the schema for one file and one type... const _schema = TJS.generateSchema(_program, "*", _settings); // Flatten the Schema. const _flattendSchema = flattenSchema(_schema as any) // Receive the Element schemaMapping[file.className] = _flattendSchema; // Generate the Path. const fileName = join(options.tempDir, 'schema', file.name + '.json') // Write down the Schema: await createFile( fileName, JSON.stringify(_flattendSchema, undefined, 4) ); if (options.logger) { options.logger.info('Generated -> ' + fileName); } } return { schemaMapping, analysis: result }; }