Merge branch 'release/1.0.0' into feat/datasource-engine

This commit is contained in:
guokai.jgk 2020-10-23 15:21:41 +08:00
commit b0ef9914e8
1936 changed files with 287229 additions and 70874 deletions

View File

@ -1,9 +1,9 @@
# http://editorconfig.org
root = true root = true
[*] [*]
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
end_of_line = lf
charset = utf-8 charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true

View File

@ -1,7 +1,17 @@
.idea/ # 忽略目录
.vscode/ node_modules
build/ test-cases
test
output
build
dist
demo
es
lib
.* .*
~* ~*
node_modules
# 忽略文件
**/*.min.js
**/*-min.js
**/*.bundle.js

View File

@ -1,3 +0,0 @@
{
"extends": "./node_modules/@ali/lowcode-config/.eslintrc"
}

3
.eslintrc.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
extends: 'eslint-config-ali/typescript/react',
};

View File

@ -1,4 +0,0 @@
**/test/**/*.ts
**/test/**/*.js
**/template/**/*.template
**/template/**/*.tpl

View File

@ -1,8 +0,0 @@
{
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"printWidth": 120,
"arrowParens": "always"
}

7
.prettierrc.js Normal file
View File

@ -0,0 +1,7 @@
module.exports = {
printWidth: 100,
tabWidth: 2,
semi: true,
singleQuote: true,
trailingComma: 'all',
};

9
.stylelintignore Normal file
View File

@ -0,0 +1,9 @@
# 忽略目录
node_modules/
build/
dist/
# 忽略文件
**/*.min.css
**/*-min.css
**/*.bundle.css

3
.stylelintrc.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
extends: 'stylelint-config-ali',
};

11
.vscode/launch.json vendored
View File

@ -10,6 +10,15 @@
"request": "launch", "request": "launch",
"runtimeExecutable": "${workspaceFolder}/packages/material-parser/node_modules/.bin/ava", "runtimeExecutable": "${workspaceFolder}/packages/material-parser/node_modules/.bin/ava",
"runtimeArgs": ["debug", "--break", "${file}"] "runtimeArgs": ["debug", "--break", "${file}"]
} },
{
"name": "ava code-gen",
"type": "node",
"request": "launch",
"runtimeExecutable": "node",
"runtimeArgs": ["${workspaceFolder}/node_modules/.bin/ava", "--serial", "${file}"],
"cwd": "${workspaceFolder}/packages/code-generator",
"outputCapture": "std"
},
] ]
} }

View File

@ -1,22 +1,20 @@
## Ali Lowcode Engine阿里低代码引擎 ## Ali Lowcode Engine阿里低代码引擎
[Lerna](https://github.com/lerna/lerna) + [TS](https://www.typescriptlang.org/)
## 开发 ## 开发
#### 创建新包 #### 创建新包
- `./scripts/create.sh <package-name>` - `./scripts/create.sh <package-name>`
#### 跑起来: #### 运行示例
- `npm run setup` - `npm run setup`
- `npm start` - `npm start`
#### 开发提交 #### 开发提交
- `git add <your-files>` - `git add <your-files>`
- `npm run commit` # 在根目录 - `git commit -a "feat: xxx"`
## 发布 ## 发布
@ -24,7 +22,6 @@
## 注意 ## 注意
- Commit 动作尽量使用 `npm run commit`,其内部调用了 `git cz`,方便按语义化版本自动递增,以及自动生成 `CHANGELOG.md`
- `packages` 工程里一些开发时公共依赖(比如:`typescript``ava` 等)会放到工程顶层 - `packages` 工程里一些开发时公共依赖(比如:`typescript``ava` 等)会放到工程顶层
- 工程里的 `.md``test/` 等文件修改不会产生新的发布 - 工程里的 `.md``test/` 等文件修改不会产生新的发布
- 当工程里存在多个 ts 文件的目录时,最终产生的文件会按文件夹形式放到 `lib` - 当工程里存在多个 ts 文件的目录时,最终产生的文件会按文件夹形式放到 `lib`

3
commitlint.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
extends: ['ali'],
};

View File

@ -12,39 +12,34 @@
"scripts": { "scripts": {
"build": "./scripts/build.sh", "build": "./scripts/build.sh",
"clean": "rm -rf ./packages/*/lib ./packages/*/es ./packages/*/dist ./packages/*/build", "clean": "rm -rf ./packages/*/lib ./packages/*/es ./packages/*/dist ./packages/*/build",
"commit": "git-cz", "lint": "eslint --ext .ts,.tsx,.js,.jsx ./ --quiet",
"pub": "lerna publish --cd-version patch", "lint:fix": "eslint --ext .ts,.tsx,.js,.jsx ./ --quiet --fix",
"pub": "lerna publish --force-publish --cd-version patch",
"setup": "./scripts/setup.sh", "setup": "./scripts/setup.sh",
"start": "./scripts/start.sh", "start": "./scripts/start.sh",
"start:server": "./scripts/start-server.sh",
"test": "lerna run test --stream", "test": "lerna run test --stream",
"test:snapshot": "lerna run test:snapshot" "test:snapshot": "lerna run test:snapshot",
"xima:fix": "xima fix",
"xima:scan": "xima scan"
}, },
"lint-staged": { "husky": {
"*.{tsx,ts}": [ "hooks": {
"eslint --quiet", "pre-commit": "xima exec lint-staged",
"git add" "commit-msg": "xima exec commitlint -E HUSKY_GIT_PARAMS"
]
},
"config": {
"commitizen": {
"path": "node_modules/cz-conventional-changelog"
} }
}, },
"lint-staged": {
"**/*.{css,scss,less}": "xima exec stylelint",
"**/*.{js,jsx,ts,tsx}": "xima exec eslint"
},
"devDependencies": { "devDependencies": {
"@ali/lowcode-config": "^2.0.5",
"ava": "^1.0.1", "ava": "^1.0.1",
"babel-eslint": "^10.0.3",
"commitizen": "^3.0.5",
"cz-conventional-changelog": "^2.1.0",
"eslint": "^6.5.1",
"git-cz": "^4.3.1",
"husky": "^4.2.3",
"lerna": "^2.11.0", "lerna": "^2.11.0",
"lint-staged": "^8.1.0",
"prettier": "^1.15.3",
"ts-node": "^7.0.1", "ts-node": "^7.0.1",
"tslib": "^1.9.3", "tslib": "^1.9.3",
"typescript": "^3.2.2" "typescript": "^3.2.2",
"xima": "^0.2.15"
}, },
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"

View File

@ -0,0 +1 @@
test-cases/

View File

@ -3,6 +3,228 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
<a name="1.0.12"></a>
## [1.0.12](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.11...@ali/lowcode-code-generator@1.0.12) (2020-10-20)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="1.0.11"></a>
## [1.0.11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.10...@ali/lowcode-code-generator@1.0.11) (2020-10-19)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="1.0.10"></a>
## [1.0.10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.9...@ali/lowcode-code-generator@1.0.10) (2020-09-29)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="1.0.9"></a>
## [1.0.9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.8...@ali/lowcode-code-generator@1.0.9) (2020-09-28)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="1.0.8"></a>
## [1.0.8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.8-0...@ali/lowcode-code-generator@1.0.8) (2020-09-28)
### Bug Fixes
* 🐛 解决 Rax 出码到小程序的时候 require(xxx) 语句不能被编译的问题 ([332a473](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/332a473))
* 🐛 经验证发现小程序里面还是得包上 eval 否则 Rax 框架会误把 context 发送到渲染进程而出错 ([c7a10c0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/c7a10c0))
* 🐛 若全量引入 lodash 则在小程序下会跑不通,所以改成引入 lodash/clone ([a1a3b68](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a1a3b68))
* 🐛 小程序里面不支持可选链 "?.", 先直接访问 dataSourceEngine 吧 ([36c486b](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/36c486b))
* 🐛 fix typo of dataHandler ([acd1f06](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/acd1f06))
* 🐛 Rax 出码到小程序, 事件处理函数绑定 JSExpression 时也不应该包裹一个 eval, 小程序会报错 ([9f129aa](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/9f129aa))
* 🐛 Result use types package ([dd97a0c](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/dd97a0c))
* 🐛 use lowcode types ([b11425b](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b11425b))
* enhance api design ([95d67c1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/95d67c1))
* fix test result ([7f6fbe8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/7f6fbe8))
* ignore eslintrc in test-case ([c0ef4bc](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/c0ef4bc))
* merge problems & deps bugs ([7a36eab](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/7a36eab))
* miniAppBuildType config(temp) ([584b4c2](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/584b4c2))
* miss scope ([97242c3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/97242c3))
### Features
* 🎸 按 826 对齐结论调整出码和数据源引擎 ([b9a562e](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b9a562e))
* 🎸 补充对数据源的一些处理 ([4572b53](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/4572b53))
* 🎸 补充一个默认的数据源的构建后的样子 ([78f34ab](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/78f34ab))
* 🎸 出码模块的 DiskPublisher 改成支持传入自定义 FS ([46c896e](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/46c896e))
* 🎸 出码模块的 schema 相关的类型统一都改成引用 [@ali](https://gitlab.alibaba-inc.com/ali)/lowcode-types 中的,与设计器一致 ([27a9800](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/27a9800))
* 🎸 导出 Rax 的 solutions 的定义 ([27f0e13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/27f0e13))
* 🎸 搞定 Rax 出码的时候的 package.json 中的 dependencies ([eba172c](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/eba172c))
* 🎸 还原出码模块的 solutions 的导出 ([c2a7d63](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/c2a7d63))
* 🎸 解决通过 Rax 出码到小程序的时候循环里面没法用循环变量的问题 ([779ea7c](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/779ea7c))
* 🎸 容器的模块名统一都用 PascalCase, 并为页面添加特定后缀防止与组件名冲突 ([42f7bdb](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/42f7bdb))
* 🎸 数据源的类型默认是 fetch ([ec8a191](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/ec8a191))
* 🎸 数据源的requestHandlers选项改成requestHandlersMap, 命名更清晰 ([42e41bb](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/42e41bb))
* 🎸 添加 didMount 和 willUnmount 两个基本的生命周期 ([e33a95e](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/e33a95e))
* 🎸 添加一个判断 ContainerSchema 的 util 方便后续用 ([c3fdfe5](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/c3fdfe5))
* 🎸 通过 config.miniAppBuildType 来支持 Rax 的 runtime 模式 ([35fcdd9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/35fcdd9))
* 🎸 完善 Rax 出码, 补充更复杂的带有数据源绑定/条件/循环以及 Utils 的测试用例并 pass ([adcfacb](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/adcfacb))
* 🎸 完善 Rax 出码, 跑通第一个测试用例👏👏👏 ([9f62110](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/9f62110))
* 🎸 完善 Rax 出码的时候的全局样式处理 ([058b087](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/058b087))
* 🎸 为 Rax 出码增加对 i18n 的支持 ([8d198bd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/8d198bd))
* 🎸 新增 less 文件类型的定义, 以备后续某些 solution 出码用 less 文件作为样式文件 ([cac29d8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/cac29d8))
* 🎸 优化 Rax 出码时对绑定的表达式的包裹逻辑, 对于一些简单的安全的表达式不做包裹 ([facfa2a](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/facfa2a))
* 🎸 优化 ResultDir 的报错信息, 更方便定位问题 ([965ef4a](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/965ef4a))
* 🎸 优化完善 Rax 出码相关的模板和插件 ([c3d909a](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/c3d909a))
* 🎸 与国凯的数据源保持一致,将 urlParams 所需的 search 参数直接传入 ([19fabc1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/19fabc1))
* 🎸 与国凯的数据源引擎联调,对齐包名和导出方式 ([fea0946](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/fea0946))
* 🎸 支持对 JSON 文件进行 prettier 格式化 ([b7c4854](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/b7c4854))
* 🎸 add rax code generator solution and test case ([20c0953](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/20c0953))
* 🎸 custom 类型的数据源请求不需要 handler ([fa939c4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/fa939c4))
* 🎸 globalStyle 支持定制样式文件的后缀名 ([e78dae0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/e78dae0))
* 🎸 Rax 出码器支持路由功能 ([8ecc002](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/8ecc002))
* 🎸 Rax 出码适配数据源引擎的默认 requestHandlers ([5f529ae](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/5f529ae))
* 🎸 Rax 出码支持 constants 常量定义 ([fcf6c32](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/fcf6c32))
* 🎸 Rax 出码中添加数据源的 dataHandler 并与数据源引擎的对齐参数 ([42b9db3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/42b9db3))
* 🎸 Rax 出码中增加对 urlParams 这种特殊数据源的处理 ([c743afd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/c743afd))
<a name="1.0.8-0"></a>
## [1.0.8-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.7-0...@ali/lowcode-code-generator@1.0.8-0) (2020-09-09)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="1.0.7-0"></a>
## [1.0.7-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.6-0...@ali/lowcode-code-generator@1.0.7-0) (2020-09-02)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="1.0.6-0"></a>
## [1.0.6-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.5-0...@ali/lowcode-code-generator@1.0.6-0) (2020-09-02)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="1.0.5-0"></a>
## [1.0.5-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.4-0...@ali/lowcode-code-generator@1.0.5-0) (2020-08-20)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="1.0.4-0"></a>
## [1.0.4-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.3-0...@ali/lowcode-code-generator@1.0.4-0) (2020-08-20)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="1.0.3-0"></a>
## [1.0.3-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.2-0...@ali/lowcode-code-generator@1.0.3-0) (2020-08-20)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="1.0.2-0"></a>
## [1.0.2-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.1-0...@ali/lowcode-code-generator@1.0.2-0) (2020-08-20)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="1.0.1-0"></a>
## [1.0.1-0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.0...@ali/lowcode-code-generator@1.0.1-0) (2020-08-20)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="1.0.0"></a>
# [1.0.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@0.13.0...@ali/lowcode-code-generator@1.0.0) (2020-08-17)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="0.13.0"></a>
# [0.13.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@0.12.0...@ali/lowcode-code-generator@0.13.0) (2020-08-17)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="0.12.0"></a>
# [0.12.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@0.10.0...@ali/lowcode-code-generator@0.12.0) (2020-08-17)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="0.11.0"></a>
# [0.11.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@0.10.0...@ali/lowcode-code-generator@0.11.0) (2020-08-17)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="0.10.0"></a>
# [0.10.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@0.9.0...@ali/lowcode-code-generator@0.10.0) (2020-08-16)
**Note:** Version bump only for package @ali/lowcode-code-generator
<a name="0.9.0"></a>
# [0.9.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@0.8.10...@ali/lowcode-code-generator@0.9.0) (2020-08-14)
### Bug Fixes
* 🐛 bugs about deps ([1eabd50](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/1eabd50))
* 🐛 children in props ([fe0ace8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/fe0ace8))
* 🐛 get deps info from slot ([6c3ae36](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6c3ae36))
* 🐛 group chunks by filetype family ([db144a9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/db144a9))
* 🐛 loop bug ([8f53910](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/8f53910))
* 🐛 repair children before deps analyze ([737d06e](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/737d06e))
* 🐛 support JSFunction type ([9061e4b](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/9061e4b))
* 🐛 更改复杂类型生成工具的接口形式,减少调用复杂度 ([ce616b5](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/ce616b5))
### Features
* 🎸 add node type mapping config for jsx plugin ([19a51b8](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/19a51b8))
* 🎸 code generator fix slot support ([e51b9cb](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/e51b9cb))
<a name="0.8.10"></a> <a name="0.8.10"></a>
## [0.8.10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@0.8.9...@ali/lowcode-code-generator@0.8.10) (2020-07-21) ## [0.8.10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@0.8.9...@ali/lowcode-code-generator@0.8.10) (2020-07-21)
@ -118,14 +340,11 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
### Features ### Features
<<<<<<< HEAD
* code generator main process ([021d6e0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/021d6e0)) * code generator main process ([021d6e0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/021d6e0))
* demo schema & complex children type ([a5ee6bd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a5ee6bd)) * demo schema & complex children type ([a5ee6bd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a5ee6bd))
* fix gaps ([32af3d3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/32af3d3)) * fix gaps ([32af3d3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/32af3d3))
* project builder fix & publish demo to disk ([26983b3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/26983b3)) * project builder fix & publish demo to disk ([26983b3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/26983b3))
=======
* code generator main process ([021d6e0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/021d6e0fe9fb29a8b6c1c5d5f4d06ec71896faa5)) * code generator main process ([021d6e0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/021d6e0fe9fb29a8b6c1c5d5f4d06ec71896faa5))
* demo schema & complex children type ([a5ee6bd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a5ee6bd55806fc9aea695096ccd4c7f50b8e31c4)) * demo schema & complex children type ([a5ee6bd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a5ee6bd55806fc9aea695096ccd4c7f50b8e31c4))
* fix gaps ([32af3d3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/32af3d3a3ca4d5aca15be25e05c840c8ea0cb6ae)) * fix gaps ([32af3d3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/32af3d3a3ca4d5aca15be25e05c840c8ea0cb6ae))
* project builder fix & publish demo to disk ([26983b3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/26983b38c2b0f1d39d79964eb54d8ce60250dd82)) * project builder fix & publish demo to disk ([26983b3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/26983b38c2b0f1d39d79964eb54d8ce60250dd82))
>>>>>>> df955e1db90ff104cd11160def80113cfd6faccc

View File

@ -1,9 +1,31 @@
# 出码模块 # 出码模块
详细介绍看这里:<https://yuque.antfin-inc.com/docs/share/2b342641-6e01-4c77-b8e0-30421f55f69b>
## 安装接入 ## 安装接入
## 自定义导出 ## 自定义导出
## 开始开发 ## 开始开发
本项目隶属于 [ali-lowcode-engine](http://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine), 需要和整个 [ali-lowcode-engine](http://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine) 一起开发。
所以先要初始化整个 [ali-lowcode-engine](http://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine) 的环境:
1. 克隆 [ali-lowcode-engine](http://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine): `git clone git@gitlab.alibaba-inc.com:ali-lowcode/ali-lowcode-engine.git`
2. 运行 `setup` 脚本,初始化环境: `npm run setup`
然后,因为本项目依赖 `@ali/lowcode-types` 所以需要先构建下 `type`,即执行: `lerna run build --scope @ali/lowcode-types`
最后,可以运行 `npm start` 命令启动本地调试(本项目通过 `ava` 进行单元测试,故 `start` 脚本其实就是 `watch` 模式的 `ava`):
```sh
# 到本项目目录下执行:(推荐)
npm start
# 或直接执行 ava
npx ava --watch
# 或在 ali-lowcode-engine 工程根目录下执行: (不推荐,因为命令太长而且没法响应输入)
lerna run start --stream --scope @ali/lowcode-code-generator
```

View File

@ -0,0 +1,5 @@
{
"plugins": [
"build-plugin-component"
]
}

File diff suppressed because it is too large Load Diff

View File

@ -4,20 +4,20 @@ const CodeGenerator = require('../lib').default;
function flatFiles(rootName, dir) { function flatFiles(rootName, dir) {
const dirRoot = rootName ? `${rootName}/${dir.name}` : dir.name; const dirRoot = rootName ? `${rootName}/${dir.name}` : dir.name;
const files = dir.files.map(file => ({ const files = dir.files.map((file) => ({
name: `${dirRoot}/${file.name}.${file.ext}`, name: `${dirRoot}/${file.name}.${file.ext}`,
content: file.content, content: file.content,
ext: '', ext: '',
})); }));
const filesInSub = dir.dirs.map(subDir => flatFiles(`${dirRoot}`, subDir)); const filesInSub = dir.dirs.map((subDir) => flatFiles(`${dirRoot}`, subDir));
const result = files.concat.apply(files, filesInSub); const result = files.concat(...filesInSub);
return result; return result;
} }
function displayResultInConsole(root, fileName) { function displayResultInConsole(root, fileName) {
const files = flatFiles('.', root); const files = flatFiles('.', root);
files.forEach(file => { files.forEach((file) => {
if (!fileName || fileName === file.name) { if (!fileName || fileName === file.name) {
console.log(`========== ${file.name} Start ==========`); console.log(`========== ${file.name} Start ==========`);
console.log(file.content); console.log(file.content);
@ -37,16 +37,83 @@ async function writeResultToDisk(root, path) {
}); });
} }
function getComponentsMap() {
const assetJson = fs.readFileSync('./demo/assets.json', { encoding: 'utf8' });
const assets = JSON.parse(assetJson);
const { components } = assets;
const componentsMap = components
.filter((c) => !!c.npm)
.map((c) => ({
componentName: c.componentName,
...(c.npm || {}),
}));
return componentsMap;
}
function main() { function main() {
const schemaJson = fs.readFileSync('./demo/sampleSchema.json', { encoding: 'utf8' }); const schemaJson = fs.readFileSync('./demo/sampleSchema.json', { encoding: 'utf8' });
const createIceJsProjectBuilder = CodeGenerator.solutions.icejs; const createIceJsProjectBuilder = CodeGenerator.solutions.icejs;
const builder = createIceJsProjectBuilder(); const builder = createIceJsProjectBuilder();
builder.generateProject(schemaJson).then(result => { builder.generateProject(schemaJson).then((result) => {
displayResultInConsole(result);
writeResultToDisk(result, 'output/lowcodeDemo').then((response) => console.log('Write to disk: ', JSON.stringify(response)));
return result;
});
}
function demo() {
const schemaJson = fs.readFileSync('./demo/schema.json', { encoding: 'utf8' });
const createIceJsProjectBuilder = CodeGenerator.solutions.icejs;
const builder = createIceJsProjectBuilder();
const componentsMap = getComponentsMap();
const root = JSON.parse(schemaJson);
const fullSchema = {
version: '1.0.0',
config: {
historyMode: 'hash',
targetRootID: 'J_Container',
},
meta: {
name: 'demoproject',
},
componentsTree: [root],
componentsMap,
};
builder.generateProject(fullSchema).then((result) => {
displayResultInConsole(result);
return result;
});
}
function demo() {
const schemaJson = fs.readFileSync('./demo/schema.json', { encoding: 'utf8' });
const createIceJsProjectBuilder = CodeGenerator.solutions.icejs;
const builder = createIceJsProjectBuilder();
const componentsMap = getComponentsMap();
const root = JSON.parse(schemaJson);
const fullSchema = {
version: '1.0.0',
config: {
historyMode: 'hash',
targetRootID: 'J_Container',
},
meta: {
name: 'demoproject',
},
componentsTree: [root],
componentsMap,
};
builder.generateProject(fullSchema).then((result) => {
displayResultInConsole(result); displayResultInConsole(result);
writeResultToDisk(result, 'output/lowcodeDemo').then(response =>
console.log('Write to disk: ', JSON.stringify(response)),
);
return result; return result;
}); });
} }
@ -66,13 +133,11 @@ function exportModule() {
CodeGenerator.plugins.react.jsx(), CodeGenerator.plugins.react.jsx(),
CodeGenerator.plugins.style.css(), CodeGenerator.plugins.style.css(),
], ],
postProcessors: [ postProcessors: [CodeGenerator.postprocessor.prettier()],
CodeGenerator.postprocessor.prettier(),
],
mainFileName: 'index', mainFileName: 'index',
}); });
moduleBuilder.generateModuleCode(schemaJson).then(result => { moduleBuilder.generateModuleCode(schemaJson).then((result) => {
displayResultInConsole(result); displayResultInConsole(result);
return result; return result;
}); });
@ -108,47 +173,26 @@ function exportProject() {
CodeGenerator.plugins.react.jsx(), CodeGenerator.plugins.react.jsx(),
CodeGenerator.plugins.style.css(), CodeGenerator.plugins.style.css(),
], ],
router: [ router: [CodeGenerator.plugins.common.esmodule(), CodeGenerator.solutionParts.icejs.plugins.router()],
CodeGenerator.plugins.common.esmodule(), entry: [CodeGenerator.solutionParts.icejs.plugins.entry()],
CodeGenerator.solutionParts.icejs.plugins.router(), constants: [CodeGenerator.plugins.project.constants()],
], utils: [CodeGenerator.plugins.common.esmodule(), CodeGenerator.plugins.project.utils()],
entry: [ i18n: [CodeGenerator.plugins.project.i18n()],
CodeGenerator.solutionParts.icejs.plugins.entry(), globalStyle: [CodeGenerator.solutionParts.icejs.plugins.globalStyle()],
], htmlEntry: [CodeGenerator.solutionParts.icejs.plugins.entryHtml()],
constants: [ packageJSON: [CodeGenerator.solutionParts.icejs.plugins.packageJSON()],
CodeGenerator.plugins.project.constants(),
],
utils: [
CodeGenerator.plugins.common.esmodule(),
CodeGenerator.plugins.project.utils(),
],
i18n: [
CodeGenerator.plugins.project.i18n(),
],
globalStyle: [
CodeGenerator.solutionParts.icejs.plugins.globalStyle(),
],
htmlEntry: [
CodeGenerator.solutionParts.icejs.plugins.entryHtml(),
],
packageJSON: [
CodeGenerator.solutionParts.icejs.plugins.packageJSON(),
],
}, },
postProcessors: [ postProcessors: [CodeGenerator.postprocessor.prettier()],
CodeGenerator.postprocessor.prettier(),
],
}); });
builder.generateProject(schemaJson).then(result => { builder.generateProject(schemaJson).then((result) => {
displayResultInConsole(result); displayResultInConsole(result);
writeResultToDisk(result, 'output/lowcodeDemo').then(response => writeResultToDisk(result, 'output/lowcodeDemo').then((response) => console.log('Write to disk: ', JSON.stringify(response)));
console.log('Write to disk: ', JSON.stringify(response)),
);
return result; return result;
}); });
} }
// main(); // main();
// exportModule(); // exportModule();
exportProject(); // exportProject();
demo();

