import {ComponentDef, EndpointDef, SystemDef} from "../systemGenService"; import path from "path"; import { componentDestination, initializeComponentFile, removeTemplateFiles, removeTemplateFolder, uppercaseFirstLetter } from "../helpers"; import fs from "fs"; import {buildServiceFunctionName, getFuncParams} from "../views/service-creator"; import {METHOD, URL} from "../views/routes-creator"; import {buildDetailsView} from "./details-view-builder"; import {buildListView} from "./list-view-builder"; import {buildEditorView} from "./editor-view-builder"; import {buildTypesView} from "./types-view-builder"; import {buildRoute} from "./route-view-builder"; import {buildAppLinks} from "./app-view-builder"; const ncp = require('ncp').ncp; export function createFrontend(systemDef: SystemDef): Promise { let fePromises = []; for (let i in systemDef.components) { fePromises.push(createComponent(systemDef.components[i], systemDef).then(() => { // build app router logic return buildRoute(systemDef.components[i], componentDestination(systemDef.name, systemDef.components[i].component), systemDef); }).then(() => { // add a vue view and add it to App.vue return buildAppLinks(systemDef.components[i], componentDestination(systemDef.name, systemDef.components[i].component), systemDef); }).then(() => { return removeTemplateFiles(componentDestination(systemDef.name, systemDef.components[i].component)); })); } // TODO: after all is done clean up the template files return Promise.all(fePromises).then((res: void[]) => { return removeTemplateFolder(componentDestination(systemDef.name, '{{component}}')).catch((err) => { console.log(err); }); }); } function componentSource() { return path.join(process.cwd(), 'frontend-frame', 'src', 'components', '{{component}}'); } function createComponent(component: ComponentDef, systemDef: SystemDef) { return new Promise((componentResolve, componentReject) => { ncp(componentSource(), componentDestination(systemDef.name, component.component), (err: any) => { if (err) { console.log(err); componentReject(); return; } else { let componentDest: string = componentDestination(systemDef.name, component.component); let servicePromise = initializeComponentFile(path.join(componentDest, '{{component}}Service.ets'), path.join(componentDest, `${component.component}Service.ts`), component.component, systemDef.name); for (let i in component.endpoints) { servicePromise = servicePromise.then(() => { return insertFEServiceCode(component.endpoints[i], componentDest); }); // TODO: use templates for Details, Editor, List and Types view based on the endpoints provided // servicePromise = servicePromise.then(() => { // // return initializeComponentFile(path.join(componentDest, '{{component}}Service.ets'), path.join(componentDest, `${component.component}Service.ts`), component.component, systemDef.name); // }); } if (componentContains('list', component)) { servicePromise = servicePromise.then(() => { return initializeComponentFile(path.join(componentDest, '{{component}}List.vue'), path.join(componentDest, `${component.component}List.vue`), component.component, systemDef.name); }).then(() => { // TODO: build list contents based on the list endpoint return buildListView(component, componentDest, systemDef); }); } if (componentContains('item', component)) { servicePromise = servicePromise.then(() => { return initializeComponentFile(path.join(componentDest, '{{component}}Details.vue'), path.join(componentDest, `${component.component}Details.vue`), component.component, systemDef.name); }).then(() => { // build list contents based on the item endpoint return buildDetailsView(component, componentDest, systemDef); }); } if (componentContains('update', component) || componentContains('create', component)) { servicePromise = servicePromise.then(() => { return initializeComponentFile(path.join(componentDest, '{{component}}Editor.vue'), path.join(componentDest, `${component.component}Editor.vue`), component.component, systemDef.name); }).then(() => { // build list contents based on the item endpoint return buildEditorView(component, componentDest, systemDef); }); } if (component.endpoints.length) { // add a types file to power the whole component servicePromise = servicePromise.then(() => { return initializeComponentFile(path.join(componentDest, '{{component}}Types.ets'), path.join(componentDest, `${component.component}Types.ts`), component.component, systemDef.name); }).then(() => { return buildTypesView(component, componentDest, systemDef); }); } servicePromise.then(() => { componentResolve(); }); } }); }); } function componentContains(type: string, component: ComponentDef) { return component.endpoints.some((epd: EndpointDef) => { return epd.type === type; }); } function insertFEServiceCode(view: EndpointDef, outDir: string) { return new Promise((resolve) => { const separator: string = `// SYSTEM-BUILDER-service-functions`; let fileLocation = path.join(outDir, `${view.component}Service.ts`) let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8'); let parts = initServiceFile.split(separator); parts[0] = parts[0] + createFEServiceFunc(view); let newServiceFile = parts.join(separator); fs.writeFileSync(fileLocation, newServiceFile, 'utf8'); resolve(); }); } function createFEServiceFunc(view: EndpointDef): string { let func: string = ''; let isListOrSearch: boolean = view.type === 'list' || view.type === 'search'; let funcName: string = buildServiceFunctionName(view); let funcParams: string[] = getFuncParams(view, true); let columnParams: string[] = view.columns?.filter(column => column.param).map(column => { return '\n ' + column.param + ': ' + column.param; }); let filterParams: string[] = view.filters?.filter(filter => filter.param).map(filter => { return '\n ' + filter.param + ': ' + filter.param; }) || []; if (isListOrSearch) { funcParams.push('offset: number'); funcParams.push('limit: number'); filterParams.push('\n offset: offset'); filterParams.push('\n limit: limit'); } let isGetOrDelete = view.type === 'item' || view.type === 'delete'; let method = METHOD[view.type]; if (method === 'delete') { method = 'del'; } let addTypeDeclaration = method === 'get' || method === 'post'; let useList = view.type === 'list' || view.type === 'search'; let url = view.component + '/' + URL[view.type](view).replace(':' + view.component + '_id', '\' + ' + view.component + '_id'); let isUpdateOrCreate = view.type === 'update' || view.type === 'create'; func = ` public ${funcName}(${view.type === 'update' ? view.component + '_id: string, ': ''}${isUpdateOrCreate ? 'params: ' + uppercaseFirstLetter(view.component) + 'Config' : funcParams.join(', ')}) {`; if (!isGetOrDelete && !isUpdateOrCreate) { func = func + ` let params = {${columnParams}${columnParams.length ? ',' : ''}${filterParams} };`; } func = func + ` return ${method}${addTypeDeclaration ? '<' + uppercaseFirstLetter(view.component) + 'Config' + (useList ? '[]' : '') + '>': ''}('/api/v1/${url}${isGetOrDelete ? '' : (view.type === 'update' ? ', params' : '\', params')}); } `; return func; }