Skip to content

Commit

Permalink
- reorganize codegen api
Browse files Browse the repository at this point in the history
  • Loading branch information
mat-app committed Jun 22, 2021
1 parent 6547335 commit f097fa6
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 212 deletions.
69 changes: 69 additions & 0 deletions packages/react-lowcode/src/codegen/app.ts
Original file line number Diff line number Diff line change
@@ -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)
}
}
})
}
44 changes: 44 additions & 0 deletions packages/react-lowcode/src/codegen/detail.ts
Original file line number Diff line number Diff line change
@@ -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
}
207 changes: 4 additions & 203 deletions packages/react-lowcode/src/codegen/index.ts
Original file line number Diff line number Diff line change
@@ -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'
54 changes: 52 additions & 2 deletions packages/react-lowcode/src/codegen/interfaces.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -9,7 +12,7 @@ export interface CodegenOptions {
}

export interface InsertOptions {
entity: string
entityName: string
property: string
index?: number
}
Expand Down Expand Up @@ -39,4 +42,51 @@ export interface WidgetProperty {

export interface WidgetProperties {
properties: WidgetProperty[]
}
}

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
}
}
Loading

0 comments on commit f097fa6

Please sign in to comment.