View File

@ -0,0 +1,420 @@
{
"componentName": "Page",
"id": "node_dockcviv8fo1",
"props": {
"ref": "outterView",
"autoLoading": true,
"style": {
"padding": "0 5px 0 5px"
}
},
"fileName": "test",
"dataSource": {
"list": []
},
"state": {
"text": "outter",
"isShowDialog": false
},
"css": "body {font-size: 12px;} .botton{width:100px;color:#ff00ff}",
"lifeCycles": {
"componentDidMount": {
"type": "JSFunction",
"value": "function() {\n console.log('did mount');\n }"
},
"componentWillUnmount": {
"type": "JSFunction",
"value": "function() {\n console.log('will umount');\n }"
}
},
"methods": {
"testFunc": {
"type": "JSFunction",
"value": "function() {\n console.log('test func');\n }"
},
"onClick": {
"type": "JSFunction",
"value": "function(){\n this.setState({\n isShowDialog:true\n })\n\t}"
}
},
"children": [{
"componentName": "Box",
"id": "node_dockcy8n9xed",
"props": {
"style": {
"backgroundColor": "rgba(31,56,88,0.1)",
"padding": "12px 12px 12px 12px"
}
},
"children": [{
"componentName": "Box",
"id": "node_dockcy8n9xee",
"props": {
"style": {
"padding": "12px 12px 12px 12px",
"backgroundColor": "#ffffff"
}
},
"children": [{
"componentName": "Breadcrumb",
"id": "node_dockcy8n9xef",
"props": {
"prefix": "next-",
"maxNode": 100,
"component": "nav"
},
"children": [{
"componentName": "Breadcrumb.Item",
"id": "node_dockcy8n9xeg",
"props": {
"prefix": "next-"
},
"children": ["首页"]
}, {
"componentName": "Breadcrumb.Item",
"id": "node_dockcy8n9xei",
"props": {
"prefix": "next-"
},
"children": ["品质中台"]
}, {
"componentName": "Breadcrumb.Item",
"id": "node_dockcy8n9xek",
"props": {
"prefix": "next-"
},
"children": ["商家品质页面管理"]
}, {
"componentName": "Breadcrumb.Item",
"id": "node_dockcy8n9xem",
"props": {
"prefix": "next-"
},
"children": ["质检知识条配置"]
}]
}]
}, {
"componentName": "Box",
"id": "node_dockcy8n9xeo",
"props": {
"style": {
"marginTop": "12px",
"backgroundColor": "#ffffff"
}
},
"children": [{
"componentName": "Form",
"id": "node_dockcy8n9xep",
"props": {
"inline": true,
"style": {
"marginTop": "12px",
"marginRight": "12px",
"marginLeft": "12px"
},
"__events": []
},
"children": [{
"componentName": "Form.Item",
"id": "node_dockcy8n9xeq",
"props": {
"style": {
"marginBottom": "0"
},
"label": "类目名:"
},
"children": [{
"componentName": "Select",
"id": "node_dockcy8n9xer",
"props": {
"mode": "single",
"hasArrow": true,
"cacheValue": true,
"style": {
"width": "150px"
}
}
}]
}, {
"componentName": "Form.Item",
"id": "node_dockcy8n9xes",
"props": {
"style": {
"marginBottom": "0"
},
"label": "项目类型:"
},
"children": [{
"componentName": "Select",
"id": "node_dockcy8n9xet",
"props": {
"mode": "single",
"hasArrow": true,
"cacheValue": true,
"style": {
"width": "200px"
}
}
}]
}, {
"componentName": "Form.Item",
"id": "node_dockcy8n9xeu",
"props": {
"style": {
"marginBottom": "0"
},
"label": "项目 ID"
},
"children": [{
"componentName": "Input",
"id": "node_dockcy8n9xev",
"props": {
"hasBorder": true,
"size": "medium",
"autoComplete": "off",
"style": {
"width": "200px"
}
}
}]
}, {
"componentName": "Button.Group",
"id": "node_dockcy8n9xew",
"props": {},
"children": [{
"componentName": "Button",
"id": "node_dockcy8n9xex",
"props": {
"type": "primary",
"style": {
"margin": "0 5px 0 5px"
},
"htmlType": "submit"
},
"children": [{
"componentName": "Icon",
"id": "node_dockcy8n9xey",
"props": {
"type": "success"
}
}, "搜索"]
}, {
"componentName": "Button",
"id": "node_dockcy8n9xe10",
"props": {
"type": "normal",
"style": {
"margin": "0 5px 0 5px"
},
"htmlType": "reset"
},
"children": ["清空"]
}]
}]
}]
}, {
"componentName": "Box",
"id": "node_dockcy8n9xe12",
"props": {
"style": {
"justifyContent": "flex-end",
"display": "flex",
"backgroundColor": "#ffffff",
"flexDirection": "row",
"paddingRight": "24px"
}
},
"children": [{
"componentName": "Button",
"id": "node_dockcy8n9xe13",
"props": {
"prefix": "next-",
"type": "primary",
"size": "medium",
"htmlType": "button",
"component": "button",
"style": {
"width": "100px"
},
"events": {
"onClick": {
"type": "JSFunction",
"value": "function(){ this.onClick() }",
"__eventData": {
"type": "componentEvent",
"name": "onClick",
"relatedEventName": "onClick"
}
}
},
"__events": [{
"type": "componentEvent",
"name": "onClick",
"relatedEventName": "onClick"
}],
"onClick": {
"type": "JSFunction",
"value": "function(){ this.onClick() }"
}
},
"children": ["新建配置"]
}]
}, {
"componentName": "Box",
"id": "node_dockcy8n9xe15",
"props": {
"style": {
"backgroundColor": "#ffffff"
}
},
"children": [{
"componentName": "Table",
"id": "node_dockcy8n9xe16",
"props": {
"dataSource": [{
"firstCategory": "其他",
"secondCategory": "新品预览",
"leafCategory": "",
"projectType": "标识判断",
"projectId": "",
"title": "其他类目->新品预览类目类型知识库",
"url": "其他",
"operation": "编辑"
}, {
"firstCategory": "其他",
"secondCategory": "新品预览",
"leafCategory": "",
"projectType": "",
"projectId": "1",
"title": "其他类目->新品预览项目Id知识库",
"url": "其他",
"operation": "编辑"
}],
"size": "medium",
"prefix": "next-",
"hasBorder": true,
"hasHeader": true,
"isZebra": false,
"loading": false,
"expandedIndexSimulate": false,
"primaryKey": "id",
"locale": "zhCN.Table",
"crossline": false,
"style": {
"margin": "24px 12px 24px 12px"
}
},
"children": [{
"componentName": "Table.Column",
"id": "node_dockcy8n9xe17",
"props": {
"title": "一级类目",
"dataIndex": "firstCategory"
}
}, {
"componentName": "Table.Column",
"id": "node_dockcy8n9xe18",
"props": {
"title": "二级类目",
"dataIndex": "secondCategory"
}
}, {
"componentName": "Table.Column",
"id": "node_dockcy8n9xe19",
"props": {
"title": "叶子类目",
"dataIndex": "leafCategory"
}
}, {
"componentName": "Table.Column",
"id": "node_dockcy8n9xe1a",
"props": {
"title": "项目类型",
"dataIndex": "projectType"
}
}, {
"componentName": "Table.Column",
"id": "node_dockcy8n9xe1b",
"props": {
"title": "项目 ID",
"dataIndex": "projectId"
}
}, {
"componentName": "Table.Column",
"id": "node_dockcy8n9xe1c",
"props": {
"title": "知识条标题",
"dataIndex": "title"
}
}, {
"componentName": "Table.Column",
"id": "node_dockcy8n9xe1d",
"props": {
"title": "知识条链接",
"dataIndex": "url"
}
}, {
"componentName": "Table.Column",
"id": "node_dockcy8n9xe1e",
"props": {
"title": "操作",
"dataIndex": "operation"
}
}]
}]
}, {
"componentName": "Box",
"id": "node_dockcy8n9xe1f",
"props": {
"style": {
"backgroundColor": "#ffffff",
"paddingBottom": "24px"
}
},
"children": [{
"componentName": "Pagination",
"id": "node_dockcy8n9xe1g",
"props": {
"prefix": "next-",
"type": "normal",
"shape": "normal",
"size": "medium",
"defaultCurrent": 1,
"total": 100,
"pageShowCount": 5,
"pageSize": 10,
"pageSizePosition": "start",
"showJump": true,
"style": {
"display": "flex",
"justifyContent": "flex-end"
}
}
}]
}]
}, {
"componentName": "Dialog",
"id": "node_dockcy8n9xe1h",
"props": {
"prefix": "next-",
"footerAlign": "right",
"footerActions": ["ok", "cancel"],
"closeable": "esc,close",
"hasMask": true,
"align": "cc cc",
"minMargin": 40,
"visible": {
"type": "JSExpression",
"value": "this.state.isShowDialog"
},
"children": {
"type": "JSSlot"
},
"title": "标题",
"footer": {
"type": "JSSlot"
},
"events": []
}
}]
}

View File

