From f097fa67917e05000ce9802cc43ef87e496ddde1 Mon Sep 17 00:00:00 2001
From: mat-app <matejchatrnuch@gmail.com>
Date: Tue, 22 Jun 2021 17:53:41 +0200
Subject: [PATCH] - reorganize codegen api

---
 packages/react-lowcode/src/codegen/app.ts     |  69 ++++++
 packages/react-lowcode/src/codegen/detail.ts  |  44 ++++
 packages/react-lowcode/src/codegen/index.ts   | 207 +-----------------
 .../react-lowcode/src/codegen/interfaces.ts   |  54 ++++-
 packages/react-lowcode/src/codegen/list.ts    |  48 ++++
 .../src/codegen/tests/api/api.test.ts         |   7 +-
 .../src/codegen/tests/detail/detail.test.ts   |   1 -
 .../src/codegen/tests/list/list.test.ts       |   4 +-
 8 files changed, 222 insertions(+), 212 deletions(-)
 create mode 100644 packages/react-lowcode/src/codegen/app.ts
 create mode 100644 packages/react-lowcode/src/codegen/detail.ts
 create mode 100644 packages/react-lowcode/src/codegen/list.ts

diff --git a/packages/react-lowcode/src/codegen/app.ts b/packages/react-lowcode/src/codegen/app.ts
new file mode 100644
index 000000000..020b8e45e
--- /dev/null
+++ b/packages/react-lowcode/src/codegen/app.ts
@@ -0,0 +1,69 @@
+import { AppGenerator } from './generation/generators/app-generator'
+import { UiFramework, TableType, Formatter } from './definition/context-types'
+import { CodeDir, CodeRW } from '../io'
+import ts, { factory } from "typescript"
+import { Project } from "ts-morph"
+import { CodegenOptions } from './interfaces'
+import TemplateResolver from './generation/generators/template/template-resolver'
+
+// generates CRUD React pages (master-detail, eg. orders list, order detail form) from typescript
+export function generatePages(inputSourceCode: string, io: CodeRW & CodeDir, options?: CodegenOptions) {
+    const project = new Project({})
+    const myClassFile = project.createSourceFile("src/types.ts", inputSourceCode)
+    const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed })
+
+    options?.names.map((typeName) => {
+        const typeAlias = myClassFile.getTypeAlias(typeName)
+        const props = typeAlias?.getType()?.getProperties() ?? []
+        if (typeAlias) {
+            const entity = {
+                getName: () => typeName,
+                getType: () => typeAlias,
+                properties: props.map((prop) => ({
+                    getName: () => prop.getName(),
+                    getType: () => prop.getTypeAtLocation(myClassFile),
+                    getTypeText: () => prop.getDeclarations()[0].getText()
+                }))
+            }
+
+            let context = {uiFramework: UiFramework.MaterialUI, formatter: Formatter.None, index: {tableType: TableType.BasicTable, height: "400px"}};
+            
+            const generator = new AppGenerator(context, entity)
+            const page = generator.generateListComponent(/* TODO entity / type name should be input - not in context */)
+            
+            const filePath = `src/components/${typeName}.tsx`
+            const sourceFile = ts.createSourceFile(
+                filePath,
+                '',
+                ts.ScriptTarget.ESNext,
+                true,
+                ts.ScriptKind.TSX
+            )
+            const pageSouceCode = printer.printList(ts.ListFormat.MultiLine, factory.createNodeArray([...page!.imports, page!.functionDeclaration]), sourceFile)
+            io.writeFile(filePath, pageSouceCode)
+
+            //generate list wrapper
+            const indexWrapperTemplatePath = 'path-to-template'//TODO: put here real template path when template will be done
+            let template = ''
+            io.readFile(indexWrapperTemplatePath).then((source => {if(source) template = source;}))
+
+            const templateResolver = new TemplateResolver(entity);
+            const listWrapper = templateResolver.generateListPage(template);
+
+            if(listWrapper) {
+                const listWrapperFilePath = `src/components/${typeName}Page.tsx`
+                const sourceFileWrapperSourceFile = ts.createSourceFile(
+                    listWrapperFilePath,
+                    listWrapper,
+                    ts.ScriptTarget.ESNext,
+                    true,
+                    ts.ScriptKind.TSX
+                )
+    
+                // TODO:PC: Need print here? or only: io.writeFile(listWrapperFilePath, listWrapper)
+                const wrapperPageSourceCode = printer.printFile(sourceFileWrapperSourceFile);
+                io.writeFile(listWrapperFilePath, wrapperPageSourceCode)
+            }
+        }
+    })
+}
\ No newline at end of file
diff --git a/packages/react-lowcode/src/codegen/detail.ts b/packages/react-lowcode/src/codegen/detail.ts
new file mode 100644
index 000000000..509e52b1e
--- /dev/null
+++ b/packages/react-lowcode/src/codegen/detail.ts
@@ -0,0 +1,44 @@
+import { SourceLineCol } from "../ast";
+import { CodeRW } from "../io";
+import { isFormWidget } from "./ast/widgetDeclaration";
+import { 
+    insertFormWidget, 
+    getColumnSourcePosition as fGetColumnSourcePosition,
+    getFormWidgetProperties as fGetFormWidgetProperties,
+    setFormWidgetProperties as fSetFormWidgetProperties
+} from './facade/facadeApi'
+import { Property } from "./generation/entity";
+import { InsertOptions, WidgetProperties } from "./interfaces";
+import { getEntityProperty } from "./tests/helper";
+
+export function isSelectedFormWidget(sourceCode:string, formPosition: SourceLineCol){
+    return isFormWidget(sourceCode, formPosition)
+}
+
+export async function getFormWidgetProperties(io: CodeRW, 
+    sourceCode:SourceLineCol): Promise<WidgetProperties>{
+    return await fGetFormWidgetProperties(sourceCode, io);
+}
+
+export async function setFormWidgetProperties(io: CodeRW, 
+    sourceCode:SourceLineCol,
+    properties: WidgetProperties): Promise<string | undefined>{
+
+    return await fSetFormWidgetProperties(sourceCode, io, properties);
+}
+
+export async function addFormInput(typesSourceCode: string, 
+    io: CodeRW, 
+    sourceLine:SourceLineCol, 
+    options: InsertOptions): Promise<string | undefined>{
+
+    const property: Property = getEntityProperty(typesSourceCode, options.property, options.entityName)[0]
+    let generatedSource = undefined
+
+    if(property){
+        generatedSource = await insertFormWidget(sourceLine, 
+        {entityField: property, index: options.index}, 
+        io)
+    }
+    return generatedSource
+}
\ No newline at end of file
diff --git a/packages/react-lowcode/src/codegen/index.ts b/packages/react-lowcode/src/codegen/index.ts
index da20f337a..d85581019 100644
--- a/packages/react-lowcode/src/codegen/index.ts
+++ b/packages/react-lowcode/src/codegen/index.ts
@@ -1,203 +1,4 @@
-import { AppGenerator } from './generation/generators/app-generator'
-import { UiFramework, TableType, Formatter } from './definition/context-types'
-import { CodeDir, CodeRW } from '../io'
-
-import ts, { factory } from "typescript"
-import { Project } from "ts-morph"
-import { HookImport } from '../ast/hooks'
-import { TagImport } from '../ast/tags'
-import { 
-    insertColumn, 
-    insertFormWidget, 
-    deleteColumn as fDeleteColumn, 
-    getColumnSourcePosition as fGetColumnSourcePosition,
-    getFormWidgetProperties as fGetFormWidgetProperties,
-    setFormWidgetProperties as fSetFormWidgetProperties
-} from './facade/facadeApi'
-import { SourceLineCol } from '../ast'
-import { Property } from './generation/entity'
-import { getEntityProperty } from './tests/helper'
-import { isDataTableWidget, isFormWidget } from './ast/widgetDeclaration'
-import { CodegenOptions, ColumnSourcePositionOptions, ColumnSourcePositionResult, DeleteOptions, InsertOptions, WidgetProperties } from './interfaces'
-import TemplateResolver from './generation/generators/template/template-resolver'
-
-
-// generates CRUD React pages (master-detail, eg. orders list, order detail form) from typescript
-export function generatePages(inputSourceCode: string, io: CodeRW & CodeDir, options?: CodegenOptions) {
-    const project = new Project({})
-    const myClassFile = project.createSourceFile("src/types.ts", inputSourceCode)
-    const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed })
-
-    options?.names.map((typeName) => {
-        const typeAlias = myClassFile.getTypeAlias(typeName)
-        const props = typeAlias?.getType()?.getProperties() ?? []
-        if (typeAlias) {
-            const entity = {
-                getName: () => typeName,
-                getType: () => typeAlias,
-                properties: props.map((prop) => ({
-                    getName: () => prop.getName(),
-                    getType: () => prop.getTypeAtLocation(myClassFile),
-                    getTypeText: () => prop.getDeclarations()[0].getText()
-                }))
-            }
-
-            let context = {uiFramework: UiFramework.MaterialUI, formatter: Formatter.None, index: {tableType: TableType.BasicTable, height: "400px"}};
-            
-            const generator = new AppGenerator(context, entity)
-            const page = generator.generateListComponent(/* TODO entity / type name should be input - not in context */)
-            
-            const filePath = `src/components/${typeName}.tsx`
-            const sourceFile = ts.createSourceFile(
-                filePath,
-                '',
-                ts.ScriptTarget.ESNext,
-                true,
-                ts.ScriptKind.TSX
-            )
-            const pageSouceCode = printer.printList(ts.ListFormat.MultiLine, factory.createNodeArray([...page!.imports, page!.functionDeclaration]), sourceFile)
-            io.writeFile(filePath, pageSouceCode)
-
-            //generate list wrapper
-            const indexWrapperTemplatePath = 'path-to-template'//TODO: put here real template path when template will be done
-            let template = ''
-            io.readFile(indexWrapperTemplatePath).then((source => {if(source) template = source;}))
-
-            const templateResolver = new TemplateResolver(entity);
-            const listWrapper = templateResolver.generateListPage(template);
-
-            if(listWrapper) {
-                const listWrapperFilePath = `src/components/${typeName}Page.tsx`
-                const sourceFileWrapperSourceFile = ts.createSourceFile(
-                    listWrapperFilePath,
-                    listWrapper,
-                    ts.ScriptTarget.ESNext,
-                    true,
-                    ts.ScriptKind.TSX
-                )
-    
-                // TODO:PC: Need print here? or only: io.writeFile(listWrapperFilePath, listWrapper)
-                const wrapperPageSourceCode = printer.printFile(sourceFileWrapperSourceFile);
-                io.writeFile(listWrapperFilePath, wrapperPageSourceCode)
-            }
-        }
-    })
-}
-
-export function isSelectedDataTable(sourceCode:string, tablePosition: SourceLineCol){
-    return isDataTableWidget(sourceCode, tablePosition)
-}
-
-export function isSelectedFormWidget(sourceCode:string, formPosition: SourceLineCol){
-    return isFormWidget(sourceCode, formPosition)
-}
-
-export async function addColumn(typesSourceCode: string, 
-                                io: CodeRW, 
-                                sourceCode:SourceLineCol, 
-                                options: InsertOptions): Promise<string | undefined>{
-                                    
-    const property: Property = getEntityProperty(typesSourceCode, options.property, options.entity)[0]
-    let generatedSource = undefined
-
-    if(property){
-        generatedSource = await insertColumn(sourceCode, 
-            {entityField: property, index: options.index}, 
-            io)
-    }
-
-    return generatedSource
-}
-
-export async function deleteColumn(io: CodeRW, 
-                                   sourceCode:SourceLineCol, 
-                                   options: DeleteOptions): Promise<string | undefined> {
-
-    let generatedSource = await fDeleteColumn(sourceCode, options, io);
-
-    return generatedSource
-}
-
-export async function getFormWidgetProperties(io: CodeRW, 
-                                              sourceCode:SourceLineCol): Promise<WidgetProperties>{
-    return await fGetFormWidgetProperties(sourceCode, io);
-}
-
-export async function setFormWidgetProperties(io: CodeRW, 
-                                              sourceCode:SourceLineCol,
-                                              properties: WidgetProperties): Promise<string | undefined>{
-
-    return await fSetFormWidgetProperties(sourceCode, io, properties);
-}
-
-export async function addFormInput(typesSourceCode: string, 
-                                   io: CodeRW, 
-                                   sourceLine:SourceLineCol, 
-                                   options: InsertOptions): Promise<string | undefined>{
-
-    const property: Property = getEntityProperty(typesSourceCode, options.property, options.entity)[0]
-    let generatedSource = undefined
-
-    if(property){
-
-        generatedSource = await insertFormWidget(sourceLine, 
-            {entityField: property, index: options.index}, 
-            io)
-    }
-
-    return generatedSource
-}
-
-export async function getColumnSourcePosition(io: CodeRW, 
-                                              sourceCode:SourceLineCol,
-                                              options: ColumnSourcePositionOptions): Promise<ColumnSourcePositionResult | undefined> {
-
-    return await fGetColumnSourcePosition(sourceCode, options, io);
-}
-
-interface ThemeCodegen {
-    providerTag(...children: ts.JsxChild[]): any
-}
-
- interface IntlCodegen {
-    providerTag(...children: ts.JsxChild[]): any
- }
-
-export interface AppGenerators {
-    newSourceFileContext(path: string): JsxFileContext
-    theme: ThemeCodegen,
-    intl: IntlCodegen,
-    //authorization: AuthorizationCodegen
-}
-
-export class JsxFileContext {
-
-    uniqueImports() {
-        return []
-    }
-  
-    useHook(hook: HookImport, ...params: []) {
-        // TODO unique import
-        return null
-    }
-  
-    tag(tag: TagImport, ...children: ts.JsxChild[]) {
-        // TODO unique import
-        return null
-    }
-  
-    returnFragment(...children: ts.JsxChild[]): ts.Statement | null {
-  
-        if (children?.length == 1) {
-            // TODO handle one child
-        }
-    
-        factory.createReturnStatement(factory.createJsxFragment(
-            factory.createJsxOpeningFragment(),
-            children,
-            factory.createJsxJsxClosingFragment()
-          ))
-    
-        return null
-    }
-}  
+export * from './interfaces'
+export * from './list'
+export * from './detail'
+export * from './app'
\ No newline at end of file
diff --git a/packages/react-lowcode/src/codegen/interfaces.ts b/packages/react-lowcode/src/codegen/interfaces.ts
index 699b16eb7..3a4c99730 100644
--- a/packages/react-lowcode/src/codegen/interfaces.ts
+++ b/packages/react-lowcode/src/codegen/interfaces.ts
@@ -1,4 +1,7 @@
+import ts, { factory } from "typescript";
 import { SourceLineCol } from "../ast";
