From e906683a1ab97da56054e009825285d6b9fa3ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=A5=E5=B8=8C?= Date: Mon, 20 Apr 2020 13:52:34 +0800 Subject: [PATCH 1/8] feat: add template create tool --- packages/code-generator/package.json | 4 +- .../code-generator/tools/createTemplate.js | 85 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 packages/code-generator/tools/createTemplate.js diff --git a/packages/code-generator/package.json b/packages/code-generator/package.json index a05582171..a368b8f38 100644 --- a/packages/code-generator/package.json +++ b/packages/code-generator/package.json @@ -9,10 +9,12 @@ "scripts": { "build": "rimraf lib && tsc", "demo": "ts-node -r tsconfig-paths/register ./src/demo/main.ts", - "test": "ava" + "test": "ava", + "template": "node ./tools/createTemplate.js" }, "dependencies": { "@ali/am-eslint-config": "*", + "@babel/parser": "^7.9.4", "@types/prettier": "^1.19.1", "change-case": "^3.1.0", "prettier": "^2.0.2", diff --git a/packages/code-generator/tools/createTemplate.js b/packages/code-generator/tools/createTemplate.js new file mode 100644 index 000000000..b7568f7a9 --- /dev/null +++ b/packages/code-generator/tools/createTemplate.js @@ -0,0 +1,85 @@ +const fs = require("fs"); +const path = require('path'); + +console.log(process.argv); +let root = ''; +const pathArgIndex = process.argv.indexOf('--path'); +if (pathArgIndex >= 0) { + root = process.argv[pathArgIndex + 1]; +} + +if (!root) { + throw new Error('Can\'t find path argument'); +} + +function cloneStr(str, times) { + let count = times; + const arr = []; + while(count > 0) { + arr.push(str); + count -= 1; + } + return arr.join(''); +} + +function createTemplateFile(root, internalPath, fileName) { + //不是文件夹,则添加type属性为文件后缀名 + const fileTypeSrc = path.extname(fileName); + const fileType = fileTypeSrc.substring(1); + const baseName = path.basename(fileName, fileTypeSrc); + + const filePath = path.join(root, internalPath); + const pieces = filePath.split(path.sep); + const depPrefix = cloneStr('../', pieces.length - 1); + const content = fs.readFileSync(path.join(filePath, fileName), { + encoding: 'utf8', + }); + const pathList = (internalPath.split(path.sep) || []).filter(p => !!p); + const modulePathStr = JSON.stringify(pathList).replace(/\"/g, '\''); + + const templateContent = ` +import ResultFile from '${depPrefix}model/ResultFile'; +import { IResultFile } from '${depPrefix}types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + '${baseName}', + '${fileType || ''}', + \` +${content} + \`, + ); + + return [${modulePathStr}, file]; +} + `; + + const targetFile = path.join(filePath, `${fileName}.ts`); + console.log(`=== Create File: ${targetFile}`); + fs.writeFileSync(targetFile, templateContent); +} + +function fileDisplay(root, internalPath) { + const dirPath = path.join(root, internalPath); + const filesList = fs.readdirSync(dirPath); + for(let i = 0; i < filesList.length; i++){ + //描述此文件/文件夹的对象 + const fileName = filesList[i]; + //拼接当前文件的路径(上一层路径+当前file的名字) + const filePath = path.join(dirPath, fileName); + //根据文件路径获取文件信息,返回一个fs.Stats对象 + const stats = fs.statSync(filePath); + if(stats.isDirectory()){ + //递归调用 + fileDisplay(root, path.join(internalPath, fileName)); + }else{ + createTemplateFile(root, internalPath, fileName); + } + } +} + +//调用函数遍历根目录,同时传递 文件夹路径和对应的数组 +//请使用同步读取 +fileDisplay(root, ''); +//读取完毕则写入到txt文件中 +// fs.writeFileSync('./data.txt', JSON.stringify(arr)); From 267953b82bfb768e821afeebf70a0fefe2aecbb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=A5=E5=B8=8C?= Date: Mon, 20 Apr 2020 13:53:16 +0800 Subject: [PATCH 2/8] feat: add recore project template --- .../recore/template/files/.editorconfig.ts | 32 +++++++ .../recore/template/files/.eslintignore.ts | 26 ++++++ .../recore/template/files/.gitignore.ts | 57 ++++++++++++ .../recore/template/files/.prettierrc.ts | 22 +++++ .../recore/template/files/README.md.ts | 36 ++++++++ .../recore/template/files/abc.json.ts | 28 ++++++ .../recore/template/files/build.json.ts | 32 +++++++ .../recore/template/files/package.json.ts | 67 ++++++++++++++ .../template/files/public/index.html.ts | 51 +++++++++++ .../template/files/src/config/app.ts.ts | 77 ++++++++++++++++ .../files/src/config/components.ts.ts | 43 +++++++++ .../template/files/src/config/utils.ts.ts | 29 ++++++ .../recore/template/files/src/index.ts.ts | 28 ++++++ .../template/files/src/plugins/provider.ts.ts | 89 +++++++++++++++++++ .../recore/template/files/src/router.ts.ts | 22 +++++ .../recore/template/files/tsconfig.json.ts | 53 +++++++++++ 16 files changed, 692 insertions(+) create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/.editorconfig.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/.eslintignore.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/.gitignore.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/.prettierrc.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/README.md.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/abc.json.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/build.json.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/package.json.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/public/index.html.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/app.ts.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/components.ts.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/utils.ts.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/src/index.ts.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/src/plugins/provider.ts.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/src/router.ts.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/tsconfig.json.ts diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/.editorconfig.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/.editorconfig.ts new file mode 100644 index 000000000..737b32b80 --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/.editorconfig.ts @@ -0,0 +1,32 @@ + +import ResultFile from '../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + '.editorconfig', + '', + ` +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Tab indentation +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + + `, + ); + + return [[], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/.eslintignore.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/.eslintignore.ts new file mode 100644 index 000000000..42c1be15c --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/.eslintignore.ts @@ -0,0 +1,26 @@ + +import ResultFile from '../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + '.eslintignore', + '', + ` +.idea +.vscode +.theia +.recore +build/ +.* +~* +node_modules + +packages/solution + + `, + ); + + return [[], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/.gitignore.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/.gitignore.ts new file mode 100644 index 000000000..fd89e80eb --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/.gitignore.ts @@ -0,0 +1,57 @@ + +import ResultFile from '../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + '.gitignore', + '', + ` +node_modules/ +coverage/ +build/ +dist/ +.idea/ +.vscode/ +.theia/ +.recore/ +.Trash-*/ +~* +package-lock.json + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store + *.swp +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + + `, + ); + + return [[], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/.prettierrc.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/.prettierrc.ts new file mode 100644 index 000000000..19cd24fbc --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/.prettierrc.ts @@ -0,0 +1,22 @@ + +import ResultFile from '../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + '.prettierrc', + '', + ` +{ + "semi": true, + "singleQuote": true, + "printWidth": 120, + "trailingComma": "all" +} + + `, + ); + + return [[], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/README.md.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/README.md.ts new file mode 100644 index 000000000..2c01ceaef --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/README.md.ts @@ -0,0 +1,36 @@ + +import ResultFile from '../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + 'README', + 'md', + ` +# runtime-code + +乐高接出码模块测试项目 + + +## 安装运行 + +```bash +# install dependencies +tnpm install + +# serve with hot reload at localhost:8080 +npm start + +# test projects +npm test + +# local build +npm run build +``` + + `, + ); + + return [[], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/abc.json.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/abc.json.ts new file mode 100644 index 000000000..b7523b5f9 --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/abc.json.ts @@ -0,0 +1,28 @@ + +import ResultFile from '../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + 'abc', + 'json', + ` +{ + "name": "test", + "assets": { + "type": "command", + "command": { + "cmd": [ + "tnpm ii", + "tnpm run build" + ] + } + } +} + + `, + ); + + return [[], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/build.json.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/build.json.ts new file mode 100644 index 000000000..08c1ab78a --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/build.json.ts @@ -0,0 +1,32 @@ + +import ResultFile from '../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + 'build', + 'json', + ` +{ + "entry": "src/index", + "alias": { + "@": "./src" + }, + "publicPath": "./", + "outputAssetsPath": { + "js": "", + "css": "" + }, + "plugins": [ + "build-plugin-react-app", + "@ali/build-plugin-recore-lowcode" + ], + "externals": { "react": "window.React", "react-dom": "window.ReactDOM", "@ali/recore": "window.Recore" } +} + + `, + ); + + return [[], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/package.json.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/package.json.ts new file mode 100644 index 000000000..c321542ab --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/package.json.ts @@ -0,0 +1,67 @@ + +import ResultFile from '../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + 'package', + 'json', + ` +{ + "name": "test", + "version": "1.0.0", + "description": "test", + "scripts": { + "start": "build-scripts start", + "build": "build-scripts build", + "test": "build-scripts test" + }, + "engines": { + "node": ">= 8.9.0", + "npm": ">=6.1.0" + }, + "dependencies": { + "@ali/recore": "^1.6.10", + "@ali/lowcode-runtime": "^0.8.0", + "@ali/recore-renderer": "^0.0.1", + "@ali/vc-shell": "1.3.1", + "@ali/vc-block": "^3.0.3-beta.1", + "@ali/vc-deep": "1.2.38", + "@ali/vc-div": "^1.0.1", + "@ali/vc-page": "^1.0.5", + "@ali/vc-slot": "^2.0.1", + "@ali/vc-text": "^4.0.1", + "@ali/vu-dataSource": "^1.0.4", + "@ali/vu-formatter": "^2.0.0", + "@ali/vu-fusion": "^2.0.1-beta.0", + "@ali/vu-legao-builtin": "^1.4.0-beta.2", + "react": "^16" + }, + "devDependencies": { + "build-plugin-react-app": "^1.0.15", + "@ali/build-plugin-recore-lowcode": "^0.0.1", + "@alib/build-scripts": "^0.1.0", + "@types/node": "^7", + "@types/react": "^16", + "eslint": "^6.5.1", + "tslib": "^1.9.3", + "typescript": "^3.1.3", + "prettier": "^1.18.2" + }, + "lint-staged": { + "./src/**/*.{ts,tsx}": [ + "tslint --fix", + "git add" + ] + }, + "nowa": { + "solution": "@ali/nowa-recore-solution" + } +} + + `, + ); + + return [[], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/public/index.html.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/public/index.html.ts new file mode 100644 index 000000000..af229a7e0 --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/public/index.html.ts @@ -0,0 +1,51 @@ + +import ResultFile from '../../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + 'index', + 'html', + ` + + + + + + + lowcode-runtime-test + + + + + + + + + + + + + + + + `, + ); + + return [['public'], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/app.ts.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/app.ts.ts new file mode 100644 index 000000000..784662e28 --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/app.ts.ts @@ -0,0 +1,77 @@ + +import ResultFile from '../../../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + 'app', + 'ts', + ` +export default { + "sdkVersion": "1.0.3", + "history": "hash", // 浏览器路由:brower 哈希路由:hash + "containerId": "app", + "layout": { + "componentName": "BasicLayout", + "props": { + "navConfig": { + "showLanguageChange": true, + "data": [ + { + "hidden": false, + "navUuid": "FORM-CP5669B1-3AW9DCLHZAY8EIY6WE6X1-GFZM3V1K-6", + "children": [], + "icon": "", + "targetNew": false, + "title": "测试基础表格", + "inner": true, + "relateUuid": "FORM-CP5669B1-3AW9DCLHZAY8EIY6WE6X1-GFZM3V1K-6", + "slug": "qihfg" + }, + { + "hidden": false, + "navUuid": "FORM-CP5669B1-8AW9XCUT4PCH15SMDWUM3-ZPQP3V1K-1", + "children": [], + "icon": "", + "targetNew": false, + "title": "测试查询表格", + "inner": true, + "relateUuid": "zqhej", + "slug": "zqhej" + } + ], + "systemLink": "/my_dev_center_code/0.1.0", + "appName": "乐高转码demo", + "isFoldHorizontal": "n", + "showAppTitle": true, + "isFold": "n", + "searchBarType": "icon", + "singletons": {}, + "navTheme": "default", + "type": "top_side_fold", + "navStyle": "orange", + "layout": "auto", + "bgColor": "white", + "languageChangeUrl": "/common/account/changeAccountLanguage.json", + "showSearch": "n", + "openSubMode": false, + "showCrumb": true, + "isFixed": "y", + "showIcon": false, + "showNav": true + } + }, + }, + "theme": { + "package": "@alife/theme-fusion", + "version": "^0.1.0" + }, + "compDependencies": [] +} + + `, + ); + + return [['src','config'], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/components.ts.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/components.ts.ts new file mode 100644 index 000000000..c894e92aa --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/components.ts.ts @@ -0,0 +1,43 @@ + +import ResultFile from '../../../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + 'components', + 'ts', + ` +/** + * 乐高组件 + */ +import Div from '@ali/vc-div/build/view'; +import Text from '@ali/vc-text/build/view'; +import Slot from '@ali/vc-slot/build/view'; +import Deep from '@ali/vc-deep/build/view'; +import Page from '@ali/vc-page/build/view'; +import Block from '@ali/vc-block/build/view'; + +const components = [Div, Text, Slot, Deep, Page, Block]; +const componentsMap = { +}; + +const processComponents = (deps) => { + deps.forEach((dep) => { + if (Array.isArray(dep)) { + processComponents(dep); + } else { + componentsMap[dep.displayName] = dep; + } + }); +}; + +processComponents(components); + +export default componentsMap; + + `, + ); + + return [['src','config'], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/utils.ts.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/utils.ts.ts new file mode 100644 index 000000000..84237ae1d --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/utils.ts.ts @@ -0,0 +1,29 @@ + +import ResultFile from '../../../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + 'utils', + 'ts', + ` +import toolkit from '@ali/vu-toolkit'; +import fusion from '@ali/vu-fusion'; +import dataSource from '@ali/vu-dataSource'; +import legaoBuiltin from '@ali/vu-legao-builtin'; +import formatter from '@ali/vu-formatter'; + +export default { + ...toolkit, + ...fusion, + legaoBuiltin, + dataSource, + formatter +} + + `, + ); + + return [['src','config'], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/index.ts.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/index.ts.ts new file mode 100644 index 000000000..8fdb5c211 --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/index.ts.ts @@ -0,0 +1,28 @@ + +import ResultFile from '../../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + 'index', + 'ts', + ` +import { app } from '@ali/lowcode-runtime'; +import Shell from '@ali/vc-shell'; +import StaticRender from './plugins/provider'; + +// 注册布局组件,可注册多个 +app.registerLayout(Shell, { + componentName: 'BasicLayout', +}); + +app.registerProvider(StaticRender); + +app.run(); + + `, + ); + + return [['src'], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/plugins/provider.ts.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/plugins/provider.ts.ts new file mode 100644 index 000000000..8d20e365d --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/plugins/provider.ts.ts @@ -0,0 +1,89 @@ + +import ResultFile from '../../../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + 'provider', + 'ts', + ` +import { ReactProvider } from '@ali/lowcode-runtime'; +import Router from '@/router'; +import appConfig from '@/config/app'; +import components from '@/config/components'; +import utils from '@/config/utils'; + +// 定制加载应用配置的逻辑 +export default class MyProvider extends ReactProvider { + // 初始化时调用,如可以在这里注入全局API + init() { + const gConfig = (window as any).g_config || {}; + const LeGao = { + __ctx__: {}, + createContext: (cfg: any) => { + const { schema } = cfg || {}; + // 1. 根据参数拉取schema + if (schema && typeof schema === 'string') { + this.setHomePage(schema); + } + const { isSectionalRender, autoRender } = gConfig || {}; + if (isSectionalRender && !autoRender) { + // 2. 渲染 + this.setSectionalRender(); + this.ready(); + } + const provider = this; + class Context { + get utils() { + return provider.getUtils(); + } + get components() { + return provider.getComponents(); + } + } + const ctx = new Context(); + (LeGao.__ctx__ as any)[this.getContainerId()] = ctx; + return ctx; + }, + getContext: (id: string) => { + if (!id) { + for (id in LeGao.__ctx__) { + return (LeGao.__ctx__ as any)[id]; + } + } + return (LeGao.__ctx__ as any)[id]; + } + }; + (window as any).LeGao = LeGao; + if (gConfig.index) { + this.setHomePage(gConfig.index); + } + if (gConfig.isSectionalRender) { + this.setSectionalRender(); + if (!gConfig.autoRender) { + return; + } + } + this.ready(); + } + + // 定制获取、处理应用配置(组件、插件、路由模式、布局等)的逻辑 + getAppData() { + return { + ...appConfig, + components, + utils: utils, + } + } + + getRouterView() { + return Router; + } +} + + `, + ); + + return [['src','plugins'], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/router.ts.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/router.ts.ts new file mode 100644 index 000000000..4dd0e3124 --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/router.ts.ts @@ -0,0 +1,22 @@ + +import ResultFile from '../../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + 'router', + 'ts', + ` +export default { + baseDir: './pages', + exact: true, + routes: [ + { main: './index', path: '/' }, + ], +}; + `, + ); + + return [['src'], file]; +} + \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/tsconfig.json.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/tsconfig.json.ts new file mode 100644 index 000000000..c30c3e46b --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/tsconfig.json.ts @@ -0,0 +1,53 @@ + +import ResultFile from '../../../../../../model/ResultFile'; +import { IResultFile } from '../../../../../../types'; + +export default function getFile(): [string[], IResultFile] { + const file = new ResultFile( + 'tsconfig', + 'json', + ` +{ + "compilerOptions": { + "lib": ["es2015", "dom"], + // Target latest version of ECMAScript. + "target": "esnext", + // Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. + "module": "esnext", + // Search under node_modules for non-relative imports. + "moduleResolution": "node", + // Process & infer types from .js files. + "allowJs": true, + // Report errors in .js files. + "checkJs": false, + // Don't emit; allow Babel to transform files. + "noEmit": true, + // Enable strictest settings like strictNullChecks & noImplicitAny. + "strict": true, + // Allow default imports from modules with no default export. This does not affect code emit, just typechecking. + "allowSyntheticDefaultImports": true, + // Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. + "esModuleInterop": true, + // Specify JSX code generation: 'preserve', 'react-native', or 'react'. + "jsx": "preserve", + // Import emit helpers (e.g. __extends, __rest, etc..) from tslib + "importHelpers": true, + // Enables experimental support for ES7 decorators. + "experimentalDecorators": true, + // Generates corresponding .map file. + "sourceMap": true, + // Disallow inconsistently-cased references to the same file. + "forceConsistentCasingInFileNames": true, + // Allow json import + "resolveJsonModule": true, + // skip type checking of declaration files + "skipLibCheck": true, + } +} + + `, + ); + + return [[], file]; +} + \ No newline at end of file From d6855e2236aa9db5b1deb2961159a6e734459566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=A5=E5=B8=8C?= Date: Mon, 20 Apr 2020 14:19:33 +0800 Subject: [PATCH 3/8] fix: same name chunk case --- packages/code-generator/src/generator/CodeBuilder.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/code-generator/src/generator/CodeBuilder.ts b/packages/code-generator/src/generator/CodeBuilder.ts index 1e5c3c999..ad7f33622 100644 --- a/packages/code-generator/src/generator/CodeBuilder.ts +++ b/packages/code-generator/src/generator/CodeBuilder.ts @@ -62,10 +62,12 @@ export default class Builder implements ICodeBuilder { } unprocessedChunks.splice(indexToRemove, 1); - unprocessedChunks.forEach( - // remove the processed chunk from all the linkAfter arrays from the remaining chunks - ch => (ch.linkAfter = ch.linkAfter.filter(after => after !== name)), - ); + if (!unprocessedChunks.some(ch => ch.name === name)) { + unprocessedChunks.forEach( + // remove the processed chunk from all the linkAfter arrays from the remaining chunks + ch => (ch.linkAfter = ch.linkAfter.filter(after => after !== name)), + ); + } } return resultingString.join('\n'); From 237b86656b189bb20b8726f5d3d518e48dea0439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=A5=E5=B8=8C?= Date: Mon, 20 Apr 2020 14:27:51 +0800 Subject: [PATCH 4/8] fix: factory api --- .../src/plugins/common/esmodule.ts | 48 +++-- .../src/plugins/common/requireUtils.ts | 32 ++-- .../plugins/component/react/containerClass.ts | 178 +++++++++--------- .../component/react/containerDataSource.ts | 52 ++--- .../component/react/containerInitState.ts | 52 ++--- .../component/react/containerInjectUtils.ts | 32 ++-- .../component/react/containerLifeCycle.ts | 124 ++++++------ .../component/react/containerMethod.ts | 60 +++--- .../src/plugins/component/react/jsx.ts | 60 +++--- .../component/react/reactCommonDeps.ts | 32 ++-- .../src/plugins/component/style/css.ts | 52 ++--- .../src/plugins/project/constants.ts | 84 +++++---- .../project/framework/icejs/plugins/entry.ts | 86 +++++---- .../framework/icejs/plugins/entryHtml.ts | 62 +++--- .../framework/icejs/plugins/globalStyle.ts | 82 ++++---- .../framework/icejs/plugins/packageJSON.ts | 124 ++++++------ .../project/framework/icejs/plugins/router.ts | 134 ++++++------- .../src/plugins/project/i18n.ts | 108 ++++++----- .../src/plugins/project/utils.ts | 120 ++++++------ .../src/postprocessor/prettier/index.ts | 32 ++-- .../code-generator/src/solutions/icejs.ts | 58 +++--- packages/code-generator/src/types/core.ts | 7 +- packages/code-generator/src/types/schema.ts | 9 +- packages/code-generator/tsconfig.json | 1 + 24 files changed, 860 insertions(+), 769 deletions(-) diff --git a/packages/code-generator/src/plugins/common/esmodule.ts b/packages/code-generator/src/plugins/common/esmodule.ts index 7043a4aa9..1e26706cd 100644 --- a/packages/code-generator/src/plugins/common/esmodule.ts +++ b/packages/code-generator/src/plugins/common/esmodule.ts @@ -2,6 +2,7 @@ import { COMMON_CHUNK_NAME } from '../../const/generator'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, CodeGeneratorError, DependencyType, @@ -41,7 +42,7 @@ function groupDepsByPack(deps: IDependency[]): Record { function buildPackageImport( pkg: string, deps: IDependency[], - isJSX: boolean, + targetFileType: string, ): ICodeChunk[] { const chunks: ICodeChunk[] = []; let defaultImport: string = ''; @@ -58,7 +59,7 @@ function buildPackageImport( if (dep.subName) { chunks.push({ type: ChunkType.STRING, - fileType: isJSX ? FileType.JSX : FileType.JS, + fileType: targetFileType, name: COMMON_CHUNK_NAME.FileVarDefine, content: `const ${targetName} = ${srcName}.${dep.subName};`, linkAfter: [ @@ -103,7 +104,7 @@ function buildPackageImport( statementL.push(`'@/${(deps[0] as IInternalDependency).type}/${pkg}';`); chunks.push({ type: ChunkType.STRING, - fileType: isJSX ? FileType.JSX : FileType.JS, + fileType: targetFileType, name: COMMON_CHUNK_NAME.InternalDepsImport, content: statementL.join(' '), linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport], @@ -112,7 +113,7 @@ function buildPackageImport( statementL.push(`'${pkg}';`); chunks.push({ type: ChunkType.STRING, - fileType: isJSX ? FileType.JSX : FileType.JS, + fileType: targetFileType, name: COMMON_CHUNK_NAME.ExternalDepsImport, content: statementL.join(' '), linkAfter: [], @@ -122,25 +123,36 @@ function buildPackageImport( return chunks; } -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +interface PluginConfig { + fileType: string; +} + +const pluginFactory: BuilderComponentPluginFactory = (config?: PluginConfig) => { + const cfg: PluginConfig = { + fileType: FileType.JS, + ...config, }; - const isJSX = next.chunks.some(chunk => chunk.fileType === FileType.JSX); + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; - const ir = next.ir as IWithDependency; + const ir = next.ir as IWithDependency; - if (ir && ir.deps && ir.deps.length > 0) { - const packs = groupDepsByPack(ir.deps); + if (ir && ir.deps && ir.deps.length > 0) { + const packs = groupDepsByPack(ir.deps); - Object.keys(packs).forEach(pkg => { - const chunks = buildPackageImport(pkg, packs[pkg], isJSX); - next.chunks.push.apply(next.chunks, chunks); - }); - } + Object.keys(packs).forEach(pkg => { + const chunks = buildPackageImport(pkg, packs[pkg], cfg.fileType); + next.chunks.push.apply(next.chunks, chunks); + }); + } - return next; + return next; + }; + + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/common/requireUtils.ts b/packages/code-generator/src/plugins/common/requireUtils.ts index d5b4747b9..9da850a4c 100644 --- a/packages/code-generator/src/plugins/common/requireUtils.ts +++ b/packages/code-generator/src/plugins/common/requireUtils.ts @@ -2,26 +2,30 @@ import { COMMON_CHUNK_NAME } from '../../const/generator'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, } from '../../types'; // TODO: How to merge this logic to common deps -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: COMMON_CHUNK_NAME.InternalDepsImport, + content: `import * from 'react';`, + linkAfter: [], + }); + + return next; }; - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: COMMON_CHUNK_NAME.InternalDepsImport, - content: `import * from 'react';`, - linkAfter: [], - }); - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/react/containerClass.ts b/packages/code-generator/src/plugins/component/react/containerClass.ts index ddf1ef337..7cba8c66e 100644 --- a/packages/code-generator/src/plugins/component/react/containerClass.ts +++ b/packages/code-generator/src/plugins/component/react/containerClass.ts @@ -3,99 +3,103 @@ import { REACT_CHUNK_NAME } from './const'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, IContainerInfo, } from '../../../types'; -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IContainerInfo; + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: REACT_CHUNK_NAME.ClassStart, + content: `class ${ir.componentName} extends React.Component {`, + linkAfter: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.FileVarDefine, + COMMON_CHUNK_NAME.FileUtilDefine, + ], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: REACT_CHUNK_NAME.ClassEnd, + content: `}`, + linkAfter: [REACT_CHUNK_NAME.ClassStart, REACT_CHUNK_NAME.ClassRenderEnd], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: REACT_CHUNK_NAME.ClassConstructorStart, + content: 'constructor(props, context) { super(props); ', + linkAfter: [REACT_CHUNK_NAME.ClassStart], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: REACT_CHUNK_NAME.ClassConstructorEnd, + content: '}', + linkAfter: [ + REACT_CHUNK_NAME.ClassConstructorStart, + REACT_CHUNK_NAME.ClassConstructorContent, + ], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: REACT_CHUNK_NAME.ClassRenderStart, + content: 'render() {', + linkAfter: [ + REACT_CHUNK_NAME.ClassStart, + REACT_CHUNK_NAME.ClassConstructorEnd, + REACT_CHUNK_NAME.ClassLifeCycle, + REACT_CHUNK_NAME.ClassMethod, + ], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: REACT_CHUNK_NAME.ClassRenderEnd, + content: '}', + linkAfter: [ + REACT_CHUNK_NAME.ClassRenderStart, + REACT_CHUNK_NAME.ClassRenderPre, + REACT_CHUNK_NAME.ClassRenderJSX, + ], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: COMMON_CHUNK_NAME.FileExport, + content: `export default ${ir.componentName};`, + linkAfter: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.FileVarDefine, + COMMON_CHUNK_NAME.FileUtilDefine, + REACT_CHUNK_NAME.ClassEnd, + ], + }); + + return next; }; - - const ir = next.ir as IContainerInfo; - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassStart, - content: `class ${ir.componentName} extends React.Component {`, - linkAfter: [ - COMMON_CHUNK_NAME.ExternalDepsImport, - COMMON_CHUNK_NAME.InternalDepsImport, - COMMON_CHUNK_NAME.FileVarDefine, - COMMON_CHUNK_NAME.FileUtilDefine, - ], - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassEnd, - content: `}`, - linkAfter: [REACT_CHUNK_NAME.ClassStart, REACT_CHUNK_NAME.ClassRenderEnd], - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassConstructorStart, - content: 'constructor(props, context) { super(props); ', - linkAfter: [REACT_CHUNK_NAME.ClassStart], - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassConstructorEnd, - content: '}', - linkAfter: [ - REACT_CHUNK_NAME.ClassConstructorStart, - REACT_CHUNK_NAME.ClassConstructorContent, - ], - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassRenderStart, - content: 'render() {', - linkAfter: [ - REACT_CHUNK_NAME.ClassStart, - REACT_CHUNK_NAME.ClassConstructorEnd, - REACT_CHUNK_NAME.ClassLifeCycle, - REACT_CHUNK_NAME.ClassMethod, - ], - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassRenderEnd, - content: '}', - linkAfter: [ - REACT_CHUNK_NAME.ClassRenderStart, - REACT_CHUNK_NAME.ClassRenderPre, - REACT_CHUNK_NAME.ClassRenderJSX, - ], - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: COMMON_CHUNK_NAME.FileExport, - content: `export default ${ir.componentName};`, - linkAfter: [ - COMMON_CHUNK_NAME.ExternalDepsImport, - COMMON_CHUNK_NAME.InternalDepsImport, - COMMON_CHUNK_NAME.FileVarDefine, - COMMON_CHUNK_NAME.FileUtilDefine, - REACT_CHUNK_NAME.ClassEnd, - ], - }); - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/react/containerDataSource.ts b/packages/code-generator/src/plugins/component/react/containerDataSource.ts index c71859a21..74c02e1f4 100644 --- a/packages/code-generator/src/plugins/component/react/containerDataSource.ts +++ b/packages/code-generator/src/plugins/component/react/containerDataSource.ts @@ -4,36 +4,40 @@ import { generateCompositeType } from '../../utils/compositeType'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, IContainerInfo, } from '../../../types'; -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IContainerInfo; + + if (ir.state) { + const state = ir.state; + const fields = Object.keys(state).map(stateName => { + const [isString, value] = generateCompositeType(state[stateName]); + return `${stateName}: ${isString ? `'${value}'` : value},`; + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: REACT_CHUNK_NAME.ClassConstructorContent, + content: `this.state = { ${fields.join('')} };`, + linkAfter: [REACT_CHUNK_NAME.ClassConstructorStart], + }); + } + + return next; }; - - const ir = next.ir as IContainerInfo; - - if (ir.state) { - const state = ir.state; - const fields = Object.keys(state).map(stateName => { - const [isString, value] = generateCompositeType(state[stateName]); - return `${stateName}: ${isString ? `'${value}'` : value},`; - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassConstructorContent, - content: `this.state = { ${fields.join('')} };`, - linkAfter: [REACT_CHUNK_NAME.ClassConstructorStart], - }); - } - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/react/containerInitState.ts b/packages/code-generator/src/plugins/component/react/containerInitState.ts index c71859a21..74c02e1f4 100644 --- a/packages/code-generator/src/plugins/component/react/containerInitState.ts +++ b/packages/code-generator/src/plugins/component/react/containerInitState.ts @@ -4,36 +4,40 @@ import { generateCompositeType } from '../../utils/compositeType'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, IContainerInfo, } from '../../../types'; -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IContainerInfo; + + if (ir.state) { + const state = ir.state; + const fields = Object.keys(state).map(stateName => { + const [isString, value] = generateCompositeType(state[stateName]); + return `${stateName}: ${isString ? `'${value}'` : value},`; + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: REACT_CHUNK_NAME.ClassConstructorContent, + content: `this.state = { ${fields.join('')} };`, + linkAfter: [REACT_CHUNK_NAME.ClassConstructorStart], + }); + } + + return next; }; - - const ir = next.ir as IContainerInfo; - - if (ir.state) { - const state = ir.state; - const fields = Object.keys(state).map(stateName => { - const [isString, value] = generateCompositeType(state[stateName]); - return `${stateName}: ${isString ? `'${value}'` : value},`; - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassConstructorContent, - content: `this.state = { ${fields.join('')} };`, - linkAfter: [REACT_CHUNK_NAME.ClassConstructorStart], - }); - } - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts b/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts index a6670230a..8fd70c9ba 100644 --- a/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts +++ b/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts @@ -2,25 +2,29 @@ import { REACT_CHUNK_NAME } from './const'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, } from '../../../types'; -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: REACT_CHUNK_NAME.ClassConstructorContent, + content: `this.utils = utils;`, + linkAfter: [REACT_CHUNK_NAME.ClassConstructorStart], + }); + + return next; }; - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassConstructorContent, - content: `this.utils = utils;`, - linkAfter: [REACT_CHUNK_NAME.ClassConstructorStart], - }); - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts b/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts index 3e83e659a..39e2a017a 100644 --- a/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts +++ b/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts @@ -7,6 +7,7 @@ import { import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, CodeGeneratorError, FileType, @@ -16,66 +17,69 @@ import { IJSExpression, } from '../../../types'; -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IContainerInfo; + + if (ir.lifeCycles) { + const lifeCycles = ir.lifeCycles; + const chunks = Object.keys(lifeCycles).map(lifeCycleName => { + if (lifeCycleName === 'constructor') { + return { + type: ChunkType.STRING, + fileType: FileType.JSX, + name: REACT_CHUNK_NAME.ClassConstructorContent, + content: getFuncExprBody( + (lifeCycles[lifeCycleName] as IJSExpression).value, + ), + linkAfter: [REACT_CHUNK_NAME.ClassConstructorStart], + }; + } + if (lifeCycleName === 'render') { + return { + type: ChunkType.STRING, + fileType: FileType.JSX, + name: REACT_CHUNK_NAME.ClassRenderPre, + content: getFuncExprBody( + (lifeCycles[lifeCycleName] as IJSExpression).value, + ), + linkAfter: [REACT_CHUNK_NAME.ClassRenderStart], + }; + } + if ( + lifeCycleName === 'componentDidMount' || + lifeCycleName === 'componentDidUpdate' || + lifeCycleName === 'componentWillUnmount' || + lifeCycleName === 'componentDidCatch' + ) { + return { + type: ChunkType.STRING, + fileType: FileType.JSX, + name: REACT_CHUNK_NAME.ClassLifeCycle, + content: transformFuncExpr2MethodMember( + lifeCycleName, + (lifeCycles[lifeCycleName] as IJSExpression).value, + ), + linkAfter: [ + REACT_CHUNK_NAME.ClassStart, + REACT_CHUNK_NAME.ClassConstructorEnd, + ], + }; + } + + throw new CodeGeneratorError('Unknown life cycle method name'); + }); + + next.chunks.push.apply(next.chunks, chunks); + } + + return next; }; - - const ir = next.ir as IContainerInfo; - - if (ir.lifeCycles) { - const lifeCycles = ir.lifeCycles; - const chunks = Object.keys(lifeCycles).map(lifeCycleName => { - if (lifeCycleName === 'constructor') { - return { - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassConstructorContent, - content: getFuncExprBody( - (lifeCycles[lifeCycleName] as IJSExpression).value, - ), - linkAfter: [REACT_CHUNK_NAME.ClassConstructorStart], - }; - } - if (lifeCycleName === 'render') { - return { - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassRenderPre, - content: getFuncExprBody( - (lifeCycles[lifeCycleName] as IJSExpression).value, - ), - linkAfter: [REACT_CHUNK_NAME.ClassRenderStart], - }; - } - if ( - lifeCycleName === 'componentDidMount' || - lifeCycleName === 'componentDidUpdate' || - lifeCycleName === 'componentWillUnmount' || - lifeCycleName === 'componentDidCatch' - ) { - return { - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassLifeCycle, - content: transformFuncExpr2MethodMember( - lifeCycleName, - (lifeCycles[lifeCycleName] as IJSExpression).value, - ), - linkAfter: [ - REACT_CHUNK_NAME.ClassStart, - REACT_CHUNK_NAME.ClassConstructorEnd, - ], - }; - } - - throw new CodeGeneratorError('Unknown life cycle method name'); - }); - - next.chunks.push.apply(next.chunks, chunks); - } - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/react/containerMethod.ts b/packages/code-generator/src/plugins/component/react/containerMethod.ts index cff01d9ce..1f141dbf6 100644 --- a/packages/code-generator/src/plugins/component/react/containerMethod.ts +++ b/packages/code-generator/src/plugins/component/react/containerMethod.ts @@ -4,6 +4,7 @@ import { transformFuncExpr2MethodMember } from '../../utils/jsExpression'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeChunk, @@ -12,34 +13,37 @@ import { IJSExpression, } from '../../../types'; -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IContainerInfo; + + if (ir.methods) { + const methods = ir.methods; + const chunks = Object.keys(methods).map(methodName => ({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: REACT_CHUNK_NAME.ClassMethod, + content: transformFuncExpr2MethodMember( + methodName, + (methods[methodName] as IJSExpression).value, + ), + linkAfter: [ + REACT_CHUNK_NAME.ClassStart, + REACT_CHUNK_NAME.ClassConstructorEnd, + REACT_CHUNK_NAME.ClassLifeCycle, + ], + })); + + next.chunks.push.apply(next.chunks, chunks); + } + + return next; }; - - const ir = next.ir as IContainerInfo; - - if (ir.methods) { - const methods = ir.methods; - const chunks = Object.keys(methods).map(methodName => ({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassMethod, - content: transformFuncExpr2MethodMember( - methodName, - (methods[methodName] as IJSExpression).value, - ), - linkAfter: [ - REACT_CHUNK_NAME.ClassStart, - REACT_CHUNK_NAME.ClassConstructorEnd, - REACT_CHUNK_NAME.ClassLifeCycle, - ], - })); - - next.chunks.push.apply(next.chunks, chunks); - } - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/react/jsx.ts b/packages/code-generator/src/plugins/component/react/jsx.ts index 3e5162353..e1b5d2306 100644 --- a/packages/code-generator/src/plugins/component/react/jsx.ts +++ b/packages/code-generator/src/plugins/component/react/jsx.ts @@ -1,5 +1,6 @@ import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChildNodeItem, ChildNodeType, ChunkType, @@ -111,39 +112,42 @@ function generateChildren(children: ChildNodeType): string[] { }); } -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, - }; +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; - const ir = next.ir as IContainerInfo; + const ir = next.ir as IContainerInfo; - let jsxContent: string; - if (!ir.children || (ir.children as unknown[]).length === 0) { - jsxContent = 'null'; - } else { - const childrenCode = generateChildren(ir.children); - if (childrenCode.length === 1) { - jsxContent = `(${childrenCode[0]})`; + let jsxContent: string; + if (!ir.children || (ir.children as unknown[]).length === 0) { + jsxContent = 'null'; } else { - jsxContent = `(${childrenCode.join( - '', - )})`; + const childrenCode = generateChildren(ir.children); + if (childrenCode.length === 1) { + jsxContent = `(${childrenCode[0]})`; + } else { + jsxContent = `(${childrenCode.join( + '', + )})`; + } } - } - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassRenderJSX, - content: `return ${jsxContent};`, - linkAfter: [ - REACT_CHUNK_NAME.ClassRenderStart, - REACT_CHUNK_NAME.ClassRenderPre, - ], - }); + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: REACT_CHUNK_NAME.ClassRenderJSX, + content: `return ${jsxContent};`, + linkAfter: [ + REACT_CHUNK_NAME.ClassRenderStart, + REACT_CHUNK_NAME.ClassRenderPre, + ], + }); - return next; + return next; + }; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/react/reactCommonDeps.ts b/packages/code-generator/src/plugins/component/react/reactCommonDeps.ts index 38936fc68..434f44564 100644 --- a/packages/code-generator/src/plugins/component/react/reactCommonDeps.ts +++ b/packages/code-generator/src/plugins/component/react/reactCommonDeps.ts @@ -2,6 +2,7 @@ import { COMMON_CHUNK_NAME } from '../../../const/generator'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, @@ -9,20 +10,23 @@ import { } from '../../../types'; // TODO: How to merge this logic to common deps -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: COMMON_CHUNK_NAME.ExternalDepsImport, + content: `import React from 'react';`, + linkAfter: [], + }); + + return next; }; - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: COMMON_CHUNK_NAME.ExternalDepsImport, - content: `import React from 'react';`, - linkAfter: [], - }); - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/style/css.ts b/packages/code-generator/src/plugins/component/style/css.ts index c4ce2160e..c803d24a9 100644 --- a/packages/code-generator/src/plugins/component/style/css.ts +++ b/packages/code-generator/src/plugins/component/style/css.ts @@ -2,36 +2,40 @@ import { COMMON_CHUNK_NAME } from '../../../const/generator'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, IContainerInfo, } from '../../../types'; -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IContainerInfo; + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.CSS, + name: COMMON_CHUNK_NAME.StyleCssContent, + content: ir.css, + linkAfter: [COMMON_CHUNK_NAME.StyleDepsImport], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: COMMON_CHUNK_NAME.InternalDepsImport, + content: `import './index.css';`, + linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport], + }); + + return next; }; - - const ir = next.ir as IContainerInfo; - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.CSS, - name: COMMON_CHUNK_NAME.StyleCssContent, - content: ir.css, - linkAfter: [COMMON_CHUNK_NAME.StyleDepsImport], - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: COMMON_CHUNK_NAME.InternalDepsImport, - content: `import './index.css';`, - linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport], - }); - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/project/constants.ts b/packages/code-generator/src/plugins/project/constants.ts index 7be166d3d..8c1302cd0 100644 --- a/packages/code-generator/src/plugins/project/constants.ts +++ b/packages/code-generator/src/plugins/project/constants.ts @@ -2,6 +2,7 @@ import { COMMON_CHUNK_NAME } from '../../const/generator'; import { generateCompositeType } from '../../plugins/utils/compositeType'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, @@ -9,46 +10,49 @@ import { } from '../../types'; // TODO: How to merge this logic to common deps -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IProjectInfo; + if (ir.constants) { + const [, constantStr] = generateCompositeType(ir.constants); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JS, + name: COMMON_CHUNK_NAME.FileVarDefine, + content: ` + const constantConfig = ${constantStr}; + `, + linkAfter: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + ], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JS, + name: COMMON_CHUNK_NAME.FileExport, + content: ` + export default constantConfig; + `, + linkAfter: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.FileVarDefine, + COMMON_CHUNK_NAME.FileUtilDefine, + COMMON_CHUNK_NAME.FileMainContent, + ], + }); + } + + return next; }; - - const ir = next.ir as IProjectInfo; - if (ir.constants) { - const [, constantStr] = generateCompositeType(ir.constants); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JS, - name: COMMON_CHUNK_NAME.FileVarDefine, - content: ` - const constantConfig = ${constantStr}; - `, - linkAfter: [ - COMMON_CHUNK_NAME.ExternalDepsImport, - COMMON_CHUNK_NAME.InternalDepsImport, - ], - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JS, - name: COMMON_CHUNK_NAME.FileExport, - content: ` - export default constantConfig; - `, - linkAfter: [ - COMMON_CHUNK_NAME.ExternalDepsImport, - COMMON_CHUNK_NAME.InternalDepsImport, - COMMON_CHUNK_NAME.FileVarDefine, - COMMON_CHUNK_NAME.FileUtilDefine, - COMMON_CHUNK_NAME.FileMainContent, - ], - }); - } - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/project/framework/icejs/plugins/entry.ts b/packages/code-generator/src/plugins/project/framework/icejs/plugins/entry.ts index f32d9abed..28bef8f0a 100644 --- a/packages/code-generator/src/plugins/project/framework/icejs/plugins/entry.ts +++ b/packages/code-generator/src/plugins/project/framework/icejs/plugins/entry.ts @@ -2,6 +2,7 @@ import { COMMON_CHUNK_NAME } from '../../../../../const/generator'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, @@ -9,47 +10,50 @@ import { } from '../../../../../types'; // TODO: How to merge this logic to common deps -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IProjectInfo; + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JS, + name: COMMON_CHUNK_NAME.ExternalDepsImport, + content: ` + import { createApp } from 'ice'; + `, + linkAfter: [], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JS, + name: COMMON_CHUNK_NAME.FileMainContent, + content: ` + const appConfig = { + app: { + rootId: '${ir.config.targetRootID}', + }, + router: { + type: '${ir.config.historyMode}', + }, + }; + createApp(appConfig); + `, + linkAfter: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.FileVarDefine, + COMMON_CHUNK_NAME.FileUtilDefine, + ], + }); + + return next; }; - - const ir = next.ir as IProjectInfo; - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JS, - name: COMMON_CHUNK_NAME.ExternalDepsImport, - content: ` - import { createApp } from 'ice'; - `, - linkAfter: [], - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JS, - name: COMMON_CHUNK_NAME.FileMainContent, - content: ` - const appConfig = { - app: { - rootId: '${ir.config.targetRootID}', - }, - router: { - type: '${ir.config.historyMode}', - }, - }; - createApp(appConfig); - `, - linkAfter: [ - COMMON_CHUNK_NAME.ExternalDepsImport, - COMMON_CHUNK_NAME.InternalDepsImport, - COMMON_CHUNK_NAME.FileVarDefine, - COMMON_CHUNK_NAME.FileUtilDefine, - ], - }); - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/project/framework/icejs/plugins/entryHtml.ts b/packages/code-generator/src/plugins/project/framework/icejs/plugins/entryHtml.ts index 6ceebc04a..644e6155d 100644 --- a/packages/code-generator/src/plugins/project/framework/icejs/plugins/entryHtml.ts +++ b/packages/code-generator/src/plugins/project/framework/icejs/plugins/entryHtml.ts @@ -2,6 +2,7 @@ import { COMMON_CHUNK_NAME } from '../../../../../const/generator'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, @@ -9,35 +10,38 @@ import { } from '../../../../../types'; // TODO: How to merge this logic to common deps -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IProjectInfo; + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.HTML, + name: COMMON_CHUNK_NAME.HtmlContent, + content: ` + + + + + + + ${ir.meta.name} + + +
+ + + `, + linkAfter: [], + }); + + return next; }; - - const ir = next.ir as IProjectInfo; - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.HTML, - name: COMMON_CHUNK_NAME.HtmlContent, - content: ` - - - - - - - ${ir.meta.name} - - -
- - - `, - linkAfter: [], - }); - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/project/framework/icejs/plugins/globalStyle.ts b/packages/code-generator/src/plugins/project/framework/icejs/plugins/globalStyle.ts index 3e083c1f1..92ebf8730 100644 --- a/packages/code-generator/src/plugins/project/framework/icejs/plugins/globalStyle.ts +++ b/packages/code-generator/src/plugins/project/framework/icejs/plugins/globalStyle.ts @@ -2,6 +2,7 @@ import { COMMON_CHUNK_NAME } from '../../../../../const/generator'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, @@ -9,45 +10,48 @@ import { } from '../../../../../types'; // TODO: How to merge this logic to common deps -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IProjectInfo; + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.SCSS, + name: COMMON_CHUNK_NAME.StyleDepsImport, + content: ` + // 引入默认全局样式 + @import '@alifd/next/reset.scss'; + `, + linkAfter: [], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.SCSS, + name: COMMON_CHUNK_NAME.StyleCssContent, + content: ` + body { + -webkit-font-smoothing: antialiased; + } + `, + linkAfter: [COMMON_CHUNK_NAME.StyleDepsImport], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.SCSS, + name: COMMON_CHUNK_NAME.StyleCssContent, + content: ir.css || '', + linkAfter: [COMMON_CHUNK_NAME.StyleDepsImport], + }); + + return next; }; - - const ir = next.ir as IProjectInfo; - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.SCSS, - name: COMMON_CHUNK_NAME.StyleDepsImport, - content: ` - // 引入默认全局样式 - @import '@alifd/next/reset.scss'; - `, - linkAfter: [], - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.SCSS, - name: COMMON_CHUNK_NAME.StyleCssContent, - content: ` - body { - -webkit-font-smoothing: antialiased; - } - `, - linkAfter: [COMMON_CHUNK_NAME.StyleDepsImport], - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.SCSS, - name: COMMON_CHUNK_NAME.StyleCssContent, - content: ir.css || '', - linkAfter: [COMMON_CHUNK_NAME.StyleDepsImport], - }); - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/project/framework/icejs/plugins/packageJSON.ts b/packages/code-generator/src/plugins/project/framework/icejs/plugins/packageJSON.ts index 0e4c48805..f00381a0b 100644 --- a/packages/code-generator/src/plugins/project/framework/icejs/plugins/packageJSON.ts +++ b/packages/code-generator/src/plugins/project/framework/icejs/plugins/packageJSON.ts @@ -2,6 +2,7 @@ import { COMMON_CHUNK_NAME } from '../../../../../const/generator'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, @@ -21,66 +22,69 @@ interface IIceJsPackageJSON extends IPackageJSON { } // TODO: How to merge this logic to common deps -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IProjectInfo; + + const packageJson: IIceJsPackageJSON = { + name: '@alifd/scaffold-lite-js', + version: '0.1.5', + description: '轻量级模板,使用 JavaScript,仅包含基础的 Layout。', + dependencies: { + moment: '^2.24.0', + react: '^16.4.1', + 'react-dom': '^16.4.1', + '@alifd/theme-design-pro': '^0.x', + }, + devDependencies: { + '@ice/spec': '^1.0.0', + 'build-plugin-fusion': '^0.1.0', + 'build-plugin-moment-locales': '^0.1.0', + eslint: '^6.0.1', + 'ice.js': '^1.0.0', + stylelint: '^13.2.0', + '@ali/build-plugin-ice-def': '^0.1.0', + }, + scripts: { + start: 'icejs start', + build: 'icejs build', + lint: 'npm run eslint && npm run stylelint', + eslint: 'eslint --cache --ext .js,.jsx ./', + stylelint: 'stylelint ./**/*.scss', + }, + ideMode: { + name: 'ice-react', + }, + iceworks: { + type: 'react', + adapter: 'adapter-react-v3', + }, + engines: { + node: '>=8.0.0', + }, + repository: { + type: 'git', + url: 'http://gitlab.alibaba-inc.com/msd/leak-scan/tree/master', + }, + private: true, + originTemplate: '@alifd/scaffold-lite-js', + }; + + next.chunks.push({ + type: ChunkType.JSON, + fileType: FileType.JSON, + name: COMMON_CHUNK_NAME.FileMainContent, + content: packageJson, + linkAfter: [], + }); + + return next; }; - - const ir = next.ir as IProjectInfo; - - const packageJson: IIceJsPackageJSON = { - name: '@alifd/scaffold-lite-js', - version: '0.1.5', - description: '轻量级模板,使用 JavaScript,仅包含基础的 Layout。', - dependencies: { - moment: '^2.24.0', - react: '^16.4.1', - 'react-dom': '^16.4.1', - '@alifd/theme-design-pro': '^0.x', - }, - devDependencies: { - '@ice/spec': '^1.0.0', - 'build-plugin-fusion': '^0.1.0', - 'build-plugin-moment-locales': '^0.1.0', - eslint: '^6.0.1', - 'ice.js': '^1.0.0', - stylelint: '^13.2.0', - '@ali/build-plugin-ice-def': '^0.1.0', - }, - scripts: { - start: 'icejs start', - build: 'icejs build', - lint: 'npm run eslint && npm run stylelint', - eslint: 'eslint --cache --ext .js,.jsx ./', - stylelint: 'stylelint ./**/*.scss', - }, - ideMode: { - name: 'ice-react', - }, - iceworks: { - type: 'react', - adapter: 'adapter-react-v3', - }, - engines: { - node: '>=8.0.0', - }, - repository: { - type: 'git', - url: 'http://gitlab.alibaba-inc.com/msd/leak-scan/tree/master', - }, - private: true, - originTemplate: '@alifd/scaffold-lite-js', - }; - - next.chunks.push({ - type: ChunkType.JSON, - fileType: FileType.JSON, - name: COMMON_CHUNK_NAME.FileMainContent, - content: packageJson, - linkAfter: [], - }); - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/project/framework/icejs/plugins/router.ts b/packages/code-generator/src/plugins/project/framework/icejs/plugins/router.ts index b2c9c0805..9dc436b28 100644 --- a/packages/code-generator/src/plugins/project/framework/icejs/plugins/router.ts +++ b/packages/code-generator/src/plugins/project/framework/icejs/plugins/router.ts @@ -2,6 +2,7 @@ import { COMMON_CHUNK_NAME } from '../../../../../const/generator'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, @@ -9,71 +10,74 @@ import { } from '../../../../../types'; // TODO: How to merge this logic to common deps -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IRouterInfo; + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JS, + name: COMMON_CHUNK_NAME.InternalDepsImport, + content: ` + import BasicLayout from '@/layouts/BasicLayout'; + `, + linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JS, + name: COMMON_CHUNK_NAME.FileVarDefine, + content: ` + const routerConfig = [ + { + path: '/', + component: BasicLayout, + children: [ + ${ir.routes + .map( + route => ` + { + path: '${route.path}', + component: ${route.componentName}, + } + `, + ) + .join(',')} + ], + }, + ]; + `, + linkAfter: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.FileUtilDefine, + ], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JS, + name: COMMON_CHUNK_NAME.FileExport, + content: ` + export default routerConfig; + `, + linkAfter: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.FileUtilDefine, + COMMON_CHUNK_NAME.FileVarDefine, + COMMON_CHUNK_NAME.FileMainContent, + ], + }); + + return next; }; - - const ir = next.ir as IRouterInfo; - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JS, - name: COMMON_CHUNK_NAME.InternalDepsImport, - content: ` - import BasicLayout from '@/layouts/BasicLayout'; - `, - linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport], - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JS, - name: COMMON_CHUNK_NAME.FileVarDefine, - content: ` - const routerConfig = [ - { - path: '/', - component: BasicLayout, - children: [ - ${ir.routes - .map( - route => ` - { - path: '${route.path}', - component: ${route.componentName}, - } - `, - ) - .join(',')} - ], - }, - ]; - `, - linkAfter: [ - COMMON_CHUNK_NAME.ExternalDepsImport, - COMMON_CHUNK_NAME.InternalDepsImport, - COMMON_CHUNK_NAME.FileUtilDefine, - ], - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JS, - name: COMMON_CHUNK_NAME.FileExport, - content: ` - export default routerConfig; - `, - linkAfter: [ - COMMON_CHUNK_NAME.ExternalDepsImport, - COMMON_CHUNK_NAME.InternalDepsImport, - COMMON_CHUNK_NAME.FileUtilDefine, - COMMON_CHUNK_NAME.FileVarDefine, - COMMON_CHUNK_NAME.FileMainContent, - ], - }); - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/project/i18n.ts b/packages/code-generator/src/plugins/project/i18n.ts index 14b9ef773..bb3623032 100644 --- a/packages/code-generator/src/plugins/project/i18n.ts +++ b/packages/code-generator/src/plugins/project/i18n.ts @@ -2,6 +2,7 @@ import { COMMON_CHUNK_NAME } from '../../const/generator'; import { generateCompositeType } from '../../plugins/utils/compositeType'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, @@ -9,58 +10,61 @@ import { } from '../../types'; // TODO: How to merge this logic to common deps -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IProjectInfo; + if (ir.i18n) { + const [, i18nStr] = generateCompositeType(ir.i18n); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JS, + name: COMMON_CHUNK_NAME.FileMainContent, + content: ` + const i18nConfig = ${i18nStr}; + let locale = 'en_US'; + + const changeLocale = (target) => { + locale = target; + }; + + const i18n = key => i18nConfig && i18nConfig[locale] && i18nConfig[locale][key] || ''; + `, + linkAfter: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.FileVarDefine, + COMMON_CHUNK_NAME.FileUtilDefine, + ], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JS, + name: COMMON_CHUNK_NAME.FileExport, + content: ` + export { + changeLocale, + i18n, + }; + `, + linkAfter: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.FileVarDefine, + COMMON_CHUNK_NAME.FileUtilDefine, + COMMON_CHUNK_NAME.FileMainContent, + ], + }); + } + + return next; }; - - const ir = next.ir as IProjectInfo; - if (ir.i18n) { - const [, i18nStr] = generateCompositeType(ir.i18n); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JS, - name: COMMON_CHUNK_NAME.FileMainContent, - content: ` - const i18nConfig = ${i18nStr}; - let locale = 'en_US'; - - const changeLocale = (target) => { - locale = target; - }; - - const i18n = key => i18nConfig && i18nConfig[locale] && i18nConfig[locale][key] || ''; - `, - linkAfter: [ - COMMON_CHUNK_NAME.ExternalDepsImport, - COMMON_CHUNK_NAME.InternalDepsImport, - COMMON_CHUNK_NAME.FileVarDefine, - COMMON_CHUNK_NAME.FileUtilDefine, - ], - }); - - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JS, - name: COMMON_CHUNK_NAME.FileExport, - content: ` - export { - changeLocale, - i18n, - }; - `, - linkAfter: [ - COMMON_CHUNK_NAME.ExternalDepsImport, - COMMON_CHUNK_NAME.InternalDepsImport, - COMMON_CHUNK_NAME.FileVarDefine, - COMMON_CHUNK_NAME.FileUtilDefine, - COMMON_CHUNK_NAME.FileMainContent, - ], - }); - } - - return next; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/project/utils.ts b/packages/code-generator/src/plugins/project/utils.ts index f40ba41e2..1f6f0a152 100644 --- a/packages/code-generator/src/plugins/project/utils.ts +++ b/packages/code-generator/src/plugins/project/utils.ts @@ -2,6 +2,7 @@ import { COMMON_CHUNK_NAME } from '../../const/generator'; import { BuilderComponentPlugin, + BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct, @@ -9,52 +10,21 @@ import { } from '../../types'; // TODO: How to merge this logic to common deps -const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { - const next: ICodeStruct = { - ...pre, - }; +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; - const ir = next.ir as IUtilInfo; - - if (ir.utils) { - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JS, - name: COMMON_CHUNK_NAME.FileExport, - content: ` - export default { - `, - linkAfter: [ - COMMON_CHUNK_NAME.ExternalDepsImport, - COMMON_CHUNK_NAME.InternalDepsImport, - COMMON_CHUNK_NAME.FileVarDefine, - COMMON_CHUNK_NAME.FileUtilDefine, - COMMON_CHUNK_NAME.FileMainContent, - ], - }); - - ir.utils.forEach(util => { - if (util.type === 'function') { - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JS, - name: COMMON_CHUNK_NAME.FileVarDefine, - content: ` - const ${util.name} = ${util.content}; - `, - linkAfter: [ - COMMON_CHUNK_NAME.ExternalDepsImport, - COMMON_CHUNK_NAME.InternalDepsImport, - ], - }); - } + const ir = next.ir as IUtilInfo; + if (ir.utils) { next.chunks.push({ type: ChunkType.STRING, fileType: FileType.JS, name: COMMON_CHUNK_NAME.FileExport, content: ` - ${util.name}, + export default { `, linkAfter: [ COMMON_CHUNK_NAME.ExternalDepsImport, @@ -64,26 +34,60 @@ const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { COMMON_CHUNK_NAME.FileMainContent, ], }); - }); - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JS, - name: COMMON_CHUNK_NAME.FileExport, - content: ` - }; - `, - linkAfter: [ - COMMON_CHUNK_NAME.ExternalDepsImport, - COMMON_CHUNK_NAME.InternalDepsImport, - COMMON_CHUNK_NAME.FileVarDefine, - COMMON_CHUNK_NAME.FileUtilDefine, - COMMON_CHUNK_NAME.FileMainContent, - ], - }); - } + ir.utils.forEach(util => { + if (util.type === 'function') { + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JS, + name: COMMON_CHUNK_NAME.FileVarDefine, + content: ` + const ${util.name} = ${util.content}; + `, + linkAfter: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + ], + }); + } - return next; + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JS, + name: COMMON_CHUNK_NAME.FileExport, + content: ` + ${util.name}, + `, + linkAfter: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.FileVarDefine, + COMMON_CHUNK_NAME.FileUtilDefine, + COMMON_CHUNK_NAME.FileMainContent, + ], + }); + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JS, + name: COMMON_CHUNK_NAME.FileExport, + content: ` + }; + `, + linkAfter: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.FileVarDefine, + COMMON_CHUNK_NAME.FileUtilDefine, + COMMON_CHUNK_NAME.FileMainContent, + ], + }); + } + + return next; + }; + return plugin; }; -export default plugin; +export default pluginFactory; diff --git a/packages/code-generator/src/postprocessor/prettier/index.ts b/packages/code-generator/src/postprocessor/prettier/index.ts index 947c535c2..f36009fad 100644 --- a/packages/code-generator/src/postprocessor/prettier/index.ts +++ b/packages/code-generator/src/postprocessor/prettier/index.ts @@ -1,22 +1,26 @@ import prettier from 'prettier'; -import { PostProcessor } from '../../types'; +import { PostProcessor, PostProcessorFactory } from '../../types'; const PARSERS = ['css', 'scss', 'less', 'json', 'html', 'vue']; -const codePrettier: PostProcessor = (content: string, fileType: string) => { - let parser: prettier.BuiltInParserName; - if (fileType === 'js' || fileType === 'jsx') { - parser = 'babel'; - } else if (PARSERS.indexOf(fileType) >= 0) { - parser = fileType as prettier.BuiltInParserName; - } else { - return content; - } +const factory: PostProcessorFactory = () => { + const codePrettier: PostProcessor = (content: string, fileType: string) => { + let parser: prettier.BuiltInParserName; + if (fileType === 'js' || fileType === 'jsx' || fileType === 'ts' || fileType === 'tsx') { + parser = 'babel'; + } else if (PARSERS.indexOf(fileType) >= 0) { + parser = fileType as prettier.BuiltInParserName; + } else { + return content; + } - return prettier.format(content, { - parser, - }); + return prettier.format(content, { + parser, + }); + }; + + return codePrettier; }; -export default codePrettier; +export default factory; diff --git a/packages/code-generator/src/solutions/icejs.ts b/packages/code-generator/src/solutions/icejs.ts index 10fe842c4..62a514008 100644 --- a/packages/code-generator/src/solutions/icejs.ts +++ b/packages/code-generator/src/solutions/icejs.ts @@ -28,36 +28,40 @@ export default function createIceJsProjectBuilder(): IProjectBuilder { template, plugins: { components: [ - reactCommonDeps, - esmodule, - containerClass, - containerInjectUtils, - containerInitState, - containerLifeCycle, - containerMethod, - jsx, - css, + reactCommonDeps(), + esmodule({ + fileType: 'jsx', + }), + containerClass(), + containerInjectUtils(), + containerInitState(), + containerLifeCycle(), + containerMethod(), + jsx(), + css(), ], pages: [ - reactCommonDeps, - esmodule, - containerClass, - containerInjectUtils, - containerInitState, - containerLifeCycle, - containerMethod, - jsx, - css, + reactCommonDeps(), + esmodule({ + fileType: 'jsx', + }), + containerClass(), + containerInjectUtils(), + containerInitState(), + containerLifeCycle(), + containerMethod(), + jsx(), + css(), ], - router: [esmodule, iceJsRouter], - entry: [iceJsEntry], - constants: [constants], - utils: [esmodule, utils], - i18n: [i18n], - globalStyle: [iceJsGlobalStyle], - htmlEntry: [iceJsEntryHtml], - packageJSON: [iceJsPackageJSON], + router: [esmodule(), iceJsRouter()], + entry: [iceJsEntry()], + constants: [constants()], + utils: [esmodule(), utils()], + i18n: [i18n()], + globalStyle: [iceJsGlobalStyle()], + htmlEntry: [iceJsEntryHtml()], + packageJSON: [iceJsPackageJSON()], }, - postProcessors: [prettier], + postProcessors: [prettier()], }); } diff --git a/packages/code-generator/src/types/core.ts b/packages/code-generator/src/types/core.ts index 804f4f3e6..750d41395 100644 --- a/packages/code-generator/src/types/core.ts +++ b/packages/code-generator/src/types/core.ts @@ -12,6 +12,8 @@ export enum FileType { HTML = 'html', JS = 'js', JSX = 'jsx', + TS = 'ts', + TSX = 'tsx', JSON = 'json', } @@ -32,7 +34,7 @@ export type CodeGeneratorFunction = (content: T) => string; export interface ICodeChunk { type: ChunkType; - fileType: FileType; + fileType: string; name: string; subModule?: string; content: ChunkContent; @@ -53,6 +55,8 @@ export type BuilderComponentPlugin = ( initStruct: ICodeStruct, ) => Promise; +export type BuilderComponentPluginFactory = (config?: T) => BuilderComponentPlugin; + export interface IChunkBuilder { run( ir: any, @@ -142,6 +146,7 @@ export interface IProjectBuilder { generateProject(schema: IProjectSchema): Promise; } +export type PostProcessorFactory = (config?: T) => PostProcessor; export type PostProcessor = (content: string, fileType: string) => string; // TODO: temp interface, need modify diff --git a/packages/code-generator/src/types/schema.ts b/packages/code-generator/src/types/schema.ts index f31bdeb09..343e6442d 100644 --- a/packages/code-generator/src/types/schema.ts +++ b/packages/code-generator/src/types/schema.ts @@ -144,14 +144,7 @@ export interface IContainerNodeItem extends IComponentNodeItem { * • componentWillUnmount() * • componentDidCatch(error, info) */ - lifeCycles?: { - constructor?: IJSExpression; - render?: IJSExpression; - componentDidMount?: IJSExpression; - componentDidUpdate?: IJSExpression; - componentWillUnmount?: IJSExpression; - componentDidCatch?: IJSExpression; - }; // 生命周期Hook方法 + lifeCycles?: Record; // 生命周期Hook方法 methods?: { [methodName: string]: IJSExpression; }; // 自定义方法设置 diff --git a/packages/code-generator/tsconfig.json b/packages/code-generator/tsconfig.json index eb8638577..7a6244f14 100644 --- a/packages/code-generator/tsconfig.json +++ b/packages/code-generator/tsconfig.json @@ -5,6 +5,7 @@ "lib": [ "es6" ], + "module": "commonjs", "types": ["node"], "baseUrl": ".", /* Base directory to resolve non-absolute module names. */ }, From b4a27fc055b0064359819f551184156962ae6937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=A5=E5=B8=8C?= Date: Wed, 22 Apr 2020 23:15:22 +0800 Subject: [PATCH 5/8] fix: demo data --- packages/code-generator/src/demo/main.ts | 22 +- .../code-generator/src/demo/recoreDemo.ts | 815 ++++++++++++++++++ .../code-generator/src/demo/recoreDemo1.ts | 165 ++++ .../code-generator/src/demo/simpleDemo.ts | 11 +- 4 files changed, 1004 insertions(+), 9 deletions(-) create mode 100644 packages/code-generator/src/demo/recoreDemo.ts create mode 100644 packages/code-generator/src/demo/recoreDemo1.ts diff --git a/packages/code-generator/src/demo/main.ts b/packages/code-generator/src/demo/main.ts index e0d341ef4..a0705970b 100644 --- a/packages/code-generator/src/demo/main.ts +++ b/packages/code-generator/src/demo/main.ts @@ -1,8 +1,9 @@ import { IResultDir, IResultFile } from '../types'; -import CodeGenerator from '../index'; +import codeGenerator from '../index'; import { createDiskPublisher } from '../publisher/disk'; -import demoSchema from './simpleDemo'; +// import demoSchema from './simpleDemo'; +import demoSchema from './recoreDemo'; function flatFiles(rootName: string | null, dir: IResultDir): IResultFile[] { const dirRoot: string = rootName ? `${rootName}/${dir.name}` : dir.name; @@ -40,13 +41,18 @@ async function writeResultToDisk(root: IResultDir, path: string): Promise { } function main() { - const createIceJsProjectBuilder = CodeGenerator.solutions.icejs; - const builder = createIceJsProjectBuilder(); + // const createIceJsProjectBuilder = codeGenerator.solutions.icejs; + // const builder = createIceJsProjectBuilder(); + + const createRecoreProjectBuilder = codeGenerator.solutions.recore; + const builder = createRecoreProjectBuilder(); + builder.generateProject(demoSchema).then(result => { - // displayResultInConsole(result, '././src/routes.js'); - writeResultToDisk(result, '/Users/armslave/lowcodeDemo').then(response => - console.log('Write to disk: ', JSON.stringify(response)), - ); + displayResultInConsole(result); + // writeResultToDisk(result, '/Users/armslave/lowcodeDemo').then(response => + // console.log('Write to disk: ', JSON.stringify(response)), + // ); + return result; }); } diff --git a/packages/code-generator/src/demo/recoreDemo.ts b/packages/code-generator/src/demo/recoreDemo.ts new file mode 100644 index 000000000..29e16a12b --- /dev/null +++ b/packages/code-generator/src/demo/recoreDemo.ts @@ -0,0 +1,815 @@ +import { IProjectSchema } from '../types'; + +const demoData: IProjectSchema = { + version: '1.0.0', + componentsMap: [ + { + componentName: 'Button', + 'package': 'alife/next', + version: '1.0.0', + destructuring: true, + exportName: 'Select', + subName: 'Button', + }, + ], + componentsTree: [ + { + condition: true, + css: 'body{background-color:#f2f3f5}', + children: [ + { + condition: true, + children: [ + { + condition: true, + children: [ + { + condition: true, + componentName: 'TextField', + id: 'node_k1nd576k', + props: { + fieldName: 'name', + hasClear: false, + autoFocus: false, + tips: { + en_US: '', + zh_CN: '', + type: 'JSExpression', + value: 'this.utils.getLocale() === \'en_US\' ? \'\' : \'\'', + extType: 'i18n', + }, + trim: false, + labelTextAlign: 'right', + placeholder: { + use: 'zh_CN', + en_US: 'please input', + zh_CN: '请输入', + type: 'JSExpression', + value: '"请输入"', + extType: 'i18n', + }, + state: '', + behavior: 'NORMAL', + value: { + use: 'zh_CN', + zh_CN: '', + type: 'JSExpression', + value: '""', + extType: 'i18n', + }, + addonBefore: { + use: 'zh_CN', + zh_CN: '', + type: 'JSExpression', + value: '""', + extType: 'i18n', + }, + validation: [ + { + type: 'required', + }, + ], + hasLimitHint: false, + cutString: false, + __style__: {}, + fieldId: 'textField_k1nd576w', + htmlType: 'input', + autoHeight: false, + labelColOffset: 0, + label: { + use: 'zh_CN', + en_US: 'TextField', + zh_CN: '活动名称', + type: 'JSExpression', + value: '"活动名称"', + extType: 'i18n', + }, + __category__: 'form', + labelColSpan: 4, + wrapperColSpan: 0, + rows: 4, + addonAfter: { + use: 'zh_CN', + zh_CN: '', + type: 'JSExpression', + value: '""', + extType: 'i18n', + }, + wrapperColOffset: 0, + size: 'medium', + labelAlign: 'top', + }, + }, + { + condition: true, + componentName: 'SelectField', + id: 'node_k1nd576l', + props: { + fieldName: 'type', + hasClear: false, + tips: { + en_US: '', + zh_CN: '', + type: 'JSExpression', + value: 'this.utils.getLocale() === \'en_US\' ? \'\' : \'\'', + extType: 'i18n', + }, + mode: 'single', + showSearch: false, + autoWidth: true, + labelTextAlign: 'right', + placeholder: { + use: 'zh_CN', + en_US: 'please select', + zh_CN: '请选择', + type: 'JSExpression', + value: '"请选择"', + extType: 'i18n', + }, + hasBorder: true, + behavior: 'NORMAL', + value: '', + validation: [ + { + type: 'required', + }, + ], + __style__: {}, + fieldId: 'select_k1nd576x', + notFoundContent: { + use: 'zh_CN', + type: 'JSExpression', + value: 'null', + extType: 'i18n', + }, + labelColOffset: 0, + label: { + use: 'zh_CN', + en_US: 'SelectField', + zh_CN: '活动类型', + type: 'JSExpression', + value: '"活动类型"', + extType: 'i18n', + }, + __category__: 'form', + labelColSpan: 4, + wrapperColSpan: 0, + wrapperColOffset: 0, + hasSelectAll: false, + hasArrow: true, + size: 'medium', + labelAlign: 'top', + filterLocal: true, + dataSource: [ + { + defaultChecked: false, + text: { + en_US: 'Option 1', + zh_CN: '虚拟', + type: 'JSExpression', + __sid__: 'param_k1nd5lrz', + value: 'this.utils.getLocale() === \'en_US\' ? \'Option 1\' : \'虚拟\'', + extType: 'i18n', + }, + __sid__: 'serial_k1nd576v', + value: '虚拟', + sid: 'opt_k1ofrjfz', + }, + { + defaultChecked: false, + text: { + en_US: 'Option 2', + zh_CN: '营销', + type: 'JSExpression', + __sid__: 'param_k1nd5ls3', + value: 'this.utils.getLocale() === \'en_US\' ? \'Option 2\' : \'营销\'', + extType: 'i18n', + }, + __sid__: 'serial_k1nd576w', + value: '营销', + sid: 'opt_k1ofrjg0', + }, + { + defaultChecked: false, + text: { + en_US: 'Option 3', + zh_CN: '线下', + type: 'JSExpression', + __sid__: 'param_k1nd5ls7', + value: 'this.utils.getLocale() === \'en_US\' ? \'Option 3\' : \'线下\'', + extType: 'i18n', + }, + __sid__: 'serial_k1nd576x', + value: '线下', + sid: 'opt_k1ofrjg1', + }, + ], + }, + }, + ], + componentName: 'Form', + id: 'node_k1nd576j', + props: { + size: 'medium', + labelAlign: 'top', + autoValidate: true, + scrollToFirstError: true, + autoUnmount: true, + behavior: 'NORMAL', + dataSource: { + type: 'JSExpression', + value: 'state.formData', + extType: 'variable', + }, + __style__: {}, + fieldId: 'form', + }, + }, + ], + componentName: 'Dialog', + id: 'node_k1nd576i', + props: { + hasMask: true, + closeable: 'esc', + visible: false, + footer: true, + footerActions: 'ok,cancel', + footerAlign: 'right', + title: { + rawValue: { + use: 'zh_CN', + zh_CN: '新增活动', + type: 'JSExpression', + value: '"新增活动"', + extType: 'i18n', + }, + type: 'JSExpression', + value: '(state.formData ? \'编辑\' : \'新增\') + \'活动\'', + extType: 'variable', + }, + fieldId: 'dialog', + onOk: { + rawType: 'events', + type: 'JSExpression', + value: 'this.utils.legaoBuiltin.execEventFlow.bind(this, [this.submit])', + events: [ + { + name: 'submit', + id: 'submit', + params: {}, + type: 'actionRef', + uuid: '1570937968144_0', + }, + ], + }, + }, + }, + { + condition: true, + children: [ + { + condition: true, + children: [], + componentName: 'PageHeader', + id: 'node_k1nd576c', + props: { + __slot__title: true, + extraContent: '', + __slot__extraContent: false, + __slot__action: false, + title: { + type: 'JSBlock', + value: { + condition: true, + children: [ + { + condition: true, + componentName: 'Text', + id: 'node_k1nd576e', + props: { + showTitle: false, + behavior: 'NORMAL', + content: { + use: 'zh_CN', + en_US: 'Title', + zh_CN: '基础表格', + type: 'JSExpression', + value: '"基础表格"', + extType: 'i18n', + }, + __style__: {}, + fieldId: 'text_k1nd576t', + }, + }, + ], + componentName: 'Slot', + id: 'node_k1nd576d', + props: { + slotName: 'title', + slotTitle: '标题区域', + }, + }, + }, + content: '', + __slot__logo: false, + __slot__crumb: false, + crumb: '', + tab: '', + logo: '', + action: '', + __slot__tab: false, + __style__: {}, + __slot__content: false, + fieldId: 'pageHeader_k1nd576s', + }, + }, + ], + componentName: 'RootHeader', + id: 'node_k1hjb043', + props: {}, + }, + { + condition: true, + children: [ + { + condition: true, + componentName: 'TablePc', + id: 'node_k1hjb046', + props: { + actionWidth: 0, + isTree: false, + showJump: true, + data: { + rawValue: { + data: [ + { + contractDate: { + start: 1534942658570, + end: 1534944858570, + }, + entryDate: 1534942658570, + name: '小王', + id: '1', + salary: 35000, + email: 'xw@abc.com', + moneyRange: { + lower: 108, + upper: 944, + }, + }, + { + contractDate: { + start: 1534942658570, + end: 1534944858570, + }, + entryDate: 1534942658570, + name: '小李', + id: '2', + salary: 25000, + email: 'xl@abc.com', + moneyRange: { + lower: 214, + upper: 1077, + }, + }, + ], + currentPage: 1, + totalCount: 2, + }, + type: 'JSExpression', + value: 'state.table_list_basic', + extType: 'variable', + }, + actionTitle: { + use: 'zh_CN', + en_US: 'Action', + zh_CN: '操作', + type: 'JSExpression', + value: '"操作"', + extType: 'i18n', + }, + indent: 16, + columns: [ + { + dataKey: 'name', + hidden: false, + dataType: 'text', + width: '300px', + lock: 'none', + sortable: false, + align: 'left', + title: { + en_US: 'Name', + use: 'zh_CN', + zh_CN: '活动名称', + type: 'JSExpression', + value: '"活动名称"', + extType: 'i18n', + }, + timeFormatter: 'YYYY-MM-DD HH:mm:ss', + }, + { + dataKey: 'type', + hidden: false, + dataType: 'text', + width: '150px', + lock: 'none', + sortable: false, + align: 'left', + title: { + en_US: 'Date', + use: 'zh_CN', + zh_CN: '活动类型', + type: 'JSExpression', + value: '"活动类型"', + extType: 'i18n', + }, + timeFormatter: 'YYYY-MM-DD', + }, + { + dataKey: 'created_at', + hidden: false, + dataType: 'timestamp', + width: 200, + lock: 'none', + sortable: false, + align: 'left', + title: { + en_US: 'contractDate', + use: 'zh_CN', + zh_CN: '创建时间', + type: 'JSExpression', + value: '"创建时间"', + extType: 'i18n', + }, + timeFormatter: 'YYYY-MM-DD', + }, + ], + rowSelector: 'checkboxSelector', + pageSizeSelector: false, + pageSize: 10, + setLoadingComponent: false, + showRowSelector: false, + showLinkBar: true, + searchBarPlaceholder: { + use: 'zh_CN', + en_US: 'Please Input', + zh_CN: '请搜索', + type: 'JSExpression', + value: '"请搜索"', + extType: 'i18n', + }, + type: 'normal', + pageSizePosition: 'end', + pageSizeList: '5,10,20', + fixedHeader: false, + showActionBar: true, + showSearch: true, + stickyHeader: false, + theme: 'split', + setEmptyContent: false, + actionFixed: 'none', + fieldId: 'tablePc_k1hjb4dl', + isPagination: true, + actionColumn: [ + { + pageMode: '', + callback: { + type: 'JSExpression', + value: 'edit.bind(this)', + }, + title: { + en_US: 'Detail', + use: 'zh_CN', + zh_CN: '编辑', + type: 'JSExpression', + value: '"编辑"', + extType: 'i18n', + }, + option: 'callback', + }, + { + pageMode: '', + callback: { + type: 'JSExpression', + value: 'del.bind(this)', + }, + title: { + use: 'zh_CN', + en_US: 'action', + zh_CN: '删除', + type: 'JSExpression', + value: '"删除"', + extType: 'i18n', + }, + option: 'callback', + }, + ], + hasHeader: true, + hideOnlyOnePage: false, + shape: 'arrow-only', + paginationPosition: 'right', + actionBar: [ + { + callback: { + type: 'JSExpression', + value: 'add.bind(this)', + }, + title: { + en_US: 'Action 1', + use: 'zh_CN', + zh_CN: '新增', + type: 'JSExpression', + value: '"新增"', + extType: 'i18n', + }, + option: 'callback', + }, + ], + actionType: 'link', + __router: { + type: 'JSExpression', + value: 'this.utils.router', + }, + linkBar: [], + size: 'medium', + onLoadData: { + rawType: 'events', + type: 'JSExpression', + value: 'this.utils.legaoBuiltin.execEventFlow.bind(this, [this.onLoadData])', + events: [ + { + name: 'onLoadData', + id: 'onLoadData', + params: {}, + type: 'actionRef', + uuid: '1570938442689_4', + }, + ], + }, + actionHidden: false, + dataSourceType: 'data', + primaryKey: 'id', + }, + }, + ], + componentName: 'RootContent', + id: 'node_k1hjb044', + props: { + contentBgColor: 'white', + contentPadding: '20', + }, + }, + { + condition: true, + children: [], + componentName: 'RootFooter', + id: 'node_k1hjb045', + props: {}, + }, + ], + methods: { + __initMethods__: { + type: 'JSExpression', + value: 'function (exports, module) { \'use strict\';\n\nexports.__esModule = true;\nexports.hellGlobal = hellGlobal;\n/**\n* 全局函数,可以被应用内所有页面调用\n*/\nfunction hellGlobal() {\n console.log(\'hello, global\');\n}\'use strict\';\n\nexports.__esModule = true;\nexports.add = add;\nexports.submit = submit;\nexports.onLoadData = onLoadData;\nexports.edit = edit;\nexports.del = del;\n// 点击新增\nfunction add() {\n this.state.formData = null;\n this.$(\'dialog\').show();\n}\n\n/**\n* 点击弹框的“确认”\n*/\nfunction submit() {\n const _this = this;\n\n this.$(\'form\').submit(function (data, error) {\n if (data) {\n _this.dataSourceMap[\'table_submit\'].load(data).then(function (res) {\n _this.utils.toast({\n type: \'success\',\n title: \'提交成功\'\n });\n _this.$(\'dialog\').hide();\n _this.dataSourceMap[\'table_list_basic\'].load();\n });\n }\n });\n}\n\n/**\n* tablePc onLoadData\n* @param currentPage 当前页码\n* @param pageSize 每页显示条数\n* @param searchKey 搜索关键字\n* @param orderColumn 排序列\n* @param orderType 排序方式(desc,asc)\n* @param from 触发来源(order,search,pagination)\n*/\nfunction onLoadData(currentPage, pageSize, searchKey, orderColumn, orderType, from) {\n const tableParams = {\n currentPage: from === \'search\' ? 1 : currentPage,\n pageSize: pageSize,\n searchKey: searchKey,\n orderColumn: orderColumn,\n orderType: orderType\n };\n this.setState({ tableParams: tableParams });\n}\n\n// 点击编辑\nfunction edit(rowData) {\n this.state.formData = rowData;\n this.$(\'dialog\').show();\n}\n\n// 点击删除\nfunction del(rowData) {\n const _this2 = this;\n\n this.utils.dialog({\n method: \'confirm\',\n title: \'提示\',\n content: \'确认删除该条目吗?\',\n onOk: function onOk() {\n _this2.dataSourceMap[\'table_delete\'].load({ id: rowData.id }).then(function () {\n _this2.utils.toast({\n type: \'success\',\n title: \'删除成功\'\n });\n _this2.dataSourceMap[\'table_list_basic\'].load();\n });\n }\n });\n} }', + }, + }, + componentName: 'Page', + fileName: 'page_index', + meta: { + title: '首页', + router: '/', + spmb: 'abef21', + }, + id: 'node_k1hjb042', + state: { + urlParams: { + type: 'JSExpression', + value: 'this.utils.legaoBuiltin.getUrlParams()', + }, + designUrlPrefix: { + type: 'JSExpression', + value: '"https://pre-go.alibaba-inc.com/fusion_developer/0.1.0/design"', + }, + tableParams: { + type: 'JSExpression', + value: '', + }, + }, + dataSource: { + offline: [], + globalConfig: { + fit: { + type: 'JSExpression', + value: '', + extType: 'function', + }, + }, + online: [], + list: [ + { + dataHandler: { + type: 'JSExpression', + value: 'function(data, err) { this.setState({table_list_basic: data}); return data; }', + extType: 'function', + }, + requestHandler: { + type: 'JSExpression', + value: 'this.utils.legaoBuiltin.dataSourceHandler', + }, + options: { + shouldFetch: true, + method: 'GET', + params: { + rawValue: [], + type: 'JSExpression', + value: 'state.tableParams', + extType: 'variable', + }, + isSync: false, + url: 'https://mocks.alibaba-inc.com/mock/legao_template/api/activity/list.json', + }, + id: 'table_list_basic', + type: 'legao', + isInit: true, + }, + { + dataHandler: { + type: 'JSExpression', + value: 'function(data, err) { this.setState({table_submit: data}); return data; }', + extType: 'function', + }, + requestHandler: { + type: 'JSExpression', + value: 'this.utils.legaoBuiltin.dataSourceHandler', + }, + options: { + shouldFetch: true, + method: 'POST', + params: [], + isSync: false, + url: 'https://mocks.alibaba-inc.com/mock/legao_template/api/activity/update.json', + }, + id: 'table_submit', + type: 'legao', + isInit: false, + }, + { + dataHandler: { + type: 'JSExpression', + value: 'function(data, err) { this.setState({table_delete: data}); return data; }', + extType: 'function', + }, + requestHandler: { + type: 'JSExpression', + value: 'this.utils.legaoBuiltin.dataSourceHandler', + }, + options: { + shouldFetch: true, + method: 'POST', + params: [], + isSync: true, + url: 'https://mocks.alibaba-inc.com/mock/legao_template/api/activity/delete.json', + }, + id: 'table_delete', + type: 'legao', + isInit: false, + }, + ], + sync: true, + }, + lifeCycles: { + 'init': { + type: 'JSExpression', + value: 'function constructor() {\nconst module = { exports: {} };\nconst _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === \'function\'){\n _this[item] = module.exports[item];\n }\n});\n\n}', + extType: 'function', + }, + didMount: { + type: 'JSExpression', + value: 'function main(){\n \'use strict\';\n\nconst __compiledFunc__ = function didMount() {\n // 页面节点加载渲染完毕\n console.log(\'这是个 function didMount\', this);\n};\n return __compiledFunc__.apply(this, arguments);\n}', + extType: 'function', + }, + }, + props: { + extensions: { + '启用页头': true, + }, + pageStyle: { + backgroundColor: '#f2f3f5', + }, + containerStyle: {}, + className: 'page_k1v3nkx7', + }, + }, + ], + utils: [ + { + name: 'clone', + type: 'npm', + content: { + 'package': 'lodash', + version: '0.0.1', + exportName: 'clone', + subName: '', + destructuring: false, + main: '/lib/clone', + }, + }, + { + name: 'beforeRequestHandler', + type: 'function', + content: { + type: 'JSExpression', + value: 'function(){\n ... \n}', + }, + }, + ], + constants: { + ENV: 'prod', + DOMAIN: 'xxx.alibaba-inc.com', + }, + css: 'body {font-size: 12px;} .table { width: 100px;}', + config: { + sdkVersion: '1.0.3', + historyMode: 'hash', + targetRootID: 'J_Container', + layout: { + componentName: 'BasicLayout', + props: { + navConfig: { + showLanguageChange: true, + data: [ + { + hidden: false, + navUuid: 'FORM-CP5669B1-3AW9DCLHZAY8EIY6WE6X1-GFZM3V1K-6', + children: [], + icon: '', + targetNew: false, + title: '测试基础表格', + inner: true, + relateUuid: 'FORM-CP5669B1-3AW9DCLHZAY8EIY6WE6X1-GFZM3V1K-6', + slug: 'qihfg', + }, + { + hidden: false, + navUuid: 'FORM-CP5669B1-8AW9XCUT4PCH15SMDWUM3-ZPQP3V1K-1', + children: [], + icon: '', + targetNew: false, + title: '测试查询表格', + inner: true, + relateUuid: 'zqhej', + slug: 'zqhej', + }, + ], + systemLink: '/my_dev_center_code/0.1.0', + appName: '乐高转码测试', + isFoldHorizontal: 'n', + showAppTitle: true, + isFold: 'n', + searchBarType: 'icon', + singletons: {}, + navTheme: 'default', + type: 'top_side_fold', + navStyle: 'orange', + layout: 'auto', + bgColor: 'white', + languageChangeUrl: '/common/account/changeAccountLanguage.json', + showSearch: 'n', + openSubMode: false, + showCrumb: true, + isFixed: 'y', + showIcon: false, + showNav: true, + }, + }, + }, + theme: { + 'package': '@alife/theme-fusion', + version: '^0.1.0', + primary: '#ff9966', + }, + }, + meta: { + name: 'demo应用', + git_group: 'appGroup', + project_name: 'app_demo', + description: '这是一个测试应用', + spma: 'spa23d', + creator: '月飞', + gmt_create: '2020-02-11 00:00:00', + gmt_modified: '2020-02-11 00:00:00', + }, + i18n: { + 'zh-CN': { + 'i18n-jwg27yo4': '你好', + 'i18n-jwg27yo3': '中国', + }, + 'en-US': { + 'i18n-jwg27yo4': 'Hello', + 'i18n-jwg27yo3': 'China', + }, + }, +}; + +export default demoData; diff --git a/packages/code-generator/src/demo/recoreDemo1.ts b/packages/code-generator/src/demo/recoreDemo1.ts new file mode 100644 index 000000000..1ef52bc0a --- /dev/null +++ b/packages/code-generator/src/demo/recoreDemo1.ts @@ -0,0 +1,165 @@ +import { IProjectSchema } from '../types'; + +const demoData: IProjectSchema = { + version: '1.0.0', + componentsMap: [ + { + componentName: 'Button', + 'package': 'alife/next', + version: '1.0.0', + destructuring: true, + exportName: 'Select', + subName: 'Button', + }, + ], + componentsTree: [{ + componentName: 'Page', + id: 'node$1', + props: { + ref: 'outterView', + autoLoading: true, + style: { + padding: 20, + }, + }, + fileName: 'test', + dataSource: { + list: [], + }, + state: { + text: 'outter', + }, + children: [ + { + componentName: 'Button', + id: 'node$l', + props: {}, + }, + { + componentName: 'Button', + id: 'node$n', + props: { + type: 'secondary', + baseIcon: 'set', + }, + }, + { + componentName: 'Calendar', + id: 'node$p', + props: { + shape: 'card', + }, + }, + ], + }], + utils: [ + { + name: 'clone', + type: 'npm', + content: { + 'package': 'lodash', + version: '0.0.1', + exportName: 'clone', + subName: '', + destructuring: false, + main: '/lib/clone', + }, + }, + { + name: 'beforeRequestHandler', + type: 'function', + content: { + type: 'JSExpression', + value: 'function(){\n ... \n}', + }, + }, + ], + constants: { + ENV: 'prod', + DOMAIN: 'xxx.alibaba-inc.com', + }, + css: 'body {font-size: 12px;} .table { width: 100px;}', + config: { + sdkVersion: '1.0.3', + historyMode: 'hash', + targetRootID: 'J_Container', + layout: { + componentName: 'BasicLayout', + props: { + navConfig: { + showLanguageChange: true, + data: [ + { + hidden: false, + navUuid: 'FORM-CP5669B1-3AW9DCLHZAY8EIY6WE6X1-GFZM3V1K-6', + children: [], + icon: '', + targetNew: false, + title: '测试基础表格', + inner: true, + relateUuid: 'FORM-CP5669B1-3AW9DCLHZAY8EIY6WE6X1-GFZM3V1K-6', + slug: 'qihfg', + }, + { + hidden: false, + navUuid: 'FORM-CP5669B1-8AW9XCUT4PCH15SMDWUM3-ZPQP3V1K-1', + children: [], + icon: '', + targetNew: false, + title: '测试查询表格', + inner: true, + relateUuid: 'zqhej', + slug: 'zqhej', + }, + ], + systemLink: '/my_dev_center_code/0.1.0', + appName: '乐高转码测试', + isFoldHorizontal: 'n', + showAppTitle: true, + isFold: 'n', + searchBarType: 'icon', + singletons: {}, + navTheme: 'default', + type: 'top_side_fold', + navStyle: 'orange', + layout: 'auto', + bgColor: 'white', + languageChangeUrl: '/common/account/changeAccountLanguage.json', + showSearch: 'n', + openSubMode: false, + showCrumb: true, + isFixed: 'y', + showIcon: false, + showNav: true, + }, + }, + }, + theme: { + 'package': '@alife/theme-fusion', + version: '^0.1.0', + primary: '#ff9966', + }, + }, + meta: { + name: 'demo应用', + git_group: 'appGroup', + project_name: 'app_demo', + description: '这是一个测试应用', + spma: 'spa23d', + creator: '月飞', + gmt_create: '2020-02-11 00:00:00', + gmt_modified: '2020-02-11 00:00:00', + }, + i18n: { + 'zh-CN': { + 'i18n-jwg27yo4': '你好', + 'i18n-jwg27yo3': '中国', + }, + 'en-US': { + 'i18n-jwg27yo4': 'Hello', + 'i18n-jwg27yo3': 'China', + }, + }, +}; + +export default demoData; diff --git a/packages/code-generator/src/demo/simpleDemo.ts b/packages/code-generator/src/demo/simpleDemo.ts index cf124ceb9..410ca35e3 100644 --- a/packages/code-generator/src/demo/simpleDemo.ts +++ b/packages/code-generator/src/demo/simpleDemo.ts @@ -71,12 +71,21 @@ const demoData: IProjectSchema = { state: { text: 'outter', }, + lifeCycles: { + componentDidMount: { + type: 'JSExpression', + value: 'function() { this.utils.request(this.props.url); }', + }, + }, children: [ { componentName: 'Form', id: 'node$2', props: { - labelCol: 4, + labelCol: { + type: 'JSExpression', + value: 'this.state.colNum', + }, style: {}, ref: 'testForm', }, From 3bfe7580db15d81560a4fee153e6d493a04ecfd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=A5=E5=B8=8C?= Date: Wed, 22 Apr 2020 23:16:03 +0800 Subject: [PATCH 6/8] feat: recore solution --- packages/code-generator/package.json | 6 + .../src/@types/ali__my-prettier/index.d.ts | 6 + .../code-generator/src/const/generator.ts | 101 ++++++++ .../code-generator/src/demo/expressionTest.ts | 22 ++ .../src/generator/ProjectBuilder.ts | 68 ++---- packages/code-generator/src/index.ts | 2 + .../code-generator/src/parser/SchemaParser.ts | 21 +- .../src/plugins/component/react/const.ts | 7 - .../plugins/component/react/containerClass.ts | 31 ++- .../component/react/containerDataSource.ts | 21 +- .../component/react/containerInitState.ts | 41 +++- .../component/react/containerInjectUtils.ts | 19 +- .../component/react/containerLifeCycle.ts | 63 ++--- .../component/react/containerMethod.ts | 25 +- .../src/plugins/component/react/jsx.ts | 125 +--------- .../component/react/reactCommonDeps.ts | 1 - .../src/plugins/component/recore/const.ts | 3 + .../component/recore/pageDataSource.ts | 73 ++++++ .../src/plugins/component/recore/pageFrame.ts | 81 +++++++ .../src/plugins/component/recore/pageStyle.ts | 35 +++ .../plugins/component/recore/pageVmBody.ts | 82 +++++++ .../plugins/component/recore/pageVmHeader.ts | 29 +++ .../src/plugins/component/style/css.ts | 19 +- .../src/plugins/project/constants.ts | 3 +- .../project/framework/icejs/plugins/entry.ts | 1 - .../framework/icejs/plugins/entryHtml.ts | 1 - .../framework/icejs/plugins/globalStyle.ts | 1 - .../framework/icejs/plugins/packageJSON.ts | 1 - .../project/framework/icejs/plugins/router.ts | 1 - .../project/framework/icejs/template/index.ts | 25 +- .../recore/template/files/README.md.ts | 5 +- .../recore/template/files/package.json.ts | 19 +- .../recore/template/files/src/index.ts.ts | 73 ++++++ .../template/files/src/plugins/provider.ts.ts | 89 ------- .../recore/template/files/src/router.ts.ts | 3 +- .../framework/recore/template/index.ts | 54 +++++ .../src/plugins/project/i18n.ts | 3 +- .../src/plugins/project/utils.ts | 1 - .../src/plugins/utils/compositeType.ts | 45 ---- .../src/plugins/utils/jsExpression.ts | 39 ---- .../src/postprocessor/prettier/index.ts | 20 +- .../code-generator/src/solutions/recore.ts | 51 ++++ packages/code-generator/src/types/core.ts | 61 +++-- .../code-generator/src/types/intermediate.ts | 2 +- packages/code-generator/src/types/schema.ts | 10 +- packages/code-generator/src/utils/children.ts | 35 --- .../code-generator/src/utils/compositeType.ts | 88 +++++++ .../code-generator/src/utils/jsExpression.ts | 85 +++++++ .../code-generator/src/utils/nodeToJSX.ts | 221 ++++++++++++++++++ .../src/utils/templateHelper.ts | 25 ++ 50 files changed, 1293 insertions(+), 550 deletions(-) create mode 100644 packages/code-generator/src/@types/ali__my-prettier/index.d.ts create mode 100644 packages/code-generator/src/demo/expressionTest.ts create mode 100644 packages/code-generator/src/plugins/component/recore/const.ts create mode 100644 packages/code-generator/src/plugins/component/recore/pageDataSource.ts create mode 100644 packages/code-generator/src/plugins/component/recore/pageFrame.ts create mode 100644 packages/code-generator/src/plugins/component/recore/pageStyle.ts create mode 100644 packages/code-generator/src/plugins/component/recore/pageVmBody.ts create mode 100644 packages/code-generator/src/plugins/component/recore/pageVmHeader.ts delete mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/files/src/plugins/provider.ts.ts create mode 100644 packages/code-generator/src/plugins/project/framework/recore/template/index.ts delete mode 100644 packages/code-generator/src/plugins/utils/compositeType.ts delete mode 100644 packages/code-generator/src/plugins/utils/jsExpression.ts create mode 100644 packages/code-generator/src/solutions/recore.ts delete mode 100644 packages/code-generator/src/utils/children.ts create mode 100644 packages/code-generator/src/utils/compositeType.ts create mode 100644 packages/code-generator/src/utils/jsExpression.ts create mode 100644 packages/code-generator/src/utils/nodeToJSX.ts create mode 100644 packages/code-generator/src/utils/templateHelper.ts diff --git a/packages/code-generator/package.json b/packages/code-generator/package.json index a368b8f38..808cf1ced 100644 --- a/packages/code-generator/package.json +++ b/packages/code-generator/package.json @@ -9,18 +9,24 @@ "scripts": { "build": "rimraf lib && tsc", "demo": "ts-node -r tsconfig-paths/register ./src/demo/main.ts", + "demo:exp": "ts-node -r tsconfig-paths/register ./src/demo/expressionTest.ts", "test": "ava", "template": "node ./tools/createTemplate.js" }, "dependencies": { "@ali/am-eslint-config": "*", + "@ali/my-prettier": "^1.0.0", + "@babel/generator": "^7.9.5", "@babel/parser": "^7.9.4", + "@babel/traverse": "^7.9.5", + "@babel/types": "^7.9.5", "@types/prettier": "^1.19.1", "change-case": "^3.1.0", "prettier": "^2.0.2", "short-uuid": "^3.1.1" }, "devDependencies": { + "@types/babel__traverse": "^7.0.10", "ava": "^1.0.1", "rimraf": "^3.0.2", "ts-loader": "^6.2.2", diff --git a/packages/code-generator/src/@types/ali__my-prettier/index.d.ts b/packages/code-generator/src/@types/ali__my-prettier/index.d.ts new file mode 100644 index 000000000..d21a2b18a --- /dev/null +++ b/packages/code-generator/src/@types/ali__my-prettier/index.d.ts @@ -0,0 +1,6 @@ +/// + +declare module '@ali/my-prettier' { + function format(text: string, type: string): string; + export default format; +} diff --git a/packages/code-generator/src/const/generator.ts b/packages/code-generator/src/const/generator.ts index 329adfee5..9028b9226 100644 --- a/packages/code-generator/src/const/generator.ts +++ b/packages/code-generator/src/const/generator.ts @@ -8,6 +8,107 @@ export const COMMON_CHUNK_NAME = { StyleDepsImport: 'CommonStyleDepsImport', StyleCssContent: 'CommonStyleCssContent', HtmlContent: 'CommonHtmlContent', + CustomContent: 'CommonCustomContent', }; +export const CLASS_DEFINE_CHUNK_NAME = { + Start: 'CommonClassDefineStart', + ConstructorStart: 'CommonClassDefineConstructorStart', + ConstructorContent: 'CommonClassDefineConstructorContent', + ConstructorEnd: 'CommonClassDefineConstructorEnd', + StaticVar: 'CommonClassDefineStaticVar', + StaticMethod: 'CommonClassDefineStaticMethod', + InsVar: 'CommonClassDefineInsVar', + InsVarMethod: 'CommonClassDefineInsVarMethod', + InsMethod: 'CommonClassDefineInsMethod', + End: 'CommonClassDefineEnd', +}; + +export const DEFAULT_LINK_AFTER = { + [COMMON_CHUNK_NAME.ExternalDepsImport]: [], + [COMMON_CHUNK_NAME.InternalDepsImport]: [COMMON_CHUNK_NAME.ExternalDepsImport], + [COMMON_CHUNK_NAME.FileVarDefine]: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + ], + [COMMON_CHUNK_NAME.FileUtilDefine]: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.FileVarDefine, + ], + [CLASS_DEFINE_CHUNK_NAME.Start]: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.FileVarDefine, + COMMON_CHUNK_NAME.FileUtilDefine, + ], + [CLASS_DEFINE_CHUNK_NAME.ConstructorStart]: [ + CLASS_DEFINE_CHUNK_NAME.Start, + CLASS_DEFINE_CHUNK_NAME.StaticVar, + CLASS_DEFINE_CHUNK_NAME.StaticMethod, + CLASS_DEFINE_CHUNK_NAME.InsVar, + CLASS_DEFINE_CHUNK_NAME.InsVarMethod, + ], + [CLASS_DEFINE_CHUNK_NAME.ConstructorContent]: [ + CLASS_DEFINE_CHUNK_NAME.ConstructorStart, + ], + [CLASS_DEFINE_CHUNK_NAME.ConstructorEnd]: [ + CLASS_DEFINE_CHUNK_NAME.ConstructorStart, + CLASS_DEFINE_CHUNK_NAME.ConstructorContent, + ], + [CLASS_DEFINE_CHUNK_NAME.StaticVar]: [ + CLASS_DEFINE_CHUNK_NAME.Start, + ], + [CLASS_DEFINE_CHUNK_NAME.StaticMethod]: [ + CLASS_DEFINE_CHUNK_NAME.Start, + CLASS_DEFINE_CHUNK_NAME.StaticVar, + ], + [CLASS_DEFINE_CHUNK_NAME.InsVar]: [ + CLASS_DEFINE_CHUNK_NAME.Start, + CLASS_DEFINE_CHUNK_NAME.StaticVar, + CLASS_DEFINE_CHUNK_NAME.StaticMethod, + ], + [CLASS_DEFINE_CHUNK_NAME.InsVarMethod]: [ + CLASS_DEFINE_CHUNK_NAME.Start, + CLASS_DEFINE_CHUNK_NAME.StaticVar, + CLASS_DEFINE_CHUNK_NAME.StaticMethod, + CLASS_DEFINE_CHUNK_NAME.InsVar, + ], + [CLASS_DEFINE_CHUNK_NAME.InsMethod]: [ + CLASS_DEFINE_CHUNK_NAME.Start, + CLASS_DEFINE_CHUNK_NAME.StaticVar, + CLASS_DEFINE_CHUNK_NAME.StaticMethod, + CLASS_DEFINE_CHUNK_NAME.InsVar, + CLASS_DEFINE_CHUNK_NAME.InsVarMethod, + CLASS_DEFINE_CHUNK_NAME.ConstructorEnd, + ], + [CLASS_DEFINE_CHUNK_NAME.End]: [ + CLASS_DEFINE_CHUNK_NAME.Start, + CLASS_DEFINE_CHUNK_NAME.StaticVar, + CLASS_DEFINE_CHUNK_NAME.StaticMethod, + CLASS_DEFINE_CHUNK_NAME.InsVar, + CLASS_DEFINE_CHUNK_NAME.InsVarMethod, + CLASS_DEFINE_CHUNK_NAME.InsMethod, + CLASS_DEFINE_CHUNK_NAME.ConstructorEnd, + ], + [COMMON_CHUNK_NAME.FileMainContent]: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.FileVarDefine, + COMMON_CHUNK_NAME.FileUtilDefine, + CLASS_DEFINE_CHUNK_NAME.End, + ], + [COMMON_CHUNK_NAME.FileExport]: [ + COMMON_CHUNK_NAME.ExternalDepsImport, + COMMON_CHUNK_NAME.InternalDepsImport, + COMMON_CHUNK_NAME.FileVarDefine, + COMMON_CHUNK_NAME.FileUtilDefine, + CLASS_DEFINE_CHUNK_NAME.End, + COMMON_CHUNK_NAME.FileMainContent, + ], + [COMMON_CHUNK_NAME.StyleDepsImport]: [], + [COMMON_CHUNK_NAME.StyleCssContent]: [COMMON_CHUNK_NAME.StyleDepsImport], + [COMMON_CHUNK_NAME.HtmlContent]: [], +} + export const COMMON_SUB_MODULE_NAME = 'index'; diff --git a/packages/code-generator/src/demo/expressionTest.ts b/packages/code-generator/src/demo/expressionTest.ts new file mode 100644 index 000000000..e6d8831ad --- /dev/null +++ b/packages/code-generator/src/demo/expressionTest.ts @@ -0,0 +1,22 @@ +import traverse from '@babel/traverse'; +import * as parser from '@babel/parser'; + +function test(functionBody: string) { + console.log(functionBody); + console.log('---->'); + try { + const parseResult = parser.parse(functionBody); + console.log(JSON.stringify(parseResult)); + traverse(parseResult, { + enter(path) { + console.log('path: ', path.type, path); + } + }); + } catch (error) { + console.log('Error'); + console.log(error.message); + } + console.log('====================='); +} + +test('function main() { state.fff = 1; }'); diff --git a/packages/code-generator/src/generator/ProjectBuilder.ts b/packages/code-generator/src/generator/ProjectBuilder.ts index ada2e47fd..bd36291f6 100644 --- a/packages/code-generator/src/generator/ProjectBuilder.ts +++ b/packages/code-generator/src/generator/ProjectBuilder.ts @@ -90,7 +90,7 @@ export class ProjectBuilder implements IProjectBuilder { const { files } = await builder.generateModule(containerInfo); return { - moduleName: containerInfo.fileName, + moduleName: containerInfo.moduleName, path, files, }; @@ -215,61 +215,19 @@ export class ProjectBuilder implements IProjectBuilder { private createModuleBuilders(): Record { const builders: Record = {}; - builders.components = createModuleBuilder({ - plugins: this.plugins.components, - postProcessors: this.postProcessors, + Object.keys(this.plugins).forEach(pluginName => { + if (this.plugins[pluginName].length > 0) { + const options: { mainFileName?: string } = {}; + if (this.template.slots[pluginName] && this.template.slots[pluginName].fileName) { + options.mainFileName = this.template.slots[pluginName].fileName; + } + builders[pluginName] = createModuleBuilder({ + plugins: this.plugins[pluginName], + postProcessors: this.postProcessors, + ...options, + }); + } }); - builders.pages = createModuleBuilder({ - plugins: this.plugins.pages, - postProcessors: this.postProcessors, - }); - builders.router = createModuleBuilder({ - plugins: this.plugins.router, - mainFileName: this.template.slots.router.fileName, - postProcessors: this.postProcessors, - }); - builders.entry = createModuleBuilder({ - plugins: this.plugins.entry, - mainFileName: this.template.slots.entry.fileName, - postProcessors: this.postProcessors, - }); - builders.globalStyle = createModuleBuilder({ - plugins: this.plugins.globalStyle, - mainFileName: this.template.slots.globalStyle.fileName, - postProcessors: this.postProcessors, - }); - builders.htmlEntry = createModuleBuilder({ - plugins: this.plugins.htmlEntry, - mainFileName: this.template.slots.htmlEntry.fileName, - postProcessors: this.postProcessors, - }); - builders.packageJSON = createModuleBuilder({ - plugins: this.plugins.packageJSON, - mainFileName: this.template.slots.packageJSON.fileName, - postProcessors: this.postProcessors, - }); - - if (this.template.slots.constants && this.plugins.constants) { - builders.constants = createModuleBuilder({ - plugins: this.plugins.constants, - mainFileName: this.template.slots.constants.fileName, - postProcessors: this.postProcessors, - }); - } - if (this.template.slots.utils && this.plugins.utils) { - builders.utils = createModuleBuilder({ - plugins: this.plugins.utils, - mainFileName: this.template.slots.utils.fileName, - postProcessors: this.postProcessors, - }); - } - if (this.template.slots.i18n && this.plugins.i18n) { - builders.i18n = createModuleBuilder({ - plugins: this.plugins.i18n, - mainFileName: this.template.slots.i18n.fileName, - postProcessors: this.postProcessors, - }); - } return builders; } diff --git a/packages/code-generator/src/index.ts b/packages/code-generator/src/index.ts index a0af2ba95..8d2433d25 100644 --- a/packages/code-generator/src/index.ts +++ b/packages/code-generator/src/index.ts @@ -5,6 +5,7 @@ import { createProjectBuilder } from './generator/ProjectBuilder'; import { createDiskPublisher } from './publisher/disk'; import createIceJsProjectBuilder from './solutions/icejs'; +import createRecoreProjectBuilder from './solutions/recore'; export * from './types'; @@ -12,6 +13,7 @@ export default { createProjectBuilder, solutions: { icejs: createIceJsProjectBuilder, + recore: createRecoreProjectBuilder, }, publishers: { disk: createDiskPublisher, diff --git a/packages/code-generator/src/parser/SchemaParser.ts b/packages/code-generator/src/parser/SchemaParser.ts index 4288d894b..a98309a41 100644 --- a/packages/code-generator/src/parser/SchemaParser.ts +++ b/packages/code-generator/src/parser/SchemaParser.ts @@ -5,7 +5,7 @@ import { SUPPORT_SCHEMA_VERSION_LIST } from '../const'; -import { handleChildren } from '../utils/children'; +import { handleChildren } from '../utils/nodeToJSX'; import { ChildNodeType, @@ -28,7 +28,8 @@ import { const defaultContainer: IContainerInfo = { containerType: 'Component', - componentName: 'Index', + componentName: 'Component', + moduleName: 'Index', fileName: 'Index', css: '', props: {}, @@ -78,7 +79,7 @@ class SchemaParser implements ISchemaParser { const container: IContainerInfo = { ...subRoot, containerType: subRoot.componentName, - componentName: subRoot.fileName, + moduleName: subRoot.fileName, // TODO: 驼峰化名称 }; return container; }); @@ -104,9 +105,9 @@ class SchemaParser implements ISchemaParser { const dep: IInternalDependency = { type, - moduleName: container.componentName, + moduleName: container.moduleName, destructuring: false, - exportName: container.componentName, + exportName: container.moduleName, dependencyType: DependencyType.Internal, }; @@ -131,9 +132,15 @@ class SchemaParser implements ISchemaParser { .filter(container => container.containerType === 'Page') .map(page => { const meta = page.meta as IPageMeta; + if (meta) { + return { + path: meta.router, + componentName: page.moduleName, + }; + } return { - path: meta.router, - componentName: page.componentName, + path: '', + componentName: page.moduleName, }; }); diff --git a/packages/code-generator/src/plugins/component/react/const.ts b/packages/code-generator/src/plugins/component/react/const.ts index 0aa8b9c30..629466827 100644 --- a/packages/code-generator/src/plugins/component/react/const.ts +++ b/packages/code-generator/src/plugins/component/react/const.ts @@ -1,15 +1,8 @@ export const REACT_CHUNK_NAME = { - ClassStart: 'ReactComponentClassDefineStart', - ClassEnd: 'ReactComponentClassDefineEnd', - ClassLifeCycle: 'ReactComponentClassMemberLifeCycle', - ClassMethod: 'ReactComponentClassMemberMethod', ClassRenderStart: 'ReactComponentClassRenderStart', ClassRenderPre: 'ReactComponentClassRenderPre', ClassRenderEnd: 'ReactComponentClassRenderEnd', ClassRenderJSX: 'ReactComponentClassRenderJSX', - ClassConstructorStart: 'ReactComponentClassConstructorStart', - ClassConstructorEnd: 'ReactComponentClassConstructorEnd', - ClassConstructorContent: 'ReactComponentClassConstructorContent', ClassDidMountStart: 'ReactComponentClassDidMountStart', ClassDidMountEnd: 'ReactComponentClassDidMountEnd', ClassDidMountContent: 'ReactComponentClassDidMountContent', diff --git a/packages/code-generator/src/plugins/component/react/containerClass.ts b/packages/code-generator/src/plugins/component/react/containerClass.ts index 7cba8c66e..7ca5e685f 100644 --- a/packages/code-generator/src/plugins/component/react/containerClass.ts +++ b/packages/code-generator/src/plugins/component/react/containerClass.ts @@ -1,4 +1,4 @@ -import { COMMON_CHUNK_NAME } from '../../../const/generator'; +import { COMMON_CHUNK_NAME, CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator'; import { REACT_CHUNK_NAME } from './const'; import { @@ -21,8 +21,8 @@ const pluginFactory: BuilderComponentPluginFactory = () => { next.chunks.push({ type: ChunkType.STRING, fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassStart, - content: `class ${ir.componentName} extends React.Component {`, + name: CLASS_DEFINE_CHUNK_NAME.Start, + content: `class ${ir.moduleName} extends React.Component {`, linkAfter: [ COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport, @@ -34,27 +34,27 @@ const pluginFactory: BuilderComponentPluginFactory = () => { next.chunks.push({ type: ChunkType.STRING, fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassEnd, + name: CLASS_DEFINE_CHUNK_NAME.End, content: `}`, - linkAfter: [REACT_CHUNK_NAME.ClassStart, REACT_CHUNK_NAME.ClassRenderEnd], + linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start, REACT_CHUNK_NAME.ClassRenderEnd], }); next.chunks.push({ type: ChunkType.STRING, fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassConstructorStart, + name: CLASS_DEFINE_CHUNK_NAME.ConstructorStart, content: 'constructor(props, context) { super(props); ', - linkAfter: [REACT_CHUNK_NAME.ClassStart], + linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start], }); next.chunks.push({ type: ChunkType.STRING, fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassConstructorEnd, + name: CLASS_DEFINE_CHUNK_NAME.ConstructorEnd, content: '}', linkAfter: [ - REACT_CHUNK_NAME.ClassConstructorStart, - REACT_CHUNK_NAME.ClassConstructorContent, + CLASS_DEFINE_CHUNK_NAME.ConstructorStart, + CLASS_DEFINE_CHUNK_NAME.ConstructorContent, ], }); @@ -64,10 +64,9 @@ const pluginFactory: BuilderComponentPluginFactory = () => { name: REACT_CHUNK_NAME.ClassRenderStart, content: 'render() {', linkAfter: [ - REACT_CHUNK_NAME.ClassStart, - REACT_CHUNK_NAME.ClassConstructorEnd, - REACT_CHUNK_NAME.ClassLifeCycle, - REACT_CHUNK_NAME.ClassMethod, + CLASS_DEFINE_CHUNK_NAME.Start, + CLASS_DEFINE_CHUNK_NAME.ConstructorEnd, + CLASS_DEFINE_CHUNK_NAME.InsMethod, ], }); @@ -87,13 +86,13 @@ const pluginFactory: BuilderComponentPluginFactory = () => { type: ChunkType.STRING, fileType: FileType.JSX, name: COMMON_CHUNK_NAME.FileExport, - content: `export default ${ir.componentName};`, + content: `export default ${ir.moduleName};`, linkAfter: [ COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport, COMMON_CHUNK_NAME.FileVarDefine, COMMON_CHUNK_NAME.FileUtilDefine, - REACT_CHUNK_NAME.ClassEnd, + CLASS_DEFINE_CHUNK_NAME.End, ], }); diff --git a/packages/code-generator/src/plugins/component/react/containerDataSource.ts b/packages/code-generator/src/plugins/component/react/containerDataSource.ts index 74c02e1f4..1f6132b10 100644 --- a/packages/code-generator/src/plugins/component/react/containerDataSource.ts +++ b/packages/code-generator/src/plugins/component/react/containerDataSource.ts @@ -1,6 +1,6 @@ -import { REACT_CHUNK_NAME } from './const'; +import { CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator'; -import { generateCompositeType } from '../../utils/compositeType'; +import { generateCompositeType } from '../../../utils/compositeType'; import { BuilderComponentPlugin, @@ -11,7 +11,16 @@ import { IContainerInfo, } from '../../../types'; -const pluginFactory: BuilderComponentPluginFactory = () => { +interface PluginConfig { + fileType: string; +} + +const pluginFactory: BuilderComponentPluginFactory = (config?) => { + const cfg: PluginConfig = { + fileType: FileType.JSX, + ...config, + }; + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { ...pre, @@ -28,10 +37,10 @@ const pluginFactory: BuilderComponentPluginFactory = () => { next.chunks.push({ type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassConstructorContent, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent, content: `this.state = { ${fields.join('')} };`, - linkAfter: [REACT_CHUNK_NAME.ClassConstructorStart], + linkAfter: [CLASS_DEFINE_CHUNK_NAME.ConstructorStart], }); } diff --git a/packages/code-generator/src/plugins/component/react/containerInitState.ts b/packages/code-generator/src/plugins/component/react/containerInitState.ts index 74c02e1f4..c3ba77c2b 100644 --- a/packages/code-generator/src/plugins/component/react/containerInitState.ts +++ b/packages/code-generator/src/plugins/component/react/containerInitState.ts @@ -1,6 +1,6 @@ -import { REACT_CHUNK_NAME } from './const'; +import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator'; -import { generateCompositeType } from '../../utils/compositeType'; +import { generateCompositeType } from '../../../utils/compositeType'; import { BuilderComponentPlugin, @@ -11,7 +11,18 @@ import { IContainerInfo, } from '../../../types'; -const pluginFactory: BuilderComponentPluginFactory = () => { +interface PluginConfig { + fileType: string; + implementType: 'inConstructor' | 'insMember' | 'hooks'; +} + +const pluginFactory: BuilderComponentPluginFactory = (config?) => { + const cfg: PluginConfig = { + fileType: FileType.JSX, + implementType: 'inConstructor', + ...config, + }; + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { ...pre, @@ -26,13 +37,23 @@ const pluginFactory: BuilderComponentPluginFactory = () => { return `${stateName}: ${isString ? `'${value}'` : value},`; }); - next.chunks.push({ - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassConstructorContent, - content: `this.state = { ${fields.join('')} };`, - linkAfter: [REACT_CHUNK_NAME.ClassConstructorStart], - }); + if (cfg.implementType === 'inConstructor') { + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent, + content: `this.state = { ${fields.join('')} };`, + linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.ConstructorContent]], + }); + } else if (cfg.implementType === 'insMember') { + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.InsVar, + content: `state = { ${fields.join('')} };`, + linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsVar]], + }); + } } return next; diff --git a/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts b/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts index 8fd70c9ba..bdc6dbd6d 100644 --- a/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts +++ b/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts @@ -1,4 +1,4 @@ -import { REACT_CHUNK_NAME } from './const'; +import { CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator'; import { BuilderComponentPlugin, @@ -8,7 +8,16 @@ import { ICodeStruct, } from '../../../types'; -const pluginFactory: BuilderComponentPluginFactory = () => { +interface PluginConfig { + fileType: string; +} + +const pluginFactory: BuilderComponentPluginFactory = (config?) => { + const cfg: PluginConfig = { + fileType: FileType.JSX, + ...config, + }; + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { ...pre, @@ -16,10 +25,10 @@ const pluginFactory: BuilderComponentPluginFactory = () => { next.chunks.push({ type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassConstructorContent, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent, content: `this.utils = utils;`, - linkAfter: [REACT_CHUNK_NAME.ClassConstructorStart], + linkAfter: [CLASS_DEFINE_CHUNK_NAME.ConstructorStart], }); return next; diff --git a/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts b/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts index 39e2a017a..08c88f968 100644 --- a/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts +++ b/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts @@ -1,9 +1,10 @@ +import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator'; import { REACT_CHUNK_NAME } from './const'; import { getFuncExprBody, transformFuncExpr2MethodMember, -} from '../../utils/jsExpression'; +} from '../../../utils/jsExpression'; import { BuilderComponentPlugin, @@ -17,7 +18,20 @@ import { IJSExpression, } from '../../../types'; -const pluginFactory: BuilderComponentPluginFactory = () => { +interface PluginConfig { + fileType: string; + exportNameMapping: Record; + normalizeNameMapping: Record; +} + +const pluginFactory: BuilderComponentPluginFactory = (config?) => { + const cfg: PluginConfig = { + fileType: FileType.JSX, + exportNameMapping: {}, + normalizeNameMapping: {}, + ...config, + }; + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { ...pre, @@ -28,21 +42,23 @@ const pluginFactory: BuilderComponentPluginFactory = () => { if (ir.lifeCycles) { const lifeCycles = ir.lifeCycles; const chunks = Object.keys(lifeCycles).map(lifeCycleName => { - if (lifeCycleName === 'constructor') { + const normalizeName = cfg.normalizeNameMapping[lifeCycleName] || lifeCycleName; + const exportName = cfg.exportNameMapping[lifeCycleName] || lifeCycleName; + if (normalizeName === 'constructor') { return { type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassConstructorContent, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent, content: getFuncExprBody( (lifeCycles[lifeCycleName] as IJSExpression).value, ), - linkAfter: [REACT_CHUNK_NAME.ClassConstructorStart], + linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.ConstructorStart]], }; } - if (lifeCycleName === 'render') { + if (normalizeName === 'render') { return { type: ChunkType.STRING, - fileType: FileType.JSX, + fileType: cfg.fileType, name: REACT_CHUNK_NAME.ClassRenderPre, content: getFuncExprBody( (lifeCycles[lifeCycleName] as IJSExpression).value, @@ -50,28 +66,17 @@ const pluginFactory: BuilderComponentPluginFactory = () => { linkAfter: [REACT_CHUNK_NAME.ClassRenderStart], }; } - if ( - lifeCycleName === 'componentDidMount' || - lifeCycleName === 'componentDidUpdate' || - lifeCycleName === 'componentWillUnmount' || - lifeCycleName === 'componentDidCatch' - ) { - return { - type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassLifeCycle, - content: transformFuncExpr2MethodMember( - lifeCycleName, - (lifeCycles[lifeCycleName] as IJSExpression).value, - ), - linkAfter: [ - REACT_CHUNK_NAME.ClassStart, - REACT_CHUNK_NAME.ClassConstructorEnd, - ], - }; - } - throw new CodeGeneratorError('Unknown life cycle method name'); + return { + type: ChunkType.STRING, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.InsMethod, + content: transformFuncExpr2MethodMember( + exportName, + (lifeCycles[lifeCycleName] as IJSExpression).value, + ), + linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]], + }; }); next.chunks.push.apply(next.chunks, chunks); diff --git a/packages/code-generator/src/plugins/component/react/containerMethod.ts b/packages/code-generator/src/plugins/component/react/containerMethod.ts index 1f141dbf6..527aad815 100644 --- a/packages/code-generator/src/plugins/component/react/containerMethod.ts +++ b/packages/code-generator/src/plugins/component/react/containerMethod.ts @@ -1,6 +1,6 @@ -import { REACT_CHUNK_NAME } from './const'; +import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator'; -import { transformFuncExpr2MethodMember } from '../../utils/jsExpression'; +import { transformFuncExpr2MethodMember } from '../../../utils/jsExpression'; import { BuilderComponentPlugin, @@ -13,7 +13,16 @@ import { IJSExpression, } from '../../../types'; -const pluginFactory: BuilderComponentPluginFactory = () => { +interface PluginConfig { + fileType: string; +} + +const pluginFactory: BuilderComponentPluginFactory = (config?) => { + const cfg: PluginConfig = { + fileType: FileType.JSX, + ...config, + }; + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { ...pre, @@ -25,17 +34,13 @@ const pluginFactory: BuilderComponentPluginFactory = () => { const methods = ir.methods; const chunks = Object.keys(methods).map(methodName => ({ type: ChunkType.STRING, - fileType: FileType.JSX, - name: REACT_CHUNK_NAME.ClassMethod, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.InsMethod, content: transformFuncExpr2MethodMember( methodName, (methods[methodName] as IJSExpression).value, ), - linkAfter: [ - REACT_CHUNK_NAME.ClassStart, - REACT_CHUNK_NAME.ClassConstructorEnd, - REACT_CHUNK_NAME.ClassLifeCycle, - ], + linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]], })); next.chunks.push.apply(next.chunks, chunks); diff --git a/packages/code-generator/src/plugins/component/react/jsx.ts b/packages/code-generator/src/plugins/component/react/jsx.ts index e1b5d2306..2b168d259 100644 --- a/packages/code-generator/src/plugins/component/react/jsx.ts +++ b/packages/code-generator/src/plugins/component/react/jsx.ts @@ -1,142 +1,39 @@ import { BuilderComponentPlugin, BuilderComponentPluginFactory, - ChildNodeItem, - ChildNodeType, ChunkType, FileType, ICodeStruct, - IComponentNodeItem, IContainerInfo, - IInlineStyle, - IJSExpression, } from '../../../types'; -import { handleChildren } from '../../../utils/children'; -import { generateCompositeType } from '../../utils/compositeType'; import { REACT_CHUNK_NAME } from './const'; -function generateInlineStyle(style: IInlineStyle): string | null { - const attrLines = Object.keys(style).map((cssAttribute: string) => { - const [isString, valueStr] = generateCompositeType(style[cssAttribute]); - const valuePart = isString ? `'${valueStr}'` : valueStr; - return `${cssAttribute}: ${valuePart},`; - }); +import { createReactNodeGenerator } from '../../../utils/nodeToJSX'; - if (attrLines.length === 0) { - return null; - } - - return `{ ${attrLines.join('')} }`; +interface PluginConfig { + fileType: string; } -function generateAttr(attrName: string, attrValue: any): string { - if (attrName === 'initValue' || attrName === 'labelCol') { - return ''; - } - const [isString, valueStr] = generateCompositeType(attrValue); - return `${attrName}=${isString ? `"${valueStr}"` : `{${valueStr}}`}`; -} +const pluginFactory: BuilderComponentPluginFactory = (config?) => { + const cfg: PluginConfig = { + fileType: FileType.JSX, + ...config, + }; -function mapNodeName(src: string): string { - if (src === 'Div') { - return 'div'; - } - return src; -} + const generator = createReactNodeGenerator(); -function generateNode(nodeItem: IComponentNodeItem): string { - const codePieces: string[] = []; - let propLines: string[] = []; - const { className, style, ...props } = nodeItem.props; - - codePieces.push(`<${mapNodeName(nodeItem.componentName)}`); - if (className) { - propLines.push(`className="${className}"`); - } - if (style) { - const inlineStyle = generateInlineStyle(style); - if (inlineStyle !== null) { - propLines.push(`style={${inlineStyle}}`); - } - } - - propLines = propLines.concat( - Object.keys(props).map((propName: string) => - generateAttr(propName, props[propName]), - ), - ); - codePieces.push(` ${propLines.join(' ')} `); - - if (nodeItem.children && (nodeItem.children as unknown[]).length > 0) { - codePieces.push('>'); - const childrenLines = generateChildren(nodeItem.children); - codePieces.push.apply(codePieces, childrenLines); - codePieces.push(``); - } else { - codePieces.push('/>'); - } - - if (nodeItem.loop && nodeItem.loopArgs) { - let loopDataExp; - if ((nodeItem.loop as IJSExpression).type === 'JSExpression') { - loopDataExp = `(${(nodeItem.loop as IJSExpression).value})`; - } else { - loopDataExp = JSON.stringify(nodeItem.loop); - } - codePieces.unshift( - `${loopDataExp}.map((${nodeItem.loopArgs[0]}, ${nodeItem.loopArgs[1]}) => (`, - ); - codePieces.push('))'); - } - - if (nodeItem.condition) { - codePieces.unshift(`(${generateCompositeType(nodeItem.condition)}) && (`); - codePieces.push(')'); - } - - if (nodeItem.condition || (nodeItem.loop && nodeItem.loopArgs)) { - codePieces.unshift('{'); - codePieces.push('}'); - } - - return codePieces.join(''); -} - -function generateChildren(children: ChildNodeType): string[] { - return handleChildren(children, { - // TODO: 如果容器直接只有一个 字符串 children 呢? - string: (input: string) => [input], - expression: (input: IJSExpression) => [`{${input.value}}`], - node: (input: IComponentNodeItem) => [generateNode(input)], - }); -} - -const pluginFactory: BuilderComponentPluginFactory = () => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { ...pre, }; const ir = next.ir as IContainerInfo; - - let jsxContent: string; - if (!ir.children || (ir.children as unknown[]).length === 0) { - jsxContent = 'null'; - } else { - const childrenCode = generateChildren(ir.children); - if (childrenCode.length === 1) { - jsxContent = `(${childrenCode[0]})`; - } else { - jsxContent = `(${childrenCode.join( - '', - )})`; - } - } + const jsxContent = generator(ir); next.chunks.push({ type: ChunkType.STRING, - fileType: FileType.JSX, + fileType: cfg.fileType, name: REACT_CHUNK_NAME.ClassRenderJSX, content: `return ${jsxContent};`, linkAfter: [ diff --git a/packages/code-generator/src/plugins/component/react/reactCommonDeps.ts b/packages/code-generator/src/plugins/component/react/reactCommonDeps.ts index 434f44564..2e75a5b7e 100644 --- a/packages/code-generator/src/plugins/component/react/reactCommonDeps.ts +++ b/packages/code-generator/src/plugins/component/react/reactCommonDeps.ts @@ -9,7 +9,6 @@ import { IContainerInfo, } from '../../../types'; -// TODO: How to merge this logic to common deps const pluginFactory: BuilderComponentPluginFactory = () => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { diff --git a/packages/code-generator/src/plugins/component/recore/const.ts b/packages/code-generator/src/plugins/component/recore/const.ts new file mode 100644 index 000000000..b848f42aa --- /dev/null +++ b/packages/code-generator/src/plugins/component/recore/const.ts @@ -0,0 +1,3 @@ +export const RECORE_CHUNK_NAME = { + +}; diff --git a/packages/code-generator/src/plugins/component/recore/pageDataSource.ts b/packages/code-generator/src/plugins/component/recore/pageDataSource.ts new file mode 100644 index 000000000..da7de385e --- /dev/null +++ b/packages/code-generator/src/plugins/component/recore/pageDataSource.ts @@ -0,0 +1,73 @@ +import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator'; + +import { + BuilderComponentPlugin, + BuilderComponentPluginFactory, + ChunkType, + FileType, + ICodeStruct, + IContainerInfo, + IJSExpression, + CompositeValue, +} from '../../../types'; + +import { generateCompositeType, handleStringValueDefault } from '../../../utils/compositeType'; +import { generateExpression } from '../../../utils/jsExpression'; + +function packJsExpression(exp: unknown): string { + const expression = exp as IJSExpression; + const funcStr = generateExpression(expression); + return `function() { return (${funcStr}); }`; +} + +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IContainerInfo; + if (ir.dataSource) { + const { dataSource } = ir; + const { + list, + dataHandler, + ...rest + } = dataSource; + + let attrs: string[] = []; + + const extConfigs = Object.keys(rest).map(extConfigName => { + const value = rest[extConfigName] as CompositeValue; + const [isString, valueStr] = generateCompositeType(value); + return `${extConfigName}: ${isString ? `'${valueStr}'` : valueStr}`; + }); + + attrs = [...attrs, ...extConfigs]; + + if (dataHandler) { + const handlerContent = packJsExpression(dataHandler); + attrs.push(`dataHandler: ${handlerContent}`); + } + + const listProp = handleStringValueDefault(generateCompositeType(list as unknown as CompositeValue, { + expression: packJsExpression, + })); + + attrs.push(`list: ${listProp}`); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.TS, + name: CLASS_DEFINE_CHUNK_NAME.InsVar, + content: `dataSourceOptions = { ${attrs.join(',\n')} };`, + linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsVar]], + }); + } + + return next; + }; + return plugin; +}; + +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/recore/pageFrame.ts b/packages/code-generator/src/plugins/component/recore/pageFrame.ts new file mode 100644 index 000000000..e302a64bc --- /dev/null +++ b/packages/code-generator/src/plugins/component/recore/pageFrame.ts @@ -0,0 +1,81 @@ +import { COMMON_CHUNK_NAME, CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator'; + +import { + BuilderComponentPlugin, + BuilderComponentPluginFactory, + ChunkType, + FileType, + ICodeStruct, + IContainerInfo, +} from '../../../types'; + +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IContainerInfo; + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.TS, + name: COMMON_CHUNK_NAME.ExternalDepsImport, + content: `import { BaseController } from '@ali/recore-renderer';`, + linkAfter: [], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.TS, + name: CLASS_DEFINE_CHUNK_NAME.Start, + content: `class ${ir.moduleName} extends BaseController {`, + linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.Start]], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.TS, + name: CLASS_DEFINE_CHUNK_NAME.End, + content: `}`, + linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.End]], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.TS, + name: CLASS_DEFINE_CHUNK_NAME.ConstructorStart, + content: 'init() {', + linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.ConstructorStart]], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.TS, + name: CLASS_DEFINE_CHUNK_NAME.ConstructorEnd, + content: '}', + linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.ConstructorEnd]], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.TS, + name: CLASS_DEFINE_CHUNK_NAME.InsVar, + content: 'globalProps = (window as any)?.g_config?.globalProps || {};', + linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsVar]], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.TS, + name: COMMON_CHUNK_NAME.FileExport, + content: `export default ${ir.moduleName};`, + linkAfter: [...DEFAULT_LINK_AFTER[COMMON_CHUNK_NAME.FileExport]], + }); + + return next; + }; + return plugin; +}; + +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/recore/pageStyle.ts b/packages/code-generator/src/plugins/component/recore/pageStyle.ts new file mode 100644 index 000000000..fc8ca41b5 --- /dev/null +++ b/packages/code-generator/src/plugins/component/recore/pageStyle.ts @@ -0,0 +1,35 @@ +import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator'; + +import { + BuilderComponentPlugin, + BuilderComponentPluginFactory, + ChunkType, + ICodeStruct, + FileType, + IContainerInfo, +} from '../../../types'; + +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IContainerInfo; + + if (ir.css) { + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.TS, + name: CLASS_DEFINE_CHUNK_NAME.StaticVar, + content: `static cssText = '${ir.css.replace(/\'/g, '\\\'')}';`, + linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.StaticVar]], + }); + } + + return next; + }; + return plugin; +}; + +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/recore/pageVmBody.ts b/packages/code-generator/src/plugins/component/recore/pageVmBody.ts new file mode 100644 index 000000000..8660ad195 --- /dev/null +++ b/packages/code-generator/src/plugins/component/recore/pageVmBody.ts @@ -0,0 +1,82 @@ +import { + BuilderComponentPlugin, + BuilderComponentPluginFactory, + ChunkType, + ICodeStruct, + IContainerInfo, + IComponentNodeItem, + CodePiece, + PIECE_TYPE, +} from '../../../types'; +import { COMMON_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator'; + +import { createNodeGenerator, generateString } from '../../../utils/nodeToJSX'; +import { generateExpression } from '../../../utils/jsExpression'; +import { generateCompositeType, handleStringValueDefault } from '../../../utils/compositeType'; + +const generateGlobalProps = (nodeItem: IComponentNodeItem): CodePiece[] => { + return [{ + value: `{...globalProps.${nodeItem.componentName}}`, + type: PIECE_TYPE.ATTR, + }]; +}; + +const generateCtrlLine = (nodeItem: IComponentNodeItem): CodePiece[] => { + const pieces: CodePiece[] = []; + + if (nodeItem.loop && nodeItem.loopArgs) { + const loopDataExp = handleStringValueDefault(generateCompositeType(nodeItem.loop)); + pieces.push({ + type: PIECE_TYPE.ATTR, + value: `x-for={${loopDataExp}}`, + }); + + pieces.push({ + type: PIECE_TYPE.ATTR, + value: `x-each="${nodeItem.loopArgs[0]},${nodeItem.loopArgs[1]}"`, + }); + } + + if (nodeItem.condition) { + const conditionExp = handleStringValueDefault(generateCompositeType(nodeItem.condition)); + pieces.push({ + type: PIECE_TYPE.ATTR, + value: `x-if={${conditionExp}}`, + }); + } + + return pieces; +}; + +const pluginFactory: BuilderComponentPluginFactory = () => { + const generator = createNodeGenerator({ + string: generateString, + expression: (input) => [generateExpression(input)], + }, [ + generateGlobalProps, + generateCtrlLine, + ]); + + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IContainerInfo; + + const vxContent = generator(ir); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: 'vx', + name: COMMON_CHUNK_NAME.CustomContent, + content: vxContent, + linkAfter: [], + }); + + return next; + }; + return plugin; +}; + +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/recore/pageVmHeader.ts b/packages/code-generator/src/plugins/component/recore/pageVmHeader.ts new file mode 100644 index 000000000..6ef8ac44f --- /dev/null +++ b/packages/code-generator/src/plugins/component/recore/pageVmHeader.ts @@ -0,0 +1,29 @@ +import { COMMON_CHUNK_NAME } from '../../../const/generator'; + +import { + BuilderComponentPlugin, + BuilderComponentPluginFactory, + ChunkType, + ICodeStruct, +} from '../../../types'; + +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + next.chunks.push({ + type: ChunkType.STRING, + fileType: 'vx', + name: COMMON_CHUNK_NAME.CustomContent, + content: `
`, + linkAfter: [], + }); + + return next; + }; + return plugin; +}; + +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/style/css.ts b/packages/code-generator/src/plugins/component/style/css.ts index c803d24a9..cb9b2a4e0 100644 --- a/packages/code-generator/src/plugins/component/style/css.ts +++ b/packages/code-generator/src/plugins/component/style/css.ts @@ -9,7 +9,18 @@ import { IContainerInfo, } from '../../../types'; -const pluginFactory: BuilderComponentPluginFactory = () => { +interface PluginConfig { + fileType: string; + moduleFileType: string; +} + +const pluginFactory: BuilderComponentPluginFactory = (config?) => { + const cfg: PluginConfig = { + fileType: FileType.CSS, + moduleFileType: FileType.JSX, + ...config, + }; + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { ...pre, @@ -19,7 +30,7 @@ const pluginFactory: BuilderComponentPluginFactory = () => { next.chunks.push({ type: ChunkType.STRING, - fileType: FileType.CSS, + fileType: cfg.fileType, name: COMMON_CHUNK_NAME.StyleCssContent, content: ir.css, linkAfter: [COMMON_CHUNK_NAME.StyleDepsImport], @@ -27,9 +38,9 @@ const pluginFactory: BuilderComponentPluginFactory = () => { next.chunks.push({ type: ChunkType.STRING, - fileType: FileType.JSX, + fileType: cfg.moduleFileType, name: COMMON_CHUNK_NAME.InternalDepsImport, - content: `import './index.css';`, + content: `import './index.${cfg.fileType}';`, linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport], }); diff --git a/packages/code-generator/src/plugins/project/constants.ts b/packages/code-generator/src/plugins/project/constants.ts index 8c1302cd0..5c02e9711 100644 --- a/packages/code-generator/src/plugins/project/constants.ts +++ b/packages/code-generator/src/plugins/project/constants.ts @@ -1,5 +1,5 @@ import { COMMON_CHUNK_NAME } from '../../const/generator'; -import { generateCompositeType } from '../../plugins/utils/compositeType'; +import { generateCompositeType } from '../../utils/compositeType'; import { BuilderComponentPlugin, BuilderComponentPluginFactory, @@ -9,7 +9,6 @@ import { IProjectInfo, } from '../../types'; -// TODO: How to merge this logic to common deps const pluginFactory: BuilderComponentPluginFactory = () => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { diff --git a/packages/code-generator/src/plugins/project/framework/icejs/plugins/entry.ts b/packages/code-generator/src/plugins/project/framework/icejs/plugins/entry.ts index 28bef8f0a..0cf3918a7 100644 --- a/packages/code-generator/src/plugins/project/framework/icejs/plugins/entry.ts +++ b/packages/code-generator/src/plugins/project/framework/icejs/plugins/entry.ts @@ -9,7 +9,6 @@ import { IProjectInfo, } from '../../../../../types'; -// TODO: How to merge this logic to common deps const pluginFactory: BuilderComponentPluginFactory = () => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { diff --git a/packages/code-generator/src/plugins/project/framework/icejs/plugins/entryHtml.ts b/packages/code-generator/src/plugins/project/framework/icejs/plugins/entryHtml.ts index 644e6155d..a0ca3cdf1 100644 --- a/packages/code-generator/src/plugins/project/framework/icejs/plugins/entryHtml.ts +++ b/packages/code-generator/src/plugins/project/framework/icejs/plugins/entryHtml.ts @@ -9,7 +9,6 @@ import { IProjectInfo, } from '../../../../../types'; -// TODO: How to merge this logic to common deps const pluginFactory: BuilderComponentPluginFactory = () => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { diff --git a/packages/code-generator/src/plugins/project/framework/icejs/plugins/globalStyle.ts b/packages/code-generator/src/plugins/project/framework/icejs/plugins/globalStyle.ts index 92ebf8730..3daaeecdc 100644 --- a/packages/code-generator/src/plugins/project/framework/icejs/plugins/globalStyle.ts +++ b/packages/code-generator/src/plugins/project/framework/icejs/plugins/globalStyle.ts @@ -9,7 +9,6 @@ import { IProjectInfo, } from '../../../../../types'; -// TODO: How to merge this logic to common deps const pluginFactory: BuilderComponentPluginFactory = () => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { diff --git a/packages/code-generator/src/plugins/project/framework/icejs/plugins/packageJSON.ts b/packages/code-generator/src/plugins/project/framework/icejs/plugins/packageJSON.ts index f00381a0b..a79dc3481 100644 --- a/packages/code-generator/src/plugins/project/framework/icejs/plugins/packageJSON.ts +++ b/packages/code-generator/src/plugins/project/framework/icejs/plugins/packageJSON.ts @@ -21,7 +21,6 @@ interface IIceJsPackageJSON extends IPackageJSON { originTemplate: string; } -// TODO: How to merge this logic to common deps const pluginFactory: BuilderComponentPluginFactory = () => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { diff --git a/packages/code-generator/src/plugins/project/framework/icejs/plugins/router.ts b/packages/code-generator/src/plugins/project/framework/icejs/plugins/router.ts index 9dc436b28..b4f2d718f 100644 --- a/packages/code-generator/src/plugins/project/framework/icejs/plugins/router.ts +++ b/packages/code-generator/src/plugins/project/framework/icejs/plugins/router.ts @@ -9,7 +9,6 @@ import { IRouterInfo, } from '../../../../../types'; -// TODO: How to merge this logic to common deps const pluginFactory: BuilderComponentPluginFactory = () => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { diff --git a/packages/code-generator/src/plugins/project/framework/icejs/template/index.ts b/packages/code-generator/src/plugins/project/framework/icejs/template/index.ts index 2a1f3a285..d048bd389 100644 --- a/packages/code-generator/src/plugins/project/framework/icejs/template/index.ts +++ b/packages/code-generator/src/plugins/project/framework/icejs/template/index.ts @@ -2,8 +2,8 @@ import ResultDir from '../../../../../model/ResultDir'; import { IProjectTemplate, IResultDir, - IResultFile, } from '../../../../../types'; +import { runFileGenerator } from '../../../../../utils/templateHelper'; import file12 from './files/abc.json'; import file11 from './files/build.json'; @@ -26,29 +26,6 @@ import file3 from './files/stylelintignore'; import file2 from './files/stylelintrc.js'; import file1 from './files/tsconfig.json'; -type FuncFileGenerator = () => [string[], IResultFile]; - -function insertFile(root: IResultDir, path: string[], file: IResultFile) { - let current: IResultDir = root; - path.forEach(pathNode => { - const dir = current.dirs.find(d => d.name === pathNode); - if (dir) { - current = dir; - } else { - const newDir = new ResultDir(pathNode); - current.addDirectory(newDir); - current = newDir; - } - }); - - current.addFile(file); -} - -function runFileGenerator(root: IResultDir, fun: FuncFileGenerator) { - const [path, file] = fun(); - insertFile(root, path, file); -} - const icejsTemplate: IProjectTemplate = { slots: { components: { diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/README.md.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/README.md.ts index 2c01ceaef..f90fd7d87 100644 --- a/packages/code-generator/src/plugins/project/framework/recore/template/files/README.md.ts +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/README.md.ts @@ -14,7 +14,7 @@ export default function getFile(): [string[], IResultFile] { ## 安装运行 -```bash +\`\`\`bash # install dependencies tnpm install @@ -26,11 +26,10 @@ npm test # local build npm run build -``` +\`\`\` `, ); return [[], file]; } - \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/package.json.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/package.json.ts index c321542ab..d8da2daaa 100644 --- a/packages/code-generator/src/plugins/project/framework/recore/template/files/package.json.ts +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/package.json.ts @@ -21,41 +21,40 @@ export default function getFile(): [string[], IResultFile] { "npm": ">=6.1.0" }, "dependencies": { - "@ali/recore": "^1.6.10", "@ali/lowcode-runtime": "^0.8.0", - "@ali/recore-renderer": "^0.0.1", - "@ali/vc-shell": "1.3.1", + "@ali/recore": "^1.6.10", + "@ali/recore-renderer": "^0.0.3", "@ali/vc-block": "^3.0.3-beta.1", "@ali/vc-deep": "1.2.38", "@ali/vc-div": "^1.0.1", "@ali/vc-page": "^1.0.5", + "@ali/vc-shell": "1.3.1", "@ali/vc-slot": "^2.0.1", "@ali/vc-text": "^4.0.1", "@ali/vu-dataSource": "^1.0.4", "@ali/vu-formatter": "^2.0.0", "@ali/vu-fusion": "^2.0.1-beta.0", "@ali/vu-legao-builtin": "^1.4.0-beta.2", + "@ali/vu-toolkit": "^1.0.5", "react": "^16" }, "devDependencies": { - "build-plugin-react-app": "^1.0.15", - "@ali/build-plugin-recore-lowcode": "^0.0.1", + "@ali/build-plugin-recore-lowcode": "^0.0.3", + "@ali/recore-lowcode-loader": "^0.0.4", "@alib/build-scripts": "^0.1.0", "@types/node": "^7", "@types/react": "^16", + "build-plugin-react-app": "^1.0.15", "eslint": "^6.5.1", + "prettier": "^1.18.2", "tslib": "^1.9.3", - "typescript": "^3.1.3", - "prettier": "^1.18.2" + "typescript": "^3.1.3" }, "lint-staged": { "./src/**/*.{ts,tsx}": [ "tslint --fix", "git add" ] - }, - "nowa": { - "solution": "@ali/nowa-recore-solution" } } diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/index.ts.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/index.ts.ts index 8fdb5c211..979b85a20 100644 --- a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/index.ts.ts +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/index.ts.ts @@ -8,8 +8,81 @@ export default function getFile(): [string[], IResultFile] { 'ts', ` import { app } from '@ali/lowcode-runtime'; +import { ReactProvider } from '@ali/lowcode-runtime'; import Shell from '@ali/vc-shell'; import StaticRender from './plugins/provider'; +import Router from '@/router'; +import appConfig from '@/config/app'; +import components from '@/config/components'; +import utils from '@/config/utils'; + +// 定制加载应用配置的逻辑 +class StaticRender extends ReactProvider { + // 初始化时调用,如可以在这里注入全局API + init() { + const gConfig = (window as any).g_config || {}; + const LeGao = { + __ctx__: {}, + createContext: (cfg: any) => { + const { schema } = cfg || {}; + // 1. 根据参数拉取schema + if (schema && typeof schema === 'string') { + this.setHomePage(schema); + } + const { isSectionalRender, autoRender } = gConfig || {}; + if (isSectionalRender && !autoRender) { + // 2. 渲染 + this.setSectionalRender(); + this.ready(); + } + const provider = this; + class Context { + get utils() { + return provider.getUtils(); + } + get components() { + return provider.getComponents(); + } + } + const ctx = new Context(); + (LeGao.__ctx__ as any)[this.getContainerId()] = ctx; + return ctx; + }, + getContext: (id: string) => { + if (!id) { + for (id in LeGao.__ctx__) { + return (LeGao.__ctx__ as any)[id]; + } + } + return (LeGao.__ctx__ as any)[id]; + } + }; + (window as any).LeGao = LeGao; + if (gConfig.index) { + this.setHomePage(gConfig.index); + } + if (gConfig.isSectionalRender) { + this.setSectionalRender(); + if (!gConfig.autoRender) { + return; + } + } + this.ready(); + } + + // 定制获取、处理应用配置(组件、插件、路由模式、布局等)的逻辑 + getAppData() { + return { + ...appConfig, + components, + utils: utils, + } + } + + getRouterView() { + return Router; + } +} // 注册布局组件,可注册多个 app.registerLayout(Shell, { diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/plugins/provider.ts.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/plugins/provider.ts.ts deleted file mode 100644 index 8d20e365d..000000000 --- a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/plugins/provider.ts.ts +++ /dev/null @@ -1,89 +0,0 @@ - -import ResultFile from '../../../../../../../../model/ResultFile'; -import { IResultFile } from '../../../../../../../../types'; - -export default function getFile(): [string[], IResultFile] { - const file = new ResultFile( - 'provider', - 'ts', - ` -import { ReactProvider } from '@ali/lowcode-runtime'; -import Router from '@/router'; -import appConfig from '@/config/app'; -import components from '@/config/components'; -import utils from '@/config/utils'; - -// 定制加载应用配置的逻辑 -export default class MyProvider extends ReactProvider { - // 初始化时调用,如可以在这里注入全局API - init() { - const gConfig = (window as any).g_config || {}; - const LeGao = { - __ctx__: {}, - createContext: (cfg: any) => { - const { schema } = cfg || {}; - // 1. 根据参数拉取schema - if (schema && typeof schema === 'string') { - this.setHomePage(schema); - } - const { isSectionalRender, autoRender } = gConfig || {}; - if (isSectionalRender && !autoRender) { - // 2. 渲染 - this.setSectionalRender(); - this.ready(); - } - const provider = this; - class Context { - get utils() { - return provider.getUtils(); - } - get components() { - return provider.getComponents(); - } - } - const ctx = new Context(); - (LeGao.__ctx__ as any)[this.getContainerId()] = ctx; - return ctx; - }, - getContext: (id: string) => { - if (!id) { - for (id in LeGao.__ctx__) { - return (LeGao.__ctx__ as any)[id]; - } - } - return (LeGao.__ctx__ as any)[id]; - } - }; - (window as any).LeGao = LeGao; - if (gConfig.index) { - this.setHomePage(gConfig.index); - } - if (gConfig.isSectionalRender) { - this.setSectionalRender(); - if (!gConfig.autoRender) { - return; - } - } - this.ready(); - } - - // 定制获取、处理应用配置(组件、插件、路由模式、布局等)的逻辑 - getAppData() { - return { - ...appConfig, - components, - utils: utils, - } - } - - getRouterView() { - return Router; - } -} - - `, - ); - - return [['src','plugins'], file]; -} - \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/router.ts.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/router.ts.ts index 4dd0e3124..78d1fff61 100644 --- a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/router.ts.ts +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/router.ts.ts @@ -11,7 +11,7 @@ export default { baseDir: './pages', exact: true, routes: [ - { main: './index', path: '/' }, + { main: './page_index', path: '/' }, ], }; `, @@ -19,4 +19,3 @@ export default { return [['src'], file]; } - \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/index.ts b/packages/code-generator/src/plugins/project/framework/recore/template/index.ts new file mode 100644 index 000000000..1fd9565b9 --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/recore/template/index.ts @@ -0,0 +1,54 @@ +import ResultDir from '../../../../../model/ResultDir'; +import { + IProjectTemplate, + IResultDir, +} from '../../../../../types'; +import { runFileGenerator } from '../../../../../utils/templateHelper'; + +import file1 from './files/abc.json'; +import file2 from './files/build.json'; +import file3 from './files/.editorconfig'; +import file4 from './files/.eslintignore'; +import file5 from './files/.gitignore'; +import file6 from './files/.prettierrc'; +import file7 from './files/README.md'; +import file8 from './files/package.json'; +import file9 from './files/public/index.html'; +import file10 from './files/src/index.ts'; +import file11 from './files/src/router.ts'; +import file13 from './files/src/config/app.ts'; +import file14 from './files/src/config/components.ts'; +import file15 from './files/src/config/utils.ts'; +import file16 from './files/tsconfig.json'; + +const icejsTemplate: IProjectTemplate = { + slots: { + pages: { + path: ['src', 'pages'], + }, + }, + + generateTemplate(): IResultDir { + const root = new ResultDir('.'); + + runFileGenerator(root, file1); + runFileGenerator(root, file2); + runFileGenerator(root, file3); + runFileGenerator(root, file4); + runFileGenerator(root, file5); + runFileGenerator(root, file6); + runFileGenerator(root, file7); + runFileGenerator(root, file8); + runFileGenerator(root, file9); + runFileGenerator(root, file10); + runFileGenerator(root, file11); + runFileGenerator(root, file13); + runFileGenerator(root, file14); + runFileGenerator(root, file15); + runFileGenerator(root, file16); + + return root; + }, +}; + +export default icejsTemplate; diff --git a/packages/code-generator/src/plugins/project/i18n.ts b/packages/code-generator/src/plugins/project/i18n.ts index bb3623032..cada1fd96 100644 --- a/packages/code-generator/src/plugins/project/i18n.ts +++ b/packages/code-generator/src/plugins/project/i18n.ts @@ -1,5 +1,5 @@ import { COMMON_CHUNK_NAME } from '../../const/generator'; -import { generateCompositeType } from '../../plugins/utils/compositeType'; +import { generateCompositeType } from '../../utils/compositeType'; import { BuilderComponentPlugin, BuilderComponentPluginFactory, @@ -9,7 +9,6 @@ import { IProjectInfo, } from '../../types'; -// TODO: How to merge this logic to common deps const pluginFactory: BuilderComponentPluginFactory = () => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { diff --git a/packages/code-generator/src/plugins/project/utils.ts b/packages/code-generator/src/plugins/project/utils.ts index 1f6f0a152..32e7c5d17 100644 --- a/packages/code-generator/src/plugins/project/utils.ts +++ b/packages/code-generator/src/plugins/project/utils.ts @@ -9,7 +9,6 @@ import { IUtilInfo, } from '../../types'; -// TODO: How to merge this logic to common deps const pluginFactory: BuilderComponentPluginFactory = () => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const next: ICodeStruct = { diff --git a/packages/code-generator/src/plugins/utils/compositeType.ts b/packages/code-generator/src/plugins/utils/compositeType.ts deleted file mode 100644 index 2e102fcac..000000000 --- a/packages/code-generator/src/plugins/utils/compositeType.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { CompositeArray, CompositeValue, ICompositeObject } from '../../types'; -import { generateValue, isJsExpression } from './jsExpression'; - -function generateArray(value: CompositeArray): string { - const body = value.map(v => generateUnknownType(v)).join(','); - return `[${body}]`; -} - -function generateObject(value: ICompositeObject): string { - if (isJsExpression(value)) { - return generateValue(value); - } - - const body = Object.keys(value) - .map(key => { - const v = generateUnknownType(value[key]); - return `${key}: ${v}`; - }) - .join(','); - - return `{${body}}`; -} - -function generateUnknownType(value: CompositeValue): string { - if (Array.isArray(value)) { - return generateArray(value as CompositeArray); - } else if (typeof value === 'object') { - return generateObject(value as ICompositeObject); - } else if (typeof value === 'string') { - return `'${value}'`; - } - return `${value}`; -} - -export function generateCompositeType( - value: CompositeValue, -): [boolean, string] { - const result = generateUnknownType(value); - - if (result.substr(0, 1) === "'" && result.substr(-1, 1) === "'") { - return [true, result.substring(1, result.length - 1)]; - } - - return [false, result]; -} diff --git a/packages/code-generator/src/plugins/utils/jsExpression.ts b/packages/code-generator/src/plugins/utils/jsExpression.ts deleted file mode 100644 index eda1883bc..000000000 --- a/packages/code-generator/src/plugins/utils/jsExpression.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { CodeGeneratorError, IJSExpression } from '../../types'; - -export function transformFuncExpr2MethodMember( - methodName: string, - functionBody: string, -): string { - if (functionBody.indexOf('function') < 8) { - return functionBody.replace('function', methodName); - } - return functionBody; -} - -export function getFuncExprBody(functionBody: string) { - const start = functionBody.indexOf('{'); - const end = functionBody.lastIndexOf('}'); - - if (start < 0 || end < 0 || end < start) { - throw new CodeGeneratorError('JSExpression has no valid body.'); - } - - const body = functionBody.slice(start + 1, end); - return body; -} - -export function generateValue(value: any): string { - if (value && (value as IJSExpression).type === 'JSExpression') { - return (value as IJSExpression).value; - } - - throw new CodeGeneratorError('Not a JSExpression'); -} - -export function isJsExpression(value: any): boolean { - return ( - value && - typeof value === 'object' && - (value as IJSExpression).type === 'JSExpression' - ); -} diff --git a/packages/code-generator/src/postprocessor/prettier/index.ts b/packages/code-generator/src/postprocessor/prettier/index.ts index f36009fad..519c2db5d 100644 --- a/packages/code-generator/src/postprocessor/prettier/index.ts +++ b/packages/code-generator/src/postprocessor/prettier/index.ts @@ -1,16 +1,32 @@ import prettier from 'prettier'; +import mypretter from '@ali/my-prettier'; import { PostProcessor, PostProcessorFactory } from '../../types'; const PARSERS = ['css', 'scss', 'less', 'json', 'html', 'vue']; -const factory: PostProcessorFactory = () => { +interface ProcessorConfig { + customFileTypeParser: Record; +} + +const factory: PostProcessorFactory = (config?: ProcessorConfig) => { + const cfg: ProcessorConfig = { + customFileTypeParser: {}, + ...config, + }; + const codePrettier: PostProcessor = (content: string, fileType: string) => { let parser: prettier.BuiltInParserName; - if (fileType === 'js' || fileType === 'jsx' || fileType === 'ts' || fileType === 'tsx') { + if (fileType === 'js' || fileType === 'jsx') { parser = 'babel'; + } else if (fileType === 'ts' || fileType === 'tsx') { + parser = 'typescript'; } else if (PARSERS.indexOf(fileType) >= 0) { parser = fileType as prettier.BuiltInParserName; + } else if (cfg.customFileTypeParser[fileType]){ + parser = cfg.customFileTypeParser[fileType] as prettier.BuiltInParserName; + } else if (fileType === 'vx') { + return mypretter(content, fileType); } else { return content; } diff --git a/packages/code-generator/src/solutions/recore.ts b/packages/code-generator/src/solutions/recore.ts new file mode 100644 index 000000000..9850f2b3a --- /dev/null +++ b/packages/code-generator/src/solutions/recore.ts @@ -0,0 +1,51 @@ +import { IProjectBuilder } from '../types'; + +import { createProjectBuilder } from '../generator/ProjectBuilder'; + +// import esmodule from '../plugins/common/esmodule'; +import containerInitState from '../plugins/component/react/containerInitState'; +import containerLifeCycle from '../plugins/component/react/containerLifeCycle'; +import containerMethod from '../plugins/component/react/containerMethod'; +import pageFrame from '../plugins/component/recore/pageFrame'; +import pageStyle from '../plugins/component/recore/pageStyle'; +import pageVmHeader from '../plugins/component/recore/pageVmHeader'; +import pageVmBody from '../plugins/component/recore/pageVmBody'; +import pageDataSource from '../plugins/component/recore/pageDataSource'; +import template from '../plugins/project/framework/recore/template'; + +import { prettier } from '../postprocessor'; + +export default function createRecoreProjectBuilder(): IProjectBuilder { + return createProjectBuilder({ + template, + plugins: { + pages: [ + pageFrame(), + pageStyle(), + containerInitState({ + fileType: 'ts', + implementType: 'insMember', + }), + containerLifeCycle({ + fileType: 'ts', + exportNameMapping: { + constructor: 'init', + componentDidMount: 'didMount', + willUnmount: 'willUnMount', + componentWillUnmount: 'willUnMount', + }, + normalizeNameMapping: { + init: 'constructor', + }, + }), + containerMethod({ + fileType: 'ts', + }), + pageDataSource(), + pageVmHeader(), + pageVmBody(), + ], + }, + postProcessors: [prettier()], + }); +} diff --git a/packages/code-generator/src/types/core.ts b/packages/code-generator/src/types/core.ts index 750d41395..5cdffaa4b 100644 --- a/packages/code-generator/src/types/core.ts +++ b/packages/code-generator/src/types/core.ts @@ -4,6 +4,8 @@ import { IProjectSchema, IResultDir, IResultFile, + IComponentNodeItem, + IJSExpression, } from './index'; export enum FileType { @@ -107,7 +109,7 @@ export interface ISchemaParser { } export interface IProjectTemplate { - slots: IProjectSlots; + slots: Record; generateTemplate(): IResultDir; } @@ -116,30 +118,21 @@ export interface IProjectSlot { fileName?: string; } -export interface IProjectSlots { - components: IProjectSlot; - pages: IProjectSlot; - router: IProjectSlot; - entry: IProjectSlot; - constants?: IProjectSlot; - utils?: IProjectSlot; - i18n?: IProjectSlot; - globalStyle: IProjectSlot; - htmlEntry: IProjectSlot; - packageJSON: IProjectSlot; -} +// export interface IProjectSlots { +// components: IProjectSlot; +// pages: IProjectSlot; +// router: IProjectSlot; +// entry: IProjectSlot; +// constants?: IProjectSlot; +// utils?: IProjectSlot; +// i18n?: IProjectSlot; +// globalStyle: IProjectSlot; +// htmlEntry: IProjectSlot; +// packageJSON: IProjectSlot; +// } export interface IProjectPlugins { - components: BuilderComponentPlugin[]; - pages: BuilderComponentPlugin[]; - router: BuilderComponentPlugin[]; - entry: BuilderComponentPlugin[]; - constants?: BuilderComponentPlugin[]; - utils?: BuilderComponentPlugin[]; - i18n?: BuilderComponentPlugin[]; - globalStyle: BuilderComponentPlugin[]; - htmlEntry: BuilderComponentPlugin[]; - packageJSON: BuilderComponentPlugin[]; + [slotName: string]: BuilderComponentPlugin[]; } export interface IProjectBuilder { @@ -153,3 +146,25 @@ export type PostProcessor = (content: string, fileType: string) => string; export interface IPluginOptions { fileDirDepth: number; } + +export enum PIECE_TYPE { + BEFORE = 'NodeCodePieceBefore', + TAG = 'NodeCodePieceTag', + ATTR = 'NodeCodePieceAttr', + CHILDREN = 'NodeCodePieceChildren', + AFTER = 'NodeCodePieceAfter', +}; + +export interface CodePiece { + value: string; + type: PIECE_TYPE; +} + +export interface HandlerSet { + string?: (input: string) => T[]; + expression?: (input: IJSExpression) => T[]; + node?: (input: IComponentNodeItem) => T[]; + common?: (input: unknown) => T[]; +} + +export type ExtGeneratorPlugin = (nodeItem: IComponentNodeItem) => CodePiece[]; diff --git a/packages/code-generator/src/types/intermediate.ts b/packages/code-generator/src/types/intermediate.ts index 7ac66079a..54142dfa0 100644 --- a/packages/code-generator/src/types/intermediate.ts +++ b/packages/code-generator/src/types/intermediate.ts @@ -17,8 +17,8 @@ export interface IParseResult { } export interface IContainerInfo extends IContainerNodeItem, IWithDependency { - componentName: string; containerType: string; + moduleName: string; } export interface IWithDependency { diff --git a/packages/code-generator/src/types/schema.ts b/packages/code-generator/src/types/schema.ts index 343e6442d..aa6e9459c 100644 --- a/packages/code-generator/src/types/schema.ts +++ b/packages/code-generator/src/types/schema.ts @@ -10,6 +10,7 @@ import { IExternalDependency } from './index'; export interface IJSExpression { type: 'JSExpression'; value: string; + [extConfigName: string]: any; } // JSON 基本类型 @@ -124,7 +125,7 @@ export interface IComponentNodeItem { * @extends {IComponentNodeItem} */ export interface IContainerNodeItem extends IComponentNodeItem { - componentName: string; // 'Page' | 'Block' | 'Component' 组件类型 必填、首字母大写 + componentName: 'Page' | 'Block' | 'Component'; // 'Page' | 'Block' | 'Component' 组件类型 必填、首字母大写 fileName: string; // 文件名称 必填、英文 defaultProps?: { [propName: string]: any; // 业务属性 @@ -166,6 +167,7 @@ export interface IDataSource { * 支持返回一个Promise,通过resolve(返回数据),常用于串型发送请求场景,配合this.dataSourceMap[oneRequest.id].load()使用; */ dataHandler?: IJSExpression; + [extConfigName: string]: any; } /** @@ -177,7 +179,7 @@ export interface IDataSource { export interface IDataSourceConfig { id: string; // 数据请求ID标识 isInit: boolean; // 是否为初始数据 支持表达式 值为true时,将在组件初始化渲染时自动发送当前数据请求 - type: 'fetch' | 'mtop' | 'jsonp' | 'custom' | 'doServer'; // 数据请求类型 + type: string; // 数据请求类型 'fetch' | 'mtop' | 'jsonp' | 'custom' requestHandler?: IJSExpression; // 自定义扩展的外部请求处理器 仅type='custom'时生效 options?: IFetchOptions; // 请求参数配置 每种请求类型对应不同参数 dataHandler?: IJSExpression; // 数据结果处理函数,形如:(data, err) => Object @@ -190,7 +192,7 @@ export interface IDataSourceConfig { * @interface IFetchOptions */ export interface IFetchOptions { - uri: string; // 请求地址 支持表达式 + url: string; // 请求地址 支持表达式 params?: { // 请求参数 [key: string]: any; @@ -202,6 +204,7 @@ export interface IFetchOptions { // 自定义请求头 [key: string]: string; }; + [extConfigName: string]: any; } export interface IBasicMeta { @@ -245,4 +248,5 @@ export interface IAppMeta { description?: string; // 应用描述 spma?: string; // 应用spma A位信息 creator?: string; // author + [otherAttrName: string]: any; } diff --git a/packages/code-generator/src/utils/children.ts b/packages/code-generator/src/utils/children.ts deleted file mode 100644 index 2f2d34fda..000000000 --- a/packages/code-generator/src/utils/children.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - ChildNodeItem, - ChildNodeType, - IComponentNodeItem, - IJSExpression, -} from '../types'; - -// tslint:disable-next-line: no-empty -const noop = () => []; - -export function handleChildren( - children: ChildNodeType, - handlers: { - string?: (input: string) => T[]; - expression?: (input: IJSExpression) => T[]; - node?: (input: IComponentNodeItem) => T[]; - common?: (input: unknown) => T[]; - }, -): T[] { - if (Array.isArray(children)) { - const list: ChildNodeItem[] = children as ChildNodeItem[]; - return list - .map(child => handleChildren(child, handlers)) - .reduce((p, c) => p.concat(c), []); - } else if (typeof children === 'string') { - const handler = handlers.string || handlers.common || noop; - return handler(children as string); - } else if ((children as IJSExpression).type === 'JSExpression') { - const handler = handlers.expression || handlers.common || noop; - return handler(children as IJSExpression); - } else { - const handler = handlers.node || handlers.common || noop; - return handler(children as IComponentNodeItem); - } -} diff --git a/packages/code-generator/src/utils/compositeType.ts b/packages/code-generator/src/utils/compositeType.ts new file mode 100644 index 000000000..b62b2afe3 --- /dev/null +++ b/packages/code-generator/src/utils/compositeType.ts @@ -0,0 +1,88 @@ +import { CompositeArray, CompositeValue, ICompositeObject } from '../types'; +import { generateExpression, isJsExpression } from './jsExpression'; + +type CustomHandler = (data: unknown) => string; +interface CustomHandlerSet { + boolean?: CustomHandler; + number?: CustomHandler; + string?: CustomHandler; + array?: CustomHandler; + object?: CustomHandler; + expression?: CustomHandler; +} + +function generateArray( + value: CompositeArray, + handlers: CustomHandlerSet = {}, +): string { + const body = value.map(v => generateUnknownType(v, handlers)).join(','); + return `[${body}]`; +} + +function generateObject( + value: ICompositeObject, + handlers: CustomHandlerSet = {}, +): string { + if (isJsExpression(value)) { + if (handlers.expression) { + return handlers.expression(value); + } + return generateExpression(value); + } + + const body = Object.keys(value) + .map(key => { + const v = generateUnknownType(value[key], handlers); + return `${key}: ${v}`; + }) + .join(',\n'); + + return `{${body}}`; +} + +export function generateUnknownType( + value: CompositeValue, + handlers: CustomHandlerSet = {}, +): string { + if (Array.isArray(value)) { + if (handlers.array) { + return handlers.array(value); + } + return generateArray(value as CompositeArray, handlers); + } else if (typeof value === 'object') { + if (handlers.object) { + return handlers.object(value); + } + return generateObject(value as ICompositeObject, handlers); + } else if (typeof value === 'string') { + if (handlers.string) { + return handlers.string(value); + } + return `'${value}'`; + } else if (typeof value === 'number' && handlers.number) { + return handlers.number(value); + } else if (typeof value === 'boolean' && handlers.boolean) { + return handlers.boolean(value); + } + return `${value}`; +} + +export function generateCompositeType( + value: CompositeValue, + handlers: CustomHandlerSet = {}, +): [boolean, string] { + const result = generateUnknownType(value, handlers); + + if (result.substr(0, 1) === "'" && result.substr(-1, 1) === "'") { + return [true, result.substring(1, result.length - 1)]; + } + + return [false, result]; +} + +export function handleStringValueDefault([isString, result]: [boolean, string]) { + if (isString) { + return `'${result}'`; + } + return result; +} diff --git a/packages/code-generator/src/utils/jsExpression.ts b/packages/code-generator/src/utils/jsExpression.ts new file mode 100644 index 000000000..9c8541692 --- /dev/null +++ b/packages/code-generator/src/utils/jsExpression.ts @@ -0,0 +1,85 @@ +import traverse from '@babel/traverse'; +import * as parser from '@babel/parser'; +import { CodeGeneratorError, IJSExpression } from '../types'; + +let count = 0; + +function test(functionBody: string) { + console.log(functionBody); + console.log('---->'); + try { + const parseResult = parser.parse(functionBody); + // console.log(JSON.stringify(parseResult)); + traverse(parseResult, { + enter(path) { + console.log('path: ', JSON.stringify(path)); + } + }); + + if (count === 0) { + count++; + + test('this.aaa && this.bbb'); + } + } catch (error) { + // console.log('Error'); + console.log(error.message); + } + console.log('====================='); +} + +export function transformFuncExpr2MethodMember( + methodName: string, + functionBody: string, +): string { + // test(functionBody); + + const args = getFuncExprArguments(functionBody); + const body = getFuncExprBody(functionBody); + + return `${methodName}(${args}) { ${body} }`; +} + +export function getFuncExprArguments(functionBody: string) { + const start = functionBody.indexOf('('); + const end = functionBody.indexOf(')'); + + if (start < 0 || end < 0 || end < start) { + throw new CodeGeneratorError('JSExpression has no valid arguments.'); + } + + const args = functionBody.slice(start + 1, end); + return args; +} + +export function getFuncExprBody(functionBody: string) { + const start = functionBody.indexOf('{'); + const end = functionBody.lastIndexOf('}'); + + // test(functionBody); + + if (start < 0 || end < 0 || end < start) { + throw new CodeGeneratorError('JSExpression has no valid body.'); + } + + const body = functionBody.slice(start + 1, end); + return body; +} + +export function generateExpression(value: any): string { + if (value && (value as IJSExpression).type === 'JSExpression') { + // test((value as IJSExpression).value); + + return (value as IJSExpression).value || 'null'; + } + + throw new CodeGeneratorError('Not a JSExpression'); +} + +export function isJsExpression(value: any): boolean { + return ( + value && + typeof value === 'object' && + (value as IJSExpression).type === 'JSExpression' + ); +} diff --git a/packages/code-generator/src/utils/nodeToJSX.ts b/packages/code-generator/src/utils/nodeToJSX.ts new file mode 100644 index 000000000..82673f851 --- /dev/null +++ b/packages/code-generator/src/utils/nodeToJSX.ts @@ -0,0 +1,221 @@ +import { + ChildNodeType, + IComponentNodeItem, + IInlineStyle, + IJSExpression, + ChildNodeItem, + CodeGeneratorError, + PIECE_TYPE, + CodePiece, + HandlerSet, + ExtGeneratorPlugin, +} from '../types'; +import { generateCompositeType } from './compositeType'; +import { generateExpression } from './jsExpression'; + +// tslint:disable-next-line: no-empty +const noop = () => []; + +export function handleChildren( + children: ChildNodeType, + handlers: HandlerSet, +): T[] { + if (Array.isArray(children)) { + const list: ChildNodeItem[] = children as ChildNodeItem[]; + return list + .map(child => handleChildren(child, handlers)) + .reduce((p, c) => p.concat(c), []); + } else if (typeof children === 'string') { + const handler = handlers.string || handlers.common || noop; + return handler(children as string); + } else if ((children as IJSExpression).type === 'JSExpression') { + const handler = handlers.expression || handlers.common || noop; + return handler(children as IJSExpression); + } else { + const handler = handlers.node || handlers.common || noop; + return handler(children as IComponentNodeItem); + } +} + +export function generateInlineStyle(style: IInlineStyle): string | null { + const attrLines = Object.keys(style).map((cssAttribute: string) => { + const [isString, valueStr] = generateCompositeType(style[cssAttribute]); + const valuePart = isString ? `'${valueStr}'` : valueStr; + return `${cssAttribute}: ${valuePart},`; + }); + + if (attrLines.length === 0) { + return null; + } + + return `{ ${attrLines.join('')} }`; +} + +export function generateAttr(attrName: string, attrValue: any): CodePiece[] { + if (attrName === 'initValue' || attrName === 'labelCol') { + return []; + } + const [isString, valueStr] = generateCompositeType(attrValue); + return [{ + value: `${attrName}=${isString ? `"${valueStr}"` : `{${valueStr}}`}`, + type: PIECE_TYPE.ATTR, + }]; +} + +export function generateAttrs(nodeItem: IComponentNodeItem): CodePiece[] { + const { className, style, ...props } = nodeItem.props; + let pieces: CodePiece[] = []; + if (className) { + pieces.push({ + value: `className="${className}"`, + type: PIECE_TYPE.ATTR, + }); + } + if (style) { + const inlineStyle = generateInlineStyle(style); + if (inlineStyle !== null) { + pieces.push({ + value: `style={${inlineStyle}}`, + type: PIECE_TYPE.ATTR, + }); + } + } + + Object.keys(props).forEach((propName: string) => + pieces = pieces.concat(generateAttr(propName, props[propName])), + ); + + return pieces; +} + +export function mapNodeName(src: string): string { + if (src === 'Div') { + return 'div'; + } + return src; +} + +export function generateBasicNode(nodeItem: IComponentNodeItem): CodePiece[] { + const pieces: CodePiece[] = []; + pieces.push({ + value: mapNodeName(nodeItem.componentName), + type: PIECE_TYPE.TAG, + }); + + return pieces; +} + +export function generateReactCtrlLine(nodeItem: IComponentNodeItem): CodePiece[] { + const pieces: CodePiece[] = []; + + if (nodeItem.loop && nodeItem.loopArgs) { + let loopDataExp; + if ((nodeItem.loop as IJSExpression).type === 'JSExpression') { + loopDataExp = `(${(nodeItem.loop as IJSExpression).value})`; + } else { + loopDataExp = JSON.stringify(nodeItem.loop); + } + pieces.unshift({ + value: `${loopDataExp}.map((${nodeItem.loopArgs[0]}, ${nodeItem.loopArgs[1]}) => (`, + type: PIECE_TYPE.BEFORE, + }); + pieces.push({ + value: '))', + type: PIECE_TYPE.AFTER, + }); + } + + if (nodeItem.condition) { + pieces.unshift({ + value: `(${generateCompositeType(nodeItem.condition)}) && (`, + type: PIECE_TYPE.BEFORE, + }); + pieces.push({ + value: ')', + type: PIECE_TYPE.AFTER, + }); + } + + if (nodeItem.condition || (nodeItem.loop && nodeItem.loopArgs)) { + pieces.unshift({ + value: '{', + type: PIECE_TYPE.BEFORE, + }); + pieces.push({ + value: '}', + type: PIECE_TYPE.AFTER, + }); + } + + return pieces; +} + +export function linkPieces(pieces: CodePiece[]): string { + if (pieces.filter(p => p.type === PIECE_TYPE.TAG).length !== 1) { + throw new CodeGeneratorError('One node only need one tag define'); + } + const tagName = pieces.filter(p => p.type === PIECE_TYPE.TAG)[0].value; + + const beforeParts = pieces + .filter(p => p.type === PIECE_TYPE.BEFORE) + .map(p => p.value) + .join(''); + + const afterParts = pieces + .filter(p => p.type === PIECE_TYPE.AFTER) + .map(p => p.value) + .join(''); + + const childrenParts = pieces + .filter(p => p.type === PIECE_TYPE.CHILDREN) + .map(p => p.value) + .join(''); + + let attrsParts = pieces + .filter(p => p.type === PIECE_TYPE.ATTR) + .map(p => p.value) + .join(' '); + + attrsParts = !!attrsParts ? ` ${attrsParts}` : ''; + + if (childrenParts) { + return `${beforeParts}<${tagName}${attrsParts}>${childrenParts}${afterParts}`; + } + + return `${beforeParts}<${tagName}${attrsParts} />${afterParts}`; +} + +export function createNodeGenerator(handlers: HandlerSet, plugins: ExtGeneratorPlugin[]) { + const generateNode = (nodeItem: IComponentNodeItem): string => { + let pieces: CodePiece[] = []; + + plugins.forEach(p => { + pieces = pieces.concat(p(nodeItem)); + }); + pieces = pieces.concat(generateBasicNode(nodeItem)); + pieces = pieces.concat(generateAttrs(nodeItem)); + if (nodeItem.children && (nodeItem.children as unknown[]).length > 0) { + pieces = pieces.concat(handleChildren(nodeItem.children, handlers).map(l => ({ + type: PIECE_TYPE.CHILDREN, + value: l, + }))); + } + + return linkPieces(pieces); + }; + + handlers.node = (input: IComponentNodeItem) => [generateNode(input)]; + + return generateNode; +} + +export const generateString = (input: string) => [input]; + +export function createReactNodeGenerator() { + return createNodeGenerator({ + string: generateString, + expression: (input) => [generateExpression(input)], + }, [ + generateReactCtrlLine, + ]); +} diff --git a/packages/code-generator/src/utils/templateHelper.ts b/packages/code-generator/src/utils/templateHelper.ts new file mode 100644 index 000000000..f1692ad67 --- /dev/null +++ b/packages/code-generator/src/utils/templateHelper.ts @@ -0,0 +1,25 @@ +import { IResultFile, IResultDir } from '../types'; +import ResultDir from '../model/ResultDir'; + +type FuncFileGenerator = () => [string[], IResultFile]; + +export function insertFile(root: IResultDir, path: string[], file: IResultFile) { + let current: IResultDir = root; + path.forEach(pathNode => { + const dir = current.dirs.find(d => d.name === pathNode); + if (dir) { + current = dir; + } else { + const newDir = new ResultDir(pathNode); + current.addDirectory(newDir); + current = newDir; + } + }); + + current.addFile(file); +} + +export function runFileGenerator(root: IResultDir, fun: FuncFileGenerator) { + const [path, file] = fun(); + insertFile(root, path, file); +} From 292c1c38d35aae6b0888537eb4f576000d30f8c3 Mon Sep 17 00:00:00 2001 From: "mario.gk" Date: Tue, 23 Jun 2020 12:00:04 +0800 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20=E5=8C=BA=E5=9D=97=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B9=8B=E5=90=8E=E6=95=B0=E6=8D=AE=E4=B8=8D?= =?UTF-8?q?=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/editor-skeleton/src/widget/panel.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/editor-skeleton/src/widget/panel.ts b/packages/editor-skeleton/src/widget/panel.ts index 6f9010dc0..263db6ae3 100644 --- a/packages/editor-skeleton/src/widget/panel.ts +++ b/packages/editor-skeleton/src/widget/panel.ts @@ -154,6 +154,9 @@ export default class Panel implements IWidget { } this.emitter.emit('activechange', true); } else if (this.inited) { + if (this.parent?.name && this.name.startsWith(this.parent.name)) { + this.inited = false; + } this._actived = false; this.parent?.unactive(this); this.emitter.emit('activechange', false); From 6cf7c3d3371620390ecf3700ee0cbc351e902107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=A5=E5=B8=8C?= Date: Wed, 1 Jul 2020 10:03:39 +0800 Subject: [PATCH 8/8] feat: rewrite demo & export plugins and utils --- packages/code-generator/demo/demo.js | 53 ++ .../code-generator/demo/sampleSchema.json | 243 ++++++ packages/code-generator/package.json | 3 +- .../code-generator/src/demo/expressionTest.ts | 22 - packages/code-generator/src/demo/main.ts | 59 -- .../code-generator/src/demo/recoreDemo.ts | 815 ------------------ .../code-generator/src/demo/recoreDemo1.ts | 165 ---- .../code-generator/src/demo/simpleDemo.ts | 243 ------ .../src/generator/ModuleBuilder.ts | 21 + .../src/generator/ProjectBuilder.ts | 4 +- packages/code-generator/src/index.ts | 59 ++ .../code-generator/src/parser/SchemaParser.ts | 13 +- .../src/plugins/common/esmodule.ts | 2 +- .../component/react/containerDataSource.ts | 2 +- .../component/react/containerInitState.ts | 2 +- .../component/react/containerInjectUtils.ts | 2 +- .../component/react/containerLifeCycle.ts | 2 +- .../component/react/containerMethod.ts | 2 +- .../src/plugins/component/react/jsx.ts | 2 +- .../component/recore/pageDataSource.ts | 8 +- .../src/plugins/component/style/css.ts | 2 +- packages/code-generator/src/types/core.ts | 19 +- packages/code-generator/src/types/schema.ts | 40 +- .../code-generator/src/utils/nodeToJSX.ts | 32 +- 24 files changed, 421 insertions(+), 1394 deletions(-) create mode 100644 packages/code-generator/demo/demo.js create mode 100644 packages/code-generator/demo/sampleSchema.json delete mode 100644 packages/code-generator/src/demo/expressionTest.ts delete mode 100644 packages/code-generator/src/demo/main.ts delete mode 100644 packages/code-generator/src/demo/recoreDemo.ts delete mode 100644 packages/code-generator/src/demo/recoreDemo1.ts delete mode 100644 packages/code-generator/src/demo/simpleDemo.ts diff --git a/packages/code-generator/demo/demo.js b/packages/code-generator/demo/demo.js new file mode 100644 index 000000000..91e145de9 --- /dev/null +++ b/packages/code-generator/demo/demo.js @@ -0,0 +1,53 @@ +const fs = require('fs'); +const CodeGenerator = require('../lib').default; + +function flatFiles(rootName, dir) { + const dirRoot = rootName ? `${rootName}/${dir.name}` : dir.name; + const files = dir.files.map(file => ({ + name: `${dirRoot}/${file.name}.${file.ext}`, + content: file.content, + ext: '', + })); + const filesInSub = dir.dirs.map(subDir => flatFiles(`${dirRoot}`, subDir)); + const result = files.concat.apply(files, filesInSub); + + return result; +} + +function displayResultInConsole(root, fileName) { + const files = flatFiles('.', root); + files.forEach(file => { + if (!fileName || fileName === file.name) { + console.log(`========== ${file.name} Start ==========`); + console.log(file.content); + console.log(`========== ${file.name} End ==========`); + } + }); +} + +async function writeResultToDisk(root, path) { + const publisher = CodeGenerator.publishers.disk(); + + return publisher.publish({ + project: root, + outputPath: path, + projectSlug: 'demo-project', + createProjectFolder: true, + }); +} + +function main() { + const schemaJson = fs.readFileSync('./demo/sampleSchema.json', { encoding: 'utf8' }); + const createIceJsProjectBuilder = CodeGenerator.solutions.icejs; + const builder = createIceJsProjectBuilder(); + + builder.generateProject(schemaJson).then(result => { + displayResultInConsole(result); + writeResultToDisk(result, 'output/lowcodeDemo').then(response => + console.log('Write to disk: ', JSON.stringify(response)), + ); + return result; + }); +} + +main(); diff --git a/packages/code-generator/demo/sampleSchema.json b/packages/code-generator/demo/sampleSchema.json new file mode 100644 index 000000000..079289fac --- /dev/null +++ b/packages/code-generator/demo/sampleSchema.json @@ -0,0 +1,243 @@ +{ + "version": "1.0.0", + "componentsMap": [ + { + "componentName": "Button", + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Button" + }, + { + "componentName": "Button.Group", + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Button", + "subName": "Group" + }, + { + "componentName": "Input", + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Input" + }, + { + "componentName": "Form", + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Form" + }, + { + "componentName": "Form.Item", + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Form", + "subName": "Item" + }, + { + "componentName": "NumberPicker", + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "NumberPicker" + }, + { + "componentName": "Select", + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Select" + } + ], + "componentsTree": [ + { + "componentName": "Page", + "id": "node$1", + "meta": { + "title": "测试", + "router": "/" + }, + "props": { + "ref": "outterView", + "autoLoading": true + }, + "fileName": "test", + "state": { + "text": "outter" + }, + "lifeCycles": { + "componentDidMount": { + "type": "JSExpression", + "value": "function() { this.utils.request(this.props.url); }" + } + }, + "children": [ + { + "componentName": "Form", + "id": "node$2", + "props": { + "labelCol": { + "type": "JSExpression", + "value": "this.state.colNum" + }, + "style": {}, + "ref": "testForm" + }, + "children": [ + { + "componentName": "Form.Item", + "id": "node$3", + "props": { + "label": "姓名:", + "name": "name", + "initValue": "李雷" + }, + "children": [ + { + "componentName": "Input", + "id": "node$4", + "props": { + "placeholder": "请输入", + "size": "medium", + "style": { + "width": 320 + } + } + } + ] + }, + { + "componentName": "Form.Item", + "id": "node$5", + "props": { + "label": "年龄:", + "name": "age", + "initValue": "22" + }, + "children": [ + { + "componentName": "NumberPicker", + "id": "node$6", + "props": { + "size": "medium", + "type": "normal" + } + } + ] + }, + { + "componentName": "Form.Item", + "id": "node$7", + "props": { + "label": "职业:", + "name": "profession" + }, + "children": [ + { + "componentName": "Select", + "id": "node$8", + "props": { + "dataSource": [ + { + "label": "教师", + "value": "t" + }, + { + "label": "医生", + "value": "d" + }, + { + "label": "歌手", + "value": "s" + } + ] + } + } + ] + }, + { + "componentName": "Div", + "id": "node$9", + "props": { + "style": { + "textAlign": "center" + } + }, + "children": [ + { + "componentName": "Button.Group", + "id": "node$a", + "props": {}, + "children": [ + { + "componentName": "Button", + "id": "node$b", + "props": { + "type": "primary", + "style": { + "margin": "0 5px 0 5px" + }, + "htmlType": "submit" + }, + "children": [ + "提交" + ] + }, + { + "componentName": "Button", + "id": "node$d", + "props": { + "type": "normal", + "style": { + "margin": "0 5px 0 5px" + }, + "htmlType": "reset" + }, + "children": [ + "重置" + ] + } + ] + } + ] + } + ] + } + ] + } + ], + "constants": { + "ENV": "prod", + "DOMAIN": "xxx.alibaba-inc.com" + }, + "css": "body {font-size: 12px;} .table { width: 100px;}", + "config": { + "sdkVersion": "1.0.3", + "historyMode": "hash", + "targetRootID": "J_Container", + "layout": { + "componentName": "BasicLayout", + "props": { + "logo": "...", + "name": "测试网站" + } + }, + "theme": { + "package": "@alife/theme-fusion", + "version": "^0.1.0", + "primary": "#ff9966" + } + }, + "meta": { + "name": "demo应用", + "git_group": "appGroup", + "project_name": "app_demo", + "description": "这是一个测试应用", + "spma": "spa23d", + "creator": "月飞" + } +} diff --git a/packages/code-generator/package.json b/packages/code-generator/package.json index 808cf1ced..b450e4de6 100644 --- a/packages/code-generator/package.json +++ b/packages/code-generator/package.json @@ -8,8 +8,7 @@ ], "scripts": { "build": "rimraf lib && tsc", - "demo": "ts-node -r tsconfig-paths/register ./src/demo/main.ts", - "demo:exp": "ts-node -r tsconfig-paths/register ./src/demo/expressionTest.ts", + "demo": "node ./demo/demo.js", "test": "ava", "template": "node ./tools/createTemplate.js" }, diff --git a/packages/code-generator/src/demo/expressionTest.ts b/packages/code-generator/src/demo/expressionTest.ts deleted file mode 100644 index e6d8831ad..000000000 --- a/packages/code-generator/src/demo/expressionTest.ts +++ /dev/null @@ -1,22 +0,0 @@ -import traverse from '@babel/traverse'; -import * as parser from '@babel/parser'; - -function test(functionBody: string) { - console.log(functionBody); - console.log('---->'); - try { - const parseResult = parser.parse(functionBody); - console.log(JSON.stringify(parseResult)); - traverse(parseResult, { - enter(path) { - console.log('path: ', path.type, path); - } - }); - } catch (error) { - console.log('Error'); - console.log(error.message); - } - console.log('====================='); -} - -test('function main() { state.fff = 1; }'); diff --git a/packages/code-generator/src/demo/main.ts b/packages/code-generator/src/demo/main.ts deleted file mode 100644 index a0705970b..000000000 --- a/packages/code-generator/src/demo/main.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { IResultDir, IResultFile } from '../types'; - -import codeGenerator from '../index'; -import { createDiskPublisher } from '../publisher/disk'; -// import demoSchema from './simpleDemo'; -import demoSchema from './recoreDemo'; - -function flatFiles(rootName: string | null, dir: IResultDir): IResultFile[] { - const dirRoot: string = rootName ? `${rootName}/${dir.name}` : dir.name; - const files: IResultFile[] = dir.files.map(file => ({ - name: `${dirRoot}/${file.name}.${file.ext}`, - content: file.content, - ext: '', - })); - const filesInSub = dir.dirs.map(subDir => flatFiles(`${dirRoot}`, subDir)); - const result: IResultFile[] = files.concat.apply(files, filesInSub); - - return result; -} - -function displayResultInConsole(root: IResultDir, fileName?: string): void { - const files = flatFiles('.', root); - files.forEach(file => { - if (!fileName || fileName === file.name) { - console.log(`========== ${file.name} Start ==========`); - console.log(file.content); - console.log(`========== ${file.name} End ==========`); - } - }); -} - -async function writeResultToDisk(root: IResultDir, path: string): Promise { - const publisher = createDiskPublisher(); - - return publisher.publish({ - project: root, - outputPath: path, - projectSlug: 'demo-project', - createProjectFolder: true, - }); -} - -function main() { - // const createIceJsProjectBuilder = codeGenerator.solutions.icejs; - // const builder = createIceJsProjectBuilder(); - - const createRecoreProjectBuilder = codeGenerator.solutions.recore; - const builder = createRecoreProjectBuilder(); - - builder.generateProject(demoSchema).then(result => { - displayResultInConsole(result); - // writeResultToDisk(result, '/Users/armslave/lowcodeDemo').then(response => - // console.log('Write to disk: ', JSON.stringify(response)), - // ); - return result; - }); -} - -main(); diff --git a/packages/code-generator/src/demo/recoreDemo.ts b/packages/code-generator/src/demo/recoreDemo.ts deleted file mode 100644 index 29e16a12b..000000000 --- a/packages/code-generator/src/demo/recoreDemo.ts +++ /dev/null @@ -1,815 +0,0 @@ -import { IProjectSchema } from '../types'; - -const demoData: IProjectSchema = { - version: '1.0.0', - componentsMap: [ - { - componentName: 'Button', - 'package': 'alife/next', - version: '1.0.0', - destructuring: true, - exportName: 'Select', - subName: 'Button', - }, - ], - componentsTree: [ - { - condition: true, - css: 'body{background-color:#f2f3f5}', - children: [ - { - condition: true, - children: [ - { - condition: true, - children: [ - { - condition: true, - componentName: 'TextField', - id: 'node_k1nd576k', - props: { - fieldName: 'name', - hasClear: false, - autoFocus: false, - tips: { - en_US: '', - zh_CN: '', - type: 'JSExpression', - value: 'this.utils.getLocale() === \'en_US\' ? \'\' : \'\'', - extType: 'i18n', - }, - trim: false, - labelTextAlign: 'right', - placeholder: { - use: 'zh_CN', - en_US: 'please input', - zh_CN: '请输入', - type: 'JSExpression', - value: '"请输入"', - extType: 'i18n', - }, - state: '', - behavior: 'NORMAL', - value: { - use: 'zh_CN', - zh_CN: '', - type: 'JSExpression', - value: '""', - extType: 'i18n', - }, - addonBefore: { - use: 'zh_CN', - zh_CN: '', - type: 'JSExpression', - value: '""', - extType: 'i18n', - }, - validation: [ - { - type: 'required', - }, - ], - hasLimitHint: false, - cutString: false, - __style__: {}, - fieldId: 'textField_k1nd576w', - htmlType: 'input', - autoHeight: false, - labelColOffset: 0, - label: { - use: 'zh_CN', - en_US: 'TextField', - zh_CN: '活动名称', - type: 'JSExpression', - value: '"活动名称"', - extType: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - rows: 4, - addonAfter: { - use: 'zh_CN', - zh_CN: '', - type: 'JSExpression', - value: '""', - extType: 'i18n', - }, - wrapperColOffset: 0, - size: 'medium', - labelAlign: 'top', - }, - }, - { - condition: true, - componentName: 'SelectField', - id: 'node_k1nd576l', - props: { - fieldName: 'type', - hasClear: false, - tips: { - en_US: '', - zh_CN: '', - type: 'JSExpression', - value: 'this.utils.getLocale() === \'en_US\' ? \'\' : \'\'', - extType: 'i18n', - }, - mode: 'single', - showSearch: false, - autoWidth: true, - labelTextAlign: 'right', - placeholder: { - use: 'zh_CN', - en_US: 'please select', - zh_CN: '请选择', - type: 'JSExpression', - value: '"请选择"', - extType: 'i18n', - }, - hasBorder: true, - behavior: 'NORMAL', - value: '', - validation: [ - { - type: 'required', - }, - ], - __style__: {}, - fieldId: 'select_k1nd576x', - notFoundContent: { - use: 'zh_CN', - type: 'JSExpression', - value: 'null', - extType: 'i18n', - }, - labelColOffset: 0, - label: { - use: 'zh_CN', - en_US: 'SelectField', - zh_CN: '活动类型', - type: 'JSExpression', - value: '"活动类型"', - extType: 'i18n', - }, - __category__: 'form', - labelColSpan: 4, - wrapperColSpan: 0, - wrapperColOffset: 0, - hasSelectAll: false, - hasArrow: true, - size: 'medium', - labelAlign: 'top', - filterLocal: true, - dataSource: [ - { - defaultChecked: false, - text: { - en_US: 'Option 1', - zh_CN: '虚拟', - type: 'JSExpression', - __sid__: 'param_k1nd5lrz', - value: 'this.utils.getLocale() === \'en_US\' ? \'Option 1\' : \'虚拟\'', - extType: 'i18n', - }, - __sid__: 'serial_k1nd576v', - value: '虚拟', - sid: 'opt_k1ofrjfz', - }, - { - defaultChecked: false, - text: { - en_US: 'Option 2', - zh_CN: '营销', - type: 'JSExpression', - __sid__: 'param_k1nd5ls3', - value: 'this.utils.getLocale() === \'en_US\' ? \'Option 2\' : \'营销\'', - extType: 'i18n', - }, - __sid__: 'serial_k1nd576w', - value: '营销', - sid: 'opt_k1ofrjg0', - }, - { - defaultChecked: false, - text: { - en_US: 'Option 3', - zh_CN: '线下', - type: 'JSExpression', - __sid__: 'param_k1nd5ls7', - value: 'this.utils.getLocale() === \'en_US\' ? \'Option 3\' : \'线下\'', - extType: 'i18n', - }, - __sid__: 'serial_k1nd576x', - value: '线下', - sid: 'opt_k1ofrjg1', - }, - ], - }, - }, - ], - componentName: 'Form', - id: 'node_k1nd576j', - props: { - size: 'medium', - labelAlign: 'top', - autoValidate: true, - scrollToFirstError: true, - autoUnmount: true, - behavior: 'NORMAL', - dataSource: { - type: 'JSExpression', - value: 'state.formData', - extType: 'variable', - }, - __style__: {}, - fieldId: 'form', - }, - }, - ], - componentName: 'Dialog', - id: 'node_k1nd576i', - props: { - hasMask: true, - closeable: 'esc', - visible: false, - footer: true, - footerActions: 'ok,cancel', - footerAlign: 'right', - title: { - rawValue: { - use: 'zh_CN', - zh_CN: '新增活动', - type: 'JSExpression', - value: '"新增活动"', - extType: 'i18n', - }, - type: 'JSExpression', - value: '(state.formData ? \'编辑\' : \'新增\') + \'活动\'', - extType: 'variable', - }, - fieldId: 'dialog', - onOk: { - rawType: 'events', - type: 'JSExpression', - value: 'this.utils.legaoBuiltin.execEventFlow.bind(this, [this.submit])', - events: [ - { - name: 'submit', - id: 'submit', - params: {}, - type: 'actionRef', - uuid: '1570937968144_0', - }, - ], - }, - }, - }, - { - condition: true, - children: [ - { - condition: true, - children: [], - componentName: 'PageHeader', - id: 'node_k1nd576c', - props: { - __slot__title: true, - extraContent: '', - __slot__extraContent: false, - __slot__action: false, - title: { - type: 'JSBlock', - value: { - condition: true, - children: [ - { - condition: true, - componentName: 'Text', - id: 'node_k1nd576e', - props: { - showTitle: false, - behavior: 'NORMAL', - content: { - use: 'zh_CN', - en_US: 'Title', - zh_CN: '基础表格', - type: 'JSExpression', - value: '"基础表格"', - extType: 'i18n', - }, - __style__: {}, - fieldId: 'text_k1nd576t', - }, - }, - ], - componentName: 'Slot', - id: 'node_k1nd576d', - props: { - slotName: 'title', - slotTitle: '标题区域', - }, - }, - }, - content: '', - __slot__logo: false, - __slot__crumb: false, - crumb: '', - tab: '', - logo: '', - action: '', - __slot__tab: false, - __style__: {}, - __slot__content: false, - fieldId: 'pageHeader_k1nd576s', - }, - }, - ], - componentName: 'RootHeader', - id: 'node_k1hjb043', - props: {}, - }, - { - condition: true, - children: [ - { - condition: true, - componentName: 'TablePc', - id: 'node_k1hjb046', - props: { - actionWidth: 0, - isTree: false, - showJump: true, - data: { - rawValue: { - data: [ - { - contractDate: { - start: 1534942658570, - end: 1534944858570, - }, - entryDate: 1534942658570, - name: '小王', - id: '1', - salary: 35000, - email: 'xw@abc.com', - moneyRange: { - lower: 108, - upper: 944, - }, - }, - { - contractDate: { - start: 1534942658570, - end: 1534944858570, - }, - entryDate: 1534942658570, - name: '小李', - id: '2', - salary: 25000, - email: 'xl@abc.com', - moneyRange: { - lower: 214, - upper: 1077, - }, - }, - ], - currentPage: 1, - totalCount: 2, - }, - type: 'JSExpression', - value: 'state.table_list_basic', - extType: 'variable', - }, - actionTitle: { - use: 'zh_CN', - en_US: 'Action', - zh_CN: '操作', - type: 'JSExpression', - value: '"操作"', - extType: 'i18n', - }, - indent: 16, - columns: [ - { - dataKey: 'name', - hidden: false, - dataType: 'text', - width: '300px', - lock: 'none', - sortable: false, - align: 'left', - title: { - en_US: 'Name', - use: 'zh_CN', - zh_CN: '活动名称', - type: 'JSExpression', - value: '"活动名称"', - extType: 'i18n', - }, - timeFormatter: 'YYYY-MM-DD HH:mm:ss', - }, - { - dataKey: 'type', - hidden: false, - dataType: 'text', - width: '150px', - lock: 'none', - sortable: false, - align: 'left', - title: { - en_US: 'Date', - use: 'zh_CN', - zh_CN: '活动类型', - type: 'JSExpression', - value: '"活动类型"', - extType: 'i18n', - }, - timeFormatter: 'YYYY-MM-DD', - }, - { - dataKey: 'created_at', - hidden: false, - dataType: 'timestamp', - width: 200, - lock: 'none', - sortable: false, - align: 'left', - title: { - en_US: 'contractDate', - use: 'zh_CN', - zh_CN: '创建时间', - type: 'JSExpression', - value: '"创建时间"', - extType: 'i18n', - }, - timeFormatter: 'YYYY-MM-DD', - }, - ], - rowSelector: 'checkboxSelector', - pageSizeSelector: false, - pageSize: 10, - setLoadingComponent: false, - showRowSelector: false, - showLinkBar: true, - searchBarPlaceholder: { - use: 'zh_CN', - en_US: 'Please Input', - zh_CN: '请搜索', - type: 'JSExpression', - value: '"请搜索"', - extType: 'i18n', - }, - type: 'normal', - pageSizePosition: 'end', - pageSizeList: '5,10,20', - fixedHeader: false, - showActionBar: true, - showSearch: true, - stickyHeader: false, - theme: 'split', - setEmptyContent: false, - actionFixed: 'none', - fieldId: 'tablePc_k1hjb4dl', - isPagination: true, - actionColumn: [ - { - pageMode: '', - callback: { - type: 'JSExpression', - value: 'edit.bind(this)', - }, - title: { - en_US: 'Detail', - use: 'zh_CN', - zh_CN: '编辑', - type: 'JSExpression', - value: '"编辑"', - extType: 'i18n', - }, - option: 'callback', - }, - { - pageMode: '', - callback: { - type: 'JSExpression', - value: 'del.bind(this)', - }, - title: { - use: 'zh_CN', - en_US: 'action', - zh_CN: '删除', - type: 'JSExpression', - value: '"删除"', - extType: 'i18n', - }, - option: 'callback', - }, - ], - hasHeader: true, - hideOnlyOnePage: false, - shape: 'arrow-only', - paginationPosition: 'right', - actionBar: [ - { - callback: { - type: 'JSExpression', - value: 'add.bind(this)', - }, - title: { - en_US: 'Action 1', - use: 'zh_CN', - zh_CN: '新增', - type: 'JSExpression', - value: '"新增"', - extType: 'i18n', - }, - option: 'callback', - }, - ], - actionType: 'link', - __router: { - type: 'JSExpression', - value: 'this.utils.router', - }, - linkBar: [], - size: 'medium', - onLoadData: { - rawType: 'events', - type: 'JSExpression', - value: 'this.utils.legaoBuiltin.execEventFlow.bind(this, [this.onLoadData])', - events: [ - { - name: 'onLoadData', - id: 'onLoadData', - params: {}, - type: 'actionRef', - uuid: '1570938442689_4', - }, - ], - }, - actionHidden: false, - dataSourceType: 'data', - primaryKey: 'id', - }, - }, - ], - componentName: 'RootContent', - id: 'node_k1hjb044', - props: { - contentBgColor: 'white', - contentPadding: '20', - }, - }, - { - condition: true, - children: [], - componentName: 'RootFooter', - id: 'node_k1hjb045', - props: {}, - }, - ], - methods: { - __initMethods__: { - type: 'JSExpression', - value: 'function (exports, module) { \'use strict\';\n\nexports.__esModule = true;\nexports.hellGlobal = hellGlobal;\n/**\n* 全局函数,可以被应用内所有页面调用\n*/\nfunction hellGlobal() {\n console.log(\'hello, global\');\n}\'use strict\';\n\nexports.__esModule = true;\nexports.add = add;\nexports.submit = submit;\nexports.onLoadData = onLoadData;\nexports.edit = edit;\nexports.del = del;\n// 点击新增\nfunction add() {\n this.state.formData = null;\n this.$(\'dialog\').show();\n}\n\n/**\n* 点击弹框的“确认”\n*/\nfunction submit() {\n const _this = this;\n\n this.$(\'form\').submit(function (data, error) {\n if (data) {\n _this.dataSourceMap[\'table_submit\'].load(data).then(function (res) {\n _this.utils.toast({\n type: \'success\',\n title: \'提交成功\'\n });\n _this.$(\'dialog\').hide();\n _this.dataSourceMap[\'table_list_basic\'].load();\n });\n }\n });\n}\n\n/**\n* tablePc onLoadData\n* @param currentPage 当前页码\n* @param pageSize 每页显示条数\n* @param searchKey 搜索关键字\n* @param orderColumn 排序列\n* @param orderType 排序方式(desc,asc)\n* @param from 触发来源(order,search,pagination)\n*/\nfunction onLoadData(currentPage, pageSize, searchKey, orderColumn, orderType, from) {\n const tableParams = {\n currentPage: from === \'search\' ? 1 : currentPage,\n pageSize: pageSize,\n searchKey: searchKey,\n orderColumn: orderColumn,\n orderType: orderType\n };\n this.setState({ tableParams: tableParams });\n}\n\n// 点击编辑\nfunction edit(rowData) {\n this.state.formData = rowData;\n this.$(\'dialog\').show();\n}\n\n// 点击删除\nfunction del(rowData) {\n const _this2 = this;\n\n this.utils.dialog({\n method: \'confirm\',\n title: \'提示\',\n content: \'确认删除该条目吗?\',\n onOk: function onOk() {\n _this2.dataSourceMap[\'table_delete\'].load({ id: rowData.id }).then(function () {\n _this2.utils.toast({\n type: \'success\',\n title: \'删除成功\'\n });\n _this2.dataSourceMap[\'table_list_basic\'].load();\n });\n }\n });\n} }', - }, - }, - componentName: 'Page', - fileName: 'page_index', - meta: { - title: '首页', - router: '/', - spmb: 'abef21', - }, - id: 'node_k1hjb042', - state: { - urlParams: { - type: 'JSExpression', - value: 'this.utils.legaoBuiltin.getUrlParams()', - }, - designUrlPrefix: { - type: 'JSExpression', - value: '"https://pre-go.alibaba-inc.com/fusion_developer/0.1.0/design"', - }, - tableParams: { - type: 'JSExpression', - value: '', - }, - }, - dataSource: { - offline: [], - globalConfig: { - fit: { - type: 'JSExpression', - value: '', - extType: 'function', - }, - }, - online: [], - list: [ - { - dataHandler: { - type: 'JSExpression', - value: 'function(data, err) { this.setState({table_list_basic: data}); return data; }', - extType: 'function', - }, - requestHandler: { - type: 'JSExpression', - value: 'this.utils.legaoBuiltin.dataSourceHandler', - }, - options: { - shouldFetch: true, - method: 'GET', - params: { - rawValue: [], - type: 'JSExpression', - value: 'state.tableParams', - extType: 'variable', - }, - isSync: false, - url: 'https://mocks.alibaba-inc.com/mock/legao_template/api/activity/list.json', - }, - id: 'table_list_basic', - type: 'legao', - isInit: true, - }, - { - dataHandler: { - type: 'JSExpression', - value: 'function(data, err) { this.setState({table_submit: data}); return data; }', - extType: 'function', - }, - requestHandler: { - type: 'JSExpression', - value: 'this.utils.legaoBuiltin.dataSourceHandler', - }, - options: { - shouldFetch: true, - method: 'POST', - params: [], - isSync: false, - url: 'https://mocks.alibaba-inc.com/mock/legao_template/api/activity/update.json', - }, - id: 'table_submit', - type: 'legao', - isInit: false, - }, - { - dataHandler: { - type: 'JSExpression', - value: 'function(data, err) { this.setState({table_delete: data}); return data; }', - extType: 'function', - }, - requestHandler: { - type: 'JSExpression', - value: 'this.utils.legaoBuiltin.dataSourceHandler', - }, - options: { - shouldFetch: true, - method: 'POST', - params: [], - isSync: true, - url: 'https://mocks.alibaba-inc.com/mock/legao_template/api/activity/delete.json', - }, - id: 'table_delete', - type: 'legao', - isInit: false, - }, - ], - sync: true, - }, - lifeCycles: { - 'init': { - type: 'JSExpression', - value: 'function constructor() {\nconst module = { exports: {} };\nconst _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === \'function\'){\n _this[item] = module.exports[item];\n }\n});\n\n}', - extType: 'function', - }, - didMount: { - type: 'JSExpression', - value: 'function main(){\n \'use strict\';\n\nconst __compiledFunc__ = function didMount() {\n // 页面节点加载渲染完毕\n console.log(\'这是个 function didMount\', this);\n};\n return __compiledFunc__.apply(this, arguments);\n}', - extType: 'function', - }, - }, - props: { - extensions: { - '启用页头': true, - }, - pageStyle: { - backgroundColor: '#f2f3f5', - }, - containerStyle: {}, - className: 'page_k1v3nkx7', - }, - }, - ], - utils: [ - { - name: 'clone', - type: 'npm', - content: { - 'package': 'lodash', - version: '0.0.1', - exportName: 'clone', - subName: '', - destructuring: false, - main: '/lib/clone', - }, - }, - { - name: 'beforeRequestHandler', - type: 'function', - content: { - type: 'JSExpression', - value: 'function(){\n ... \n}', - }, - }, - ], - constants: { - ENV: 'prod', - DOMAIN: 'xxx.alibaba-inc.com', - }, - css: 'body {font-size: 12px;} .table { width: 100px;}', - config: { - sdkVersion: '1.0.3', - historyMode: 'hash', - targetRootID: 'J_Container', - layout: { - componentName: 'BasicLayout', - props: { - navConfig: { - showLanguageChange: true, - data: [ - { - hidden: false, - navUuid: 'FORM-CP5669B1-3AW9DCLHZAY8EIY6WE6X1-GFZM3V1K-6', - children: [], - icon: '', - targetNew: false, - title: '测试基础表格', - inner: true, - relateUuid: 'FORM-CP5669B1-3AW9DCLHZAY8EIY6WE6X1-GFZM3V1K-6', - slug: 'qihfg', - }, - { - hidden: false, - navUuid: 'FORM-CP5669B1-8AW9XCUT4PCH15SMDWUM3-ZPQP3V1K-1', - children: [], - icon: '', - targetNew: false, - title: '测试查询表格', - inner: true, - relateUuid: 'zqhej', - slug: 'zqhej', - }, - ], - systemLink: '/my_dev_center_code/0.1.0', - appName: '乐高转码测试', - isFoldHorizontal: 'n', - showAppTitle: true, - isFold: 'n', - searchBarType: 'icon', - singletons: {}, - navTheme: 'default', - type: 'top_side_fold', - navStyle: 'orange', - layout: 'auto', - bgColor: 'white', - languageChangeUrl: '/common/account/changeAccountLanguage.json', - showSearch: 'n', - openSubMode: false, - showCrumb: true, - isFixed: 'y', - showIcon: false, - showNav: true, - }, - }, - }, - theme: { - 'package': '@alife/theme-fusion', - version: '^0.1.0', - primary: '#ff9966', - }, - }, - meta: { - name: 'demo应用', - git_group: 'appGroup', - project_name: 'app_demo', - description: '这是一个测试应用', - spma: 'spa23d', - creator: '月飞', - gmt_create: '2020-02-11 00:00:00', - gmt_modified: '2020-02-11 00:00:00', - }, - i18n: { - 'zh-CN': { - 'i18n-jwg27yo4': '你好', - 'i18n-jwg27yo3': '中国', - }, - 'en-US': { - 'i18n-jwg27yo4': 'Hello', - 'i18n-jwg27yo3': 'China', - }, - }, -}; - -export default demoData; diff --git a/packages/code-generator/src/demo/recoreDemo1.ts b/packages/code-generator/src/demo/recoreDemo1.ts deleted file mode 100644 index 1ef52bc0a..000000000 --- a/packages/code-generator/src/demo/recoreDemo1.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { IProjectSchema } from '../types'; - -const demoData: IProjectSchema = { - version: '1.0.0', - componentsMap: [ - { - componentName: 'Button', - 'package': 'alife/next', - version: '1.0.0', - destructuring: true, - exportName: 'Select', - subName: 'Button', - }, - ], - componentsTree: [{ - componentName: 'Page', - id: 'node$1', - props: { - ref: 'outterView', - autoLoading: true, - style: { - padding: 20, - }, - }, - fileName: 'test', - dataSource: { - list: [], - }, - state: { - text: 'outter', - }, - children: [ - { - componentName: 'Button', - id: 'node$l', - props: {}, - }, - { - componentName: 'Button', - id: 'node$n', - props: { - type: 'secondary', - baseIcon: 'set', - }, - }, - { - componentName: 'Calendar', - id: 'node$p', - props: { - shape: 'card', - }, - }, - ], - }], - utils: [ - { - name: 'clone', - type: 'npm', - content: { - 'package': 'lodash', - version: '0.0.1', - exportName: 'clone', - subName: '', - destructuring: false, - main: '/lib/clone', - }, - }, - { - name: 'beforeRequestHandler', - type: 'function', - content: { - type: 'JSExpression', - value: 'function(){\n ... \n}', - }, - }, - ], - constants: { - ENV: 'prod', - DOMAIN: 'xxx.alibaba-inc.com', - }, - css: 'body {font-size: 12px;} .table { width: 100px;}', - config: { - sdkVersion: '1.0.3', - historyMode: 'hash', - targetRootID: 'J_Container', - layout: { - componentName: 'BasicLayout', - props: { - navConfig: { - showLanguageChange: true, - data: [ - { - hidden: false, - navUuid: 'FORM-CP5669B1-3AW9DCLHZAY8EIY6WE6X1-GFZM3V1K-6', - children: [], - icon: '', - targetNew: false, - title: '测试基础表格', - inner: true, - relateUuid: 'FORM-CP5669B1-3AW9DCLHZAY8EIY6WE6X1-GFZM3V1K-6', - slug: 'qihfg', - }, - { - hidden: false, - navUuid: 'FORM-CP5669B1-8AW9XCUT4PCH15SMDWUM3-ZPQP3V1K-1', - children: [], - icon: '', - targetNew: false, - title: '测试查询表格', - inner: true, - relateUuid: 'zqhej', - slug: 'zqhej', - }, - ], - systemLink: '/my_dev_center_code/0.1.0', - appName: '乐高转码测试', - isFoldHorizontal: 'n', - showAppTitle: true, - isFold: 'n', - searchBarType: 'icon', - singletons: {}, - navTheme: 'default', - type: 'top_side_fold', - navStyle: 'orange', - layout: 'auto', - bgColor: 'white', - languageChangeUrl: '/common/account/changeAccountLanguage.json', - showSearch: 'n', - openSubMode: false, - showCrumb: true, - isFixed: 'y', - showIcon: false, - showNav: true, - }, - }, - }, - theme: { - 'package': '@alife/theme-fusion', - version: '^0.1.0', - primary: '#ff9966', - }, - }, - meta: { - name: 'demo应用', - git_group: 'appGroup', - project_name: 'app_demo', - description: '这是一个测试应用', - spma: 'spa23d', - creator: '月飞', - gmt_create: '2020-02-11 00:00:00', - gmt_modified: '2020-02-11 00:00:00', - }, - i18n: { - 'zh-CN': { - 'i18n-jwg27yo4': '你好', - 'i18n-jwg27yo3': '中国', - }, - 'en-US': { - 'i18n-jwg27yo4': 'Hello', - 'i18n-jwg27yo3': 'China', - }, - }, -}; - -export default demoData; diff --git a/packages/code-generator/src/demo/simpleDemo.ts b/packages/code-generator/src/demo/simpleDemo.ts deleted file mode 100644 index 410ca35e3..000000000 --- a/packages/code-generator/src/demo/simpleDemo.ts +++ /dev/null @@ -1,243 +0,0 @@ -import { IProjectSchema } from '../types'; - -const demoData: IProjectSchema = { - version: '1.0.0', - componentsMap: [ - { - componentName: 'Button', - package: '@alifd/next', - version: '1.19.18', - destructuring: true, - exportName: 'Button', - }, - { - componentName: 'Button.Group', - package: '@alifd/next', - version: '1.19.18', - destructuring: true, - exportName: 'Button', - subName: 'Group', - }, - { - componentName: 'Input', - package: '@alifd/next', - version: '1.19.18', - destructuring: true, - exportName: 'Input', - }, - { - componentName: 'Form', - package: '@alifd/next', - version: '1.19.18', - destructuring: true, - exportName: 'Form', - }, - { - componentName: 'Form.Item', - package: '@alifd/next', - version: '1.19.18', - destructuring: true, - exportName: 'Form', - subName: 'Item', - }, - { - componentName: 'NumberPicker', - package: '@alifd/next', - version: '1.19.18', - destructuring: true, - exportName: 'NumberPicker', - }, - { - componentName: 'Select', - package: '@alifd/next', - version: '1.19.18', - destructuring: true, - exportName: 'Select', - }, - ], - componentsTree: [ - { - componentName: 'Page', - id: 'node$1', - meta: { - title: '测试', - router: '/', - }, - props: { - ref: 'outterView', - autoLoading: true, - }, - fileName: 'test', - state: { - text: 'outter', - }, - lifeCycles: { - componentDidMount: { - type: 'JSExpression', - value: 'function() { this.utils.request(this.props.url); }', - }, - }, - children: [ - { - componentName: 'Form', - id: 'node$2', - props: { - labelCol: { - type: 'JSExpression', - value: 'this.state.colNum', - }, - style: {}, - ref: 'testForm', - }, - children: [ - { - componentName: 'Form.Item', - id: 'node$3', - props: { - label: '姓名:', - name: 'name', - initValue: '李雷', - }, - children: [ - { - componentName: 'Input', - id: 'node$4', - props: { - placeholder: '请输入', - size: 'medium', - style: { - width: 320, - }, - }, - }, - ], - }, - { - componentName: 'Form.Item', - id: 'node$5', - props: { - label: '年龄:', - name: 'age', - initValue: '22', - }, - children: [ - { - componentName: 'NumberPicker', - id: 'node$6', - props: { - size: 'medium', - type: 'normal', - }, - }, - ], - }, - { - componentName: 'Form.Item', - id: 'node$7', - props: { - label: '职业:', - name: 'profession', - }, - children: [ - { - componentName: 'Select', - id: 'node$8', - props: { - dataSource: [ - { - label: '教师', - value: 't', - }, - { - label: '医生', - value: 'd', - }, - { - label: '歌手', - value: 's', - }, - ], - }, - }, - ], - }, - { - componentName: 'Div', - id: 'node$9', - props: { - style: { - textAlign: 'center', - }, - }, - children: [ - { - componentName: 'Button.Group', - id: 'node$a', - props: {}, - children: [ - { - componentName: 'Button', - id: 'node$b', - props: { - type: 'primary', - style: { - margin: '0 5px 0 5px', - }, - htmlType: 'submit', - }, - children: ['提交'], - }, - { - componentName: 'Button', - id: 'node$d', - props: { - type: 'normal', - style: { - margin: '0 5px 0 5px', - }, - htmlType: 'reset', - }, - children: ['重置'], - }, - ], - }, - ], - }, - ], - }, - ], - }, - ], - constants: { - ENV: 'prod', - DOMAIN: 'xxx.alibaba-inc.com', - }, - css: 'body {font-size: 12px;} .table { width: 100px;}', - config: { - sdkVersion: '1.0.3', - historyMode: 'hash', - targetRootID: 'J_Container', - layout: { - componentName: 'BasicLayout', - props: { - logo: '...', - name: '测试网站', - }, - }, - theme: { - package: '@alife/theme-fusion', - version: '^0.1.0', - primary: '#ff9966', - }, - }, - meta: { - name: 'demo应用', - git_group: 'appGroup', - project_name: 'app_demo', - description: '这是一个测试应用', - spma: 'spa23d', - creator: '月飞', - }, -}; - -export default demoData; diff --git a/packages/code-generator/src/generator/ModuleBuilder.ts b/packages/code-generator/src/generator/ModuleBuilder.ts index 8115f0def..991ffedec 100644 --- a/packages/code-generator/src/generator/ModuleBuilder.ts +++ b/packages/code-generator/src/generator/ModuleBuilder.ts @@ -1,18 +1,24 @@ import { BuilderComponentPlugin, CodeGeneratorError, + IBasicSchema, ICodeChunk, ICompiledModule, IModuleBuilder, + IParseResult, + IResultDir, IResultFile, + ISchemaParser, PostProcessor, } from '../types'; import { COMMON_SUB_MODULE_NAME } from '../const/generator'; +import SchemaParser from '../parser/SchemaParser'; import ChunkBuilder from './ChunkBuilder'; import CodeBuilder from './CodeBuilder'; +import ResultDir from '../model/ResultDir'; import ResultFile from '../model/ResultFile'; export function createModuleBuilder( @@ -66,6 +72,20 @@ export function createModuleBuilder( }; }; + const generateModuleCode = async (schema: IBasicSchema | string): Promise => { + // Init + const schemaParser: ISchemaParser = new SchemaParser(); + const parseResult: IParseResult = schemaParser.parse(schema); + + const containerInfo = parseResult.containers[0]; + const { files } = await generateModule(containerInfo); + + const dir = new ResultDir(containerInfo.moduleName); + files.forEach(file => dir.addFile(file)); + + return dir; + } + const linkCodeChunks = ( chunks: Record, fileName: string, @@ -88,6 +108,7 @@ export function createModuleBuilder( return { generateModule, + generateModuleCode, linkCodeChunks, addPlugin: chunkGenerator.addPlugin.bind(chunkGenerator), }; diff --git a/packages/code-generator/src/generator/ProjectBuilder.ts b/packages/code-generator/src/generator/ProjectBuilder.ts index bd36291f6..ec1c897b6 100644 --- a/packages/code-generator/src/generator/ProjectBuilder.ts +++ b/packages/code-generator/src/generator/ProjectBuilder.ts @@ -57,8 +57,8 @@ export class ProjectBuilder implements IProjectBuilder { this.postProcessors = postProcessors; } - public async generateProject(schema: IProjectSchema): Promise { - // Init working parts + public async generateProject(schema: IProjectSchema | string): Promise { + // Init const schemaParser: ISchemaParser = new SchemaParser(); const builders = this.createModuleBuilders(); const projectRoot = this.template.generateTemplate(); diff --git a/packages/code-generator/src/index.ts b/packages/code-generator/src/index.ts index 8d2433d25..9de9c1538 100644 --- a/packages/code-generator/src/index.ts +++ b/packages/code-generator/src/index.ts @@ -3,14 +3,42 @@ * */ import { createProjectBuilder } from './generator/ProjectBuilder'; +import { createModuleBuilder } from './generator/ModuleBuilder'; import { createDiskPublisher } from './publisher/disk'; import createIceJsProjectBuilder from './solutions/icejs'; import createRecoreProjectBuilder from './solutions/recore'; +// 引入说明 +import { REACT_CHUNK_NAME } from './plugins/component/react/const'; + +// 引入通用插件组 +import esmodule from './plugins/common/esmodule'; +import requireUtils from './plugins/common/requireUtils'; +import containerClass from './plugins/component/react/containerClass'; +import containerDataSource from './plugins/component/react/containerDataSource'; +import containerInitState from './plugins/component/react/containerInitState'; +import containerInjectUtils from './plugins/component/react/containerInjectUtils'; +import containerLifeCycle from './plugins/component/react/containerLifeCycle'; +import containerMethod from './plugins/component/react/containerMethod'; +import jsx from './plugins/component/react/jsx'; +import reactCommonDeps from './plugins/component/react/reactCommonDeps'; +import css from './plugins/component/style/css'; +import constants from './plugins/project/constants'; +import i18n from './plugins/project/i18n'; +import utils from './plugins/project/utils'; + +// 引入常用工具 +import * as utilsCommon from './utils/common'; +import * as utilsCompositeType from './utils/compositeType'; +import * as utilsJsExpression from './utils/jsExpression'; +import * as utilsNodeToJSX from './utils/nodeToJSX'; +import * as utilsTemplateHelper from './utils/templateHelper'; + export * from './types'; export default { createProjectBuilder, + createModuleBuilder, solutions: { icejs: createIceJsProjectBuilder, recore: createRecoreProjectBuilder, @@ -18,4 +46,35 @@ export default { publishers: { disk: createDiskPublisher, }, + plugins: { + common: { + esmodule, + requireUtils, + }, + react: { + containerClass, + containerDataSource, + containerInitState, + containerInjectUtils, + containerLifeCycle, + containerMethod, + jsx, + reactCommonDeps, + }, + style: { + css, + }, + project: { + constants, + i18n, + utils, + }, + }, + utils: { + common: utilsCommon, + compositeType: utilsCompositeType, + jsExpression: utilsJsExpression, + nodeToJSX: utilsNodeToJSX, + templateHelper: utilsTemplateHelper, + }, }; diff --git a/packages/code-generator/src/parser/SchemaParser.ts b/packages/code-generator/src/parser/SchemaParser.ts index a98309a41..d6b679ff0 100644 --- a/packages/code-generator/src/parser/SchemaParser.ts +++ b/packages/code-generator/src/parser/SchemaParser.ts @@ -46,12 +46,23 @@ class SchemaParser implements ISchemaParser { return true; } - public parse(schema: IProjectSchema): IParseResult { + public parse(schemaSrc: IProjectSchema | string): IParseResult { // TODO: collect utils depends in JSExpression const compDeps: Record = {}; const internalDeps: Record = {}; let utilsDeps: IExternalDependency[] = []; + let schema: IProjectSchema; + if (typeof schemaSrc === 'string') { + try { + schema = JSON.parse(schemaSrc); + } catch (error) { + throw new CodeGeneratorError(`Parse schema failed: ${error.message || 'unknown reason'}`); + } + } else { + schema = schemaSrc; + } + // 解析三方组件依赖 schema.componentsMap.forEach(info => { info.dependencyType = DependencyType.External; diff --git a/packages/code-generator/src/plugins/common/esmodule.ts b/packages/code-generator/src/plugins/common/esmodule.ts index 1e26706cd..316ed5821 100644 --- a/packages/code-generator/src/plugins/common/esmodule.ts +++ b/packages/code-generator/src/plugins/common/esmodule.ts @@ -123,7 +123,7 @@ function buildPackageImport( return chunks; } -interface PluginConfig { +type PluginConfig = { fileType: string; } diff --git a/packages/code-generator/src/plugins/component/react/containerDataSource.ts b/packages/code-generator/src/plugins/component/react/containerDataSource.ts index 1f6132b10..72d7efd6b 100644 --- a/packages/code-generator/src/plugins/component/react/containerDataSource.ts +++ b/packages/code-generator/src/plugins/component/react/containerDataSource.ts @@ -11,7 +11,7 @@ import { IContainerInfo, } from '../../../types'; -interface PluginConfig { +type PluginConfig = { fileType: string; } diff --git a/packages/code-generator/src/plugins/component/react/containerInitState.ts b/packages/code-generator/src/plugins/component/react/containerInitState.ts index c3ba77c2b..50984c961 100644 --- a/packages/code-generator/src/plugins/component/react/containerInitState.ts +++ b/packages/code-generator/src/plugins/component/react/containerInitState.ts @@ -11,7 +11,7 @@ import { IContainerInfo, } from '../../../types'; -interface PluginConfig { +type PluginConfig = { fileType: string; implementType: 'inConstructor' | 'insMember' | 'hooks'; } diff --git a/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts b/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts index bdc6dbd6d..8cded167b 100644 --- a/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts +++ b/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts @@ -8,7 +8,7 @@ import { ICodeStruct, } from '../../../types'; -interface PluginConfig { +type PluginConfig = { fileType: string; } diff --git a/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts b/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts index 08c88f968..d1951d8e2 100644 --- a/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts +++ b/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts @@ -18,7 +18,7 @@ import { IJSExpression, } from '../../../types'; -interface PluginConfig { +type PluginConfig = { fileType: string; exportNameMapping: Record; normalizeNameMapping: Record; diff --git a/packages/code-generator/src/plugins/component/react/containerMethod.ts b/packages/code-generator/src/plugins/component/react/containerMethod.ts index 527aad815..9bba467da 100644 --- a/packages/code-generator/src/plugins/component/react/containerMethod.ts +++ b/packages/code-generator/src/plugins/component/react/containerMethod.ts @@ -13,7 +13,7 @@ import { IJSExpression, } from '../../../types'; -interface PluginConfig { +type PluginConfig = { fileType: string; } diff --git a/packages/code-generator/src/plugins/component/react/jsx.ts b/packages/code-generator/src/plugins/component/react/jsx.ts index 2b168d259..bb6b8f383 100644 --- a/packages/code-generator/src/plugins/component/react/jsx.ts +++ b/packages/code-generator/src/plugins/component/react/jsx.ts @@ -11,7 +11,7 @@ import { REACT_CHUNK_NAME } from './const'; import { createReactNodeGenerator } from '../../../utils/nodeToJSX'; -interface PluginConfig { +type PluginConfig = { fileType: string; } diff --git a/packages/code-generator/src/plugins/component/recore/pageDataSource.ts b/packages/code-generator/src/plugins/component/recore/pageDataSource.ts index da7de385e..87d496aaf 100644 --- a/packages/code-generator/src/plugins/component/recore/pageDataSource.ts +++ b/packages/code-generator/src/plugins/component/recore/pageDataSource.ts @@ -31,25 +31,19 @@ const pluginFactory: BuilderComponentPluginFactory = () => { const { dataSource } = ir; const { list, - dataHandler, ...rest } = dataSource; let attrs: string[] = []; const extConfigs = Object.keys(rest).map(extConfigName => { - const value = rest[extConfigName] as CompositeValue; + const value = (rest as Record)[extConfigName]; const [isString, valueStr] = generateCompositeType(value); return `${extConfigName}: ${isString ? `'${valueStr}'` : valueStr}`; }); attrs = [...attrs, ...extConfigs]; - if (dataHandler) { - const handlerContent = packJsExpression(dataHandler); - attrs.push(`dataHandler: ${handlerContent}`); - } - const listProp = handleStringValueDefault(generateCompositeType(list as unknown as CompositeValue, { expression: packJsExpression, })); diff --git a/packages/code-generator/src/plugins/component/style/css.ts b/packages/code-generator/src/plugins/component/style/css.ts index cb9b2a4e0..a2e8b46f0 100644 --- a/packages/code-generator/src/plugins/component/style/css.ts +++ b/packages/code-generator/src/plugins/component/style/css.ts @@ -9,7 +9,7 @@ import { IContainerInfo, } from '../../../types'; -interface PluginConfig { +type PluginConfig = { fileType: string; moduleFileType: string; } diff --git a/packages/code-generator/src/types/core.ts b/packages/code-generator/src/types/core.ts index 5cdffaa4b..a370d5641 100644 --- a/packages/code-generator/src/types/core.ts +++ b/packages/code-generator/src/types/core.ts @@ -78,12 +78,13 @@ export interface ICompiledModule { } export interface IModuleBuilder { - generateModule: (input: unknown) => Promise; - linkCodeChunks: ( + generateModule(input: unknown): Promise; + generateModuleCode(schema: IBasicSchema | string): Promise; + linkCodeChunks( chunks: Record, fileName: string, - ) => IResultFile[]; - addPlugin: (plugin: BuilderComponentPlugin) => void; + ): IResultFile[]; + addPlugin(plugin: BuilderComponentPlugin): void; } /** @@ -105,7 +106,7 @@ export interface ICodeGenerator { export interface ISchemaParser { validate(schema: IBasicSchema): boolean; - parse(schema: IBasicSchema): IParseResult; + parse(schema: IBasicSchema | string): IParseResult; } export interface IProjectTemplate { @@ -136,7 +137,7 @@ export interface IProjectPlugins { } export interface IProjectBuilder { - generateProject(schema: IProjectSchema): Promise; + generateProject(schema: IProjectSchema | string): Promise; } export type PostProcessorFactory = (config?: T) => PostProcessor; @@ -168,3 +169,9 @@ export interface HandlerSet { } export type ExtGeneratorPlugin = (nodeItem: IComponentNodeItem) => CodePiece[]; + +// export interface InteratorScope { +// [$item: string]: string; // $item 默认取值 "item" +// [$index: string]: string | number; // $index 默认取值 "index" +// __proto__: BlockInstance; +// } diff --git a/packages/code-generator/src/types/schema.ts b/packages/code-generator/src/types/schema.ts index aa6e9459c..68226e8ab 100644 --- a/packages/code-generator/src/types/schema.ts +++ b/packages/code-generator/src/types/schema.ts @@ -88,10 +88,6 @@ export interface IUtilItem { content: IExternalDependency | IJSExpression; } -export interface IInlineStyle { - [cssAttribute: string]: string | number | IJSExpression; -} - export type ChildNodeItem = string | IJSExpression | IComponentNodeItem; export type ChildNodeType = ChildNodeItem | ChildNodeItem[]; @@ -107,9 +103,7 @@ export interface IComponentNodeItem { id?: string; componentName: string; // 组件名称 必填、首字母大写 props: { - className?: string; // 组件样式类名 - style?: IInlineStyle; // 组件内联样式 - [propName: string]: any; // 业务属性 + [propName: string]: CompositeValue; // 业务属性 }; // 组件属性对象 condition?: CompositeValue; // 渲染条件 loop?: CompositeValue; // 循环数据 @@ -127,13 +121,10 @@ export interface IComponentNodeItem { export interface IContainerNodeItem extends IComponentNodeItem { componentName: 'Page' | 'Block' | 'Component'; // 'Page' | 'Block' | 'Component' 组件类型 必填、首字母大写 fileName: string; // 文件名称 必填、英文 - defaultProps?: { - [propName: string]: any; // 业务属性 - }; state?: { - [stateName: string]: any; // 容器初始数据 + [stateName: string]: CompositeValue; // 容器初始数据 }; - css: string; // 样式文件 用于描述容器组件内部节点的样式,对应生成一个独立的样式文件,在对应容器组件生成的 .jsx 文件中 import 引入; + css?: string; // 样式文件 用于描述容器组件内部节点的样式,对应生成一个独立的样式文件,在对应容器组件生成的 .jsx 文件中 import 引入; /** * LifeCycle * • constructor(props, context) @@ -146,30 +137,13 @@ export interface IContainerNodeItem extends IComponentNodeItem { * • componentDidCatch(error, info) */ lifeCycles?: Record; // 生命周期Hook方法 - methods?: { - [methodName: string]: IJSExpression; - }; // 自定义方法设置 - dataSource?: IDataSource; // 异步数据源配置 + methods?: Record; // 自定义方法设置 + dataSource?: { + list: IDataSourceConfig[]; + }; // 异步数据源配置 meta?: IBasicMeta | IPageMeta; } -/** - * 搭建基础协议 - 数据源 - * - * @export - * @interface IDataSource - */ -export interface IDataSource { - list: IDataSourceConfig[]; // 成为为单个请求配置 - /** - * 参数:为dataMap对象,key:数据id, value: 单个请求结果 - * 返回值:数据对象data,将会在渲染引擎和schemaToCode中通过调用this.setState(...)将返回的数据对象生效到state中; - * 支持返回一个Promise,通过resolve(返回数据),常用于串型发送请求场景,配合this.dataSourceMap[oneRequest.id].load()使用; - */ - dataHandler?: IJSExpression; - [extConfigName: string]: any; -} - /** * 搭建基础协议 - 数据源单个配置 * diff --git a/packages/code-generator/src/utils/nodeToJSX.ts b/packages/code-generator/src/utils/nodeToJSX.ts index 82673f851..a60351202 100644 --- a/packages/code-generator/src/utils/nodeToJSX.ts +++ b/packages/code-generator/src/utils/nodeToJSX.ts @@ -1,7 +1,6 @@ import { ChildNodeType, IComponentNodeItem, - IInlineStyle, IJSExpression, ChildNodeItem, CodeGeneratorError, @@ -37,20 +36,6 @@ export function handleChildren( } } -export function generateInlineStyle(style: IInlineStyle): string | null { - const attrLines = Object.keys(style).map((cssAttribute: string) => { - const [isString, valueStr] = generateCompositeType(style[cssAttribute]); - const valuePart = isString ? `'${valueStr}'` : valueStr; - return `${cssAttribute}: ${valuePart},`; - }); - - if (attrLines.length === 0) { - return null; - } - - return `{ ${attrLines.join('')} }`; -} - export function generateAttr(attrName: string, attrValue: any): CodePiece[] { if (attrName === 'initValue' || attrName === 'labelCol') { return []; @@ -63,23 +48,8 @@ export function generateAttr(attrName: string, attrValue: any): CodePiece[] { } export function generateAttrs(nodeItem: IComponentNodeItem): CodePiece[] { - const { className, style, ...props } = nodeItem.props; + const { props } = nodeItem; let pieces: CodePiece[] = []; - if (className) { - pieces.push({ - value: `className="${className}"`, - type: PIECE_TYPE.ATTR, - }); - } - if (style) { - const inlineStyle = generateInlineStyle(style); - if (inlineStyle !== null) { - pieces.push({ - value: `style={${inlineStyle}}`, - type: PIECE_TYPE.ATTR, - }); - } - } Object.keys(props).forEach((propName: string) => pieces = pieces.concat(generateAttr(propName, props[propName])),