@ -1,20 +1,27 @@
{ {
"name": "@ali/lowcode-code-generator", "name": "@ali/lowcode-code-generator",
"version": "0.8.10", "version": "1.0.12",
"description": "出码引擎 for LowCode Engine", "description": "出码引擎 for LowCode Engine",
"main": "lib/index.js", "main": "lib/index.js",
"module": "es/index.js",
"typings": "es/index.d.ts",
"files": [ "files": [
"lib", "lib",
"es",
"demo" "demo"
], ],
"scripts": { "scripts": {
"build": "rimraf lib && tsc", "start": "ava --watch",
"build": "npm run build:bs",
"build:bs": "rimraf lib && rimraf es && build-scripts build --skip-demo",
"build:tsc": "rimraf lib && tsc",
"demo": "node ./demo/demo.js", "demo": "node ./demo/demo.js",
"test": "ava", "test": "ava",
"template": "node ./tools/createTemplate.js" "template": "node ./tools/createTemplate.js"
}, },
"dependencies": { "dependencies": {
"@ali/am-eslint-config": "*", "@ali/am-eslint-config": "*",
"@ali/lowcode-types": "^1.0.12",
"@ali/my-prettier": "^1.0.0", "@ali/my-prettier": "^1.0.0",
"@babel/generator": "^7.9.5", "@babel/generator": "^7.9.5",
"@babel/parser": "^7.9.4", "@babel/parser": "^7.9.4",
@ -24,24 +31,34 @@
"change-case": "^3.1.0", "change-case": "^3.1.0",
"jszip": "^3.5.0", "jszip": "^3.5.0",
"prettier": "^2.0.2", "prettier": "^2.0.2",
"semver": "^7.3.2",
"short-uuid": "^3.1.1" "short-uuid": "^3.1.1"
}, },
"devDependencies": { "devDependencies": {
"@alib/build-scripts": "^0.1.18",
"@types/babel__traverse": "^7.0.10", "@types/babel__traverse": "^7.0.10",
"ava": "^1.0.1", "ava": "^1.0.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"ts-loader": "^6.2.2", "ts-loader": "^6.2.2",
"ts-node": "^7.0.1", "ts-node": "^8.10.2",
"tsconfig-paths": "^3.9.0" "tsconfig-paths": "^3.9.0"
}, },
"ava": { "ava": {
"compileEnhancements": false, "compileEnhancements": false,
"snapshotDir": "test/fixtures/__snapshots__", "snapshotDir": "test/fixtures/__snapshots__",
"files": [
"test/**/*.test.ts"
],
"extensions": [ "extensions": [
"ts" "ts"
], ],
"require": [ "require": [
"ts-node/register" "ts-node/register/transpile-only"
],
"sources": [
"src/**/*",
"test/**/*",
"test-cases/**/expected/**/*"
] ]
}, },
"publishConfig": { "publishConfig": {

View File

@ -1,4 +1,4 @@
/// <reference types="node" /> // / <reference types="node" />
declare module '@ali/my-prettier' { declare module '@ali/my-prettier' {
function format(text: string, type: string): string; function format(text: string, type: string): string;

View File

@ -0,0 +1,3 @@
import { FileType } from '../types/core';
export const FILE_TYPE_FAMILY = [[FileType.TSX, FileType.TS, FileType.JSX, FileType.JS]];

View File

@ -1,6 +1,7 @@
export const COMMON_CHUNK_NAME = { export const COMMON_CHUNK_NAME = {
ExternalDepsImport: 'CommonExternalDependencyImport', ExternalDepsImport: 'CommonExternalDependencyImport',
InternalDepsImport: 'CommonInternalDependencyImport', InternalDepsImport: 'CommonInternalDependencyImport',
ImportAliasDefine: 'CommonImportAliasDefine',
FileVarDefine: 'CommonFileScopeVarDefine', FileVarDefine: 'CommonFileScopeVarDefine',
FileUtilDefine: 'CommonFileScopeMethodDefine', FileUtilDefine: 'CommonFileScopeMethodDefine',
FileMainContent: 'CommonFileMainContent', FileMainContent: 'CommonFileMainContent',
@ -21,24 +22,29 @@ export const CLASS_DEFINE_CHUNK_NAME = {
InsVar: 'CommonClassDefineInsVar', InsVar: 'CommonClassDefineInsVar',
InsVarMethod: 'CommonClassDefineInsVarMethod', InsVarMethod: 'CommonClassDefineInsVarMethod',
InsMethod: 'CommonClassDefineInsMethod', InsMethod: 'CommonClassDefineInsMethod',
InsPrivateMethod: 'CommonClassDefineInsPrivateMethod',
End: 'CommonClassDefineEnd', End: 'CommonClassDefineEnd',
}; };
export const DEFAULT_LINK_AFTER = { export const DEFAULT_LINK_AFTER = {
[COMMON_CHUNK_NAME.ExternalDepsImport]: [], [COMMON_CHUNK_NAME.ExternalDepsImport]: [],
[COMMON_CHUNK_NAME.InternalDepsImport]: [COMMON_CHUNK_NAME.ExternalDepsImport], [COMMON_CHUNK_NAME.InternalDepsImport]: [COMMON_CHUNK_NAME.ExternalDepsImport],
[COMMON_CHUNK_NAME.ImportAliasDefine]: [COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport],
[COMMON_CHUNK_NAME.FileVarDefine]: [ [COMMON_CHUNK_NAME.FileVarDefine]: [
COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.ImportAliasDefine,
], ],
[COMMON_CHUNK_NAME.FileUtilDefine]: [ [COMMON_CHUNK_NAME.FileUtilDefine]: [
COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.ImportAliasDefine,
COMMON_CHUNK_NAME.FileVarDefine, COMMON_CHUNK_NAME.FileVarDefine,
], ],
[CLASS_DEFINE_CHUNK_NAME.Start]: [ [CLASS_DEFINE_CHUNK_NAME.Start]: [
COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.ImportAliasDefine,
COMMON_CHUNK_NAME.FileVarDefine, COMMON_CHUNK_NAME.FileVarDefine,
COMMON_CHUNK_NAME.FileUtilDefine, COMMON_CHUNK_NAME.FileUtilDefine,
], ],
@ -49,20 +55,13 @@ export const DEFAULT_LINK_AFTER = {
CLASS_DEFINE_CHUNK_NAME.InsVar, CLASS_DEFINE_CHUNK_NAME.InsVar,
CLASS_DEFINE_CHUNK_NAME.InsVarMethod, CLASS_DEFINE_CHUNK_NAME.InsVarMethod,
], ],
[CLASS_DEFINE_CHUNK_NAME.ConstructorContent]: [ [CLASS_DEFINE_CHUNK_NAME.ConstructorContent]: [CLASS_DEFINE_CHUNK_NAME.ConstructorStart],
CLASS_DEFINE_CHUNK_NAME.ConstructorStart,
],
[CLASS_DEFINE_CHUNK_NAME.ConstructorEnd]: [ [CLASS_DEFINE_CHUNK_NAME.ConstructorEnd]: [
CLASS_DEFINE_CHUNK_NAME.ConstructorStart, CLASS_DEFINE_CHUNK_NAME.ConstructorStart,
CLASS_DEFINE_CHUNK_NAME.ConstructorContent, CLASS_DEFINE_CHUNK_NAME.ConstructorContent,
], ],
[CLASS_DEFINE_CHUNK_NAME.StaticVar]: [ [CLASS_DEFINE_CHUNK_NAME.StaticVar]: [CLASS_DEFINE_CHUNK_NAME.Start],
CLASS_DEFINE_CHUNK_NAME.Start, [CLASS_DEFINE_CHUNK_NAME.StaticMethod]: [CLASS_DEFINE_CHUNK_NAME.Start, CLASS_DEFINE_CHUNK_NAME.StaticVar],
],
[CLASS_DEFINE_CHUNK_NAME.StaticMethod]: [
CLASS_DEFINE_CHUNK_NAME.Start,
CLASS_DEFINE_CHUNK_NAME.StaticVar,
],
[CLASS_DEFINE_CHUNK_NAME.InsVar]: [ [CLASS_DEFINE_CHUNK_NAME.InsVar]: [
CLASS_DEFINE_CHUNK_NAME.Start, CLASS_DEFINE_CHUNK_NAME.Start,
CLASS_DEFINE_CHUNK_NAME.StaticVar, CLASS_DEFINE_CHUNK_NAME.StaticVar,
@ -82,7 +81,7 @@ export const DEFAULT_LINK_AFTER = {
CLASS_DEFINE_CHUNK_NAME.InsVarMethod, CLASS_DEFINE_CHUNK_NAME.InsVarMethod,
CLASS_DEFINE_CHUNK_NAME.ConstructorEnd, CLASS_DEFINE_CHUNK_NAME.ConstructorEnd,
], ],
[CLASS_DEFINE_CHUNK_NAME.End]: [ [CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod]: [
CLASS_DEFINE_CHUNK_NAME.Start, CLASS_DEFINE_CHUNK_NAME.Start,
CLASS_DEFINE_CHUNK_NAME.StaticVar, CLASS_DEFINE_CHUNK_NAME.StaticVar,
CLASS_DEFINE_CHUNK_NAME.StaticMethod, CLASS_DEFINE_CHUNK_NAME.StaticMethod,
@ -91,9 +90,20 @@ export const DEFAULT_LINK_AFTER = {
CLASS_DEFINE_CHUNK_NAME.InsMethod, CLASS_DEFINE_CHUNK_NAME.InsMethod,
CLASS_DEFINE_CHUNK_NAME.ConstructorEnd, 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.InsPrivateMethod,
CLASS_DEFINE_CHUNK_NAME.ConstructorEnd,
],
[COMMON_CHUNK_NAME.FileMainContent]: [ [COMMON_CHUNK_NAME.FileMainContent]: [
COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.ImportAliasDefine,
COMMON_CHUNK_NAME.FileVarDefine, COMMON_CHUNK_NAME.FileVarDefine,
COMMON_CHUNK_NAME.FileUtilDefine, COMMON_CHUNK_NAME.FileUtilDefine,
CLASS_DEFINE_CHUNK_NAME.End, CLASS_DEFINE_CHUNK_NAME.End,
@ -101,6 +111,7 @@ export const DEFAULT_LINK_AFTER = {
[COMMON_CHUNK_NAME.FileExport]: [ [COMMON_CHUNK_NAME.FileExport]: [
COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.ImportAliasDefine,
COMMON_CHUNK_NAME.FileVarDefine, COMMON_CHUNK_NAME.FileVarDefine,
COMMON_CHUNK_NAME.FileUtilDefine, COMMON_CHUNK_NAME.FileUtilDefine,
CLASS_DEFINE_CHUNK_NAME.End, CLASS_DEFINE_CHUNK_NAME.End,
@ -109,6 +120,6 @@ export const DEFAULT_LINK_AFTER = {
[COMMON_CHUNK_NAME.StyleDepsImport]: [], [COMMON_CHUNK_NAME.StyleDepsImport]: [],
[COMMON_CHUNK_NAME.StyleCssContent]: [COMMON_CHUNK_NAME.StyleDepsImport], [COMMON_CHUNK_NAME.StyleCssContent]: [COMMON_CHUNK_NAME.StyleDepsImport],
[COMMON_CHUNK_NAME.HtmlContent]: [], [COMMON_CHUNK_NAME.HtmlContent]: [],
} };
export const COMMON_SUB_MODULE_NAME = 'index'; export const COMMON_SUB_MODULE_NAME = 'index';

View File

@ -1,4 +1,4 @@
export const NATIVE_ELE_PKG: string = 'native'; export const NATIVE_ELE_PKG = 'native';
export const CONTAINER_TYPE = { export const CONTAINER_TYPE = {
COMPONENT: 'Component', COMPONENT: 'Component',

View File

@ -1,28 +1,72 @@
import { import { BuilderComponentPlugin, IChunkBuilder, ICodeChunk, ICodeStruct, FileType } from '../types';
BuilderComponentPlugin,
IChunkBuilder,
ICodeChunk,
ICodeStruct,
} from '../types';
import { COMMON_SUB_MODULE_NAME } from '../const/generator'; import { COMMON_SUB_MODULE_NAME } from '../const/generator';
import { FILE_TYPE_FAMILY } from '../const/file';
type ChunkGroupInfo = {
chunk: ICodeChunk;
familyIdx?: number;
};
function whichFamily(type: FileType): [number, FileType[]] | undefined {
const idx = FILE_TYPE_FAMILY.findIndex((family) => family.indexOf(type) >= 0);
if (idx < 0) {
return undefined;
}
return [idx, FILE_TYPE_FAMILY[idx]];
}
export const groupChunks = (chunks: ICodeChunk[]): ICodeChunk[][] => { export const groupChunks = (chunks: ICodeChunk[]): ICodeChunk[][] => {
const col = chunks.reduce( const tmp: Record<string, Record<number, number>> = {};
(chunksSet: Record<string, ICodeChunk[]>, chunk) => { const col = chunks.reduce((chunksSet: Record<string, ChunkGroupInfo[]>, chunk) => {
const fileKey = `${chunk.subModule || COMMON_SUB_MODULE_NAME}.${ const fileKey = chunk.subModule || COMMON_SUB_MODULE_NAME;
chunk.fileType
}`;
if (!chunksSet[fileKey]) { if (!chunksSet[fileKey]) {
chunksSet[fileKey] = []; chunksSet[fileKey] = [];
} }
chunksSet[fileKey].push(chunk); const res = whichFamily(chunk.fileType as FileType);
return chunksSet; const info: ChunkGroupInfo = {
}, chunk,
{}, };
); if (res) {
const [familyIdx, family] = res;
const rank = family.indexOf(chunk.fileType as FileType);
if (tmp[fileKey]) {
if (tmp[fileKey][familyIdx] !== undefined) {
if (tmp[fileKey][familyIdx] > rank) {
tmp[fileKey][familyIdx] = rank;
}
} else {
tmp[fileKey][familyIdx] = rank;
}
} else {
tmp[fileKey] = {};
tmp[fileKey][familyIdx] = rank;
}
info.familyIdx = familyIdx;
}
return Object.keys(col).map(key => col[key]); chunksSet[fileKey].push(info);
return chunksSet;
}, {});
const result: ICodeChunk[][] = [];
Object.keys(col).forEach((key) => {
const byType: Record<string, ICodeChunk[]> = {};
col[key].forEach((info) => {
let t: string = info.chunk.fileType;
if (info.familyIdx !== undefined) {
t = FILE_TYPE_FAMILY[info.familyIdx][tmp[key][info.familyIdx]];
info.chunk.fileType = t;
}
if (!byType[t]) {
byType[t] = [];
}
byType[t].push(info.chunk);
});
result.push(...Object.keys(byType).map((t) => byType[t]));
});
return result;
}; };
/** /**
@ -39,7 +83,7 @@ export default class ChunkBuilder implements IChunkBuilder {
this.plugins = plugins; this.plugins = plugins;
} }
public async run( async run(
ir: unknown, ir: unknown,
initialStructure: ICodeStruct = { initialStructure: ICodeStruct = {
ir, ir,
@ -64,11 +108,11 @@ export default class ChunkBuilder implements IChunkBuilder {
}; };
} }
public getPlugins() { getPlugins() {
return this.plugins; return this.plugins;
} }
public addPlugin(plugin: BuilderComponentPlugin) { addPlugin(plugin: BuilderComponentPlugin) {
this.plugins.push(plugin); this.plugins.push(plugin);
} }
} }

View File

@ -1,18 +1,11 @@
import { import { ChunkContent, ChunkType, CodeGeneratorError, CodeGeneratorFunction, ICodeBuilder, ICodeChunk } from '../types';
ChunkContent,
ChunkType,
CodeGeneratorError,
CodeGeneratorFunction,
ICodeBuilder,
ICodeChunk,
} from '../types';
export default class Builder implements ICodeBuilder { export default class Builder implements ICodeBuilder {
private chunkDefinitions: ICodeChunk[] = []; private chunkDefinitions: ICodeChunk[] = [];
private generators: { [key: string]: CodeGeneratorFunction<ChunkContent> } = { private generators: { [key: string]: CodeGeneratorFunction<ChunkContent> } = {
[ChunkType.STRING]: (str: string) => str, // no-op for string chunks [ChunkType.STRING]: (str: string) => str, // no-op for string chunks
[ChunkType.JSON]: (json: object) => JSON.stringify(json), // stringify json to string [ChunkType.JSON]: (json: Record<string, unknown>) => JSON.stringify(json), // stringify json to string
}; };
constructor(chunkDefinitions: ICodeChunk[] = []) { constructor(chunkDefinitions: ICodeChunk[] = []) {
@ -23,13 +16,13 @@ export default class Builder implements ICodeBuilder {
* Links all chunks together based on their requirements. Returns an array * Links all chunks together based on their requirements. Returns an array
* of ordered chunk names which need to be compiled and glued together. * of ordered chunk names which need to be compiled and glued together.
*/ */
public link(chunkDefinitions: ICodeChunk[] = []): string { link(chunkDefinitions: ICodeChunk[] = []): string {
const chunks = chunkDefinitions || this.chunkDefinitions; const chunks = chunkDefinitions || this.chunkDefinitions;
if (chunks.length <= 0) { if (chunks.length <= 0) {
return ''; return '';
} }
const unprocessedChunks = chunks.map(chunk => { const unprocessedChunks = chunks.map((chunk) => {
return { return {
name: chunk.name, name: chunk.name,
type: chunk.type, type: chunk.type,
@ -50,22 +43,23 @@ export default class Builder implements ICodeBuilder {
} }
if (unprocessedChunks[indexToRemove].linkAfter.length > 0) { if (unprocessedChunks[indexToRemove].linkAfter.length > 0) {
throw new CodeGeneratorError( throw new CodeGeneratorError('Operation aborted. Reason: cyclic dependency between chunks.');
'Operation aborted. Reason: cyclic dependency between chunks.',
);
} }
const { type, content, name } = unprocessedChunks[indexToRemove]; const { type, content, name } = unprocessedChunks[indexToRemove];
const compiledContent = this.generateByType(type, content); const compiledContent = this.generateByType(type, content);
if (compiledContent) { if (compiledContent) {
resultingString.push(compiledContent + '\n'); resultingString.push(`${compiledContent }\n`);
} }
unprocessedChunks.splice(indexToRemove, 1); unprocessedChunks.splice(indexToRemove, 1);
if (!unprocessedChunks.some(ch => ch.name === name)) { if (!unprocessedChunks.some((ch) => ch.name === name)) {
unprocessedChunks.forEach( unprocessedChunks.forEach(
// remove the processed chunk from all the linkAfter arrays from the remaining chunks // remove the processed chunk from all the linkAfter arrays from the remaining chunks
ch => (ch.linkAfter = ch.linkAfter.filter(after => after !== name)), (ch) => {
// eslint-disable-next-line no-param-reassign
ch.linkAfter = ch.linkAfter.filter((after) => after !== name);
},
); );
} }
} }
@ -73,14 +67,12 @@ export default class Builder implements ICodeBuilder {
return resultingString.join('\n'); return resultingString.join('\n');
} }
public generateByType(type: string, content: unknown): string { generateByType(type: string, content: unknown): string {
if (!content) { if (!content) {
return ''; return '';
} }
if (Array.isArray(content)) { if (Array.isArray(content)) {
return content return content.map((contentItem) => this.generateByType(type, contentItem)).join('\n');
.map(contentItem => this.generateByType(type, contentItem))
.join('\n');
} }
if (!this.generators[type]) { if (!this.generators[type]) {
@ -95,8 +87,6 @@ export default class Builder implements ICodeBuilder {
// remove invalid chunks (which did not end up being created) from the linkAfter fields // remove invalid chunks (which did not end up being created) from the linkAfter fields
// one use-case is when you want to remove the import plugin // one use-case is when you want to remove the import plugin
private cleanupInvalidChunks(linkAfter: string[], chunks: ICodeChunk[]) { private cleanupInvalidChunks(linkAfter: string[], chunks: ICodeChunk[]) {
return linkAfter.filter(chunkName => return linkAfter.filter((chunkName) => chunks.some((chunk) => chunk.name === chunkName));
chunks.some(chunk => chunk.name === chunkName),
);
} }
} }

View File

@ -1,13 +1,12 @@
import { ProjectSchema, ResultFile, ResultDir } from '@ali/lowcode-types';
import { import {
BuilderComponentPlugin, BuilderComponentPlugin,
CodeGeneratorError, CodeGeneratorError,
IBasicSchema,
ICodeChunk, ICodeChunk,
ICompiledModule, ICompiledModule,
IModuleBuilder, IModuleBuilder,
IParseResult, IParseResult,
IResultDir,
IResultFile,
ISchemaParser, ISchemaParser,
PostProcessor, PostProcessor,
} from '../types'; } from '../types';
@ -17,9 +16,7 @@ import { COMMON_SUB_MODULE_NAME } from '../const/generator';
import SchemaParser from '../parser/SchemaParser'; import SchemaParser from '../parser/SchemaParser';
import ChunkBuilder from './ChunkBuilder'; import ChunkBuilder from './ChunkBuilder';
import CodeBuilder from './CodeBuilder'; import CodeBuilder from './CodeBuilder';
import { createResultFile, createResultDir, addFile } from '../utils/resultHelper';
import ResultDir from '../model/ResultDir';
import ResultFile from '../model/ResultFile';
export function createModuleBuilder( export function createModuleBuilder(
options: { options: {
@ -37,33 +34,27 @@ export function createModuleBuilder(
const generateModule = async (input: unknown): Promise<ICompiledModule> => { const generateModule = async (input: unknown): Promise<ICompiledModule> => {
const moduleMainName = options.mainFileName || COMMON_SUB_MODULE_NAME; const moduleMainName = options.mainFileName || COMMON_SUB_MODULE_NAME;
if (chunkGenerator.getPlugins().length <= 0) { if (chunkGenerator.getPlugins().length <= 0) {
throw new CodeGeneratorError( throw new CodeGeneratorError('No plugins found. Component generation cannot work without any plugins!');
'No plugins found. Component generation cannot work without any plugins!',
);
} }
let files: IResultFile[] = []; let files: ResultFile[] = [];
const { chunks } = await chunkGenerator.run(input); const { chunks } = await chunkGenerator.run(input);
chunks.forEach(fileChunkList => { chunks.forEach((fileChunkList) => {
const content = linker.link(fileChunkList); const content = linker.link(fileChunkList);
const file = new ResultFile( const file = createResultFile(fileChunkList[0].subModule || moduleMainName, fileChunkList[0].fileType, content);
fileChunkList[0].subModule || moduleMainName,
fileChunkList[0].fileType,
content,
);
files.push(file); files.push(file);
}); });
if (options.postProcessors.length > 0) { if (options.postProcessors.length > 0) {
files = files.map(file => { files = files.map((file) => {
let content = file.content; let { content } = file;
const type = file.ext; const type = file.ext;
options.postProcessors.forEach(processer => { options.postProcessors.forEach((processer) => {
content = processer(content, type); content = processer(content, type);
}); });
return new ResultFile(file.name, type, content); return createResultFile(file.name, type, content);
}); });
} }
@ -72,7 +63,7 @@ export function createModuleBuilder(
}; };
}; };
const generateModuleCode = async (schema: IBasicSchema | string): Promise<IResultDir> => { const generateModuleCode = async (schema: ProjectSchema | string): Promise<ResultDir> => {
// Init // Init
const schemaParser: ISchemaParser = new SchemaParser(); const schemaParser: ISchemaParser = new SchemaParser();
const parseResult: IParseResult = schemaParser.parse(schema); const parseResult: IParseResult = schemaParser.parse(schema);
@ -80,26 +71,19 @@ export function createModuleBuilder(
const containerInfo = parseResult.containers[0]; const containerInfo = parseResult.containers[0];
const { files } = await generateModule(containerInfo); const { files } = await generateModule(containerInfo);
const dir = new ResultDir(containerInfo.moduleName); const dir = createResultDir(containerInfo.moduleName);
files.forEach(file => dir.addFile(file)); files.forEach((file) => addFile(dir, file));
return dir; return dir;
} };
const linkCodeChunks = ( const linkCodeChunks = (chunks: Record<string, ICodeChunk[]>, fileName: string) => {
chunks: Record<string, ICodeChunk[]>, const files: ResultFile[] = [];
fileName: string,
) => {
const files: IResultFile[] = [];
Object.keys(chunks).forEach(fileKey => { Object.keys(chunks).forEach((fileKey) => {
const fileChunkList = chunks[fileKey]; const fileChunkList = chunks[fileKey];
const content = linker.link(fileChunkList); const content = linker.link(fileChunkList);
const file = new ResultFile( const file = createResultFile(fileChunkList[0].subModule || fileName, fileChunkList[0].fileType, content);
fileChunkList[0].subModule || fileName,
fileChunkList[0].fileType,
content,
);
files.push(file); files.push(file);
}); });

View File

@ -1,36 +1,35 @@
import { ResultDir, ResultFile, ProjectSchema } from '@ali/lowcode-types';
import { import {
IModuleBuilder, IModuleBuilder,
IParseResult, IParseResult,
IProjectBuilder, IProjectBuilder,
IProjectPlugins, IProjectPlugins,
IProjectSchema,
IProjectTemplate, IProjectTemplate,
IResultDir,
IResultFile,
ISchemaParser, ISchemaParser,
PostProcessor, PostProcessor,
} from '../types'; } from '../types';
import ResultDir from '../model/ResultDir';
import SchemaParser from '../parser/SchemaParser'; import SchemaParser from '../parser/SchemaParser';
import { createResultDir, addDirectory, addFile } from '../utils/resultHelper';
import { createModuleBuilder } from '../generator/ModuleBuilder'; import { createModuleBuilder } from '../generator/ModuleBuilder';
interface IModuleInfo { interface IModuleInfo {
moduleName?: string; moduleName?: string;
path: string[]; path: string[];
files: IResultFile[]; files: ResultFile[];
} }
function getDirFromRoot(root: IResultDir, path: string[]): IResultDir { function getDirFromRoot(root: ResultDir, path: string[]): ResultDir {
let current: IResultDir = root; let current: ResultDir = root;
path.forEach(p => { path.forEach((p) => {
const exist = current.dirs.find(d => d.name === p); const exist = current.dirs.find((d) => d.name === p);
if (exist) { if (exist) {
current = exist; current = exist;
} else { } else {
const newDir = new ResultDir(p); const newDir = createResultDir(p);
current.addDirectory(newDir); addDirectory(current, newDir);
current = newDir; current = newDir;
} }
}); });
@ -40,7 +39,9 @@ function getDirFromRoot(root: IResultDir, path: string[]): IResultDir {
export class ProjectBuilder implements IProjectBuilder { export class ProjectBuilder implements IProjectBuilder {
private template: IProjectTemplate; private template: IProjectTemplate;
private plugins: IProjectPlugins; private plugins: IProjectPlugins;
private postProcessors: PostProcessor[]; private postProcessors: PostProcessor[];
constructor({ constructor({
@ -57,7 +58,7 @@ export class ProjectBuilder implements IProjectBuilder {
this.postProcessors = postProcessors; this.postProcessors = postProcessors;
} }
public async generateProject(schema: IProjectSchema | string): Promise<IResultDir> { async generateProject(schema: ProjectSchema | string): Promise<ResultDir> {
// Init // Init
const schemaParser: ISchemaParser = new SchemaParser(); const schemaParser: ISchemaParser = new SchemaParser();
const builders = this.createModuleBuilders(); const builders = this.createModuleBuilders();
@ -76,7 +77,7 @@ export class ProjectBuilder implements IProjectBuilder {
// components // components
// pages // pages
const containerBuildResult: IModuleInfo[] = await Promise.all<IModuleInfo>( const containerBuildResult: IModuleInfo[] = await Promise.all<IModuleInfo>(
parseResult.containers.map(async containerInfo => { parseResult.containers.map(async (containerInfo) => {
let builder: IModuleBuilder; let builder: IModuleBuilder;
let path: string[]; let path: string[];
if (containerInfo.containerType === 'Page') { if (containerInfo.containerType === 'Page') {
@ -100,9 +101,7 @@ export class ProjectBuilder implements IProjectBuilder {
// router // router
if (parseResult.globalRouter && builders.router) { if (parseResult.globalRouter && builders.router) {
const { files } = await builders.router.generateModule( const { files } = await builders.router.generateModule(parseResult.globalRouter);
parseResult.globalRouter,
);
buildResult.push({ buildResult.push({
path: this.template.slots.router.path, path: this.template.slots.router.path,
@ -112,83 +111,87 @@ export class ProjectBuilder implements IProjectBuilder {
// entry // entry
if (parseResult.project && builders.entry) { if (parseResult.project && builders.entry) {
const { files } = await builders.entry.generateModule( const { files } = await builders.entry.generateModule(parseResult.project);
parseResult.project,
);
buildResult.push({ buildResult.push({
path: this.template.slots.entry.path, path: this.template.slots.entry.path,
files, files,
}); });
} }
// appConfig
if (builders.appConfig) {
const { files } = await builders.appConfig.generateModule(parseResult);
buildResult.push({
path: this.template.slots.appConfig.path,
files,
});
}
// buildConfig
if (builders.buildConfig) {
const { files } = await builders.buildConfig.generateModule(parseResult);
buildResult.push({
path: this.template.slots.buildConfig.path,
files,
});
}
// constants? // constants?
if ( if (parseResult.project && builders.constants && this.template.slots.constants) {
parseResult.project && const { files } = await builders.constants.generateModule(parseResult.project);
builders.constants &&
this.template.slots.constants
) {
const { files } = await builders.constants.generateModule(
parseResult.project,
);
buildResult.push({ buildResult.push({
path: this.template.slots.constants.path, path: this.template.slots.constants.path,
files, files,
}); });
} }
// utils? // utils?
if ( if (parseResult.globalUtils && builders.utils && this.template.slots.utils) {
parseResult.globalUtils && const { files } = await builders.utils.generateModule(parseResult.globalUtils);
builders.utils &&
this.template.slots.utils
) {
const { files } = await builders.utils.generateModule(
parseResult.globalUtils,
);
buildResult.push({ buildResult.push({
path: this.template.slots.utils.path, path: this.template.slots.utils.path,
files, files,
}); });
} }
// i18n? // i18n?
if (parseResult.globalI18n && builders.i18n && this.template.slots.i18n) { if (builders.i18n && this.template.slots.i18n) {
const { files } = await builders.i18n.generateModule( const { files } = await builders.i18n.generateModule(parseResult.project);
parseResult.globalI18n,
);
buildResult.push({ buildResult.push({
path: this.template.slots.i18n.path, path: this.template.slots.i18n.path,
files, files,
}); });
} }
// globalStyle // globalStyle
if (parseResult.project && builders.globalStyle) { if (parseResult.project && builders.globalStyle) {
const { files } = await builders.globalStyle.generateModule( const { files } = await builders.globalStyle.generateModule(parseResult.project);
parseResult.project,
);
buildResult.push({ buildResult.push({
path: this.template.slots.globalStyle.path, path: this.template.slots.globalStyle.path,
files, files,
}); });
} }
// htmlEntry // htmlEntry
if (parseResult.project && builders.htmlEntry) { if (parseResult.project && builders.htmlEntry) {
const { files } = await builders.htmlEntry.generateModule( const { files } = await builders.htmlEntry.generateModule(parseResult.project);
parseResult.project,
);
buildResult.push({ buildResult.push({
path: this.template.slots.htmlEntry.path, path: this.template.slots.htmlEntry.path,
files, files,
}); });
} }
// packageJSON // packageJSON
if (parseResult.project && builders.packageJSON) { if (parseResult.project && builders.packageJSON) {
const { files } = await builders.packageJSON.generateModule( const { files } = await builders.packageJSON.generateModule(parseResult.project);
parseResult.project,
);
buildResult.push({ buildResult.push({
path: this.template.slots.packageJSON.path, path: this.template.slots.packageJSON.path,
@ -196,17 +199,19 @@ export class ProjectBuilder implements IProjectBuilder {
}); });
} }
// TODO: 更多 slots 的处理??是不是可以考虑把 template 中所有的 slots 都处理下?
// Post Process // Post Process
// Combine Modules // Combine Modules
buildResult.forEach(moduleInfo => { buildResult.forEach((moduleInfo) => {
let targetDir = getDirFromRoot(projectRoot, moduleInfo.path); let targetDir = getDirFromRoot(projectRoot, moduleInfo.path);
if (moduleInfo.moduleName) { if (moduleInfo.moduleName) {
const dir = new ResultDir(moduleInfo.moduleName); const dir = createResultDir(moduleInfo.moduleName);
targetDir.addDirectory(dir); addDirectory(targetDir, dir);
targetDir = dir; targetDir = dir;
} }
moduleInfo.files.forEach(file => targetDir.addFile(file)); moduleInfo.files.forEach((file) => addFile(targetDir, file));
}); });
return projectRoot; return projectRoot;
@ -215,7 +220,7 @@ export class ProjectBuilder implements IProjectBuilder {
private createModuleBuilders(): Record<string, IModuleBuilder> { private createModuleBuilders(): Record<string, IModuleBuilder> {
const builders: Record<string, IModuleBuilder> = {}; const builders: Record<string, IModuleBuilder> = {};
Object.keys(this.plugins).forEach(pluginName => { Object.keys(this.plugins).forEach((pluginName) => {
if (this.plugins[pluginName].length > 0) { if (this.plugins[pluginName].length > 0) {
const options: { mainFileName?: string } = {}; const options: { mainFileName?: string } = {};
if (this.template.slots[pluginName] && this.template.slots[pluginName].fileName) { if (this.template.slots[pluginName] && this.template.slots[pluginName].fileName) {

View File

@ -7,15 +7,12 @@ import { createModuleBuilder } from './generator/ModuleBuilder';
import { createDiskPublisher } from './publisher/disk'; import { createDiskPublisher } from './publisher/disk';
import { createZipPublisher } from './publisher/zip'; import { createZipPublisher } from './publisher/zip';
import createIceJsProjectBuilder from './solutions/icejs'; import createIceJsProjectBuilder from './solutions/icejs';
import createRecoreProjectBuilder from './solutions/recore'; // import createRecoreProjectBuilder from './solutions/recore';
import createRaxAppProjectBuilder from './solutions/rax-app';
// 引入说明 // 引入说明
import { REACT_CHUNK_NAME } from './plugins/component/react/const'; import { REACT_CHUNK_NAME } from './plugins/component/react/const';
import { import { COMMON_CHUNK_NAME, CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from './const/generator';
COMMON_CHUNK_NAME,
CLASS_DEFINE_CHUNK_NAME,
DEFAULT_LINK_AFTER,
} from './const/generator';
// 引入通用插件组 // 引入通用插件组
import esmodule from './plugins/common/esmodule'; import esmodule from './plugins/common/esmodule';
@ -38,11 +35,15 @@ import prettier from './postprocessor/prettier';
import * as utilsCommon from './utils/common'; import * as utilsCommon from './utils/common';
import * as utilsCompositeType from './utils/compositeType'; import * as utilsCompositeType from './utils/compositeType';
import * as utilsJsExpression from './utils/jsExpression'; import * as utilsJsExpression from './utils/jsExpression';
import * as utilsJsSlot from './utils/jsSlot';
import * as utilsNodeToJSX from './utils/nodeToJSX'; import * as utilsNodeToJSX from './utils/nodeToJSX';
import * as utilsResultHelper from './utils/resultHelper';
import * as utilsTemplateHelper from './utils/templateHelper'; import * as utilsTemplateHelper from './utils/templateHelper';
import * as utilsValidate from './utils/validate';
// 引入内置解决方案模块 // 引入内置解决方案模块
import icejs from './plugins/project/framework/icejs'; import icejs from './plugins/project/framework/icejs';
import rax from './plugins/project/framework/rax';
export * from './types'; export * from './types';
@ -51,10 +52,12 @@ export default {
createModuleBuilder, createModuleBuilder,
solutions: { solutions: {
icejs: createIceJsProjectBuilder, icejs: createIceJsProjectBuilder,
recore: createRecoreProjectBuilder, // recore: createRecoreProjectBuilder,
rax: createRaxAppProjectBuilder,
}, },
solutionParts: { solutionParts: {
icejs, icejs,
rax,
}, },
publishers: { publishers: {
disk: createDiskPublisher, disk: createDiskPublisher,
@ -91,8 +94,11 @@ export default {
common: utilsCommon, common: utilsCommon,
compositeType: utilsCompositeType, compositeType: utilsCompositeType,
jsExpression: utilsJsExpression, jsExpression: utilsJsExpression,
jsSlot: utilsJsSlot,
nodeToJSX: utilsNodeToJSX, nodeToJSX: utilsNodeToJSX,
resultHelper: utilsResultHelper,
templateHelper: utilsTemplateHelper, templateHelper: utilsTemplateHelper,
validate: utilsValidate,
}, },
chunkNames: { chunkNames: {
COMMON_CHUNK_NAME, COMMON_CHUNK_NAME,

View File

@ -1,31 +0,0 @@
import { CodeGeneratorError, IResultDir, IResultFile } from '../types';
export default class ResultDir implements IResultDir {
public name: string;
public dirs: IResultDir[];
public files: IResultFile[];
constructor(name: string) {
this.name = name;
this.dirs = [];
this.files = [];
}
public addDirectory(dir: IResultDir): void {
if (this.dirs.findIndex(d => d.name === dir.name) < 0) {
this.dirs.push(dir);
} else {
throw new CodeGeneratorError('Adding same directory to one directory');
}
}
public addFile(file: IResultFile): void {
if (
this.files.findIndex(f => f.name === file.name && f.ext === file.ext) < 0
) {
this.files.push(file);
} else {
throw new CodeGeneratorError('Adding same file to one directory');
}
}
}

View File

@ -1,13 +0,0 @@
import { IResultFile } from '../types';
export default class ResultFile implements IResultFile {
public name: string;
public ext: string;
public content: string;
constructor(name: string, ext: string = 'jsx', content: string = '') {
this.name = name;
this.ext = ext;
this.content = content;
}
}

View File

@ -2,30 +2,29 @@
* 使 * 使
* schema * schema
*/ */
import changeCase from 'change-case';
import { SUPPORT_SCHEMA_VERSION_LIST } from '../const'; import { UtilItem, NodeDataType, NodeSchema, ContainerSchema, ProjectSchema, PropsMap } from '@ali/lowcode-types';
import { IPageMeta,
import { handleChildren } from '../utils/nodeToJSX';
import {
ChildNodeType,
CodeGeneratorError, CodeGeneratorError,
CompatibilityError, CompatibilityError,
DependencyType, DependencyType,
IBasicSchema,
IComponentNodeItem,
IContainerInfo, IContainerInfo,
IContainerNodeItem, IDependency,
IExternalDependency, IExternalDependency,
IInternalDependency, IInternalDependency,
InternalDependencyType, InternalDependencyType,
IPageMeta,
IParseResult, IParseResult,
IProjectSchema,
ISchemaParser, ISchemaParser,
IUtilItem, INpmPackage,
IRouterInfo,
} from '../types'; } from '../types';
import { SUPPORT_SCHEMA_VERSION_LIST } from '../const';
import { handleSubNodes } from '../utils/schema';
import { uniqueArray } from '../utils/common';
const defaultContainer: IContainerInfo = { const defaultContainer: IContainerInfo = {
containerType: 'Component', containerType: 'Component',
componentName: 'Component', componentName: 'Component',
@ -35,24 +34,34 @@ const defaultContainer: IContainerInfo = {
props: {}, props: {},
}; };
function getRootComponentName(typeName: string, maps: Record<string, IExternalDependency>): string {
if (maps[typeName]) {
const rec = maps[typeName];
const peerName = Object.keys(maps).find((depName: string) => {
const depInfo = maps[depName];
return depName !== typeName && depInfo.package === rec.package && depInfo.version === rec.version;
});
return peerName || typeName;
}
return typeName;
}
class SchemaParser implements ISchemaParser { class SchemaParser implements ISchemaParser {
public validate(schema: IBasicSchema): boolean { validate(schema: ProjectSchema): boolean {
if (SUPPORT_SCHEMA_VERSION_LIST.indexOf(schema.version) < 0) { if (SUPPORT_SCHEMA_VERSION_LIST.indexOf(schema.version) < 0) {
throw new CompatibilityError( throw new CompatibilityError(`Not support schema with version [${schema.version}]`);
`Not support schema with version [${schema.version}]`,
);
} }
return true; return true;
} }
public parse(schemaSrc: IProjectSchema | string): IParseResult { parse(schemaSrc: ProjectSchema | string): IParseResult {
// TODO: collect utils depends in JSExpression // TODO: collect utils depends in JSExpression
const compDeps: Record<string, IExternalDependency> = {}; const compDeps: Record<string, IExternalDependency> = {};
const internalDeps: Record<string, IInternalDependency> = {}; const internalDeps: Record<string, IInternalDependency> = {};
let utilsDeps: IExternalDependency[] = []; let utilsDeps: IExternalDependency[] = [];
let schema: IProjectSchema; let schema: ProjectSchema;
if (typeof schemaSrc === 'string') { if (typeof schemaSrc === 'string') {
try { try {
schema = JSON.parse(schemaSrc); schema = JSON.parse(schemaSrc);
@ -64,43 +73,50 @@ class SchemaParser implements ISchemaParser {
} }
// 解析三方组件依赖 // 解析三方组件依赖
schema.componentsMap.forEach(info => { schema.componentsMap.forEach((info) => {
info.dependencyType = DependencyType.External; if (info.componentName) {
info.importName = info.componentName; compDeps[info.componentName] = {
compDeps[info.componentName] = info; ...info,
dependencyType: DependencyType.External,
componentName: info.componentName,
exportName: info.exportName ?? info.componentName,
version: info.version || '*',
destructuring: info.destructuring ?? false,
};
}
}); });
let containers: IContainerInfo[]; let containers: IContainerInfo[];
// Test if this is a lowcode component without container // Test if this is a lowcode component without container
if (schema.componentsTree.length > 0) { if (schema.componentsTree.length > 0) {
const firstRoot: IContainerNodeItem = schema const firstRoot: ContainerSchema = schema.componentsTree[0] as ContainerSchema;
.componentsTree[0] as IContainerNodeItem;
if (!firstRoot.fileName) { if (!('fileName' in firstRoot) || !firstRoot.fileName) {
// 整个 schema 描述一个容器,且无根节点定义 // 整个 schema 描述一个容器,且无根节点定义
const container: IContainerInfo = { const container: IContainerInfo = {
...defaultContainer, ...defaultContainer,
children: schema.componentsTree as IComponentNodeItem[], children: schema.componentsTree as NodeSchema[],
}; };
containers = [container]; containers = [container];
} else { } else {
// 普通带 1 到多个容器的 schema // 普通带 1 到多个容器的 schema
containers = schema.componentsTree.map(n => { containers = schema.componentsTree.map((n) => {
const subRoot = n as IContainerNodeItem; const subRoot = n as ContainerSchema;
const container: IContainerInfo = { const container: IContainerInfo = {
...subRoot, ...subRoot,
componentName: getRootComponentName(subRoot.componentName, compDeps),
containerType: subRoot.componentName, containerType: subRoot.componentName,
moduleName: subRoot.fileName, // TODO: 驼峰化名称 moduleName: changeCase.pascalCase(subRoot.fileName),
}; };
return container; return container;
}); });
} }
} else { } else {
throw new CodeGeneratorError(`Can't find anything to generate.`); throw new CodeGeneratorError('Can\'t find anything to generate.');
} }
// 建立所有容器的内部依赖索引 // 建立所有容器的内部依赖索引
containers.forEach(container => { containers.forEach((container) => {
let type; let type;
switch (container.containerType) { switch (container.containerType) {
case 'Page': case 'Page':
@ -125,51 +141,102 @@ class SchemaParser implements ISchemaParser {
internalDeps[dep.moduleName] = dep; internalDeps[dep.moduleName] = dep;
}); });
// 分析容器内部组件依赖 const containersDeps = ([] as IDependency[]).concat(...containers.map((c) => c.deps || []));
containers.forEach(container => { // TODO: 不应该在出码部分解决?
// 处理 children 写在了 props 里的情况
containers.forEach((container) => {
if (container.children) { if (container.children) {
// const depNames = this.getComponentNames(container.children); handleSubNodes<string>(
// container.deps = uniqueArray<string>(depNames) container.children,
// .map(depName => internalDeps[depName] || compDeps[depName]) {
// .filter(dep => !!dep); node: (i: NodeSchema) => {
container.deps = Object.keys(compDeps).map( if (i.props) {
depName => compDeps[depName], if (Array.isArray(i.props)) {
// FIXME: is array type props description
} else {
const nodeProps = i.props as PropsMap;
if (nodeProps.children && !i.children) {
i.children = nodeProps.children as NodeDataType;
}
}
}
return '';
},
},
{
rerun: true,
},
); );
} }
}); });
// 分析容器内部组件依赖
containers.forEach((container) => {
const depNames = this.getComponentNames(container);
// eslint-disable-next-line no-param-reassign
container.deps = uniqueArray<string>(depNames, (i: string) => i)
.map((depName) => internalDeps[depName] || compDeps[depName])
.filter(Boolean);
// container.deps = Object.keys(compDeps).map((depName) => compDeps[depName]);
});
// 分析路由配置 // 分析路由配置
const routes = containers const routes: IRouterInfo['routes'] = containers
.filter(container => container.containerType === 'Page') .filter((container) => container.containerType === 'Page')
.map(page => { .map((page) => {
const meta = page.meta as IPageMeta; const meta = page.meta;
if (meta) { if (meta) {
return { return {
path: meta.router, path: (meta as IPageMeta).router || `/${page.fileName}`, // 如果无法找到页面路由信息,则用 fileName 做兜底
fileName: page.fileName,
componentName: page.moduleName, componentName: page.moduleName,
}; };
} }
return { return {
path: '', path: '',
fileName: page.fileName,
componentName: page.moduleName, componentName: page.moduleName,
}; };
}); });
const routerDeps = routes const routerDeps = routes
.map(r => internalDeps[r.componentName] || compDeps[r.componentName]) .map((r) => internalDeps[r.componentName] || compDeps[r.componentName])
.filter(dep => !!dep); .filter((dep) => !!dep);
// 分析 Utils 依赖 // 分析 Utils 依赖
let utils: IUtilItem[]; let utils: UtilItem[];
if (schema.utils) { if (schema.utils) {
utils = schema.utils; utils = schema.utils;
utilsDeps = schema.utils utilsDeps = schema.utils.filter((u) => u.type !== 'function').map((u) => u.content as IExternalDependency);
.filter(u => u.type !== 'function')
.map(u => u.content as IExternalDependency);
} else { } else {
utils = []; utils = [];
} }
// 分析项目 npm 依赖
let npms: INpmPackage[] = [];
containers.forEach((con) => {
const p = (con.deps || [])
.map((dep) => {
return dep.dependencyType === DependencyType.External ? dep : null;
})
.filter((dep) => dep !== null);
const npmInfos: INpmPackage[] = p
.filter(Boolean)
.map((i) => ({
package: (i as IExternalDependency).package,
version: (i as IExternalDependency).version,
}));
npms.push(...npmInfos);
});
npms.push(...(utilsDeps.map(utilsDep => ({
package: utilsDep.package,
version: utilsDep.version,
}))));
npms = uniqueArray<INpmPackage>(npms, (i) => i.package).filter(Boolean);
return { return {
containers, containers,
globalUtils: { globalUtils: {
@ -182,19 +249,28 @@ class SchemaParser implements ISchemaParser {
deps: routerDeps, deps: routerDeps,
}, },
project: { project: {
meta: schema.meta,
config: schema.config,
css: schema.css, css: schema.css,
constants: schema.constants, constants: schema.constants,
config: schema.config || {},
meta: schema.meta || {},
i18n: schema.i18n, i18n: schema.i18n,
containersDeps,
utilsDeps,
packages: npms || [],
}, },
}; };
} }
public getComponentNames(children: ChildNodeType): string[] { getComponentNames(children: NodeDataType): string[] {
return handleChildren<string>(children, { return handleSubNodes<string>(
node: (i: IComponentNodeItem) => [i.componentName], children,
}); {
node: (i: NodeSchema) => i.componentName,
},
{
rerun: true,
},
);
} }
} }

View File

@ -15,6 +15,8 @@ import {
IWithDependency, IWithDependency,
} from '../../types'; } from '../../types';
import { isValidIdentifier } from '../../utils/validate';
function groupDepsByPack(deps: IDependency[]): Record<string, IDependency[]> { function groupDepsByPack(deps: IDependency[]): Record<string, IDependency[]> {
const depMap: Record<string, IDependency[]> = {}; const depMap: Record<string, IDependency[]> = {};
@ -25,14 +27,21 @@ function groupDepsByPack(deps: IDependency[]): Record<string, IDependency[]> {
depMap[pkg].push(dep); depMap[pkg].push(dep);
}; };
deps.forEach(dep => { // TODO: main 这个信息到底怎么用,是不是外部包不需要使用?
const depMainBlackList = ['lib', 'lib/index', 'es', 'es/index', 'main'];
deps.forEach((dep) => {
if (dep.dependencyType === DependencyType.Internal) { if (dep.dependencyType === DependencyType.Internal) {
addDep( addDep(`${(dep as IInternalDependency).moduleName}${dep.main ? `/${dep.main}` : ''}`, dep);
`${(dep as IInternalDependency).moduleName}${dep.main || ''}`,
dep,
);
} else { } else {
addDep(`${(dep as IExternalDependency).package}${dep.main || ''}`, dep); let depMain = '';
// TODO: 部分类型的 main 暂时认为没用
if (dep.main && depMainBlackList.indexOf(dep.main) < 0) {
depMain = dep.main;
}
if (depMain.substring(0, 1) === '/') {
depMain = depMain.substring(1);
}
addDep(`${(dep as IExternalDependency).package}${depMain ? `/${depMain}` : ''}`, dep);
} }
}); });
@ -43,30 +52,37 @@ function buildPackageImport(
pkg: string, pkg: string,
deps: IDependency[], deps: IDependency[],
targetFileType: string, targetFileType: string,
useAliasName: boolean,
): ICodeChunk[] { ): ICodeChunk[] {
const chunks: ICodeChunk[] = []; const chunks: ICodeChunk[] = [];
let defaultImport: string = ''; let defaultImport = '';
let defaultImportAs: string = ''; let defaultImportAs = '';
const imports: Record<string, string> = {}; const imports: Record<string, string> = {};
deps.forEach(dep => { deps.forEach((dep) => {
const srcName = dep.exportName; const srcName = dep.exportName;
let targetName = dep.importName || dep.exportName; let targetName = dep.componentName || dep.exportName;
// 如果是自组件,则导出父组件,并且根据自组件命名规则,判断是否需要定义标识符
if (dep.subName) { if (dep.subName) {
return; if (targetName !== `${srcName}.${dep.subName}`) {
if (!isValidIdentifier(targetName)) {
throw new CodeGeneratorError(`Invalid Identifier [${targetName}]`);
} }
if (dep.subName) {
chunks.push({ chunks.push({
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: targetFileType, fileType: targetFileType,
name: COMMON_CHUNK_NAME.FileVarDefine, name: COMMON_CHUNK_NAME.ImportAliasDefine,
content: `const ${targetName} = ${srcName}.${dep.subName};`, content: `const ${targetName} = ${srcName}.${dep.subName};`,
linkAfter: [ linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport],
COMMON_CHUNK_NAME.ExternalDepsImport, ext: {
COMMON_CHUNK_NAME.InternalDepsImport, originalName: `${srcName}.${dep.subName}`,
], aliasName: targetName,
dependency: dep,
},
}); });
}
targetName = srcName; targetName = srcName;
} }
@ -74,22 +90,35 @@ function buildPackageImport(
if (dep.destructuring) { if (dep.destructuring) {
imports[srcName] = targetName; imports[srcName] = targetName;
} else if (defaultImport) { } else if (defaultImport) {
throw new CodeGeneratorError( throw new CodeGeneratorError(`[${pkg}] has more than one default export.`);
`[${pkg}] has more than one default export.`,
);
} else { } else {
defaultImport = srcName; defaultImport = srcName;
defaultImportAs = targetName; defaultImportAs = targetName;
} }
if (targetName !== srcName) {
chunks.push({
type: ChunkType.STRING,
fileType: targetFileType,
name: COMMON_CHUNK_NAME.ImportAliasDefine,
content: '',
linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport],
ext: {
originalName: srcName,
aliasName: targetName,
dependency: dep,
},
});
}
}); });
const items = Object.keys(imports).map(src => const items = Object.keys(imports).map((src) => {
src === imports[src] ? src : `${src} as ${imports[src]}`, return src === imports[src] || !useAliasName ? src : `${src} as ${imports[src]}`;
); });
const statementL = ['import']; const statementL = ['import'];
if (defaultImport) { if (defaultImport) {
statementL.push(defaultImportAs); statementL.push(useAliasName ? defaultImportAs : defaultImport);
if (items.length > 0) { if (items.length > 0) {
statementL.push(','); statementL.push(',');
} }
@ -124,13 +153,15 @@ function buildPackageImport(
} }
type PluginConfig = { type PluginConfig = {
fileType: string; fileType?: string; // 导出的文件类型
} useAliasName?: boolean; // 是否使用 componentName 重命名组件 identifier
};
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?: PluginConfig) => { const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?: PluginConfig) => {
const cfg: PluginConfig = { const cfg = {
fileType: FileType.JS, fileType: FileType.JS,
...config, useAliasName: true,
...(config || {}),
}; };
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
@ -143,9 +174,9 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?: Plu
if (ir && ir.deps && ir.deps.length > 0) { if (ir && ir.deps && ir.deps.length > 0) {
const packs = groupDepsByPack(ir.deps); const packs = groupDepsByPack(ir.deps);
Object.keys(packs).forEach(pkg => { Object.keys(packs).forEach((pkg) => {
const chunks = buildPackageImport(pkg, packs[pkg], cfg.fileType); const chunks = buildPackageImport(pkg, packs[pkg], cfg.fileType, cfg.useAliasName);
next.chunks.push.apply(next.chunks, chunks); next.chunks.push(...chunks);
}); });
} }

View File

@ -1,12 +1,6 @@
import { COMMON_CHUNK_NAME } from '../../const/generator'; import { COMMON_CHUNK_NAME } from '../../const/generator';
import { import { BuilderComponentPlugin, BuilderComponentPluginFactory, ChunkType, FileType, ICodeStruct } from '../../types';
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
} from '../../types';
// TODO: How to merge this logic to common deps // TODO: How to merge this logic to common deps
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => { const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
@ -19,7 +13,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: FileType.JSX, fileType: FileType.JSX,
name: COMMON_CHUNK_NAME.InternalDepsImport, name: COMMON_CHUNK_NAME.InternalDepsImport,
content: `import * from 'react';`, content: 'import * from \'react\';',
linkAfter: [], linkAfter: [],
}); });

View File

@ -0,0 +1,35 @@
import { COMMON_CHUNK_NAME } from '../../../const/generator';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
} from '../../../types';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
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: `
// 注意: 出码引擎注入的临时变量默认都以 "__$$" 开头,禁止在搭建的代码中直接访问。
// 例外rax 框架的导出名和各种组件名除外。
import { createElement, Component } from 'rax';
import { withRouter as __$$withRouter } from 'rax-app';
`,
linkAfter: [],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,18 @@
export const RAX_CHUNK_NAME = {
ClassDidMountBegin: 'RaxComponentClassDidMountBegin',
ClassDidMountContent: 'RaxComponentClassDidMountContent',
ClassDidMountEnd: 'RaxComponentClassDidMountEnd',
ClassWillUnmountBegin: 'RaxComponentClassWillUnmountBegin',
ClassWillUnmountContent: 'RaxComponentClassWillUnmountContent',
ClassWillUnmountEnd: 'RaxComponentClassWillUnmountEnd',
ClassRenderBegin: 'RaxComponentClassRenderBegin',
ClassRenderPre: 'RaxComponentClassRenderPre',
ClassRenderJSX: 'RaxComponentClassRenderJSX',
ClassRenderEnd: 'RaxComponentClassRenderEnd',
MethodsBegin: 'RaxComponentMethodsBegin',
MethodsContent: 'RaxComponentMethodsContent',
MethodsEnd: 'RaxComponentMethodsEnd',
LifeCyclesBegin: 'RaxComponentLifeCyclesBegin',
LifeCyclesContent: 'RaxComponentLifeCyclesContent',
LifeCyclesEnd: 'RaxComponentLifeCyclesEnd',
};

View File

@ -0,0 +1,144 @@
import changeCase from 'change-case';
import { COMMON_CHUNK_NAME, CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator';
import { RAX_CHUNK_NAME } from './const';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IContainerInfo,
} from '../../../types';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IContainerInfo;
// 将模块名转换成 PascalCase 的格式,并添加特定后缀,防止命名冲突
const componentClassName = `${changeCase.pascalCase(ir.moduleName)}$$Page`;
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: CLASS_DEFINE_CHUNK_NAME.Start,
content: `class ${componentClassName} extends Component {`,
linkAfter: [
COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.ImportAliasDefine,
COMMON_CHUNK_NAME.FileVarDefine,
COMMON_CHUNK_NAME.FileUtilDefine,
],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: CLASS_DEFINE_CHUNK_NAME.End,
content: '}',
linkAfter: [
CLASS_DEFINE_CHUNK_NAME.Start,
CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod,
RAX_CHUNK_NAME.ClassRenderEnd,
RAX_CHUNK_NAME.MethodsEnd,
],
});
// next.chunks.push({
// type: ChunkType.STRING,
// fileType: FileType.JSX,
// name: CLASS_DEFINE_CHUNK_NAME.ConstructorStart,
// content: 'constructor(props, context) { super(props); ',
// linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start],
// });
// next.chunks.push({
// type: ChunkType.STRING,
// fileType: FileType.JSX,
// name: CLASS_DEFINE_CHUNK_NAME.ConstructorEnd,
// content: '}',
// linkAfter: [
// CLASS_DEFINE_CHUNK_NAME.ConstructorStart,
// CLASS_DEFINE_CHUNK_NAME.ConstructorContent,
// ],
// });
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: RAX_CHUNK_NAME.ClassDidMountBegin,
content: 'componentDidMount() {',
linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start, CLASS_DEFINE_CHUNK_NAME.InsVar, CLASS_DEFINE_CHUNK_NAME.InsMethod],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: RAX_CHUNK_NAME.ClassDidMountEnd,
content: '}',
linkAfter: [RAX_CHUNK_NAME.ClassDidMountBegin, RAX_CHUNK_NAME.ClassDidMountContent],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: RAX_CHUNK_NAME.ClassWillUnmountBegin,
content: 'componentWillUnmount() {',
linkAfter: [
CLASS_DEFINE_CHUNK_NAME.Start,
CLASS_DEFINE_CHUNK_NAME.InsVar,
CLASS_DEFINE_CHUNK_NAME.InsMethod,
RAX_CHUNK_NAME.ClassDidMountEnd,
],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: RAX_CHUNK_NAME.ClassWillUnmountEnd,
content: '}',
linkAfter: [RAX_CHUNK_NAME.ClassWillUnmountBegin, RAX_CHUNK_NAME.ClassWillUnmountContent],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: RAX_CHUNK_NAME.ClassRenderBegin,
content: 'render() {',
linkAfter: [RAX_CHUNK_NAME.ClassDidMountEnd, RAX_CHUNK_NAME.ClassWillUnmountEnd],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: RAX_CHUNK_NAME.ClassRenderEnd,
content: '}',
linkAfter: [RAX_CHUNK_NAME.ClassRenderBegin, RAX_CHUNK_NAME.ClassRenderPre, RAX_CHUNK_NAME.ClassRenderJSX],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: COMMON_CHUNK_NAME.FileExport,
content: `export default __$$withRouter(${componentClassName});`,
linkAfter: [
COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.ImportAliasDefine,
COMMON_CHUNK_NAME.FileVarDefine,
COMMON_CHUNK_NAME.FileUtilDefine,
CLASS_DEFINE_CHUNK_NAME.End,
],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,68 @@
import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator';
import { generateCompositeType } from '../../../utils/compositeType';
import Scope from '../../../utils/Scope';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IContainerInfo,
} from '../../../types';
type PluginConfig = {
fileType: string;
implementType: 'inConstructor' | 'insMember' | 'hooks';
};
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
fileType: FileType.JSX,
implementType: 'insMember',
...config,
};
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IContainerInfo;
const scope = Scope.createRootScope();
if (ir.state) {
const state = ir.state;
const fields = Object.keys(state).map<string>((stateName) => {
// TODO: 这里用什么 handlers?
const value = generateCompositeType(state[stateName], scope);
return `${stateName}: ${value}`;
});
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]],
});
}
// TODO: hooks state??
}
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,116 @@
import { CLASS_DEFINE_CHUNK_NAME, COMMON_CHUNK_NAME } from '../../../const/generator';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
} from '../../../types';
import { RAX_CHUNK_NAME } from './const';
type PluginConfig = {
fileType: string;
};
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
fileType: FileType.JSX,
...config,
};
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: COMMON_CHUNK_NAME.InternalDepsImport,
content: 'import __$$constants from \'../../constants\';',
linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport],
});
// TODO: i18n 是可选的,如果没有 i18n 这个文件怎么办?该怎么判断?
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: COMMON_CHUNK_NAME.InternalDepsImport,
content: 'import * as __$$i18n from \'../../i18n\';',
linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.InsVar,
content: `
_context = this._createContext();
`,
linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod,
content: `
_createContext() {
const self = this;
// 保存下最新的状态,这样 setState 可以搞成同步一样的了
self._latestState = self.state;
const context = {
get state() {
// 这里直接获取最新的 state从而能避免一些 React/Rax 这样的框架因为异步 setState 而导致的一些问题
return self._latestState;
},
setState(newState) {
self._latestState = { ...self._latestState, ...newState };
self.setState(newState);
},
get dataSourceMap() {
return self._dataSourceEngine.dataSourceMap || {};
},
async reloadDataSource() {
await self._dataSourceEngine.reloadDataSource();
},
get utils() {
return self._utils;
},
get page() {
return context;
},
get component() {
return context;
},
get props() {
return self.props;
},
get constants() {
return __$$constants;
},
i18n: __$$i18n.i18n,
i18nFormat: __$$i18n.i18nFormat,
getLocale: __$$i18n.getLocale,
setLocale(locale) {
__$$i18n.setLocale(locale);
self.forceUpdate();
},
...this._methods,
};
return context;
}
`,
linkAfter: [RAX_CHUNK_NAME.ClassRenderEnd],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,157 @@
/* eslint-disable @typescript-eslint/indent */
import { CompositeValue, JSExpression, DataSourceConfig, isJSExpression, isJSFunction } from '@ali/lowcode-types';
import changeCase from 'change-case';
import { CLASS_DEFINE_CHUNK_NAME, COMMON_CHUNK_NAME } from '../../../const/generator';
import Scope from '../../../utils/Scope';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IScope,
} from '../../../types';
import { generateCompositeType } from '../../../utils/compositeType';
import { parseExpressionConvertThis2Context } from '../../../utils/expressionParser';
import { isContainerSchema } from '../../../utils/schema';
import { RAX_CHUNK_NAME } from './const';
type PluginConfig = {
fileType: string;
};
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
fileType: FileType.JSX,
...config,
};
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const scope = Scope.createRootScope();
const dataSourceConfig = isContainerSchema(pre.ir) ? pre.ir.dataSource : null;
const dataSourceItems: DataSourceConfig[] = (dataSourceConfig && dataSourceConfig.list) || [];
const dataSourceEngineOptions = { runtimeConfig: true };
if (dataSourceItems.length > 0) {
const requestHandlersMap = {} as Record<string, JSExpression>;
dataSourceItems.forEach((ds) => {
const dsType = ds.type || 'fetch';
if (!(dsType in requestHandlersMap) && dsType !== 'custom') {
const handlerFactoryName = '__$$create' + changeCase.pascal(dsType) + 'RequestHandler';
requestHandlersMap[dsType] = {
type: 'JSExpression',
value: handlerFactoryName + (dsType === 'urlParams' ? '(this.props.location.search)' : '()'),
};
const handlerFactoryExportName = `create${changeCase.pascal(dsType)}Handler`;
const handlerPkgName = `@ali/lowcode-datasource-${changeCase.kebab(dsType)}-handler`;
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: COMMON_CHUNK_NAME.ExternalDepsImport,
content: `
import { ${handlerFactoryExportName} as ${handlerFactoryName} } from '${handlerPkgName}';
`,
linkAfter: [],
});
}
});
Object.assign(dataSourceEngineOptions, { requestHandlersMap });
}
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: COMMON_CHUNK_NAME.ExternalDepsImport,
content: `
import { create as __$$createDataSourceEngine } from '@ali/lowcode-datasource-engine/runtime';
`,
linkAfter: [],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.InsVar,
content: `
_dataSourceConfig = this._defineDataSourceConfig();
_dataSourceEngine = __$$createDataSourceEngine(
this._dataSourceConfig,
this._context,
${generateCompositeType(dataSourceEngineOptions, scope)}
);`,
linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: RAX_CHUNK_NAME.ClassDidMountContent,
content: `
this._dataSourceEngine.reloadDataSource();
`,
linkAfter: [RAX_CHUNK_NAME.ClassDidMountBegin],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod,
content: `
_defineDataSourceConfig() {
const __$$context = this._context;
return (${generateCompositeType(
{
...dataSourceConfig,
list: [
...dataSourceItems.map((item) => ({
...item,
isInit: wrapAsFunction(item.isInit, scope),
options: wrapAsFunction(item.options, scope),
})),
],
},
scope,
{
handlers: {
function: (jsFunc) => parseExpressionConvertThis2Context(jsFunc.value, '__$$context'),
expression: (jsExpr) => parseExpressionConvertThis2Context(jsExpr.value, '__$$context'),
},
},
)});
}
`,
linkAfter: [RAX_CHUNK_NAME.ClassRenderEnd],
});
return next;
};
return plugin;
};
export default pluginFactory;
function wrapAsFunction(value: CompositeValue, scope: IScope): CompositeValue {
if (isJSExpression(value) || isJSFunction(value)) {
return {
type: 'JSExpression',
value: `function(){ return ((${value.value}))}`,
};
}
return {
type: 'JSExpression',
value: `function(){return((${generateCompositeType(value, scope)}))}`,
};
}

View File

@ -0,0 +1,74 @@
import { CLASS_DEFINE_CHUNK_NAME, COMMON_CHUNK_NAME } from '../../../const/generator';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
} from '../../../types';
import { RAX_CHUNK_NAME } from './const';
type PluginConfig = {
fileType: string;
};
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
fileType: FileType.JSX,
...config,
};
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: COMMON_CHUNK_NAME.InternalDepsImport,
// TODO: 下面这个路径有没有更好的方式来获取?而非写死
content: `
import __$$projectUtils from '../../utils';
`,
linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.InsVar,
content: '_utils = this._defineUtils();',
linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod,
// 绑定下上下文,这样在所有的 utils 里面都能通过 this.xxx 来访问上下文了
content: `
_defineUtils() {
const utils = {
...__$$projectUtils,
};
Object.entries(utils).forEach(([name, util]) => {
if (typeof util === 'function') {
utils[name] = util.bind(this._context);
}
});
return utils;
}`,
linkAfter: [RAX_CHUNK_NAME.ClassRenderEnd],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,120 @@
import _ from 'lodash';
import { CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator';
import { RAX_CHUNK_NAME } from './const';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
FileType,
ChunkType,
ICodeStruct,
IContainerInfo,
} from '../../../types';
type PluginConfig = {
fileType: string;
exportNameMapping: Record<string, string>;
normalizeNameMapping: Record<string, string>;
};
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
fileType: FileType.JSX,
exportNameMapping: {},
normalizeNameMapping: {},
...config,
};
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
// Rax 先只支持 didMount 和 willUnmount 吧
const ir = next.ir as IContainerInfo;
const lifeCycles = ir.lifeCycles;
if (lifeCycles && !_.isEmpty(lifeCycles)) {
Object.entries(lifeCycles).forEach(([lifeCycleName, lifeCycleMethodExpr]) => {
const normalizeName = cfg.normalizeNameMapping[lifeCycleName] || lifeCycleName;
const exportName = cfg.exportNameMapping[lifeCycleName] || lifeCycleName;
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: RAX_CHUNK_NAME.LifeCyclesContent,
content: `${exportName}: (${lifeCycleMethodExpr.value}),`,
linkAfter: [RAX_CHUNK_NAME.LifeCyclesBegin],
});
if (normalizeName === 'didMount') {
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: RAX_CHUNK_NAME.ClassDidMountContent,
content: `this._lifeCycles.${exportName}();`,
linkAfter: [RAX_CHUNK_NAME.ClassDidMountBegin],
});
} else if (normalizeName === 'willUnmount') {
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: RAX_CHUNK_NAME.ClassWillUnmountContent,
content: `this._lifeCycles.${exportName}();`,
linkAfter: [RAX_CHUNK_NAME.ClassWillUnmountBegin],
});
} else {
// TODO: print warnings? Unknown life cycle
}
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.InsVar,
content: '_lifeCycles = this._defineLifeCycles();',
linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: RAX_CHUNK_NAME.LifeCyclesBegin,
content: `
_defineLifeCycles() {
const __$$lifeCycles = ({
`,
linkAfter: [RAX_CHUNK_NAME.ClassRenderEnd, CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: RAX_CHUNK_NAME.LifeCyclesEnd,
content: `
});
// 为所有的方法绑定上下文
Object.entries(__$$lifeCycles).forEach(([lifeCycleName, lifeCycleMethod]) => {
if (typeof lifeCycleMethod === 'function') {
__$$lifeCycles[lifeCycleName] = (...args) => {
return lifeCycleMethod.apply(this._context, args);
}
}
});
return __$$lifeCycles;
}
`,
linkAfter: [RAX_CHUNK_NAME.LifeCyclesBegin, RAX_CHUNK_NAME.LifeCyclesContent],
});
}
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,95 @@
import { CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IContainerInfo,
} from '../../../types';
import { RAX_CHUNK_NAME } from './const';
type PluginConfig = {
fileType: string;
};
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
fileType: FileType.JSX,
...config,
};
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IContainerInfo;
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.InsVar,
content: `
_methods = this._defineMethods();
`,
linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: RAX_CHUNK_NAME.MethodsBegin,
content: `
_defineMethods() {
const __$$methods = ({
`,
linkAfter: [
RAX_CHUNK_NAME.ClassRenderEnd,
CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod,
RAX_CHUNK_NAME.LifeCyclesEnd,
],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: RAX_CHUNK_NAME.MethodsEnd,
content: `
});
// 为所有的方法绑定上下文
Object.entries(__$$methods).forEach(([methodName, method]) => {
if (typeof method === 'function') {
__$$methods[methodName] = (...args) => {
return method.apply(this._context, args);
}
}
});
return __$$methods;
}
`,
linkAfter: [RAX_CHUNK_NAME.MethodsBegin, RAX_CHUNK_NAME.MethodsContent],
});
if (ir.methods && Object.keys(ir.methods).length > 0) {
Object.entries(ir.methods).forEach(([methodName, methodDefine]) => {
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: RAX_CHUNK_NAME.MethodsContent,
content: `${methodName}: (${methodDefine.value}),`,
linkAfter: [RAX_CHUNK_NAME.MethodsBegin],
});
});
}
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,350 @@
import { NodeSchema, JSExpression, NpmInfo, CompositeValue, isJSExpression } from '@ali/lowcode-types';
import _ from 'lodash';
import changeCase from 'change-case';
import { Expression } from '@babel/types';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
CodePiece,
FileType,
ICodeChunk,
ICodeStruct,
IContainerInfo,
PIECE_TYPE,
HandlerSet,
IScope,
NodeGeneratorConfig,
NodePlugin,
AttrPlugin,
} from '../../../types';
import { RAX_CHUNK_NAME } from './const';
import { COMMON_CHUNK_NAME } from '../../../const/generator';
import { generateExpression } from '../../../utils/jsExpression';
import { createNodeGenerator, generateConditionReactCtrl, generateReactExprInJS } from '../../../utils/nodeToJSX';
import { generateCompositeType } from '../../../utils/compositeType';
import Scope from '../../../utils/Scope';
import {
parseExpression,
parseExpressionConvertThis2Context,
parseExpressionGetGlobalVariables,
} from '../../../utils/expressionParser';
type PluginConfig = {
fileType: string;
};
// TODO: componentName 若并非大写字符打头,甚至并非是一个有效的 JS 标识符怎么办??
// FIXME: 我想了下,这块应该放到解析阶段就去做掉,对所有 componentName 做 identifier validate然后对不合法的做统一替换。
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = {
fileType: FileType.JSX,
...config,
};
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IContainerInfo;
const rootScope = Scope.createRootScope();
// Rax 构建到小程序的时候,不能给组件起起别名,得直接引用,故这里将所有的别名替换掉
// 先收集下所有的 alias 的映射
const componentsNameAliasMap = new Map<string, string>();
next.chunks.forEach((chunk) => {
if (isImportAliasDefineChunk(chunk)) {
componentsNameAliasMap.set(chunk.ext.aliasName, chunk.ext.originalName);
}
});
// 注意这里其实隐含了一个假设schema 中的 componentName 应该是一个有效的 JS 标识符,而且是大写字母打头的
// FIXME: 为了快速修复临时加的逻辑,需要用 pre-process 的方式替代处理。
const mapComponentNameToAliasOrKeepIt = (componentName: string) => componentsNameAliasMap.get(componentName) || componentName;
// 然后过滤掉所有的别名 chunks
next.chunks = next.chunks.filter((chunk) => !isImportAliasDefineChunk(chunk));
// 如果直接按目前的 React 的方式之间出码 JSX 的话,会有 3 个问题:
// 1. 小程序出码的时候,循环变量没法拿到
// 2. 小程序出码的时候,很容易出现 Uncaught TypeError: Cannot read property 'avatar' of undefined 这样的异常(如下图的 50 行) -- 因为若直接出码Rax 构建到小程序的时候会立即计算所有在视图中用到的变量
// 3. 通过 this.xxx 能拿到的东西太多了,而且自定义的 methods 可能会无意间破坏 Rax 框架或小程序框架在页面 this 上的东东
const customHandlers: HandlerSet<string> = {
expression(input: JSExpression, scope: IScope) {
return transformJsExpr(generateExpression(input), scope);
},
function(input, scope: IScope) {
return transformThis2Context(input.value || 'null', scope);
},
};
// 创建代码生成器
const commonNodeGenerator = createNodeGenerator({
handlers: customHandlers,
tagMapping: mapComponentNameToAliasOrKeepIt,
nodePlugins: [generateReactExprInJS, generateConditionReactCtrl, generateRaxLoopCtrl],
attrPlugins: [generateNodeAttrForRax],
});
// 生成 JSX 代码
const jsxContent = commonNodeGenerator(ir, rootScope);
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: COMMON_CHUNK_NAME.ExternalDepsImport,
content: 'import { isMiniApp as __$$isMiniApp } from \'universal-env\';',
linkAfter: [],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: RAX_CHUNK_NAME.ClassRenderPre,
// TODO: setState, dataSourceMap, reloadDataSource, utils, i18n, i18nFormat, getLocale, setLocale 这些在 Rax 的编译模式下不能在视图中直接访问,需要转化成 this.xxx
content: `
const __$$context = this._context;
const { state, setState, dataSourceMap, reloadDataSource, utils, constants, i18n, i18nFormat, getLocale, setLocale } = __$$context;
`,
linkAfter: [RAX_CHUNK_NAME.ClassRenderBegin],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: RAX_CHUNK_NAME.ClassRenderJSX,
content: `return ${jsxContent};`,
linkAfter: [RAX_CHUNK_NAME.ClassRenderBegin, RAX_CHUNK_NAME.ClassRenderPre],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: COMMON_CHUNK_NAME.CustomContent,
content: `
function __$$eval(expr) {
try {
return expr();
} catch (err) {
console.warn('Failed to evaluate: ', expr, err);
}
}
function __$$evalArray(expr) {
const res = __$$eval(expr);
return Array.isArray(res) ? res : [];
}
`,
linkAfter: [COMMON_CHUNK_NAME.FileExport],
});
return next;
};
return plugin;
};
export default pluginFactory;
function transformJsExpr(expr: string, scope: IScope) {
if (!expr) {
return 'undefined';
}
if (isLiteralAtomicExpr(expr)) {
return expr;
}
const exprAst = parseExpression(expr);
// 对于下面这些比较安全的字面值,可以直接返回对应的表达式,而非包一层
if (isSimpleStraightLiteral(exprAst)) {
return expr;
}
switch (exprAst.type) {
// 对于直接写个函数的,则不用再包下,因为这样不会抛出异常的
case 'ArrowFunctionExpression':
case 'FunctionExpression':
return transformThis2Context(exprAst, scope);
default:
break;
}
// 其他的都需要包一层
return `__$$eval(() => (${transformThis2Context(exprAst, scope)}))`;
}
/** 判断是非是一些简单直接的字面值 */
function isSimpleStraightLiteral(expr: Expression): boolean {
switch (expr.type) {
case 'BigIntLiteral':
case 'BooleanLiteral':
case 'DecimalLiteral':
case 'NullLiteral':
case 'NumericLiteral':
case 'RegExpLiteral':
case 'StringLiteral':
return true;
default:
return false;
}
}
function isImportAliasDefineChunk(
chunk: ICodeChunk,
): chunk is ICodeChunk & {
ext: {
aliasName: string;
originalName: string;
dependency: NpmInfo;
};
} {
return (
chunk.name === COMMON_CHUNK_NAME.ImportAliasDefine &&
!!chunk.ext &&
typeof chunk.ext.aliasName === 'string' &&
typeof chunk.ext.originalName === 'string' &&
!!(chunk.ext.dependency as NpmInfo | null)?.componentName
);
}
/**
*
*/
function isLiteralAtomicExpr(expr: string): boolean {
return expr === 'null' || expr === 'undefined' || expr === 'true' || expr === 'false' || /^-?\d+(\.\d+)?$/.test(expr);
}
/**
* this.xxx __$$context.xxx
* @param expr
*/
function transformThis2Context(expr: string | Expression, scope: IScope): string {
// 下面这种字符串替换的方式虽然简单直接,但是对于复杂场景会误匹配,故后期改成了解析 AST 然后修改 AST 最后再重新生成代码的方式
// return expr
// .replace(/\bthis\.item\./g, () => 'item.')
// .replace(/\bthis\.index\./g, () => 'index.')
// .replace(/\bthis\./g, () => '__$$context.');
return parseExpressionConvertThis2Context(expr, '__$$context', scope.bindings?.getAllBindings() || []);
}
function generateRaxLoopCtrl(
nodeItem: NodeSchema,
scope: IScope,
config?: NodeGeneratorConfig,
next?: NodePlugin,
): CodePiece[] {
if (nodeItem.loop) {
const loopItemName = nodeItem.loopArgs?.[0] || 'item';
const loopIndexName = nodeItem.loopArgs?.[1] || 'index';
const subScope = scope.createSubScope([loopItemName, loopIndexName]);
const pieces: CodePiece[] = next ? next(nodeItem, subScope, config) : [];
const loopDataExpr = `__$$evalArray(() => (${transformThis2Context(
generateCompositeType(nodeItem.loop, scope, { handlers: config?.handlers }),
scope,
)}))`;
pieces.unshift({
value: `${loopDataExpr}.map((${loopItemName}, ${loopIndexName}) => (`,
type: PIECE_TYPE.BEFORE,
});
pieces.push({
value: '))',
type: PIECE_TYPE.AFTER,
});
return pieces;
}
return next ? next(nodeItem, scope, config) : [];
}
function generateNodeAttrForRax(
attrData: { attrName: string; attrValue: CompositeValue },
scope: IScope,
config?: NodeGeneratorConfig,
next?: AttrPlugin,
): CodePiece[] {
if (!/^on/.test(attrData.attrName)) {
return next ? next(attrData, scope, config) : [];
}
// else: onXxx 的都是事件处理函数需要特殊处理下
return generateEventHandlerAttrForRax(attrData.attrName, attrData.attrValue, scope, config);
}
function generateEventHandlerAttrForRax(
attrName: string,
attrValue: CompositeValue,
scope: IScope,
config?: NodeGeneratorConfig,
): CodePiece[] {
// -- 事件处理函数中 JSExpression 转成 JSFunction 来处理,避免当 JSExpression 处理的时候多包一层 eval 而导致 Rax 转码成小程序的时候出问题
const valueExpr = generateCompositeType(
isJSExpression(attrValue) ? { type: 'JSFunction', value: attrValue.value } : attrValue,
scope,
{
handlers: config?.handlers,
},
);
// 查询当前作用域下的变量
const currentScopeVariables = scope.bindings?.getAllBindings() || [];
if (currentScopeVariables.length <= 0) {
return [
{
type: PIECE_TYPE.ATTR,
name: attrName,
value: valueExpr,
},
];
}
// 提取出所有的未定义的全局变量
const undeclaredVariablesInValueExpr = parseExpressionGetGlobalVariables(valueExpr);
const referencedLocalVariables = _.intersection(undeclaredVariablesInValueExpr, currentScopeVariables);
if (referencedLocalVariables.length <= 0) {
return [
{
type: PIECE_TYPE.ATTR,
name: attrName,
value: valueExpr,
},
];
}
const wrappedAttrValueExpr = [
'(...__$$args) => {',
' if (__$$isMiniApp) {',
' const __$$event = __$$args[0];',
...referencedLocalVariables.map((localVar) => `const ${localVar} = __$$event.target.dataset.${localVar};`),
` return (${valueExpr}).apply(this, __$$args);`,
' } else {',
` return (${valueExpr}).apply(this, __$$args);`,
' }',
'}',
].join('\n');
return [
...referencedLocalVariables.map((localVar) => ({
type: PIECE_TYPE.ATTR,
name: `data-${changeCase.snake(localVar)}`,
value: localVar,
})),
{
type: PIECE_TYPE.ATTR,
name: attrName,
value: wrappedAttrValueExpr,
},
];
}

View File

@ -1,3 +1,4 @@
import changeCase from 'change-case';
import { COMMON_CHUNK_NAME, CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator'; import { COMMON_CHUNK_NAME, CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator';
import { REACT_CHUNK_NAME } from './const'; import { REACT_CHUNK_NAME } from './const';
@ -18,14 +19,18 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
const ir = next.ir as IContainerInfo; const ir = next.ir as IContainerInfo;
// 将模块名转换成 PascalCase 的格式,并添加特定后缀,防止命名冲突
const componentClassName = `${changeCase.pascalCase(ir.moduleName)}$$Page`;
next.chunks.push({ next.chunks.push({
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: FileType.JSX, fileType: FileType.JSX,
name: CLASS_DEFINE_CHUNK_NAME.Start, name: CLASS_DEFINE_CHUNK_NAME.Start,
content: `class ${ir.moduleName} extends React.Component {`, content: `class ${componentClassName} extends React.Component {`,
linkAfter: [ linkAfter: [
COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.ImportAliasDefine,
COMMON_CHUNK_NAME.FileVarDefine, COMMON_CHUNK_NAME.FileVarDefine,
COMMON_CHUNK_NAME.FileUtilDefine, COMMON_CHUNK_NAME.FileUtilDefine,
], ],
@ -35,8 +40,11 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: FileType.JSX, fileType: FileType.JSX,
name: CLASS_DEFINE_CHUNK_NAME.End, name: CLASS_DEFINE_CHUNK_NAME.End,
content: `}`, content: '}',
linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start, REACT_CHUNK_NAME.ClassRenderEnd], linkAfter: [
CLASS_DEFINE_CHUNK_NAME.Start,
REACT_CHUNK_NAME.ClassRenderEnd,
],
}); });
next.chunks.push({ next.chunks.push({
@ -52,10 +60,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
fileType: FileType.JSX, fileType: FileType.JSX,
name: CLASS_DEFINE_CHUNK_NAME.ConstructorEnd, name: CLASS_DEFINE_CHUNK_NAME.ConstructorEnd,
content: '}', content: '}',
linkAfter: [ linkAfter: [CLASS_DEFINE_CHUNK_NAME.ConstructorStart, CLASS_DEFINE_CHUNK_NAME.ConstructorContent],
CLASS_DEFINE_CHUNK_NAME.ConstructorStart,
CLASS_DEFINE_CHUNK_NAME.ConstructorContent,
],
}); });
next.chunks.push({ next.chunks.push({
@ -75,21 +80,18 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
fileType: FileType.JSX, fileType: FileType.JSX,
name: REACT_CHUNK_NAME.ClassRenderEnd, name: REACT_CHUNK_NAME.ClassRenderEnd,
content: '}', content: '}',
linkAfter: [ linkAfter: [REACT_CHUNK_NAME.ClassRenderStart, REACT_CHUNK_NAME.ClassRenderPre, REACT_CHUNK_NAME.ClassRenderJSX],
REACT_CHUNK_NAME.ClassRenderStart,
REACT_CHUNK_NAME.ClassRenderPre,
REACT_CHUNK_NAME.ClassRenderJSX,
],
}); });
next.chunks.push({ next.chunks.push({
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: FileType.JSX, fileType: FileType.JSX,
name: COMMON_CHUNK_NAME.FileExport, name: COMMON_CHUNK_NAME.FileExport,
content: `export default ${ir.moduleName};`, content: `export default ${componentClassName};`,
linkAfter: [ linkAfter: [
COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.ImportAliasDefine,
COMMON_CHUNK_NAME.FileVarDefine, COMMON_CHUNK_NAME.FileVarDefine,
COMMON_CHUNK_NAME.FileUtilDefine, COMMON_CHUNK_NAME.FileUtilDefine,
CLASS_DEFINE_CHUNK_NAME.End, CLASS_DEFINE_CHUNK_NAME.End,

View File

@ -1,6 +1,7 @@
import { CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator'; import { CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator';
import { generateCompositeType } from '../../../utils/compositeType'; import { generateCompositeType } from '../../../utils/compositeType';
import Scope from '../../../utils/Scope';
import { import {
BuilderComponentPlugin, BuilderComponentPlugin,
@ -13,7 +14,7 @@ import {
type PluginConfig = { type PluginConfig = {
fileType: string; fileType: string;
} };
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => { const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = { const cfg: PluginConfig = {
@ -28,11 +29,13 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
const ir = next.ir as IContainerInfo; const ir = next.ir as IContainerInfo;
const scope = Scope.createRootScope();
if (ir.state) { if (ir.state) {
const state = ir.state; const { state } = ir;
const fields = Object.keys(state).map<string>(stateName => { const fields = Object.keys(state).map<string>((stateName) => {
const [isString, value] = generateCompositeType(state[stateName]); const value = generateCompositeType(state[stateName], scope);
return `${stateName}: ${isString ? `'${value}'` : value},`; return `${stateName}: ${value},`;
}); });
next.chunks.push({ next.chunks.push({

View File

@ -1,6 +1,7 @@
import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator'; import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator';
import { generateCompositeType } from '../../../utils/compositeType'; import { generateCompositeType } from '../../../utils/compositeType';
import Scope from '../../../utils/Scope';
import { import {
BuilderComponentPlugin, BuilderComponentPlugin,
@ -14,7 +15,7 @@ import {
type PluginConfig = { type PluginConfig = {
fileType: string; fileType: string;
implementType: 'inConstructor' | 'insMember' | 'hooks'; implementType: 'inConstructor' | 'insMember' | 'hooks';
} };
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => { const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = { const cfg: PluginConfig = {
@ -29,12 +30,13 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
}; };
const ir = next.ir as IContainerInfo; const ir = next.ir as IContainerInfo;
const scope = Scope.createRootScope();
if (ir.state) { if (ir.state) {
const state = ir.state; const { state } = ir;
const fields = Object.keys(state).map<string>(stateName => { const fields = Object.keys(state).map<string>((stateName) => {
const [isString, value] = generateCompositeType(state[stateName]); const value = generateCompositeType(state[stateName], scope);
return `${stateName}: ${isString ? `'${value}'` : value},`; return `${stateName}: ${value},`;
}); });
if (cfg.implementType === 'inConstructor') { if (cfg.implementType === 'inConstructor') {

View File

@ -10,7 +10,7 @@ import {
type PluginConfig = { type PluginConfig = {
fileType: string; fileType: string;
} };
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => { const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = { const cfg: PluginConfig = {
@ -27,7 +27,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: cfg.fileType, fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent, name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent,
content: `this.utils = utils;`, content: 'this.utils = utils;',
linkAfter: [CLASS_DEFINE_CHUNK_NAME.ConstructorStart], linkAfter: [CLASS_DEFINE_CHUNK_NAME.ConstructorStart],
}); });

View File

@ -1,28 +1,23 @@
import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator'; import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator';
import { REACT_CHUNK_NAME } from './const'; import { REACT_CHUNK_NAME } from './const';
import { import { generateFunction } from '../../../utils/jsExpression';
getFuncExprBody,
transformFuncExpr2MethodMember,
} from '../../../utils/jsExpression';
import { import {
BuilderComponentPlugin, BuilderComponentPlugin,
BuilderComponentPluginFactory, BuilderComponentPluginFactory,
ChunkType, ChunkType,
CodeGeneratorError,
FileType, FileType,
ICodeChunk, ICodeChunk,
ICodeStruct, ICodeStruct,
IContainerInfo, IContainerInfo,
IJSExpression,
} from '../../../types'; } from '../../../types';
type PluginConfig = { type PluginConfig = {
fileType: string; fileType: string;
exportNameMapping: Record<string, string>; exportNameMapping: Record<string, string>;
normalizeNameMapping: Record<string, string>; normalizeNameMapping: Record<string, string>;
} };
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => { const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = { const cfg: PluginConfig = {
@ -40,8 +35,8 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
const ir = next.ir as IContainerInfo; const ir = next.ir as IContainerInfo;
if (ir.lifeCycles) { if (ir.lifeCycles) {
const lifeCycles = ir.lifeCycles; const { lifeCycles } = ir;
const chunks = Object.keys(lifeCycles).map<ICodeChunk>(lifeCycleName => { const chunks = Object.keys(lifeCycles).map<ICodeChunk>((lifeCycleName) => {
const normalizeName = cfg.normalizeNameMapping[lifeCycleName] || lifeCycleName; const normalizeName = cfg.normalizeNameMapping[lifeCycleName] || lifeCycleName;
const exportName = cfg.exportNameMapping[lifeCycleName] || lifeCycleName; const exportName = cfg.exportNameMapping[lifeCycleName] || lifeCycleName;
if (normalizeName === 'constructor') { if (normalizeName === 'constructor') {
@ -49,9 +44,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: cfg.fileType, fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent, name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent,
content: getFuncExprBody( content: generateFunction(lifeCycles[lifeCycleName], { isBlock: true }),
(lifeCycles[lifeCycleName] as IJSExpression).value,
),
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.ConstructorStart]], linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.ConstructorStart]],
}; };
} }
@ -60,9 +53,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: cfg.fileType, fileType: cfg.fileType,
name: REACT_CHUNK_NAME.ClassRenderPre, name: REACT_CHUNK_NAME.ClassRenderPre,
content: getFuncExprBody( content: generateFunction(lifeCycles[lifeCycleName], { isBlock: true }),
(lifeCycles[lifeCycleName] as IJSExpression).value,
),
linkAfter: [REACT_CHUNK_NAME.ClassRenderStart], linkAfter: [REACT_CHUNK_NAME.ClassRenderStart],
}; };
} }
@ -71,15 +62,12 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: cfg.fileType, fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.InsMethod, name: CLASS_DEFINE_CHUNK_NAME.InsMethod,
content: transformFuncExpr2MethodMember( content: generateFunction(lifeCycles[lifeCycleName], { name: exportName, isMember: true }),
exportName,
(lifeCycles[lifeCycleName] as IJSExpression).value,
),
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]], linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]],
}; };
}); });
next.chunks.push.apply(next.chunks, chunks); next.chunks.push(...chunks);
} }
return next; return next;

View File

@ -1,6 +1,6 @@
import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator'; import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator';
import { transformFuncExpr2MethodMember } from '../../../utils/jsExpression'; import { generateFunction } from '../../../utils/jsExpression';
import { import {
BuilderComponentPlugin, BuilderComponentPlugin,
@ -10,12 +10,11 @@ import {
ICodeChunk, ICodeChunk,
ICodeStruct, ICodeStruct,
IContainerInfo, IContainerInfo,
IJSExpression,
} from '../../../types'; } from '../../../types';
type PluginConfig = { type PluginConfig = {
fileType: string; fileType: string;
} };
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => { const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = { const cfg: PluginConfig = {
@ -31,19 +30,16 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
const ir = next.ir as IContainerInfo; const ir = next.ir as IContainerInfo;
if (ir.methods) { if (ir.methods) {
const methods = ir.methods; const { methods } = ir;
const chunks = Object.keys(methods).map<ICodeChunk>(methodName => ({ const chunks = Object.keys(methods).map<ICodeChunk>((methodName) => ({
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: cfg.fileType, fileType: cfg.fileType,
name: CLASS_DEFINE_CHUNK_NAME.InsMethod, name: CLASS_DEFINE_CHUNK_NAME.InsMethod,
content: transformFuncExpr2MethodMember( content: generateFunction(methods[methodName], { name: methodName, isMember: true }),
methodName,
(methods[methodName] as IJSExpression).value,
),
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]], linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]],
})); }));
next.chunks.push.apply(next.chunks, chunks); next.chunks.push(...chunks);
} }
return next; return next;

View File

@ -5,23 +5,31 @@ import {
FileType, FileType,
ICodeStruct, ICodeStruct,
IContainerInfo, IContainerInfo,
IScope,
} from '../../../types'; } from '../../../types';
import { REACT_CHUNK_NAME } from './const'; import { REACT_CHUNK_NAME } from './const';
import { createReactNodeGenerator } from '../../../utils/nodeToJSX'; import { createReactNodeGenerator } from '../../../utils/nodeToJSX';
import Scope from '../../../utils/Scope';
type PluginConfig = { type PluginConfig = {
fileType: string; fileType?: string;
} nodeTypeMapping?: Record<string, string>;
};
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => { const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = { const cfg = {
fileType: FileType.JSX, fileType: FileType.JSX,
nodeTypeMapping: {},
...config, ...config,
}; };
const generator = createReactNodeGenerator(); const { nodeTypeMapping } = cfg;
const generator = createReactNodeGenerator({
tagMapping: (v) => nodeTypeMapping[v] || v,
});
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = { const next: ICodeStruct = {
@ -29,17 +37,15 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
}; };
const ir = next.ir as IContainerInfo; const ir = next.ir as IContainerInfo;
const jsxContent = generator(ir); const scope: IScope = Scope.createRootScope();
const jsxContent = generator(ir, scope);
next.chunks.push({ next.chunks.push({
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: cfg.fileType, fileType: cfg.fileType,
name: REACT_CHUNK_NAME.ClassRenderJSX, name: REACT_CHUNK_NAME.ClassRenderJSX,
content: `return ${jsxContent};`, content: `return ${jsxContent};`,
linkAfter: [ linkAfter: [REACT_CHUNK_NAME.ClassRenderStart, REACT_CHUNK_NAME.ClassRenderPre],
REACT_CHUNK_NAME.ClassRenderStart,
REACT_CHUNK_NAME.ClassRenderPre,
],
}); });
return next; return next;

View File

@ -6,7 +6,6 @@ import {
ChunkType, ChunkType,
FileType, FileType,
ICodeStruct, ICodeStruct,
IContainerInfo,
} from '../../../types'; } from '../../../types';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => { const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
@ -19,7 +18,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: FileType.JSX, fileType: FileType.JSX,
name: COMMON_CHUNK_NAME.ExternalDepsImport, name: COMMON_CHUNK_NAME.ExternalDepsImport,
content: `import React from 'react';`, content: 'import React from \'react\';',
linkAfter: [], linkAfter: [],
}); });

View File

@ -1,3 +0,0 @@
export const RECORE_CHUNK_NAME = {
};

View File

@ -1,4 +1,7 @@
import { JSExpression, CompositeValue } from '@ali/lowcode-types';
import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator'; import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator';
import Scope from '../../../utils/Scope';
import { import {
BuilderComponentPlugin, BuilderComponentPlugin,
@ -7,15 +10,13 @@ import {
FileType, FileType,
ICodeStruct, ICodeStruct,
IContainerInfo, IContainerInfo,
IJSExpression,
CompositeValue,
} from '../../../types'; } from '../../../types';
import { generateCompositeType, handleStringValueDefault } from '../../../utils/compositeType'; import { generateCompositeType } from '../../../utils/compositeType';
import { generateExpression } from '../../../utils/jsExpression'; import { generateExpression } from '../../../utils/jsExpression';
function packJsExpression(exp: unknown): string { function packJsExpression(exp: unknown): string {
const expression = exp as IJSExpression; const expression = exp as JSExpression;
const funcStr = generateExpression(expression); const funcStr = generateExpression(expression);
return `function() { return (${funcStr}); }`; return `function() { return (${funcStr}); }`;
} }
@ -27,26 +28,27 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
}; };
const ir = next.ir as IContainerInfo; const ir = next.ir as IContainerInfo;
const scope = Scope.createRootScope();
if (ir.dataSource) { if (ir.dataSource) {
const { dataSource } = ir; const { dataSource } = ir;
const { const { list, ...rest } = dataSource;
list,
...rest
} = dataSource;
let attrs: string[] = []; let attrs: string[] = [];
const extConfigs = Object.keys(rest).map(extConfigName => { const extConfigs = Object.keys(rest).map((extConfigName) => {
const value = (rest as Record<string, CompositeValue>)[extConfigName]; const value = (rest as Record<string, CompositeValue>)[extConfigName];
const [isString, valueStr] = generateCompositeType(value); const valueStr = generateCompositeType(value, scope);
return `${extConfigName}: ${isString ? `'${valueStr}'` : valueStr}`; return `${extConfigName}: ${valueStr}`;
}); });
attrs = [...attrs, ...extConfigs]; attrs = [...attrs, ...extConfigs];
const listProp = handleStringValueDefault(generateCompositeType(list as unknown as CompositeValue, { const listProp = generateCompositeType((list as unknown) as CompositeValue, scope, {
handlers: {
expression: packJsExpression, expression: packJsExpression,
})); },
});
attrs.push(`list: ${listProp}`); attrs.push(`list: ${listProp}`);

View File

@ -21,7 +21,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: FileType.TS, fileType: FileType.TS,
name: COMMON_CHUNK_NAME.ExternalDepsImport, name: COMMON_CHUNK_NAME.ExternalDepsImport,
content: `import { BaseController } from '@ali/recore-renderer';`, content: 'import { BaseController } from \'@ali/recore-renderer\';',
linkAfter: [], linkAfter: [],
}); });
@ -37,7 +37,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: FileType.TS, fileType: FileType.TS,
name: CLASS_DEFINE_CHUNK_NAME.End, name: CLASS_DEFINE_CHUNK_NAME.End,
content: `}`, content: '}',
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.End]], linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.End]],
}); });

View File

@ -22,7 +22,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: FileType.TS, fileType: FileType.TS,
name: CLASS_DEFINE_CHUNK_NAME.StaticVar, name: CLASS_DEFINE_CHUNK_NAME.StaticVar,
content: `static cssText = '${ir.css.replace(/\'/g, '\\\'')}';`, content: `static cssText = '${ir.css.replace(/'/g, "\\'")}';`,
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.StaticVar]], linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.StaticVar]],
}); });
} }

View File

@ -1,31 +1,35 @@
import { NodeSchema } from '@ali/lowcode-types';
import { import {
BuilderComponentPlugin, BuilderComponentPlugin,
BuilderComponentPluginFactory, BuilderComponentPluginFactory,
ChunkType, ChunkType,
ICodeStruct, ICodeStruct,
IContainerInfo, IContainerInfo,
IComponentNodeItem, IScope,
CodePiece, CodePiece,
PIECE_TYPE, PIECE_TYPE,
} from '../../../types'; } from '../../../types';
import { COMMON_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator'; import { COMMON_CHUNK_NAME } from '../../../const/generator';
import { createNodeGenerator, generateString } from '../../../utils/nodeToJSX'; import { createNodeGenerator } from '../../../utils/nodeToJSX';
import { generateExpression } from '../../../utils/jsExpression'; import { generateCompositeType } from '../../../utils/compositeType';
import { generateCompositeType, handleStringValueDefault } from '../../../utils/compositeType'; import Scope from '../../../utils/Scope';
const generateGlobalProps = (nodeItem: IComponentNodeItem): CodePiece[] => { const generateGlobalProps = (nodeItem: NodeSchema): CodePiece[] => {
return [{ return [
{
value: `{...globalProps.${nodeItem.componentName}}`, value: `{...globalProps.${nodeItem.componentName}}`,
type: PIECE_TYPE.ATTR, type: PIECE_TYPE.ATTR,
}]; },
];
}; };
const generateCtrlLine = (nodeItem: IComponentNodeItem): CodePiece[] => { const generateCtrlLine = (nodeItem: NodeSchema, scope: IScope): CodePiece[] => {
const pieces: CodePiece[] = []; const pieces: CodePiece[] = [];
if (nodeItem.loop && nodeItem.loopArgs) { if (nodeItem.loop && nodeItem.loopArgs) {
const loopDataExp = handleStringValueDefault(generateCompositeType(nodeItem.loop)); const loopDataExp = generateCompositeType(nodeItem.loop, scope);
pieces.push({ pieces.push({
type: PIECE_TYPE.ATTR, type: PIECE_TYPE.ATTR,
value: `x-for={${loopDataExp}}`, value: `x-for={${loopDataExp}}`,
@ -38,7 +42,7 @@ const generateCtrlLine = (nodeItem: IComponentNodeItem): CodePiece[] => {
} }
if (nodeItem.condition) { if (nodeItem.condition) {
const conditionExp = handleStringValueDefault(generateCompositeType(nodeItem.condition)); const conditionExp = generateCompositeType(nodeItem.condition, scope);
pieces.push({ pieces.push({
type: PIECE_TYPE.ATTR, type: PIECE_TYPE.ATTR,
value: `x-if={${conditionExp}}`, value: `x-if={${conditionExp}}`,
@ -50,12 +54,8 @@ const generateCtrlLine = (nodeItem: IComponentNodeItem): CodePiece[] => {
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => { const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
const generator = createNodeGenerator({ const generator = createNodeGenerator({
string: generateString, nodePlugins: [generateGlobalProps, generateCtrlLine],
expression: (input) => [generateExpression(input)], });
}, [
generateGlobalProps,
generateCtrlLine,
]);
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = { const next: ICodeStruct = {
@ -63,8 +63,9 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
}; };
const ir = next.ir as IContainerInfo; const ir = next.ir as IContainerInfo;
const scope = Scope.createRootScope();
const vxContent = generator(ir); const vxContent = generator(ir, scope);
next.chunks.push({ next.chunks.push({
type: ChunkType.STRING, type: ChunkType.STRING,

View File

@ -1,11 +1,6 @@
import { COMMON_CHUNK_NAME } from '../../../const/generator'; import { COMMON_CHUNK_NAME } from '../../../const/generator';
import { import { BuilderComponentPlugin, BuilderComponentPluginFactory, ChunkType, ICodeStruct } from '../../../types';
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
ICodeStruct,
} from '../../../types';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => { const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
@ -17,7 +12,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: 'vx', fileType: 'vx',
name: COMMON_CHUNK_NAME.CustomContent, name: COMMON_CHUNK_NAME.CustomContent,
content: `<div {...globalProps.div} className="recore-loading" x-if={this.__loading} />`, content: '<div {...globalProps.div} className="recore-loading" x-if={this.__loading} />',
linkAfter: [], linkAfter: [],
}); });

View File

@ -12,7 +12,7 @@ import {
type PluginConfig = { type PluginConfig = {
fileType: string; fileType: string;
moduleFileType: string; moduleFileType: string;
} };
const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => { const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) => {
const cfg: PluginConfig = { const cfg: PluginConfig = {

View File

@ -8,6 +8,7 @@ import {
ICodeStruct, ICodeStruct,
IProjectInfo, IProjectInfo,
} from '../../types'; } from '../../types';
import Scope from '../../utils/Scope';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => { const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
@ -16,19 +17,20 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
}; };
const ir = next.ir as IProjectInfo; const ir = next.ir as IProjectInfo;
if (ir.constants) { const scope = Scope.createRootScope();
const [, constantStr] = generateCompositeType(ir.constants); const constantStr = generateCompositeType(ir.constants || {}, scope);
next.chunks.push({ next.chunks.push({
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: FileType.JS, fileType: FileType.JS,
name: COMMON_CHUNK_NAME.FileVarDefine, name: COMMON_CHUNK_NAME.FileVarDefine,
content: ` content: `
const constantConfig = ${constantStr}; const __$$constants = (${constantStr});
`, `,
linkAfter: [ linkAfter: [
COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.ImportAliasDefine,
], ],
}); });
@ -37,17 +39,17 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
fileType: FileType.JS, fileType: FileType.JS,
name: COMMON_CHUNK_NAME.FileExport, name: COMMON_CHUNK_NAME.FileExport,
content: ` content: `
export default constantConfig; export default __$$constants;
`, `,
linkAfter: [ linkAfter: [
COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.ImportAliasDefine,
COMMON_CHUNK_NAME.FileVarDefine, COMMON_CHUNK_NAME.FileVarDefine,
COMMON_CHUNK_NAME.FileUtilDefine, COMMON_CHUNK_NAME.FileUtilDefine,
COMMON_CHUNK_NAME.FileMainContent, COMMON_CHUNK_NAME.FileMainContent,
], ],
}); });
}
return next; return next;
}; };

View File

@ -6,7 +6,6 @@ import {
ChunkType, ChunkType,
FileType, FileType,
ICodeStruct, ICodeStruct,
IProjectInfo,
} from '../../../../../types'; } from '../../../../../types';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => { const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
@ -15,8 +14,6 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
...pre, ...pre,
}; };
const ir = next.ir as IProjectInfo;
next.chunks.push({ next.chunks.push({
type: ChunkType.STRING, type: ChunkType.STRING,
fileType: FileType.JS, fileType: FileType.JS,
@ -34,10 +31,10 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
content: ` content: `
const appConfig = { const appConfig = {
app: { app: {
rootId: '${ir.config.targetRootID}', rootId: 'app',
}, },
router: { router: {
type: '${ir.config.historyMode}', type: 'hash',
}, },
}; };
createApp(appConfig); createApp(appConfig);
@ -45,6 +42,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
linkAfter: [ linkAfter: [
COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.ImportAliasDefine,
COMMON_CHUNK_NAME.FileVarDefine, COMMON_CHUNK_NAME.FileVarDefine,
COMMON_CHUNK_NAME.FileUtilDefine, COMMON_CHUNK_NAME.FileUtilDefine,
], ],

View File

@ -28,10 +28,10 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1" /> <meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<title>${ir.meta.name}</title> <title>${ir?.meta?.name || 'Ice App'}</title>
</head> </head>
<body> <body>
<div id="${ir.config.targetRootID}"></div> <div id="app"></div>
</body> </body>
</html> </html>
`, `,

View File

@ -1,3 +1,5 @@
import { PackageJSON } from '@ali/lowcode-types';
import { COMMON_CHUNK_NAME } from '../../../../../const/generator'; import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
import { import {
@ -6,11 +8,10 @@ import {
ChunkType, ChunkType,
FileType, FileType,
ICodeStruct, ICodeStruct,
IPackageJSON,
IProjectInfo, IProjectInfo,
} from '../../../../../types'; } from '../../../../../types';
interface IIceJsPackageJSON extends IPackageJSON { interface IIceJsPackageJSON extends PackageJSON {
ideMode: { ideMode: {
name: string; name: string;
}; };
@ -73,6 +74,11 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
originTemplate: '@alifd/scaffold-lite-js', originTemplate: '@alifd/scaffold-lite-js',
}; };
console.log(ir.packages);
ir.packages.forEach((packageInfo) => {
packageJson.dependencies[packageInfo.package] = packageInfo.version;
});
next.chunks.push({ next.chunks.push({
type: ChunkType.JSON, type: ChunkType.JSON,
fileType: FileType.JSON, fileType: FileType.JSON,

View File

@ -39,7 +39,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
children: [ children: [
${ir.routes ${ir.routes
.map( .map(
route => ` (route) => `
{ {
path: '${route.path}', path: '${route.path}',
component: ${route.componentName}, component: ${route.componentName},
@ -54,6 +54,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
linkAfter: [ linkAfter: [
COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.ImportAliasDefine,
COMMON_CHUNK_NAME.FileUtilDefine, COMMON_CHUNK_NAME.FileUtilDefine,
], ],
}); });
@ -69,6 +70,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
COMMON_CHUNK_NAME.ExternalDepsImport, COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport, COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.FileUtilDefine, COMMON_CHUNK_NAME.FileUtilDefine,
COMMON_CHUNK_NAME.ImportAliasDefine,
COMMON_CHUNK_NAME.FileVarDefine, COMMON_CHUNK_NAME.FileVarDefine,
COMMON_CHUNK_NAME.FileMainContent, COMMON_CHUNK_NAME.FileMainContent,
], ],

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../types'; import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'README', 'README',
'md', 'md',
` `

View File

@ -1,17 +1,17 @@
import ResultFile from '../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../types';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( return [
'abc', [],
'json', {
` name: 'abc',
ext: 'json',
content: `
{ {
"type": "ice-app", "type": "ice-app",
"builder": "@ali/builder-ice-app" "builder": "@ali/builder-ice-app"
} }
`, `,
); },
];
return [[], file];
} }

View File

@ -1,11 +1,12 @@
import ResultFile from '../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../types';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( return [
'build', [],
'json', {
` name: 'build',
ext: 'json',
content: `
{ {
"entry": "src/app.js", "entry": "src/app.js",
"plugins": [ "plugins": [
@ -27,7 +28,6 @@ export default function getFile(): [string[], IResultFile] {
] ]
} }
`, `,
); },
];
return [[], file];
} }

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../types'; import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'.editorconfig', '.editorconfig',
'', '',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../types'; import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'.eslintignore', '.eslintignore',
'', '',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../types'; import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'.eslintrc', '.eslintrc',
'js', 'js',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../types'; import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'.gitignore', '.gitignore',
'', '',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../types'; import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'jsconfig', 'jsconfig',
'json', 'json',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../types'; import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'.prettierignore', '.prettierignore',
'', '',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../types'; import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'.prettierrc', '.prettierrc',
'js', 'js',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../../../../../../types'; import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'index', 'index',
'jsx', 'jsx',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../../../../../../types'; import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'index', 'index',
'module.scss', 'module.scss',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../../../../../../types'; import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'index', 'index',
'jsx', 'jsx',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../../../../../../types'; import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'index', 'index',
'module.scss', 'module.scss',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../../../../../../types'; import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'index', 'index',
'jsx', 'jsx',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../../../../types'; import { createResultFile } from '../../../../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'index', 'index',
'jsx', 'jsx',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../../../../types'; import { createResultFile } from '../../../../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'menuConfig', 'menuConfig',
'js', 'js',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../types'; import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'.stylelintignore', '.stylelintignore',
'', '',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../types'; import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'.stylelintrc', '.stylelintrc',
'js', 'js',
` `

View File

@ -1,8 +1,8 @@
import ResultFile from '../../../../../../model/ResultFile'; import { ResultFile } from '@ali/lowcode-types';
import { IResultFile } from '../../../../../../types'; import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], IResultFile] { export default function getFile(): [string[], ResultFile] {
const file = new ResultFile( const file = createResultFile(
'tsconfig', 'tsconfig',
'json', 'json',
` `

View File

@ -1,8 +1,7 @@
import ResultDir from '../../../../../model/ResultDir'; import { ResultDir } from '@ali/lowcode-types';
import { import { IProjectTemplate } from '../../../../../types';
IProjectTemplate,
IResultDir, import { createResultDir } from '../../../../../utils/resultHelper';
} from '../../../../../types';
import { runFileGenerator } from '../../../../../utils/templateHelper'; import { runFileGenerator } from '../../../../../utils/templateHelper';
import file12 from './files/abc.json'; import file12 from './files/abc.json';
@ -68,8 +67,8 @@ const icejsTemplate: IProjectTemplate = {
}, },
}, },
generateTemplate(): IResultDir { generateTemplate(): ResultDir {
const root = new ResultDir('.'); const root = createResultDir('.');
runFileGenerator(root, file1); runFileGenerator(root, file1);
runFileGenerator(root, file2); runFileGenerator(root, file2);

View File

@ -0,0 +1,19 @@
import template from './template';
import entry from './plugins/entry';
import appConfig from './plugins/appConfig';
import buildConfig from './plugins/buildConfig';
import entryDocument from './plugins/entryDocument';
import globalStyle from './plugins/globalStyle';
import packageJSON from './plugins/packageJSON';
export default {
template,
plugins: {
appConfig,
buildConfig,
entry,
entryDocument,
globalStyle,
packageJSON,
},
};

View File

@ -0,0 +1,46 @@
import changeCase from 'change-case';
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IParseResult,
} from '../../../../../types';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IParseResult;
const routes = ir.globalRouter?.routes?.map((route) => ({
path: route.path,
source: `pages/${changeCase.pascalCase(route.fileName)}/index`,
})) || [{ path: '/', source: 'pages/Home/index' }];
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSON,
name: COMMON_CHUNK_NAME.CustomContent,
content: `
{
"routes": ${JSON.stringify(routes, null, 2)},
"window": {
"title": ${JSON.stringify(ir.project?.meta?.title || ir.project?.meta?.name || '')}
}
}
`,
linkAfter: [],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,53 @@
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IParseResult,
} from '../../../../../types';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IParseResult;
// TODO: miniAppBuildType 这个东西是不是不应该在 schema 里面,而是应该由 plugin 的构造参数传入
const miniAppBuildType = ir.project?.config?.miniAppBuildType;
const buildCfg = {
inlineStyle: false,
plugins: [
[
'build-plugin-rax-app',
{
targets: ['web', 'miniapp'],
miniapp: miniAppBuildType
? {
buildType: miniAppBuildType,
}
: undefined,
},
],
'@ali/build-plugin-rax-app-def',
],
};
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSON,
name: COMMON_CHUNK_NAME.CustomContent,
content: JSON.stringify(buildCfg, null, 2) + '\n',
linkAfter: [],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,51 @@
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
} from '../../../../../types';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JS,
name: COMMON_CHUNK_NAME.ExternalDepsImport,
content: `
import { runApp } from 'rax-app';
import appConfig from './app.json';
import './global.scss';
`,
linkAfter: [],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JS,
name: COMMON_CHUNK_NAME.FileMainContent,
content: `
runApp(appConfig);
`,
linkAfter: [
COMMON_CHUNK_NAME.ExternalDepsImport,
COMMON_CHUNK_NAME.InternalDepsImport,
COMMON_CHUNK_NAME.ImportAliasDefine,
COMMON_CHUNK_NAME.FileVarDefine,
COMMON_CHUNK_NAME.FileUtilDefine,
],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,59 @@
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IProjectInfo,
} from '../../../../../types';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IProjectInfo;
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: COMMON_CHUNK_NAME.CustomContent,
content: `
import { createElement } from 'rax';
import { Root, Style, Script } from 'rax-document';
function Document() {
return (
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,viewport-fit=cover"
/>
<title>${ir?.meta?.name || 'Rax App'}</title>
<Style />
</head>
<body>
{/* root container */}
<Root />
<Script />
</body>
</html>
);
}
export default Document;
`,
linkAfter: [],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,64 @@
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IProjectInfo,
} from '../../../../../types';
export type GlobalStylePluginConfig = {
fileType: string;
};
const pluginFactory: BuilderComponentPluginFactory<GlobalStylePluginConfig> = (
config?: Partial<GlobalStylePluginConfig>,
) => {
const cfg: GlobalStylePluginConfig = {
fileType: FileType.SCSS,
...config,
};
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IProjectInfo;
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: COMMON_CHUNK_NAME.StyleDepsImport,
content: '',
linkAfter: [],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: COMMON_CHUNK_NAME.StyleCssContent,
content: `
body {
-webkit-font-smoothing: antialiased;
}
`,
linkAfter: [COMMON_CHUNK_NAME.StyleDepsImport],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: cfg.fileType,
name: COMMON_CHUNK_NAME.StyleCssContent,
content: ir.css || '',
linkAfter: [COMMON_CHUNK_NAME.StyleDepsImport],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,115 @@
import { NpmInfo, PackageJSON } from '@ali/lowcode-types';
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IProjectInfo,
} from '../../../../../types';
import { isNpmInfo } from '../../../../../utils/schema';
import { calcCompatibleVersion } from '../../../../../utils/version';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IProjectInfo;
const npmDeps = getNpmDependencies(ir);
const packageJson: PackageJSON = {
name: '@ali/rax-app-demo',
private: true,
version: '1.0.0',
scripts: {
build: 'build-scripts build',
start: 'build-scripts start',
lint: 'eslint --ext .js --ext .jsx ./',
},
dependencies: {
'@ali/lowcode-datasource-engine': '*',
// TODO: 如何动态获取下面这些依赖?
'@ali/lowcode-datasource-url-params-handler': '*',
'@ali/lowcode-datasource-fetch-handler': '*',
'@ali/lowcode-datasource-mtop-handler': '*',
'@ali/lowcode-datasource-mopen-handler': '*',
'universal-env': '^3.2.0',
'intl-messageformat': '^9.3.6',
rax: '^1.1.0',
'rax-app': '^2.0.0',
'rax-document': '^0.1.0',
...npmDeps.reduce(
(acc, npm) => ({
...acc,
[npm.package]: npm.version || '*',
}),
{} as Record<string, string>,
),
},
devDependencies: {
'build-plugin-rax-app': '^5.0.0',
'@alib/build-scripts': '^0.1.0',
'@typescript-eslint/eslint-plugin': '^2.11.0',
'@typescript-eslint/parser': '^2.11.0',
'babel-eslint': '^10.0.3',
eslint: '^6.8.0',
'eslint-config-rax': '^0.1.0',
'eslint-plugin-import': '^2.20.0',
'eslint-plugin-module': '^0.1.0',
'eslint-plugin-react': '^7.18.0',
'@ali/build-plugin-rax-app-def': '^1.0.0',
},
};
next.chunks.push({
type: ChunkType.JSON,
fileType: FileType.JSON,
name: COMMON_CHUNK_NAME.FileMainContent,
content: packageJson,
linkAfter: [],
});
return next;
};
return plugin;
};
export default pluginFactory;
function getNpmDependencies(project: IProjectInfo): NpmInfo[] {
const npmDeps: NpmInfo[] = [];
const npmNameToPkgMap = new Map<string, NpmInfo>();
const allDeps = project.packages;
allDeps.forEach((dep) => {
if (!isNpmInfo(dep)) {
return;
}
const existing = npmNameToPkgMap.get(dep.package);
if (!existing) {
npmNameToPkgMap.set(dep.package, dep);
npmDeps.push(dep);
return;
}
if (existing.version !== dep.version) {
try {
npmNameToPkgMap.set(dep.package, {
...existing,
version: calcCompatibleVersion(existing.version, dep.version),
});
} catch (e) {
throw new Error(`Cannot find compatible version for ${dep.package}. Detail: ${e.message}`);
}
}
});
return npmDeps;
}

View File

@ -0,0 +1,27 @@
import { ResultFile } from '@ali/lowcode-types';
import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'README',
'md',
`# @ali/rax-component-demo
## Getting Started
### \`npm run start\`
Runs the app in development mode.
Open [http://localhost:9999](http://localhost:9999) to view it in the browser.
The page will reload if you make edits.
### \`npm run build\`
Builds the app for production to the \`build\` folder.
`,
);
return [[], file];
}

View File

@ -0,0 +1,19 @@
import { ResultFile } from '@ali/lowcode-types';
import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'abc',
'json',
`{
"type": "rax",
"builder": "@ali/builder-rax-v1",
"info": {
"raxVersion": "1.x"
}
}
`,
);
return [[], file];
}

View File

@ -0,0 +1,24 @@
import { ResultFile } from '@ali/lowcode-types';
import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'build',
'json',
`{
"inlineStyle": false,
"plugins": [
[
"build-plugin-rax-app",
{
"targets": ["web", "miniapp"]
}
],
"@ali/build-plugin-rax-app-def"
]
}
`,
);
return [[], file];
}

View File

@ -0,0 +1,25 @@
import { ResultFile } from '@ali/lowcode-types';
import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'.editorconfig',
'',
`# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
`,
);
return [[], file];
}

View File

@ -0,0 +1,23 @@
import { ResultFile } from '@ali/lowcode-types';
import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'.eslintignore',
'',
`# 忽略目录
build/
tests/
demo/
# node
coverage/
#
**/*-min.js
**/*.min.js
`,
);
return [[], file];
}

View File

@ -0,0 +1,16 @@
import { ResultFile } from '@ali/lowcode-types';
import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'.eslintrc',
'js',
`module.exports = {
extends: ['rax'],
};
`,
);
return [[], file];
}

View File

@ -0,0 +1,29 @@
import { ResultFile } from '@ali/lowcode-types';
import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'.gitignore',
'',
`# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
*~
*.swp
*.log
.DS_Store
.idea/
.temp/
build/
dist/
lib/
coverage/
node_modules/
template.yml
`,
);
return [[], file];
}

Some files were not shown because too many files have changed in this diff Show More