+import { HookImport } from "../ast/hooks";
+import { TagImport } from "../ast/tags";
 import { UiFramework } from "./definition/context-types";
 
 export interface CodegenOptions {
@@ -9,7 +12,7 @@ export interface CodegenOptions {
 }
 
 export interface InsertOptions {
-    entity: string
+    entityName: string
     property: string
     index?: number
 }
@@ -39,4 +42,51 @@ export interface WidgetProperty {
 
 export interface WidgetProperties {
     properties: WidgetProperty[]
-}
\ No newline at end of file
+}
+
+interface ThemeCodegen {
+    providerTag(...children: ts.JsxChild[]): any
+}
+
+interface IntlCodegen {
+    providerTag(...children: ts.JsxChild[]): any
+ }
+
+export interface AppGenerators {
+    newSourceFileContext(path: string): JsxFileContext
+    theme: ThemeCodegen,
+    intl: IntlCodegen,
+    //authorization: AuthorizationCodegen
+}
+
+export class JsxFileContext {
+
+    uniqueImports() {
+        return []
+    }
+  
+    useHook(hook: HookImport, ...params: []) {
+        // TODO unique import
+        return null
+    }
+  
+    tag(tag: TagImport, ...children: ts.JsxChild[]) {
+        // TODO unique import
+        return null
+    }
+  
+    returnFragment(...children: ts.JsxChild[]): ts.Statement | null {
+  
+        if (children?.length == 1) {
+            // TODO handle one child
+        }
+    
+        factory.createReturnStatement(factory.createJsxFragment(
+            factory.createJsxOpeningFragment(),
+            children,
+            factory.createJsxJsxClosingFragment()
+          ))
+    
+        return null
+    }
+}  
\ No newline at end of file
diff --git a/packages/react-lowcode/src/codegen/list.ts b/packages/react-lowcode/src/codegen/list.ts
new file mode 100644
index 000000000..9c890beba
--- /dev/null
+++ b/packages/react-lowcode/src/codegen/list.ts
@@ -0,0 +1,48 @@
+import { SourceLineCol } from "../ast"
+import { CodeRW } from "../io"
+import { isDataTableWidget } from "./ast/widgetDeclaration"
+import { ColumnSourcePositionOptions, ColumnSourcePositionResult, DeleteOptions, InsertOptions } from "./interfaces"
+import { getEntityProperty } from "./tests/helper"
+import { 
+    insertColumn, 
+    deleteColumn as fDeleteColumn, 
+    getColumnSourcePosition as fGetColumnSourcePosition,
+} from './facade/facadeApi'
+import { Property } from "./generation/entity"
+
+export function isSelectedDataTable(sourceCode:string, tablePosition: SourceLineCol){
+    return isDataTableWidget(sourceCode, tablePosition)
+}
+
+export async function addColumn(typesSourceCode: string, 
+    io: CodeRW, 
+    sourceCode:SourceLineCol, 
+    options: InsertOptions): Promise<string | undefined>{
+        
+    const property: Property = getEntityProperty(typesSourceCode, options.property, options.entityName)[0]
+    let generatedSource = undefined
+
+    if(property){
+        generatedSource = await insertColumn(sourceCode, 
+        {entityField: property, index: options.index}, 
+        io)
+    }
+
+    return generatedSource
+}
+
+export async function deleteColumn(io: CodeRW, 
+    sourceCode:SourceLineCol, 
+    options: DeleteOptions): Promise<string | undefined> {
+
+let generatedSource = await fDeleteColumn(sourceCode, options, io);
+
+return generatedSource
+}
+
+export async function getColumnSourcePosition(io: CodeRW, 
+    sourceCode:SourceLineCol,
+    options: ColumnSourcePositionOptions): Promise<ColumnSourcePositionResult | undefined> {
+
+return await fGetColumnSourcePosition(sourceCode, options, io);
+}
\ No newline at end of file
diff --git a/packages/react-lowcode/src/codegen/tests/api/api.test.ts b/packages/react-lowcode/src/codegen/tests/api/api.test.ts
index 42f8ce2d1..fc243f7d4 100644
--- a/packages/react-lowcode/src/codegen/tests/api/api.test.ts
+++ b/packages/react-lowcode/src/codegen/tests/api/api.test.ts
@@ -2,10 +2,11 @@ import fs from 'fs'
 import path from 'path'
 import { ts } from 'ts-morph';
 import { SyntaxKind } from 'typescript';
-import { addColumn, addFormInput, deleteColumn, getColumnSourcePosition, getFormWidgetProperties, isSelectedDataTable, isSelectedFormWidget, setFormWidgetProperties } from '../..';
 import { findByCondition, SourceLineCol } from '../../../ast';
+import { addFormInput, getFormWidgetProperties, isSelectedFormWidget, setFormWidgetProperties } from '../../detail';
 import MuiDetailGenerator from '../../generation/generators/detail/mui-detail-generator';
 import { CodegenRw } from '../../io/codegenRw';
+import { addColumn, deleteColumn, getColumnSourcePosition, isSelectedDataTable } from '../../list';
 import { createAst } from '../helper';
 import { TestListHelper } from '../list/list-helper';
 import { graphqlGenTs1 } from '../typeAlias.example';
@@ -58,7 +59,7 @@ describe(".api tests", () => {
         test(".add column (MUI DataTable)", async () => {
             const filePath = 'src/codegen/tests/list/files/is-datatable-test-file.txt';
             const source : SourceLineCol = {lineNumber: 12, columnNumber:17, fileName:filePath};
-            const result = await addColumn(graphqlGenTs1, new CodegenRw(), source, {property: 'testdate', entity: 'Customer'});
+            const result = await addColumn(graphqlGenTs1, new CodegenRw(), source, {property: 'testdate', entityName: 'Customer'});
     
             expect(result).not.toBe(undefined);
     
@@ -83,7 +84,7 @@ describe(".api tests", () => {
             // TODO:PC: Expected result: 
             // - added property "test2" to initialValues
             // - added TextField with id: test2
-            addFormInput(graphqlGenTs1, new CodegenRw(), source, {property: 'test2', entity: 'Customer'}).then(generated => console.log(generated));
+            addFormInput(graphqlGenTs1, new CodegenRw(), source, {property: 'test2', entityName: 'Customer'}).then(generated => console.log(generated));
         });
     
         test(".delete column (MUI DataTable)", async () => {
diff --git a/packages/react-lowcode/src/codegen/tests/detail/detail.test.ts b/packages/react-lowcode/src/codegen/tests/detail/detail.test.ts
index c29012407..783b247b6 100644
--- a/packages/react-lowcode/src/codegen/tests/detail/detail.test.ts
+++ b/packages/react-lowcode/src/codegen/tests/detail/detail.test.ts
@@ -1,5 +1,4 @@
 // TODO https://github.com/vvakame/typescript-formatter/blob/master/lib/formatter.ts
-import { Project, SourceFile } from "ts-morph"
 import ts, { factory } from "typescript"
 import { graphqlGenTs1 } from "../typeAlias.example"
 import { UiFramework, TableType, Formatter } from '../../definition/context-types'
diff --git a/packages/react-lowcode/src/codegen/tests/list/list.test.ts b/packages/react-lowcode/src/codegen/tests/list/list.test.ts
index bed4ca4a7..97f72eb5e 100644
--- a/packages/react-lowcode/src/codegen/tests/list/list.test.ts
+++ b/packages/react-lowcode/src/codegen/tests/list/list.test.ts
@@ -2,11 +2,9 @@ import ts, { factory } from "typescript"
 import { graphqlGenTs1 } from "../typeAlias.example"
 import { Formatter, TableType, UiFramework } from '../../definition/context-types'
 import { AppGenerator } from '../../generation/generators/app-generator'
-import {generatePages} from '../../index'
 import { CodeDir, CodeRW } from "../../../io"
 import { sourceFileEntity, createAst, parseGraphqlTypes } from "../helper"
-import path from 'path'
-import fs from "fs"
+import { generatePages } from "../../app"
 
 class testDemoWriter implements CodeRW, CodeDir {
   private _sourceCodeString: string = ''