nope/lib/helpers/generateTemplate.ts
2020-11-06 09:10:30 +01:00

244 lines
7.0 KiB
TypeScript

/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-11-06 08:53:45
* @modify date 2020-11-06 08:53:45
* @desc [description]
*/
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 "./fileMethods";
/**
* Generate the Client Templates.
* @param options
*/
export async function generateClientTemplate(options: {
pathToClassInterfaceTemplate: string,
pathToFunctionInterfaceTemplate: string,
pathToBackendInterfaceTemplate: string,
outputDir: string,
inputDir: string,
tsConfigFilePath: string,
// A Logger.
logger?: Logger
}) {
// If there is a Logger Show a hint of the folders.
if (options.logger) {
options.logger.info('Templates will be stored in ' + options.outputDir);
}
// Define an Array containing elements that should be copied
// Copying the stuff is relevant for
const filesToCopy: {
src: string[],
name: string,
path: string[]
}[] = [
{
name: 'nopeDispatcher.ts',
path: ['dispatcher'],
src: ['lib', 'dispatcher']
},
{
name: 'nopeRemoteObservable.ts',
path: ['observables'],
src: ['lib', 'observables']
}
];
// Copy the Files.
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 -> ' + join(options.outputDir, ...file.path, file.name));
}
}
// Function to Determine new project files.
const project = new Project({
tsConfigFilePath: options.tsConfigFilePath,
addFilesFromTsConfig: false,
});
// Readin the Sources of the Dir.
project.addSourceFilesAtPaths(options.inputDir);
// Readin the Source-Files.
const sourceFiles = project.getSourceFiles();
// Generate the Files
const result = analyzeFiles(sourceFiles);
// Enrich the Information.
result.classes = result.classes.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 as any).fileName = item.className + '_ClientInterface';
(item as any).classUri = classUri;
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 = 'this.uri + \'.' + methodUri + '\'';
return m;
});
item.properties = item.properties.map(p => {
const parameterUri = p.decoratorSettings.exportPropertyToDispatcher ? p.decoratorSettings.exportPropertyToDispatcher.uri || p.name : p.name;
(p as any).uri = 'this.uri + \'.' + parameterUri + '\'';
return p;
});
return item;
});
result.functions = result.functions.map(item => {
// Adding a generatorName
(item as any).generatorName = 'generate_' + item.name + '_Accessor';
(item as any).orginalName = item.name;
(item as any).fileName = item.name + '_ClientInterface';
return item;
});
// load the File.
const clientTemplate = await readFile(options.pathToClassInterfaceTemplate, {
encoding: 'utf-8'
})
// Renderfuncting
const renderClientInterface = handlebars.compile(clientTemplate);
// Generate the Files.
const classFiles: {
name: string,
content: string,
}[] = result.classes.map(item => {
return {
name: (item as any).fileName + '.ts',
content: renderClientInterface(item)
}
});
// load the File.
const functionTemplate = await readFile(options.pathToFunctionInterfaceTemplate, {
encoding: 'utf-8'
})
// Renderfuncting
const renderFunctionInterface = handlebars.compile(functionTemplate);
const functionFiles: {
name: string,
content: string,
}[] = result.functions.map(func => {
return {
name: (func as any).fileName + '.ts',
content: renderFunctionInterface(func)
}
});
// load the File.
const interfaceTemplate = await readFile(options.pathToBackendInterfaceTemplate, {
encoding: 'utf-8'
})
// Renderfuncting
const renderInterface = handlebars.compile(interfaceTemplate);
await createFile(
// Generate the Path.
join(options.outputDir, 'BackendInterface.ts'),
renderInterface(result)
);
if (options.logger) {
options.logger.info('Generated -> ' + join(options.outputDir, 'BackendInterface.ts'));
}
for (const file of classFiles) {
// Define the File Name:
const fileName = join(options.outputDir, 'clients', file.name);
await createFile(
// Generate the Path.
fileName,
file.content
);
if (options.logger) {
options.logger.info('Generated -> ' + 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();
}
}
for (const file of functionFiles) {
// Define the File Name:
const fileName = join(options.outputDir, 'functions', file.name);
await createFile(
// Generate the Path.
fileName,
file.content
);
if (options.logger) {
options.logger.info('Generated -> ' + 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;
}