diff --git a/packages/react-renderer/README.md b/examples/base/src/index.ts similarity index 100% rename from packages/react-renderer/README.md rename to examples/base/src/index.ts diff --git a/package.json b/package.json index d9f5efbf4..e9de84d9a 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "workspaces": { "packages": [ "packages/*", - "runtime/*" + "runtime/*", + "examples/*" ], "nohoist": [ "**/css-modules-typescript-loader", diff --git a/packages/ignitor/babel.config.js b/packages/ignitor/babel.config.js deleted file mode 100644 index c5986f2bc..000000000 --- a/packages/ignitor/babel.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../babel.config'); \ No newline at end of file diff --git a/packages/ignitor/build.json b/packages/ignitor/build.json deleted file mode 100644 index f1956cf52..000000000 --- a/packages/ignitor/build.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "entry": { - "AliLowCodeEngine": "../engine/src/index.ts", - "ReactSimulatorRenderer": "../react-simulator-renderer/src/index.ts" - }, - "vendor": false, - "devServer": { - "liveReload": false, - "hot": false - }, - "library": "[name]", - "publicPath": "/", - "externals": { - "react": "var window.React", - "react-dom": "var window.ReactDOM", - "prop-types": "var window.PropTypes", - "@alifd/next": "var window.Next", - "rax": "var window.Rax", - "@alilc/lowcode-engine": "var window.AliLowCodeEngine", - "@alilc/lowcode-engine-ext": "var window.AliLowCodeEngineExt", - "moment": "var moment", - "lodash": "var _" - }, - "plugins": [ - [ - "build-plugin-react-app" - ], - [ - "build-plugin-fusion", - { - "themePackage": "@alifd/theme-lowcode-light", - "externalNext": "umd" - } - ], - [ - "build-plugin-moment-locales", - { - "locales": [ - "zh-cn" - ] - } - ], - "./build.plugin.js" - ] -} diff --git a/packages/ignitor/build.plugin.js b/packages/ignitor/build.plugin.js deleted file mode 100644 index ffa1d4957..000000000 --- a/packages/ignitor/build.plugin.js +++ /dev/null @@ -1,25 +0,0 @@ -const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); -const fse = require('fs-extra'); -// read from lerna -const lernaConfig = JSON.parse(fse.readFileSync('../../lerna.json', 'utf8')); -const { version } = lernaConfig; - -module.exports = ({ context, onGetWebpackConfig }) => { - onGetWebpackConfig((config) => { - config.resolve.plugin('tsconfigpaths').use(TsconfigPathsPlugin, [ - { - configFile: './tsconfig.json', - }, - ]); - config - .plugin('define') - .use(context.webpack.DefinePlugin, [{ - VERSION_PLACEHOLDER: JSON.stringify(version), - }]); - config.plugins.delete('hot'); - config.devServer.hot(false); - if (context.command === 'start') { - config.devtool('inline-source-map'); - } - }); -}; diff --git a/packages/ignitor/jest.config.js b/packages/ignitor/jest.config.js deleted file mode 100644 index 788c0ac79..000000000 --- a/packages/ignitor/jest.config.js +++ /dev/null @@ -1,47 +0,0 @@ -const fs = require('fs'); -const { join } = require('path'); -const esModules = [].join('|'); -const pkgNames = fs.readdirSync(join('..')).filter(pkgName => !pkgName.startsWith('.')); - -const jestConfig = { - // transform: { - // '^.+\\.[jt]sx?$': 'babel-jest', - // // '^.+\\.(ts|tsx)$': 'ts-jest', - // // '^.+\\.(js|jsx)$': 'babel-jest', - // }, - // testMatch: ['**/node-children.test.ts'], - // testMatch: ['**/plugin-manager.test.ts'], - // testMatch: ['**/history/history.test.ts'], - // testMatch: ['**/document-model.test.ts'], - // testMatch: ['**/prop.test.ts'], - // testMatch: ['(/tests?/.*(test))\\.[jt]s$'], - transformIgnorePatterns: [ - `/node_modules/(?!${esModules})/`, - ], - setupFiles: ['./tests/fixtures/unhandled-rejection.ts'], - moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], - collectCoverage: false, - collectCoverageFrom: [ - 'src/**/*.ts', - '!src/**/*.d.ts', - '!src/icons/**', - '!src/locale/**', - '!src/builtin-simulator/utils/**', - '!src/plugin/sequencify.ts', - '!src/document/node/exclusive-group.ts', - '!src/document/node/props/value-to-source.ts', - '!src/builtin-simulator/live-editing/live-editing.ts', - '!src/designer/offset-observer.ts', - '!src/designer/clipboard.ts', - '!src/designer/scroller.ts', - '!src/builtin-simulator/host.ts', - '!**/node_modules/**', - '!**/vendor/**', - ], -}; - -// 只对本仓库内的 pkg 做 mapping -jestConfig.moduleNameMapper = {}; -jestConfig.moduleNameMapper[`^@alilc/lowcode\\-(${pkgNames.join('|')})$`] = '/../$1/src'; - -module.exports = jestConfig; \ No newline at end of file diff --git a/packages/ignitor/package.json b/packages/ignitor/package.json deleted file mode 100644 index 0b109a7ad..000000000 --- a/packages/ignitor/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "@alilc/lowcode-ignitor", - "version": "1.3.2", - "description": "点火器,bootstrap lce project", - "main": "lib/index.js", - "private": true, - "files": [ - "dist", - "es", - "lib" - ], - "scripts": { - "start": "build-scripts start --disable-open --port 5555" - }, - "license": "MIT", - "devDependencies": { - "@alib/build-scripts": "^0.1.18", - "fs-extra": "^10.0.0" - }, - "repository": { - "type": "http", - "url": "https://github.com/alibaba/lowcode-engine/tree/main/packages/ignitor" - }, - "bugs": "https://github.com/alibaba/lowcode-engine/issues", - "homepage": "https://github.com/alibaba/lowcode-engine/#readme" -} diff --git a/packages/ignitor/public/favicon.png b/packages/ignitor/public/favicon.png deleted file mode 100644 index 307ffbd82..000000000 Binary files a/packages/ignitor/public/favicon.png and /dev/null differ diff --git a/packages/ignitor/public/index.html b/packages/ignitor/public/index.html deleted file mode 100644 index 147fe334d..000000000 --- a/packages/ignitor/public/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - LowCodeEngine Editor DEMO - - -

- This project only provides engine resource files. For usage, go for - Lowcode Demo -

-

- For local debugging of lowcode engine, please visit - proxy documentation - to get more information. -

- - diff --git a/packages/ignitor/tsconfig.json b/packages/ignitor/tsconfig.json deleted file mode 100644 index c37b76ecc..000000000 --- a/packages/ignitor/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "lib" - }, - "include": [ - "./src/" - ] -} diff --git a/packages/react-renderer/build.json b/packages/react-renderer/build.json deleted file mode 100644 index d0aec1038..000000000 --- a/packages/react-renderer/build.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": [ - "@alilc/build-plugin-lce", - "build-plugin-fusion", - ["build-plugin-moment-locales", { - "locales": ["zh-cn"] - }] - ] -} diff --git a/packages/react-renderer/build.test.json b/packages/react-renderer/build.test.json deleted file mode 100644 index 9cc30d746..000000000 --- a/packages/react-renderer/build.test.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "plugins": [ - "@alilc/build-plugin-lce", - "@alilc/lowcode-test-mate/plugin/index.ts" - ] -} diff --git a/packages/react-renderer/build.umd.json b/packages/react-renderer/build.umd.json deleted file mode 100644 index bc1a5e8d8..000000000 --- a/packages/react-renderer/build.umd.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "entry": { - "react-renderer": "src/index" - }, - "sourceMap": true, - "library": "AliLowCodeReactRenderer", - "libraryTarget": "umd", - "externals": { - "react": "var window.React", - "react-dom": "var window.ReactDOM", - "prop-types": "var window.PropTypes", - "@alifd/next": "var Next", - "moment": "var window.moment" - }, - "polyfill": false, - "outputDir": "dist", - "vendor": false, - "ignoreHtmlTemplate": true, - "plugins": [ - "build-plugin-react-app", - ["build-plugin-fusion", { - "externalNext": "umd" - }], - ["build-plugin-moment-locales", { - "locales": ["zh-cn"] - }] - ] -} \ No newline at end of file diff --git a/packages/react-renderer/demo/compose.md b/packages/react-renderer/demo/compose.md deleted file mode 100644 index b828cd604..000000000 --- a/packages/react-renderer/demo/compose.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: 复杂组件 -order: 2 ---- - -````jsx -import React, { PureComponent } from 'react'; -import ReactDOM from 'react-dom'; -import ReactRenderer from '@alilc/lowcode-react-renderer'; -import schema from './schemas/compose'; -import components from './config/components/index'; -import utils from './config/utils'; -import constants from './config/constants'; - -class Demo extends PureComponent { - static displayName = 'renderer-demo'; - render() { - return ( -
- -
- ); - } -} - -ReactDOM.render(( - -), mountNode); -```` diff --git a/packages/react-renderer/demo/config/components/A.jsx b/packages/react-renderer/demo/config/components/A.jsx deleted file mode 100644 index 845740ecd..000000000 --- a/packages/react-renderer/demo/config/components/A.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import React, { PureComponent } from 'react'; - -export default class AView extends PureComponent { - static displayName = 'A'; - - static version = '0.0.0'; - - render() { - return ; - } -} diff --git a/packages/react-renderer/demo/config/components/Div.jsx b/packages/react-renderer/demo/config/components/Div.jsx deleted file mode 100644 index a392b2a03..000000000 --- a/packages/react-renderer/demo/config/components/Div.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import React, { PureComponent } from 'react'; - -export default class DivView extends PureComponent { - static displayName = 'Div'; - - static version = '0.0.0'; - - render() { - return
; - } -} diff --git a/packages/react-renderer/demo/config/components/Image.jsx b/packages/react-renderer/demo/config/components/Image.jsx deleted file mode 100644 index bbf788f34..000000000 --- a/packages/react-renderer/demo/config/components/Image.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import React, { PureComponent } from 'react'; - -export default class ImageView extends PureComponent { - static displayName = 'Image'; - - static version = '0.0.0'; - - static defaultProps = { - src: '//img.alicdn.com/tfs/TB1knm4bqNj0u4jSZFyXXXgMVXa-240-240.png_100x100.jpg', - }; - - render() { - return ; - } -} diff --git a/packages/react-renderer/demo/config/components/Text.jsx b/packages/react-renderer/demo/config/components/Text.jsx deleted file mode 100644 index 6862977f6..000000000 --- a/packages/react-renderer/demo/config/components/Text.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; - -export default class TextView extends PureComponent { - static displayName = 'Text'; - - static version = '0.0.0'; - - static propTypes = { - text: PropTypes.string, - }; - - render() { - const { text, ...restProps } = this.props; - let textNode = text; - // 强制类型转换 - try { - textNode = text.toString(); - } catch (e) { - textNode = ''; - } - if (window.__ctx && window.__ctx.canvasAppHelper) { - textNode = textNode || 'Text'; - } - return {textNode}; - } -} diff --git a/packages/react-renderer/demo/config/components/index.js b/packages/react-renderer/demo/config/components/index.js deleted file mode 100644 index 740f6498f..000000000 --- a/packages/react-renderer/demo/config/components/index.js +++ /dev/null @@ -1,175 +0,0 @@ -import Div from './Div'; -import Text from './Text'; -import A from './A'; -import Image from './Image'; - -import { - Balloon, - Button, - Checkbox, - Dropdown, - Grid, - Menu, - Select, - Tab, - Table, - Radio, - Pagination, - Input, - Icon, - Switch, - Tree, - NumberPicker, - Collapse, - Range, - Dialog, - Overlay, - Search, - Loading, - MenuButton, - Badge, - Message, - Slider, - SplitButton, - Paragraph, - Nav, - Breadcrumb, - Step, - DatePicker, - TimePicker, - Rating, - Upload, - Tag, - Card, - Calendar, - Progress, - Cascader, - ConfigProvider, - Animate, - CascaderSelect, - Transfer, - TreeSelect, - Timeline, - VirtualList, -} from '@alifd/next'; - -const { Row, Col } = Grid; -const { - Item: MenuItem, - Group: MenuGroup, - SubMenu, - PopupItem: MenuPopupItem, - CheckboxItem: MenuCheckboxItem, - RadioItem: MenuRadioItem, - Divider: MenuDivider, -} = Menu; -const { Item: TabItem } = Tab; -const { Column: TableColumn, ColumnGroup: TableColumnGroup } = Table; -const { Group: ButtonGroup } = Button; -const { Group: RadioGroup } = Radio; -const { Node: TreeNode } = Tree; -const { Panel: CollapsePanel } = Collapse; -const { Tooltip } = Balloon; -const { AutoComplete: SelectAutoComplete, OptionGroup: SelectOptionGroup, Option: SelectOption } = Select; -const { Item: MenuButtonItem } = MenuButton; -const { Item: StepItem } = Step; -const { Item: NavItem, SubNav, PopupItem: NavPopItem, Group: NavGroup } = Nav; -const { Item: BreadcrumbItem } = Breadcrumb; -const { MonthPicker, RangePicker, YearPicker } = DatePicker; -const { Card: UploadCard, Dragger: UploadDragger, Selecter: UploadSelecter } = Upload; -const { Closeable: TagCloseable, Selectable: TagSelectable } = Tag; -const { Popup } = Overlay; -const { Node: TreeSelectNode } = TreeSelect; -const { Item: TimelineItem } = Timeline; - -export default { - Div, - A, - Text, - Image, - - Balloon, - Tooltip, - Button, - ButtonGroup, - Checkbox, - Row, - Col, - Select, - SelectAutoComplete, - SelectOptionGroup, - SelectOption, - Dropdown, - Menu, - MenuItem, - MenuGroup, - MenuDivider, - SubMenu, - MenuPopupItem, - MenuCheckboxItem, - MenuRadioItem, - MenuButton, - MenuButtonItem, - Loading, - Tab, - TabItem, - Table, - TableColumn, - TableColumnGroup, - Radio, - RadioGroup, - Pagination, - Input, - Icon, - Switch, - Tree, - TreeNode, - NumberPicker, - Collapse, - Dialog, - Overlay, - Popup, - CollapsePanel, - Range, - Search, - Badge, - Message, - Slider, - SplitButton, - Paragraph, - Nav, - NavItem, - NavPopItem, - NavGroup, - SubNav, - Breadcrumb, - BreadcrumbItem, - Rating, - Step, - StepItem, - DatePicker, - MonthPicker, - RangePicker, - YearPicker, - TimePicker, - Upload, - UploadCard, - UploadDragger, - UploadSelecter, - Tag, - TagCloseable, - TagSelectable, - Card, - Calendar, - Progress, - Cascader, - ConfigProvider, - Animate, - CascaderSelect, - Transfer, - TreeSelect, - TreeSelectNode, - Timeline, - TimelineItem, - VirtualList, -}; diff --git a/packages/react-renderer/demo/config/constants.js b/packages/react-renderer/demo/config/constants.js deleted file mode 100644 index a8ea91c6c..000000000 --- a/packages/react-renderer/demo/config/constants.js +++ /dev/null @@ -1,3 +0,0 @@ -export default { - name: 'renderer-demo', -}; diff --git a/packages/react-renderer/demo/config/utils.js b/packages/react-renderer/demo/config/utils.js deleted file mode 100644 index 2e95f7660..000000000 --- a/packages/react-renderer/demo/config/utils.js +++ /dev/null @@ -1,10 +0,0 @@ -import { Message } from '@alifd/next'; -import moment from 'moment'; - -export default { - Message, - moment, - test(msg) { - this.Message.notice(msg); - }, -}; diff --git a/packages/react-renderer/demo/dataSource.md b/packages/react-renderer/demo/dataSource.md deleted file mode 100644 index 5ec00ae8b..000000000 --- a/packages/react-renderer/demo/dataSource.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: 数据源使用 -order: 4 ---- - -````jsx -import React, { PureComponent } from 'react'; -import ReactDOM from 'react-dom'; -import ReactRenderer from '@alilc/lowcode-react-renderer'; -import schema from './schemas/dataSource'; -import components from './config/components/index'; -import utils from './config/utils'; -import constants from './config/constants'; - -class Demo extends PureComponent { - static displayName = 'renderer-demo'; - render() { - return ( -
- -
- ); - } -} - -ReactDOM.render(( - -), mountNode); -```` diff --git a/packages/react-renderer/demo/i18n.md b/packages/react-renderer/demo/i18n.md deleted file mode 100644 index bbea036d1..000000000 --- a/packages/react-renderer/demo/i18n.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: 国际化 -order: 5 ---- - -````jsx -import React, { PureComponent } from 'react'; -import ReactDOM from 'react-dom'; -import ReactRenderer from '@alilc/lowcode-react-renderer'; -import schema from './schemas/i18n'; -import components from './config/components/index'; -import utils from './config/utils'; -import constants from './config/constants'; - -class Demo extends PureComponent { - static displayName = 'renderer-demo'; - render() { - return ( -
- -
- ); - } -} - -ReactDOM.render(( - -), mountNode); -```` diff --git a/packages/react-renderer/demo/list.md b/packages/react-renderer/demo/list.md deleted file mode 100644 index d0e34ee68..000000000 --- a/packages/react-renderer/demo/list.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: 列表 -order: 1 ---- - -````jsx -import React, { PureComponent } from 'react'; -import ReactDOM from 'react-dom'; -import ReactRenderer from '@alilc/lowcode-react-renderer'; -import schema from './schemas/list'; -import components from './config/components/index'; -import utils from './config/utils'; -import constants from './config/constants'; - -class Demo extends PureComponent { - static displayName = 'renderer-demo'; - render() { - return ( -
- -
- ); - } -} - -ReactDOM.render(( - -), mountNode); -```` diff --git a/packages/react-renderer/demo/schemas/compose.js b/packages/react-renderer/demo/schemas/compose.js deleted file mode 100644 index 9cd6f60ec..000000000 --- a/packages/react-renderer/demo/schemas/compose.js +++ /dev/null @@ -1,61 +0,0 @@ -/* eslint-disable */ -export default { - componentName: 'Page', - fileName: 'compose', - props: { - }, - children: [ - { - "componentName": "Dropdown", - "props": { - "trigger": [ - { - "componentName": "Button", - "props": { - "type": "primary" - }, - "children": "确定" - } - ], - "triggerType": "click" - }, - "children": [ - { - "componentName": "Menu", - "props": { - "style": { - "width": 200 - } - }, - "children": [ - { "componentName": "MenuItem", "props": {}, "children": "Option 1" }, - { "componentName": "MenuItem", "props": { "disabled": false }, "children": "option 2" }, - { "componentName": "MenuItem", "props": { "disabled": false }, "children": "option 3" } - ] - } - ] - }, - { - "componentName": "Menu", - "props": { - "style": { - "width": 200 - } - }, - "children": [ - { - "componentName": "MenuItem", - "props": { - }, - "children": "Option 1" - }, - { - "componentName": "MenuItem", - "props": { - }, - "children": "Option 2" - } - ] - } - ] -}; diff --git a/packages/react-renderer/demo/schemas/dataSource.js b/packages/react-renderer/demo/schemas/dataSource.js deleted file mode 100644 index 35c2117f1..000000000 --- a/packages/react-renderer/demo/schemas/dataSource.js +++ /dev/null @@ -1,37 +0,0 @@ -export default { - componentName: 'Page', - fileName: 'dataSource', - props: {}, - children: [{ - componentName: 'Div', - props: {}, - children: [{ - componentName: 'Text', - props: { - text: '{{this.item.title}}', - }, - }, { - componentName: 'Switch', - props: { - checkedChildren: '开', - unCheckedChildren: '关', - checked: '{{this.item.done}}', - }, - }], - loop: '{{this.dataSourceMap.todos.data}}', - }], - dataSource: { - list: [{ - id: 'todos', - isInit: true, - type: 'jsonp', - options: { - method: 'GET', - uri: 'https://mocks.alibaba-inc.com/mock/D8iUX7zB/todo_getAll', - }, - dataHandler: function dataHandler(data) { - return data.data; - }, - }], - }, -}; diff --git a/packages/react-renderer/demo/schemas/i18n.js b/packages/react-renderer/demo/schemas/i18n.js deleted file mode 100644 index fb41b917b..000000000 --- a/packages/react-renderer/demo/schemas/i18n.js +++ /dev/null @@ -1,20 +0,0 @@ -export default { - componentName: 'Page', - fileName: 'i18n', - props: {}, - children: [{ - componentName: 'Div', - props: {}, - children: [{ - componentName: 'Text', - props: { - text: "{{this.i18n('hello')}}", - }, - }, { - componentName: 'Text', - props: { - text: "{{this.i18n('china')}}", - }, - }], - }], -}; diff --git a/packages/react-renderer/demo/schemas/list.js b/packages/react-renderer/demo/schemas/list.js deleted file mode 100644 index 0e66c422e..000000000 --- a/packages/react-renderer/demo/schemas/list.js +++ /dev/null @@ -1,225 +0,0 @@ -/* eslint-disable */ -export default { - componentName: 'Page', - fileName: 'tab_article', - props: { - style: { - paddingTop: 20, - paddingRight: 20, - paddingLeft: 20 - } - }, - children: [ - { - componentName: 'Div', - props: { - style: { - marginTop: 5, - marginBottom: 15, - borderBottom: '1px solid rgba(244,244,244)' - } - }, - children: [ - { - componentName: 'Div', - props: { - style: { - marginBottom: 15 - } - }, - children: [ - { - componentName: 'Text', - props: { - text: '{{this.item.title}}', - style: { - color: 'rgba(51,51,51)' - } - } - }, - { - componentName: 'Text', - props: { - text: '{{this.item.datetime}}', - style: { - fontSize: '12px', - color: '#666', - float: 'right' - } - } - } - ] - }, - { - componentName: 'Div', - props: { - style: { - paddingBottom: 15, - fontSize: '13px', - color: '#666' - } - }, - children: '{{this.item.description}}' - }, - { - componentName: 'Div', - props: { - style: { - marginBottom: 15 - } - }, - children: [ - { - componentName: 'Button', - props: { - type: 'normal', - style: { - marginRight: 5, - marginLeft: 5 - }, - size: 'small' - }, - children: '{{this.item}}', - loop: '{{this.item.tags}}' - }, - { - componentName: 'Div', - props: { - style: { - marginBottom: 15, - float: 'right' - } - }, - children: [ - { - componentName: 'Div', - props: { - style: { - display: 'inline-block', - marginRight: 5, - marginBottom: 15, - marginLeft: 5, - fontSize: 12, - color: '#666', - float: 'none' - } - }, - children: '{{"点赞:"+this.item.star}}' - }, - { - componentName: 'Div', - props: { - style: { - display: 'inline-block', - marginRight: 5, - marginBottom: 15, - marginLeft: 5, - fontSize: 12, - color: '#666', - float: 'none' - } - }, - children: '{{"喜爱:"+this.item.like}}' - }, - { - componentName: 'Div', - props: { - style: { - display: 'inline-block', - marginRight: 5, - marginBottom: 15, - marginLeft: 5, - fontSize: 12, - color: '#66', - float: 'none' - } - }, - children: '{{"评论:"+this.item.comment}}' - } - ] - } - ] - } - ], - loop: '{{this.state.dataSource}}' - }, - { - componentName: 'Pagination', - props: { - shape: 'normal', - type: 'normal', - size: 'medium', - style: { - marginTop: 10, - marginBottom: 30, - textAlign: 'right' - }, - onChange: function onChange(current, e) { - //页码发生改变时的回调函数 - //@param {Number} current 改变后的页码数 - //@param {Object} e 点击事件对象 - this.page.reloadDataSource(); - } - } - } - ], - dataSource: { - dataHandler: function dataHandler(dataMap) { - const dataSource = [ - { - title: '越夏越嗨皮-7月官方营销活动-技能提升方向', - description: - '商家通过V任务选择主播并达成合作,费用按照商品链接计算,一个商品为一个价格,建议主播在一场直播里最多接60个商品,并提供不少于两个小时的直播服务,每个商品讲解时间不少于5分钟。 ', - tags: ['直播', '大促', '简介'], - datetime: '2017年12月12日 18:00', - star: Math.floor(Math.random() * 100) + 100, - like: Math.floor(Math.random() * 100) + 200, - comment: Math.floor(Math.random() * 100) + 100 - }, - { - title: '越夏越嗨皮-7月官方营销活动-技能提升方向', - description: - '商家通过V任务选择主播并达成合作,费用按照商品链接计算,一个商品为一个价格,建议主播在一场直播里最多接60个商品,并提供不少于两个小时的直播服务,每个商品讲解时间不少于5分钟。 ', - tags: ['直播', '大促', '简介'], - datetime: '2017年12月12日 18:00', - star: Math.floor(Math.random() * 100) + 100, - like: Math.floor(Math.random() * 100) + 200, - comment: Math.floor(Math.random() * 100) + 100 - }, - { - title: '越夏越嗨皮-7月官方营销活动-技能提升方向', - description: - '商家通过V任务选择主播并达成合作,费用按照商品链接计算,一个商品为一个价格,建议主播在一场直播里最多接60个商品,并提供不少于两个小时的直播服务,每个商品讲解时间不少于5分钟。 ', - tags: ['直播', '大促', '简介'], - datetime: '2017年12月12日 18:00', - star: Math.floor(Math.random() * 100) + 100, - like: Math.floor(Math.random() * 100) + 200, - comment: Math.floor(Math.random() * 100) + 100 - }, - { - title: '越夏越嗨皮-7月官方营销活动-技能提升方向', - description: - '商家通过V任务选择主播并达成合作,费用按照商品链接计算,一个商品为一个价格,建议主播在一场直播里最多接60个商品,并提供不少于两个小时的直播服务,每个商品讲解时间不少于5分钟。 ', - tags: ['直播', '大促', '简介'], - datetime: '2017年12月12日 18:00', - star: Math.floor(Math.random() * 100) + 100, - like: Math.floor(Math.random() * 100) + 200, - comment: Math.floor(Math.random() * 100) + 100 - }, - { - title: '越夏越嗨皮-7月官方营销活动-技能提升方向', - description: - '商家通过V任务选择主播并达成合作,费用按照商品链接计算,一个商品为一个价格,建议主播在一场直播里最多接60个商品,并提供不少于两个小时的直播服务,每个商品讲解时间不少于5分钟。 ', - tags: ['直播', '大促', '简介'], - datetime: '2017年12月12日 18:00', - star: Math.floor(Math.random() * 100) + 100, - like: Math.floor(Math.random() * 100) + 200, - comment: Math.floor(Math.random() * 100) + 100 - } - ]; - return { - dataSource - }; - } - } -}; diff --git a/packages/react-renderer/demo/schemas/table.js b/packages/react-renderer/demo/schemas/table.js deleted file mode 100644 index 94153fb48..000000000 --- a/packages/react-renderer/demo/schemas/table.js +++ /dev/null @@ -1,367 +0,0 @@ -/* eslint-disable */ -export default { - componentName: 'Page', - fileName: 'filterTable', - props: { - style: { - paddingRight: 20, - paddingLeft: 20 - } - }, - children: [ - { - componentName: 'Table', - props: { - hasBorder: false, - hasHeader: true, - dataSource: [ - { - id: 1, - title: '2017秋冬新款背带裙复古格子连衣裙清新背心裙a字裙短裙子', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - }, - { - id: 2, - title: '2017秋冬新款 高质感特定纱线欧美宽松马海毛羊毛毛衣', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - }, - { - id: 3, - title: '日式天然玉米皮草编碗垫锅垫隔热垫茶垫加厚餐垫GD-29', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - }, - { - id: 4, - title: '2017秋冬新款 绑带腰封设计感超顺滑质感落肩铜氨丝连衣裙', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - }, - { - id: 5, - title: '日式糖果色陶瓷柄不锈钢餐具西餐牛扒刀叉勺子咖啡勺', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - }, - { - id: 6, - title: '日式和风深蓝素色文艺餐巾餐垫围裙锅垫隔热手套厨房桌布', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - }, - { - id: 7, - title: '日式雪点樱花手绘陶瓷餐具米饭碗拉面碗寿司盘子汤碗', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - }, - { - id: 8, - title: '川岛屋 釉下彩复古日式陶瓷盘子菜盘圆盘调味碟 米饭碗日式餐具', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - } - ] - }, - children: [ - { - componentName: 'TableColumn', - props: { - dataIndex: 'title', - title: '问题描述', - resizable: false - } - }, - { - componentName: 'TableColumn', - props: { - title: '问题分类', - dataIndex: 'type' - } - }, - { - componentName: 'TableColumn', - props: { - title: '发布时间', - dataIndex: 'publishTime' - } - }, - { - componentName: 'TableColumn', - props: { - title: '状态', - dataIndex: 'publishStatus', - cell: [ - { - componentName: 'Button', - props: { - type: 'normal', - size: 'small', - component: 'div', - text: true, - ghost: false, - style: { - width: '30px', - fontSize: '12px', - color: '#666', - cursor: 'auto', - background: '#f7f8fa' - } - }, - children: '已发布', - condition: false - }, - { - componentName: 'Text', - props: { - text: '已发布', - style: { - paddingTop: 2, - paddingRight: 5, - paddingBottom: 2, - paddingLeft: 5, - fontSize: '12px', - color: '#666', - borderRadius: 3, - cursor: 'auto', - background: '#f7f8fa' - } - } - } - ] - } - }, - { - componentName: 'TableColumn', - props: { - title: '操作', - cell: [ - { - componentName: 'Button', - props: { - type: 'normal', - component: 'a', - size: 'medium', - loading: false, - text: true, - style: { - paddingRight: 10, - paddingLeft: 10, - color: '#2077ff' - } - }, - children: '解决' - }, - { - componentName: 'Button', - props: { - type: 'normal', - component: 'a', - text: true, - style: { - paddingRight: 10, - paddingLeft: 10, - color: '#2077ff' - } - }, - children: '详情' - }, - { - componentName: 'Button', - props: { - type: 'normal', - text: true, - component: 'a', - style: { - paddingRight: 10, - paddingLeft: 10, - color: '#2077ff' - } - }, - children: '分类' - } - ] - } - } - ], - loopArgs: ['', ''] - }, - { - componentName: 'Div', - props: { - style: { - textAlign: 'right' - } - }, - children: [ - { - componentName: 'Pagination', - props: { - shape: 'normal', - type: 'normal', - size: 'medium', - style: { - marginTop: 20 - } - } - } - ] - } - ], - dataSource: { - dataHandler: function dataHandler(dataMap) { - let dataSource = [ - { - id: 1, - title: '2017秋冬新款背带裙复古格子连衣裙清新背心裙a字裙短裙子', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - }, - { - id: 2, - title: '2017秋冬新款 高质感特定纱线欧美宽松马海毛羊毛毛衣', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - }, - { - id: 3, - title: '日式天然玉米皮草编碗垫锅垫隔热垫茶垫加厚餐垫GD-29', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - }, - { - id: 4, - title: '2017秋冬新款 绑带腰封设计感超顺滑质感落肩铜氨丝连衣裙', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - }, - { - id: 5, - title: '日式糖果色陶瓷柄不锈钢餐具西餐牛扒刀叉勺子咖啡勺', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - }, - { - id: 6, - title: '日式和风深蓝素色文艺餐巾餐垫围裙锅垫隔热手套厨房桌布', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - }, - { - id: 7, - title: '日式雪点樱花手绘陶瓷餐具米饭碗拉面碗寿司盘子汤碗', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - }, - { - id: 8, - title: '川岛屋 釉下彩复古日式陶瓷盘子菜盘圆盘调味碟 米饭碗日式餐具', - url: 'https://item.taobao.com/item.htm?id=558559528377', - type: '清单', - publishTime: '17-04-28 20:29:20', - publishStatus: '已发布', - pushStatus: '已推送至订阅号', - operation: { - edit: true - } - } - ]; - return { - ...dataMap, - dataSource - }; - } - } -}; diff --git a/packages/react-renderer/demo/table.md b/packages/react-renderer/demo/table.md deleted file mode 100644 index 3c7cb307c..000000000 --- a/packages/react-renderer/demo/table.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: 表格 -order: 1 ---- - -````jsx -import React, { PureComponent } from 'react'; -import ReactDOM from 'react-dom'; -import ReactRenderer from '@alilc/lowcode-react-renderer'; -import schema from './schemas/table'; -import components from './config/components/index'; -import utils from './config/utils'; -import constants from './config/constants'; - -class Demo extends PureComponent { - static displayName = 'renderer-demo'; - render() { - return ( -
- -
- ); - } -} - -ReactDOM.render(( - -), mountNode); -```` diff --git a/packages/react-renderer/jest.config.js b/packages/react-renderer/jest.config.js deleted file mode 100644 index df1400719..000000000 --- a/packages/react-renderer/jest.config.js +++ /dev/null @@ -1,32 +0,0 @@ -const fs = require('fs'); -const { join } = require('path'); -const esModules = [].join('|'); -const pkgNames = fs.readdirSync(join('..')).filter(pkgName => !pkgName.startsWith('.')); - -const jestConfig = { - // transform: { - // '^.+\\.[jt]sx?$': 'babel-jest', - // // '^.+\\.(ts|tsx)$': 'ts-jest', - // // '^.+\\.(js|jsx)$': 'babel-jest', - // }, - // testMatch: ['**/document/node/node.test.ts'], - // testMatch: ['**/designer/builtin-hotkey.test.ts'], - // testMatch: ['**/plugin/plugin-manager.test.ts'], - // testMatch: ['(/tests?/.*(test))\\.[jt]s$'], - transformIgnorePatterns: [ - `/node_modules/(?!${esModules})/`, - ], - moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], - collectCoverage: true, - collectCoverageFrom: [ - 'src/**/*.ts', - '!src/**/*.d.ts', - '!**/node_modules/**', - ], -}; - -// 只对本仓库内的 pkg 做 mapping -jestConfig.moduleNameMapper = {}; -jestConfig.moduleNameMapper[`^@alilc/lowcode\\-(${pkgNames.join('|')})$`] = '/../$1/src'; - -module.exports = jestConfig; \ No newline at end of file diff --git a/packages/react-renderer/package.json b/packages/react-renderer/package.json deleted file mode 100644 index 625801bc2..000000000 --- a/packages/react-renderer/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "@alilc/lowcode-react-renderer", - "version": "1.3.2", - "description": "react renderer for ali lowcode engine", - "main": "lib/index.js", - "module": "es/index.js", - "files": [ - "lib", - "es", - "dist" - ], - "scripts": { - "test": "build-scripts test --config build.test.json", - "start": "build-scripts start", - "build": "build-scripts build", - "build:umd": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --config build.umd.json" - }, - "keywords": [ - "lowcode", - "engine", - "react" - ], - "dependencies": { - "@alifd/next": "^1.21.16", - "@alilc/lowcode-renderer-core": "1.3.2" - }, - "devDependencies": { - "@alib/build-scripts": "^0.1.18", - "@alifd/next": "^1.19.17", - "build-plugin-fusion": "^0.1.0", - "build-plugin-moment-locales": "^0.1.0", - "react": "^16.4.1", - "react-dom": "^16.4.1", - "react-test-renderer": "^16" - }, - "publishConfig": { - "access": "public", - "registry": "https://registry.npmjs.org/" - }, - "repository": { - "type": "http", - "url": "https://github.com/alibaba/lowcode-engine/tree/main/packages/react-renderer" - }, - "homepage": "https://github.com/alibaba/lowcode-engine/#readme", - "gitHead": "2669f179e6f899d395ce1942d0fe04f9c5ed48a6", - "bugs": "https://github.com/alibaba/lowcode-engine/issues" -} diff --git a/packages/react-renderer/src/index.ts b/packages/react-renderer/src/index.ts deleted file mode 100644 index d4df73319..000000000 --- a/packages/react-renderer/src/index.ts +++ /dev/null @@ -1,66 +0,0 @@ -import React, { Component, PureComponent, createElement, createContext, forwardRef, ReactInstance, ContextType } from 'react'; -import ReactDOM from 'react-dom'; -import { - adapter, - pageRendererFactory, - componentRendererFactory, - blockRendererFactory, - addonRendererFactory, - tempRendererFactory, - rendererFactory, - types, -} from '@alilc/lowcode-renderer-core'; -import ConfigProvider from '@alifd/next/lib/config-provider'; - -window.React = React; -(window as any).ReactDom = ReactDOM; - -adapter.setRuntime({ - Component, - PureComponent, - createContext, - createElement, - forwardRef, - findDOMNode: ReactDOM.findDOMNode, -}); - -adapter.setRenderers({ - PageRenderer: pageRendererFactory(), - ComponentRenderer: componentRendererFactory(), - BlockRenderer: blockRendererFactory(), - AddonRenderer: addonRendererFactory(), - TempRenderer: tempRendererFactory(), - DivRenderer: blockRendererFactory(), -}); - -adapter.setConfigProvider(ConfigProvider); - -function factory(): types.IRenderComponent { - const Renderer = rendererFactory(); - return class ReactRenderer extends Renderer implements Component { - readonly props: types.IRendererProps; - - context: ContextType; - - setState: ( - state: types.IRendererState, - callback?: () => void, - ) => void; - - forceUpdate: (callback?: () => void) => void; - - refs: { - [key: string]: ReactInstance; - }; - - constructor(props: types.IRendererProps, context: ContextType) { - super(props, context); - } - - isValidComponent(obj: any) { - return obj?.prototype?.isReactComponent || obj?.prototype instanceof Component; - } - }; -} - -export default factory(); diff --git a/packages/react-renderer/tests/__snapshots__/index.test.tsx.snap b/packages/react-renderer/tests/__snapshots__/index.test.tsx.snap deleted file mode 100644 index 7a4d4b365..000000000 --- a/packages/react-renderer/tests/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,865 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`React Renderer render basic case 1`] = ` -
-
-
- -
-
-
-
-
- -
-
- - - - - - - - 请选择 - - -   - - - - - - - - - - - - - - - - -
-
-
-
- -
-
- - - - - - - - 请选择 - - -   - - - - - - - - - - - - - - - - -
-
-
-
- -
-
- - - - - -
-
-
- - -
- -
-
- -
-
-
- - - - - - - - - - - - - - - - - - -
-
- 姓名 -
-
-
- 年龄 -
-
-
- 邮箱 -
-
-
- 没有数据 -
-
-
-
-
-
- -
- - - - - - -
- - - - 1 - - / - 10 - - - 到第 - - - - - - 页 - - -
-
-
-
-
-
- Component Not Found -
-
-`; diff --git a/packages/react-renderer/tests/fixtures/schema/basic.ts b/packages/react-renderer/tests/fixtures/schema/basic.ts deleted file mode 100644 index cc587163a..000000000 --- a/packages/react-renderer/tests/fixtures/schema/basic.ts +++ /dev/null @@ -1,567 +0,0 @@ -export default { - 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 }', - }, - closeDialog: { - type: 'JSFunction', - value: 'function() {\n this.setState({\n isShowDialog: false\n })\n }', - }, - }, - 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: 'Button', - id: 'node_dockcy8n9xe10', - props: { - type: 'normal', - style: { - margin: '0 5px 0 5px', - }, - htmlType: 'reset', - children: '清空', - }, - }, - ], - }, - ], - }, - ], - }, - { - componentName: 'Box', - id: 'node_dockcy8n9xe1f', - props: { - style: { - backgroundColor: '#ffffff', - paddingBottom: '24px', - display: 'flex', - flexDirection: 'row', - justifyContent: 'flex-end', - }, - }, - children: [ - { - componentName: 'Button', - id: 'node_dockd5nrh9p4', - props: { - type: 'primary', - size: 'medium', - htmlType: 'button', - component: 'button', - children: '新建配置', - style: {}, - __events: [ - { - type: 'componentEvent', - name: 'onClick', - relatedEventName: 'onClick', - }, - ], - onClick: { - type: 'JSFunction', - value: 'function(){ this.onClick() }', - }, - }, - }, - ], - }, - { - componentName: 'Box', - id: 'node_dockd5nrh9p5', - props: {}, - children: [ - { - componentName: 'Table', - id: 'node_dockjielosj1', - props: { - showMiniPager: true, - showActionBar: true, - actionBar: [ - { - title: '新增', - type: 'primary', - }, - { - title: '编辑', - }, - ], - columns: [ - { - dataKey: 'name', - width: 200, - align: 'center', - title: '姓名', - editType: 'text', - }, - { - dataKey: 'age', - width: 200, - align: 'center', - title: '年龄', - }, - { - dataKey: 'email', - width: 200, - align: 'center', - title: '邮箱', - }, - ], - data: [ - { - name: '王小', - id: '1', - age: 15000, - email: 'aaa@abc.com', - }, - { - name: '王中', - id: '2', - age: 25000, - email: 'bbb@abc.com', - }, - { - name: '王大', - id: '3', - age: 35000, - email: 'ccc@abc.com', - }, - ], - actionTitle: '操作', - actionWidth: 180, - actionType: 'link', - actionFixed: 'right', - actionHidden: false, - maxWebShownActionCount: 2, - actionColumn: [ - { - title: '编辑', - callback: { - type: 'JSFunction', - value: '(rowData, action, table) => {\n return table.editRow(rowData).then((row) => {\n console.log(row);\n });\n }', - }, - device: [ - 'desktop', - ], - }, - { - title: '保存', - callback: { - type: 'JSFunction', - value: '(rowData, action, table) => { \nreturn table.saveRow(rowData).then((row) => { \nconsole.log(row); \n}); \n}', - }, - mode: 'EDIT', - }, - ], - }, - }, - { - componentName: 'Box', - id: 'node_dockd5nrh9pg', - props: { - style: { - display: 'flex', - flexDirection: 'row', - justifyContent: 'flex-end', - }, - }, - children: [ - { - componentName: 'Pagination', - id: 'node_dockd5nrh9pf', - props: { - prefix: 'next-', - type: 'normal', - shape: 'normal', - size: 'medium', - defaultCurrent: 1, - total: 100, - pageShowCount: 5, - pageSize: 10, - pageSizePosition: 'start', - showJump: true, - style: {}, - }, - }, - ], - }, - ], - }, - ], - }, - { - 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', - }, - title: '标题', - events: [], - __events: [ - { - type: 'componentEvent', - name: 'onCancel', - relatedEventName: 'closeDialog', - }, - { - type: 'componentEvent', - name: 'onClose', - relatedEventName: 'closeDialog', - }, - { - type: 'componentEvent', - name: 'onOk', - relatedEventName: 'testFunc', - }, - ], - onCancel: { - type: 'JSFunction', - value: 'function(){ this.closeDialog() }', - }, - onClose: { - type: 'JSFunction', - value: 'function(){ this.closeDialog() }', - }, - onOk: { - type: 'JSFunction', - value: 'function(){ this.testFunc() }', - }, - }, - children: [ - { - componentName: 'Form', - id: 'node_dockd5nrh9pi', - props: { - inline: false, - labelAlign: 'top', - labelTextAlign: 'right', - size: 'medium', - }, - children: [ - { - componentName: 'Form.Item', - id: 'node_dockd5nrh9pj', - props: { - style: { - marginBottom: '0', - minWidth: '200px', - minHeight: '28px', - }, - label: '商品类目', - }, - children: [ - { - componentName: 'Select', - id: 'node_dockd5nrh9pk', - props: { - mode: 'single', - hasArrow: true, - cacheValue: true, - }, - }, - ], - }, - { - componentName: 'Form.Item', - id: 'node_dockd5nrh9pl', - props: { - style: { - marginBottom: '0', - minWidth: '200px', - minHeight: '28px', - }, - label: '商品类目', - }, - children: [ - { - componentName: 'Select', - id: 'node_dockd5nrh9pm', - props: { - mode: 'single', - hasArrow: true, - cacheValue: true, - }, - }, - ], - }, - { - componentName: 'Form.Item', - id: 'node_dockd5nrh9pn', - props: { - style: { - marginBottom: '0', - minWidth: '200px', - minHeight: '28px', - }, - label: '商品类目', - asterisk: true, - }, - children: [ - { - componentName: 'Select', - id: 'node_dockd5nrh9po', - props: { - mode: 'single', - hasArrow: true, - cacheValue: true, - }, - }, - ], - }, - { - componentName: 'Form.Item', - id: 'node_dockd5nrh9pp', - props: { - style: { - marginBottom: '0', - minWidth: '200px', - minHeight: '28px', - }, - label: '商品类目', - }, - children: [ - { - componentName: 'Input', - id: 'node_dockd5nrh9pr', - props: { - hasBorder: true, - size: 'medium', - autoComplete: 'off', - }, - }, - ], - }, - ], - }, - ], - }, - { - componentName: 'ErrorComponent', - id: 'node_dockd5nrh9pr', - props: { - name: 'error', - }, - }, - ], -}; diff --git a/packages/react-renderer/tests/index.test.tsx b/packages/react-renderer/tests/index.test.tsx deleted file mode 100644 index 72bc04a1a..000000000 --- a/packages/react-renderer/tests/index.test.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import renderer from 'react-test-renderer'; -import { Box, Breadcrumb, Form, Select, Input, Button, Table, Pagination, Dialog } from '@alifd/next'; -import ReactRenderer from '../src'; -import schema from './fixtures/schema/basic'; - -describe('React Renderer', () => { - it('render basic case', () => { - const components = { - Box, - Breadcrumb, - 'Breadcrumb.Item': Breadcrumb.Item, - Form, - 'Form.Item': Form.Item, - Select, - Input, - Button, - 'Button.Group': Button.Group, - Table, - Pagination, - Dialog, - }; - const content = ( - ); - const tree = renderer.create(content).toJSON(); - expect(tree).toMatchSnapshot(); - }); -}); diff --git a/packages/react-renderer/tsconfig.json b/packages/react-renderer/tsconfig.json deleted file mode 100644 index c37b76ecc..000000000 --- a/packages/react-renderer/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "lib" - }, - "include": [ - "./src/" - ] -} diff --git a/packages/react-simulator-renderer/.babelrc b/packages/react-simulator-renderer/.babelrc deleted file mode 100644 index 469e76ee6..000000000 --- a/packages/react-simulator-renderer/.babelrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "plugins": [ - ["@babel/plugin-proposal-decorators", { "legacy": true }], - ["@babel/plugin-proposal-class-properties", { "loose": true }] - ] -} diff --git a/packages/renderer-core/babel.config.js b/packages/renderer-core/babel.config.js deleted file mode 100644 index c5986f2bc..000000000 --- a/packages/renderer-core/babel.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../babel.config'); \ No newline at end of file diff --git a/packages/renderer-core/build.json b/packages/renderer-core/build.json deleted file mode 100644 index 9140815c5..000000000 --- a/packages/renderer-core/build.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "plugins": [ - [ - "@alilc/build-plugin-lce", - { - "babelPlugins": ["@babel/plugin-transform-typescript"] - } - ] - ] -} diff --git a/packages/renderer-core/build.test.json b/packages/renderer-core/build.test.json deleted file mode 100644 index 9cc30d746..000000000 --- a/packages/renderer-core/build.test.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "plugins": [ - "@alilc/build-plugin-lce", - "@alilc/lowcode-test-mate/plugin/index.ts" - ] -} diff --git a/packages/renderer-core/jest.config.js b/packages/renderer-core/jest.config.js deleted file mode 100644 index 1ea4204de..000000000 --- a/packages/renderer-core/jest.config.js +++ /dev/null @@ -1,38 +0,0 @@ -const fs = require('fs'); -const { join } = require('path'); -const esModules = [].join('|'); -const pkgNames = fs.readdirSync(join('..')).filter(pkgName => !pkgName.startsWith('.')); - -const jestConfig = { - // transform: { - // // '^.+\\.[jt]sx?$': 'babel-jest', - // '^.+\\.(ts|tsx)$': 'ts-jest', - // // '^.+\\.(js|jsx)$': 'babel-jest', - // }, - // testMatch: ['(/tests?/.*(test))\\.[jt]s$'], - // testMatch: ['**/*/base.test.tsx'], - // testMatch: ['**/utils/common.test.ts'], - // testMatch: ['**/*/leaf.test.tsx'], - // testMatch: ['**/*/is-use-loop.test.ts'], - transformIgnorePatterns: [ - `/node_modules/(?!${esModules})/`, - ], - setupFiles: [ - './tests/fixtures/unhandled-rejection.ts', - './tests/setup.ts', - ], - moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], - collectCoverage: true, - collectCoverageFrom: [ - 'src/**/*.ts', - 'src/**/*.tsx', - '!src/utils/logger.ts', - '!src/types/index.ts', - ], -}; - -// 只对本仓库内的 pkg 做 mapping -jestConfig.moduleNameMapper = {}; -jestConfig.moduleNameMapper[`^@alilc/lowcode\\-(${pkgNames.join('|')})$`] = '/../$1/src'; - -module.exports = jestConfig; \ No newline at end of file diff --git a/packages/renderer-core/package.json b/packages/renderer-core/package.json deleted file mode 100644 index 199eac1ca..000000000 --- a/packages/renderer-core/package.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "@alilc/lowcode-renderer-core", - "version": "1.3.2", - "description": "renderer core", - "license": "MIT", - "main": "lib/index.js", - "module": "es/index.js", - "files": [ - "lib", - "es" - ], - "scripts": { - "build": "build-scripts build", - "test": "build-scripts test --config build.test.json", - "test:cov": "build-scripts test --config build.test.json --jest-coverage" - }, - "dependencies": { - "@alilc/lowcode-datasource-engine": "^1.0.0", - "@alilc/lowcode-types": "1.3.2", - "@alilc/lowcode-utils": "1.3.2", - "classnames": "^2.2.6", - "debug": "^4.1.1", - "fetch-jsonp": "^1.1.3", - "intl-messageformat": "^9.3.1", - "jsonuri": "^2.1.2", - "lodash": "^4.17.11", - "prop-types": "^15.7.2", - "react-is": "^16.10.1", - "socket.io-client": "^2.2.0", - "whatwg-fetch": "^3.0.0" - }, - "devDependencies": { - "@alib/build-scripts": "^0.1.18", - "@alifd/next": "^1.26.0", - "@alilc/lowcode-designer": "1.3.2", - "@babel/plugin-transform-typescript": "^7.16.8", - "@testing-library/react": "^11.2.2", - "@types/classnames": "^2.2.11", - "@types/debug": "^4.1.5", - "@types/jest": "^26.0.16", - "@types/lodash": "^4.14.167", - "@types/node": "^13.7.1", - "@types/prop-types": "^15.7.3", - "@types/react-is": "^17.0.3", - "@types/react-test-renderer": "^17.0.1", - "jest": "^26.6.3", - "react-test-renderer": "^17.0.2", - "ts-jest": "^26.5.0" - }, - "publishConfig": { - "access": "public", - "registry": "https://registry.npmjs.org/" - }, - "repository": { - "type": "http", - "url": "https://github.com/alibaba/lowcode-engine/tree/main/packages/renderer-core" - }, - "gitHead": "2669f179e6f899d395ce1942d0fe04f9c5ed48a6", - "bugs": "https://github.com/alibaba/lowcode-engine/issues", - "homepage": "https://github.com/alibaba/lowcode-engine/#readme" -} diff --git a/packages/renderer-core/src/adapter/index.ts b/packages/renderer-core/src/adapter/index.ts deleted file mode 100644 index 7a56bc039..000000000 --- a/packages/renderer-core/src/adapter/index.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { IRuntime, IRendererModules, IGeneralConstructor } from '../types'; - -export enum Env { - React = 'react', -} - -class Adapter { - runtime: IRuntime; - - builtinModules = ['Component', 'PureComponent', 'createElement', 'createContext', 'forwardRef', 'findDOMNode']; - - env: Env; - - renderers: IRendererModules; - - configProvider: any; - - constructor() { - this.initRuntime(); - } - - initRuntime() { - const Component: IGeneralConstructor = class { - state: Readonly; - props: Readonly & Readonly<{ children?: any | undefined }>; - refs: Record; - context: Record; - setState() {} - forceUpdate() {} - render() {} - }; - const PureComponent = class { - state: Readonly; - props: Readonly & Readonly<{ children?: any | undefined }>; - refs: Record; - context: Record; - setState() {} - forceUpdate() {} - render() {} - }; - const createElement = () => {}; - const createContext = () => {}; - const forwardRef = () => {}; - const findDOMNode = () => {}; - this.runtime = { - Component, - PureComponent, - createElement, - createContext, - forwardRef, - findDOMNode, - }; - } - - setRuntime(runtime: IRuntime) { - if (this.isValidRuntime(runtime)) { - this.runtime = runtime; - } - } - - isValidRuntime(runtime: IRuntime) { - if (typeof runtime !== 'object' || Array.isArray(runtime)) { - return false; - } - - return this.builtinModules.every((m) => { - const flag = !!runtime[m]; - if (!flag) { - throw new Error(`runtime is invalid, module '${m}' does not exist`); - } - return flag; - }); - } - - getRuntime() { - return this.runtime; - } - - setEnv(env: Env) { - this.env = env; - } - - isReact() { - return this.env === Env.React; - } - - setRenderers(renderers: IRendererModules) { - this.renderers = renderers; - } - - getRenderers() { - return this.renderers || {}; - } - - setConfigProvider(Comp: any) { - this.configProvider = Comp; - } - - getConfigProvider() { - return this.configProvider; - } -} - -export default new Adapter(); diff --git a/packages/renderer-core/src/components/Div.tsx b/packages/renderer-core/src/components/Div.tsx deleted file mode 100644 index e5c1bcde4..000000000 --- a/packages/renderer-core/src/components/Div.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import adapter from '../adapter'; -import { IGeneralConstructor } from '../types'; - -export default function divFactory(): IGeneralConstructor { - const { PureComponent, createElement } = adapter.getRuntime(); - return class Div extends PureComponent { - static displayName = 'Div'; - - static version = '0.0.0'; - - render() { - return createElement('div', this.props); - } - }; -} diff --git a/packages/renderer-core/src/components/VisualDom/index.css b/packages/renderer-core/src/components/VisualDom/index.css deleted file mode 100644 index a9b944a7f..000000000 --- a/packages/renderer-core/src/components/VisualDom/index.css +++ /dev/null @@ -1,19 +0,0 @@ -.visual-dom .panel-container { - box-sizing: border-box; - border: 1px solid #e9e9e9; -} - -.visual-dom .panel-container .title { - display: block; - font-size: 12px; - color: #333; - background-color: #ebecf0; - line-height: 28px; - padding: 0 12px; - border-bottom: 1px solid #e9e9e9; -} - -.visual-dom .panel-container .content { - min-height: 20px; - padding: 5px; -} diff --git a/packages/renderer-core/src/components/VisualDom/index.tsx b/packages/renderer-core/src/components/VisualDom/index.tsx deleted file mode 100644 index a10a90cae..000000000 --- a/packages/renderer-core/src/components/VisualDom/index.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import PropTypes from 'prop-types'; -import adapter from '../../adapter'; -import { IGeneralConstructor } from '../../types'; -import './index.css'; - -export default function visualDomFactory(): IGeneralConstructor { - const { PureComponent, createElement } = adapter.getRuntime(); - return class VisualDom extends PureComponent { - static displayName = 'VisualDom'; - - static propTypes = { - children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]), - }; - - static defaultProps = { - children: null, - }; - - render() { - const { children, cell, title, label, text, __componentName } = this.props; - let mainContent = children; - if (cell && typeof cell === 'function') { - mainContent = cell(); - } - return createElement('div', { className: 'visual-dom' }, - createElement('div', { className: 'panel-container' }, - [ - createElement('span', { className: 'title' }, title || label || text || __componentName), - createElement('div', { className: 'content' }, mainContent), - ])); - } - }; -} diff --git a/packages/renderer-core/src/context/index.ts b/packages/renderer-core/src/context/index.ts deleted file mode 100644 index 38335f6b4..000000000 --- a/packages/renderer-core/src/context/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import adapter from '../adapter'; - - -export default function contextFactory() { - const { createContext } = adapter.getRuntime(); - - let context = (window as any).__appContext; - if (!context) { - context = createContext({}); - (window as any).__appContext = context; - } - return context; -} diff --git a/packages/renderer-core/src/hoc/index.tsx b/packages/renderer-core/src/hoc/index.tsx deleted file mode 100644 index 4851ea486..000000000 --- a/packages/renderer-core/src/hoc/index.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { cloneEnumerableProperty } from '@alilc/lowcode-utils'; -import adapter from '../adapter'; -import { IBaseRendererInstance, IRendererProps } from '../types'; - -interface Options { - baseRenderer: IBaseRendererInstance; - schema: any; -} - -function patchDidCatch(Comp: any, { baseRenderer }: Options) { - if (Comp.patchedCatch) { - return; - } - Comp.patchedCatch = true; - const { PureComponent } = adapter.getRuntime(); - // Rax 的 getDerivedStateFromError 有 BUG,这里先用 componentDidCatch 来替代 - // @see https://github.com/alibaba/rax/issues/2211 - const originalDidCatch = Comp.prototype.componentDidCatch; - Comp.prototype.componentDidCatch = function didCatch(this: any, error: Error, errorInfo: any) { - this.setState({ engineRenderError: true, error }); - if (originalDidCatch && typeof originalDidCatch === 'function') { - originalDidCatch.call(this, error, errorInfo); - } - }; - - const { engine } = baseRenderer.context; - const originRender = Comp.prototype.render; - Comp.prototype.render = function () { - if (this.state && this.state.engineRenderError) { - this.state.engineRenderError = false; - return engine.createElement(engine.getFaultComponent(), { - ...this.props, - error: this.state.error, - componentName: this.props._componentName, - }); - } - return originRender.call(this); - }; - if (!(Comp.prototype instanceof PureComponent)) { - const originShouldComponentUpdate = Comp.prototype.shouldComponentUpdate; - Comp.prototype.shouldComponentUpdate = function (nextProps: IRendererProps, nextState: any) { - if (nextState && nextState.engineRenderError) { - return true; - } - return originShouldComponentUpdate - ? originShouldComponentUpdate.call(this, nextProps, nextState) - : true; - }; - } -} - -const cache = new Map(); - -export function compWrapper(Comp: any, options: Options) { - const { createElement, Component, forwardRef } = adapter.getRuntime(); - if ( - Comp?.prototype?.isReactComponent || // react - Comp?.prototype?.setState || // rax - Comp?.prototype instanceof Component - ) { - patchDidCatch(Comp, options); - return Comp; - } - - if (cache.has(options.schema.id) && cache.get(options.schema.id)?.Comp === Comp) { - return cache.get(options.schema.id)?.WrapperComponent; - } - - class Wrapper extends Component { - render() { - return createElement(Comp, { ...this.props, ref: this.props.forwardRef }); - } - } - (Wrapper as any).displayName = Comp.displayName; - - patchDidCatch(Wrapper, options); - - const WrapperComponent = cloneEnumerableProperty( - forwardRef((props: any, ref: any) => { - return createElement(Wrapper, { ...props, forwardRef: ref }); - }), - Comp, - ); - - cache.set(options.schema.id, { WrapperComponent, Comp }); - - return WrapperComponent; -} diff --git a/packages/renderer-core/src/hoc/leaf.tsx b/packages/renderer-core/src/hoc/leaf.tsx deleted file mode 100644 index 2bb3c0b36..000000000 --- a/packages/renderer-core/src/hoc/leaf.tsx +++ /dev/null @@ -1,600 +0,0 @@ -import { INode, IPublicTypePropChangeOptions } from '@alilc/lowcode-designer'; -import { GlobalEvent, IPublicEnumTransformStage, IPublicTypeNodeSchema, IPublicTypeEngineOptions } from '@alilc/lowcode-types'; -import { isReactComponent, cloneEnumerableProperty } from '@alilc/lowcode-utils'; -import { debounce } from '../utils/common'; -import adapter from '../adapter'; -import * as types from '../types/index'; -import logger from '../utils/logger'; - -export interface IComponentHocInfo { - schema: any; - baseRenderer: types.IBaseRendererInstance; - componentInfo: any; - scope: any; -} - -export interface IComponentHocProps { - __tag: any; - componentId: any; - _leaf: any; - forwardedRef?: any; -} - -export interface IComponentHocState { - childrenInState: boolean; - nodeChildren: any; - nodeCacheProps: any; - - /** 控制是否显示隐藏 */ - visible: boolean; - - /** 控制是否渲染 */ - condition: boolean; - nodeProps: any; -} - -type DesignMode = Pick['designMode']; - -export interface IComponentHoc { - designMode: DesignMode | DesignMode[]; - hoc: IComponentConstruct; -} - -export type IComponentConstruct = (Comp: types.IBaseRenderComponent, info: IComponentHocInfo) => types.IGeneralConstructor; - -interface IProps { - _leaf: INode | undefined; - - visible: boolean; - - componentId: number; - - children?: INode[]; - - __tag: number; - - forwardedRef?: any; -} - -enum RerenderType { - All = 'All', - ChildChanged = 'ChildChanged', - PropsChanged = 'PropsChanged', - VisibleChanged = 'VisibleChanged', - MinimalRenderUnit = 'MinimalRenderUnit', -} - -// 缓存 Leaf 层组件,防止重新渲染问题 -class LeafCache { - - /** 组件缓存 */ - component = new Map(); - - /** - * 状态缓存,场景:属性变化后,改组件被销毁,state 为空,没有展示修改后的属性 - */ - state = new Map(); - - /** - * 订阅事件缓存,导致 rerender 的订阅事件 - */ - event = new Map(); - - ref = new Map(); - - constructor(public documentId: string, public device: string) { - } -} - -let cache: LeafCache; - -/** 部分没有渲染的 node 节点进行兜底处理 or 渲染方式没有渲染 LeafWrapper */ -function initRerenderEvent({ - schema, - __debug, - container, - getNode, -}: any) { - const leaf = getNode?.(schema.id); - if (!leaf - || cache.event.get(schema.id)?.clear - || leaf === cache.event.get(schema.id) - ) { - return; - } - cache.event.get(schema.id)?.dispose.forEach((disposeFn: any) => disposeFn && disposeFn()); - const debounceRerender = debounce(() => { - container.rerender(); - }, 20); - cache.event.set(schema.id, { - clear: false, - leaf, - dispose: [ - leaf?.onPropChange?.(() => { - if (!container.autoRepaintNode) { - return; - } - __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onPropsChange make rerender`); - debounceRerender(); - }), - leaf?.onChildrenChange?.(() => { - if (!container.autoRepaintNode) { - return; - } - __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onChildrenChange make rerender`); - debounceRerender(); - }) as Function, - leaf?.onVisibleChange?.(() => { - if (!container.autoRepaintNode) { - return; - } - __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onVisibleChange make rerender`); - debounceRerender(); - }), - ], - }); -} - -/** 渲染的 node 节点全局注册事件清除 */ -function clearRerenderEvent(id: string): void { - if (cache.event.get(id)?.clear) { - return; - } - cache.event.get(id)?.dispose?.forEach((disposeFn: any) => disposeFn && disposeFn()); - cache.event.set(id, { - clear: true, - dispose: [], - }); -} - -// 给每个组件包裹一个 HOC Leaf,支持组件内部属性变化,自响应渲染 -export function leafWrapper(Comp: types.IBaseRenderComponent, { - schema, - baseRenderer, - componentInfo, - scope, -}: IComponentHocInfo) { - const { - __debug, - __getComponentProps: getProps, - __getSchemaChildrenVirtualDom: getChildren, - __parseData, - } = baseRenderer; - const { engine } = baseRenderer.context; - const host = baseRenderer.props?.__host; - const curDocumentId = baseRenderer.props?.documentId ?? ''; - const curDevice = baseRenderer.props?.device ?? ''; - const getNode = baseRenderer.props?.getNode; - const container = baseRenderer.props?.__container; - const setSchemaChangedSymbol = baseRenderer.props?.setSchemaChangedSymbol; - const editor = host?.designer?.editor; - const runtime = adapter.getRuntime(); - const { forwardRef, createElement } = runtime; - const Component = runtime.Component as types.IGeneralConstructor< - IComponentHocProps, IComponentHocState - >; - - const componentCacheId = schema.id; - - if (!cache || (curDocumentId && curDocumentId !== cache.documentId) || (curDevice && curDevice !== cache.device)) { - cache?.event.forEach(event => { - event.dispose?.forEach((disposeFn: any) => disposeFn && disposeFn()); - }); - cache = new LeafCache(curDocumentId, curDevice); - } - - if (!isReactComponent(Comp)) { - logger.error(`${schema.componentName} component may be has errors: `, Comp); - } - - initRerenderEvent({ - schema, - __debug, - container, - getNode, - }); - - if (curDocumentId && cache.component.has(componentCacheId) && (cache.component.get(componentCacheId).Comp === Comp)) { - return cache.component.get(componentCacheId).LeafWrapper; - } - - class LeafHoc extends Component { - recordInfo: { - startTime?: number | null; - type?: string; - node?: INode; - } = {}; - - private curEventLeaf: INode | undefined; - - static displayName = schema.componentName; - - disposeFunctions: Array<((() => void) | Function)> = []; - - __component_tag = 'leafWrapper'; - - renderUnitInfo: { - minimalUnitId?: string; - minimalUnitName?: string; - singleRender?: boolean; - }; - - // 最小渲染单元做防抖处理 - makeUnitRenderDebounced = debounce(() => { - this.beforeRender(RerenderType.MinimalRenderUnit); - const schema = this.leaf?.export?.(IPublicEnumTransformStage.Render); - if (!schema) { - return; - } - const nextProps = getProps(schema, scope, Comp, componentInfo); - const children = getChildren(schema, scope, Comp); - const nextState = { - nodeProps: nextProps, - nodeChildren: children, - childrenInState: true, - }; - if ('children' in nextProps) { - nextState.nodeChildren = nextProps.children; - } - - __debug(`${this.leaf?.componentName}(${this.props.componentId}) MinimalRenderUnit Render!`); - this.setState(nextState); - }, 20); - - constructor(props: IProps, context: any) { - super(props, context); - // 监听以下事件,当变化时更新自己 - __debug(`${schema.componentName}[${this.props.componentId}] leaf render in SimulatorRendererView`); - clearRerenderEvent(componentCacheId); - this.curEventLeaf = this.leaf; - - cache.ref.set(componentCacheId, { - makeUnitRender: this.makeUnitRender, - }); - - let cacheState = cache.state.get(componentCacheId); - if (!cacheState || cacheState.__tag !== props.__tag) { - cacheState = this.getDefaultState(props); - } - - this.state = cacheState; - } - - recordTime = () => { - if (!this.recordInfo.startTime) { - return; - } - const endTime = Date.now(); - const nodeCount = host?.designer?.currentDocument?.getNodeCount?.(); - const componentName = this.recordInfo.node?.componentName || this.leaf?.componentName || 'UnknownComponent'; - editor?.eventBus.emit(GlobalEvent.Node.Rerender, { - componentName, - time: endTime - this.recordInfo.startTime, - type: this.recordInfo.type, - nodeCount, - }); - this.recordInfo.startTime = null; - }; - - makeUnitRender = () => { - this.makeUnitRenderDebounced(); - }; - - get autoRepaintNode() { - return container?.autoRepaintNode; - } - - componentDidUpdate() { - this.recordTime(); - } - - componentDidMount() { - const _leaf = this.leaf; - this.initOnPropsChangeEvent(_leaf); - this.initOnChildrenChangeEvent(_leaf); - this.initOnVisibleChangeEvent(_leaf); - this.recordTime(); - } - - getDefaultState(nextProps: any) { - const { - hidden = false, - condition = true, - } = nextProps.__inner__ || this.leaf?.export?.(IPublicEnumTransformStage.Render) || {}; - return { - nodeChildren: null, - childrenInState: false, - visible: !hidden, - condition: __parseData?.(condition, scope), - nodeCacheProps: {}, - nodeProps: {}, - }; - } - - setState(state: any) { - cache.state.set(componentCacheId, { - ...this.state, - ...state, - __tag: this.props.__tag, - }); - super.setState(state); - } - - /** 由于内部属性变化,在触发渲染前,会执行该函数 */ - beforeRender(type: string, node?: INode): void { - this.recordInfo.startTime = Date.now(); - this.recordInfo.type = type; - this.recordInfo.node = node; - setSchemaChangedSymbol?.(true); - } - - judgeMiniUnitRender() { - if (!this.renderUnitInfo) { - this.getRenderUnitInfo(); - } - - const renderUnitInfo = this.renderUnitInfo || { - singleRender: true, - }; - - if (renderUnitInfo.singleRender) { - return; - } - - const ref = cache.ref.get(renderUnitInfo.minimalUnitId); - - if (!ref) { - __debug('Cant find minimalRenderUnit ref! This make rerender!'); - container?.rerender(); - return; - } - __debug(`${this.leaf?.componentName}(${this.props.componentId}) need render, make its minimalRenderUnit ${renderUnitInfo.minimalUnitName}(${renderUnitInfo.minimalUnitId})`); - ref.makeUnitRender(); - } - - getRenderUnitInfo(leaf = this.leaf) { - // leaf 在低代码组件中存在 mock 的情况,退出最小渲染单元判断 - if (!leaf || typeof leaf.isRoot !== 'function') { - return; - } - - if (leaf.isRootNode) { - this.renderUnitInfo = { - singleRender: true, - ...(this.renderUnitInfo || {}), - }; - } - if (leaf.componentMeta.isMinimalRenderUnit) { - this.renderUnitInfo = { - minimalUnitId: leaf.id, - minimalUnitName: leaf.componentName, - singleRender: false, - }; - } - if (leaf.hasLoop()) { - // 含有循环配置的元素,父元素是最小渲染单元 - this.renderUnitInfo = { - minimalUnitId: leaf?.parent?.id, - minimalUnitName: leaf?.parent?.componentName, - singleRender: false, - }; - } - if (leaf.parent) { - this.getRenderUnitInfo(leaf.parent); - } - } - - componentWillReceiveProps(nextProps: any) { - let { componentId } = nextProps; - if (nextProps.__tag === this.props.__tag) { - return null; - } - - const _leaf = getNode?.(componentId); - if (_leaf && this.curEventLeaf && _leaf !== this.curEventLeaf) { - this.disposeFunctions.forEach((fn) => fn()); - this.disposeFunctions = []; - this.initOnChildrenChangeEvent(_leaf); - this.initOnPropsChangeEvent(_leaf); - this.initOnVisibleChangeEvent(_leaf); - this.curEventLeaf = _leaf; - } - - const { - visible, - ...resetState - } = this.getDefaultState(nextProps); - this.setState(resetState); - } - - /** 监听参数变化 */ - initOnPropsChangeEvent(leaf = this.leaf): void { - const handlePropsChange = debounce((propChangeInfo: IPublicTypePropChangeOptions) => { - const { - key, - newValue = null, - } = propChangeInfo; - const node = leaf; - - if (key === '___condition___') { - const { condition = true } = this.leaf?.export(IPublicEnumTransformStage.Render) || {}; - const conditionValue = __parseData?.(condition, scope); - __debug(`key is ___condition___, change condition value to [${condition}]`); - // 条件表达式改变 - this.setState({ - condition: conditionValue, - }); - return; - } - - // 如果循坏条件变化,从根节点重新渲染 - // 目前多层循坏无法判断需要从哪一层开始渲染,故先粗暴解决 - if (key === '___loop___') { - __debug('key is ___loop___, render a page!'); - container?.rerender(); - // 由于 scope 变化,需要清空缓存,使用新的 scope - cache.component.delete(componentCacheId); - return; - } - this.beforeRender(RerenderType.PropsChanged); - const { state } = this; - const { nodeCacheProps } = state; - const nodeProps = getProps(node?.export?.(IPublicEnumTransformStage.Render) as IPublicTypeNodeSchema, scope, Comp, componentInfo); - if (key && !(key in nodeProps) && (key in this.props)) { - // 当 key 在 this.props 中时,且不存在在计算值中,需要用 newValue 覆盖掉 this.props 的取值 - nodeCacheProps[key] = newValue; - } - __debug(`${leaf?.componentName}[${this.props.componentId}] component trigger onPropsChange!`, nodeProps, nodeCacheProps, key, newValue); - this.setState('children' in nodeProps ? { - nodeChildren: nodeProps.children, - nodeProps, - childrenInState: true, - nodeCacheProps, - } : { - nodeProps, - nodeCacheProps, - }); - - this.judgeMiniUnitRender(); - }); - const dispose = leaf?.onPropChange?.((propChangeInfo: IPublicTypePropChangeOptions) => { - if (!this.autoRepaintNode) { - return; - } - handlePropsChange(propChangeInfo); - }); - - dispose && this.disposeFunctions.push(dispose); - } - - /** - * 监听显隐变化 - */ - initOnVisibleChangeEvent(leaf = this.leaf) { - const dispose = leaf?.onVisibleChange?.((flag: boolean) => { - if (!this.autoRepaintNode) { - return; - } - if (this.state.visible === flag) { - return; - } - - __debug(`${leaf?.componentName}[${this.props.componentId}] component trigger onVisibleChange(${flag}) event`); - this.beforeRender(RerenderType.VisibleChanged); - this.setState({ - visible: flag, - }); - this.judgeMiniUnitRender(); - }); - - dispose && this.disposeFunctions.push(dispose); - } - - /** - * 监听子元素变化(拖拽,删除...) - */ - initOnChildrenChangeEvent(leaf = this.leaf) { - const dispose = leaf?.onChildrenChange?.((param): void => { - if (!this.autoRepaintNode) { - return; - } - const { - type, - node, - } = param || {}; - this.beforeRender(`${RerenderType.ChildChanged}-${type}`, node); - // TODO: 缓存同级其他元素的 children。 - // 缓存二级 children Next 查询筛选组件有问题 - // 缓存一级 children Next Tab 组件有问题 - const nextChild = getChildren(leaf?.export?.(IPublicEnumTransformStage.Render) as types.ISchema, scope, Comp); - __debug(`${schema.componentName}[${this.props.componentId}] component trigger onChildrenChange event`, nextChild); - this.setState({ - nodeChildren: nextChild, - childrenInState: true, - }); - this.judgeMiniUnitRender(); - }); - dispose && this.disposeFunctions.push(dispose); - } - - componentWillUnmount() { - this.disposeFunctions.forEach(fn => fn()); - } - - get hasChildren(): boolean { - if (!this.state.childrenInState) { - return 'children' in this.props; - } - - return true; - } - - get children(): any { - if (this.state.childrenInState) { - return this.state.nodeChildren; - } - if (this.props.children && !Array.isArray(this.props.children)) { - return [this.props.children]; - } - if (this.props.children && this.props.children.length) { - return this.props.children; - } - return this.props.children; - } - - get leaf(): INode | undefined { - if (this.props._leaf?.isMock) { - // 低代码组件作为一个整体更新,其内部的组件不需要监听相关事件 - return undefined; - } - - return getNode?.(componentCacheId); - } - - render() { - if (!this.state.visible || !this.state.condition) { - return null; - } - - const { - forwardedRef, - ...rest - } = this.props; - - const compProps = { - ...rest, - ...(this.state.nodeCacheProps || {}), - ...(this.state.nodeProps || {}), - children: [], - __id: this.props.componentId, - ref: forwardedRef, - }; - - delete compProps.__inner__; - - if (this.hasChildren) { - return engine.createElement(Comp, compProps, this.children); - } - - return engine.createElement(Comp, compProps); - } - } - - let LeafWrapper = forwardRef((props: any, ref: any) => { - return createElement(LeafHoc, { - ...props, - forwardedRef: ref, - }); - }); - - LeafWrapper = cloneEnumerableProperty(LeafWrapper, Comp); - - LeafWrapper.displayName = (Comp as any).displayName; - - cache.component.set(componentCacheId, { - LeafWrapper, - Comp, - }); - - return LeafWrapper; -} \ No newline at end of file diff --git a/packages/renderer-core/src/index.ts b/packages/renderer-core/src/index.ts deleted file mode 100644 index 0609a0fdf..000000000 --- a/packages/renderer-core/src/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import adapter from './adapter'; -import contextFactory from './context'; - -export { adapter, contextFactory }; - -export * from './renderer'; -export * as types from './types'; -export * as utils from './utils'; -export * from './hoc'; diff --git a/packages/renderer-core/src/renderer/addon.tsx b/packages/renderer-core/src/renderer/addon.tsx deleted file mode 100644 index 211ec182f..000000000 --- a/packages/renderer-core/src/renderer/addon.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import PropTypes from 'prop-types'; -import baseRendererFactory from './base'; -import { isEmpty } from '../utils'; -import { IRendererAppHelper, IBaseRendererProps, IBaseRenderComponent } from '../types'; -import logger from '../utils/logger'; - -export default function addonRendererFactory(): IBaseRenderComponent { - const BaseRenderer = baseRendererFactory(); - return class AddonRenderer extends BaseRenderer { - static displayName = 'AddonRenderer'; - - __namespace = 'addon'; - - static propTypes = { - config: PropTypes.object, - __schema: PropTypes.object, - }; - - static defaultProps = { - config: {}, - __schema: {}, - }; - - addonKey: any; - appHelper: IRendererAppHelper; - open: () => any; - close: () => any; - - __afterInit(props: IBaseRendererProps) { - this.__generateCtx({ - component: this, - }); - const schema = props.__schema || {}; - this.state = this.__parseData(schema.state || {}); - if (isEmpty(props.config) || !props.config?.addonKey) { - logger.warn('lce addon has wrong config'); - this.setState({ - __hasError: true, - }); - return; - } - // 注册插件 - this.addonKey = props.config.addonKey; - this.appHelper.addons = this.appHelper.addons || {}; - this.appHelper.addons[this.addonKey] = this; - this.__initDataSource(props); - this.open = this.open || (() => { }); - this.close = this.close || (() => { }); - this.__executeLifeCycleMethod('constructor', [...arguments]); - } - - async componentWillUnmount() { - super.componentWillUnmount?.apply(this, [...arguments] as any); - // 注销插件 - const config = this.props.config || {}; - if (config && this.appHelper.addons) { - delete this.appHelper.addons[config.addonKey]; - } - } - - get utils() { - const { utils = {} } = this.context.config || {}; - return { ...this.appHelper.utils, ...utils }; - } - - render() { - const { __schema } = this.props; - - if (this.__checkSchema(__schema)) { - return '插件 schema 结构异常!'; - } - - this.__debug(`${AddonRenderer.displayName} render - ${__schema.fileName}`); - this.__generateCtx({ - component: this, - }); - this.__render(); - - return this.__renderContent(this.__renderContextProvider({ compContext: this })); - } - }; -} diff --git a/packages/renderer-core/src/renderer/base.tsx b/packages/renderer-core/src/renderer/base.tsx deleted file mode 100644 index d24009560..000000000 --- a/packages/renderer-core/src/renderer/base.tsx +++ /dev/null @@ -1,1050 +0,0 @@ -/* eslint-disable no-console */ -/* eslint-disable max-len */ -/* eslint-disable react/prop-types */ -import classnames from 'classnames'; -import { create as createDataSourceEngine } from '@alilc/lowcode-datasource-engine/interpret'; -import { IPublicTypeNodeSchema, IPublicTypeNodeData, IPublicTypeJSONValue, IPublicTypeCompositeValue } from '@alilc/lowcode-types'; -import { checkPropTypes, isI18nData, isJSExpression, isJSFunction } from '@alilc/lowcode-utils'; -import adapter from '../adapter'; -import divFactory from '../components/Div'; -import visualDomFactory from '../components/VisualDom'; -import contextFactory from '../context'; -import { - forEach, - getValue, - parseData, - parseExpression, - parseThisRequiredExpression, - parseI18n, - isEmpty, - isSchema, - isFileSchema, - transformArrayToMap, - transformStringToFunction, - getI18n, - getFileCssName, - capitalizeFirstLetter, - DataHelper, - isVariable, - isJSSlot, -} from '../utils'; -import { IBaseRendererProps, INodeInfo, IBaseRenderComponent, IBaseRendererContext, IRendererAppHelper, DataSource } from '../types'; -import { compWrapper } from '../hoc'; -import { IComponentConstruct, leafWrapper } from '../hoc/leaf'; -import logger from '../utils/logger'; -import isUseLoop from '../utils/is-use-loop'; - -/** - * execute method in schema.lifeCycles with context - * @PRIVATE - */ -export function executeLifeCycleMethod(context: any, schema: IPublicTypeNodeSchema, method: string, args: any, thisRequiredInJSE: boolean | undefined): any { - if (!context || !isSchema(schema) || !method) { - return; - } - const lifeCycleMethods = getValue(schema, 'lifeCycles', {}); - let fn = lifeCycleMethods[method]; - - if (!fn) { - return; - } - - // TODO: cache - if (isJSExpression(fn) || isJSFunction(fn)) { - fn = thisRequiredInJSE ? parseThisRequiredExpression(fn, context) : parseExpression(fn, context); - } - - if (typeof fn !== 'function') { - logger.error(`生命周期${method}类型不符`, fn); - return; - } - - try { - return fn.apply(context, args); - } catch (e) { - logger.error(`[${schema.componentName}]生命周期${method}出错`, e); - } -} - -/** - * get children from a node schema - * @PRIVATE - */ -export function getSchemaChildren(schema: IPublicTypeNodeSchema | undefined) { - if (!schema) { - return; - } - - if (!schema.props) { - return schema.children; - } - - if (!schema.children) { - return schema.props.children; - } - - if (!schema.props.children) { - return schema.children; - } - - let result = ([] as IPublicTypeNodeData[]).concat(schema.children); - if (Array.isArray(schema.props.children)) { - result = result.concat(schema.props.children); - } else { - result.push(schema.props.children); - } - return result; -} - -export default function baseRendererFactory(): IBaseRenderComponent { - const { BaseRenderer: customBaseRenderer } = adapter.getRenderers(); - - if (customBaseRenderer) { - return customBaseRenderer; - } - - const { Component, createElement } = adapter.getRuntime(); - const Div = divFactory(); - const VisualDom = visualDomFactory(); - const AppContext = contextFactory(); - - const DESIGN_MODE = { - EXTEND: 'extend', - BORDER: 'border', - PREVIEW: 'preview', - }; - const OVERLAY_LIST = ['Dialog', 'Overlay', 'Animate', 'ConfigProvider']; - const DEFAULT_LOOP_ARG_ITEM = 'item'; - const DEFAULT_LOOP_ARG_INDEX = 'index'; - let scopeIdx = 0; - - return class BaseRenderer extends Component> { - [key: string]: any; - - static displayName = 'BaseRenderer'; - - static defaultProps = { - __schema: {}, - }; - - static contextType = AppContext; - - i18n: any; - getLocale: any; - setLocale: any; - dataSourceMap: Record = {}; - - __namespace = 'base'; - __compScopes: Record = {}; - __instanceMap: Record = {}; - __dataHelper: any; - - /** - * keep track of customMethods added to this context - * - * @type {any} - */ - __customMethodsList: any[] = []; - __parseExpression: any; - __ref: any; - - /** - * reference of style element contains schema.css - * - * @type {any} - */ - __styleElement: any; - - constructor(props: IBaseRendererProps, context: IBaseRendererContext) { - super(props, context); - this.context = context; - this.__parseExpression = (str: string, self: any) => { - return parseExpression({ str, self, thisRequired: props?.thisRequiredInJSE, logScope: props.componentName }); - }; - this.__beforeInit(props); - this.__init(props); - this.__afterInit(props); - this.__debug(`constructor - ${props?.__schema?.fileName}`); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - __beforeInit(_props: IBaseRendererProps) { } - - __init(props: IBaseRendererProps) { - this.__compScopes = {}; - this.__instanceMap = {}; - this.__bindCustomMethods(props); - this.__initI18nAPIs(); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - __afterInit(_props: IBaseRendererProps) { } - - static getDerivedStateFromProps(props: IBaseRendererProps, state: any) { - const result = executeLifeCycleMethod(this, props?.__schema, 'getDerivedStateFromProps', [props, state], props.thisRequiredInJSE); - return result === undefined ? null : result; - } - - async getSnapshotBeforeUpdate(...args: any[]) { - this.__executeLifeCycleMethod('getSnapshotBeforeUpdate', args); - this.__debug(`getSnapshotBeforeUpdate - ${this.props?.__schema?.fileName}`); - } - - async componentDidMount(...args: any[]) { - this.reloadDataSource(); - this.__executeLifeCycleMethod('componentDidMount', args); - this.__debug(`componentDidMount - ${this.props?.__schema?.fileName}`); - } - - async componentDidUpdate(...args: any[]) { - this.__executeLifeCycleMethod('componentDidUpdate', args); - this.__debug(`componentDidUpdate - ${this.props.__schema.fileName}`); - } - - async componentWillUnmount(...args: any[]) { - this.__executeLifeCycleMethod('componentWillUnmount', args); - this.__debug(`componentWillUnmount - ${this.props?.__schema?.fileName}`); - } - - async componentDidCatch(...args: any[]) { - this.__executeLifeCycleMethod('componentDidCatch', args); - logger.warn(args); - } - - reloadDataSource = () => new Promise((resolve, reject) => { - this.__debug('reload data source'); - if (!this.__dataHelper) { - return resolve({}); - } - this.__dataHelper.getInitData() - .then((res: any) => { - if (isEmpty(res)) { - this.forceUpdate(); - return resolve({}); - } - this.setState(res, resolve as () => void); - }) - .catch((err: Error) => { - reject(err); - }); - }); - - shouldComponentUpdate() { - if (this.props.getSchemaChangedSymbol?.() && this.props.__container?.rerender) { - this.props.__container?.rerender(); - return false; - } - return true; - } - - forceUpdate() { - if (this.shouldComponentUpdate()) { - super.forceUpdate(); - } - } - - /** - * execute method in schema.lifeCycles - * @PRIVATE - */ - __executeLifeCycleMethod = (method: string, args?: any) => { - executeLifeCycleMethod(this, this.props.__schema, method, args, this.props.thisRequiredInJSE); - }; - - /** - * this method is for legacy purpose only, which used _ prefix instead of __ as private for some historical reasons - * @LEGACY - */ - _getComponentView = (componentName: string) => { - const { __components } = this.props; - if (!__components) { - return; - } - return __components[componentName]; - }; - - __bindCustomMethods = (props: IBaseRendererProps) => { - const { __schema } = props; - const customMethodsList = Object.keys(__schema.methods || {}) || []; - (this.__customMethodsList || []).forEach((item: any) => { - if (!customMethodsList.includes(item)) { - delete this[item]; - } - }); - this.__customMethodsList = customMethodsList; - forEach(__schema.methods, (val: any, key: string) => { - let value = val; - if (isJSExpression(value) || isJSFunction(value)) { - value = this.__parseExpression(value, this); - } - if (typeof value !== 'function') { - logger.error(`custom method ${key} can not be parsed to a valid function`, value); - return; - } - this[key] = value.bind(this); - }); - }; - - __generateCtx = (ctx: Record) => { - const { pageContext, compContext } = this.context; - const obj = { - page: pageContext, - component: compContext, - ...ctx, - }; - forEach(obj, (val: any, key: string) => { - this[key] = val; - }); - }; - - __parseData = (data: any, ctx?: Record) => { - const { __ctx, thisRequiredInJSE, componentName } = this.props; - return parseData(data, ctx || __ctx || this, { thisRequiredInJSE, logScope: componentName }); - }; - - __initDataSource = (props: IBaseRendererProps) => { - if (!props) { - return; - } - const schema = props.__schema || {}; - const defaultDataSource: DataSource = { - list: [], - }; - const dataSource = schema.dataSource || defaultDataSource; - // requestHandlersMap 存在才走数据源引擎方案 - // TODO: 下面if else 抽成独立函数 - const useDataSourceEngine = !!(props.__appHelper?.requestHandlersMap); - if (useDataSourceEngine) { - this.__dataHelper = { - updateConfig: (updateDataSource: any) => { - const { dataSourceMap, reloadDataSource } = createDataSourceEngine( - updateDataSource ?? {}, - this, - props.__appHelper.requestHandlersMap ? { requestHandlersMap: props.__appHelper.requestHandlersMap } : undefined, - ); - - this.reloadDataSource = () => new Promise((resolve) => { - this.__debug('reload data source'); - reloadDataSource().then(() => { - resolve({}); - }); - }); - return dataSourceMap; - }, - }; - this.dataSourceMap = this.__dataHelper.updateConfig(dataSource); - } else { - const appHelper = props.__appHelper; - this.__dataHelper = new DataHelper(this, dataSource, appHelper, (config: any) => this.__parseData(config)); - this.dataSourceMap = this.__dataHelper.dataSourceMap; - this.reloadDataSource = () => new Promise((resolve, reject) => { - this.__debug('reload data source'); - if (!this.__dataHelper) { - return resolve({}); - } - this.__dataHelper.getInitData() - .then((res: any) => { - if (isEmpty(res)) { - return resolve({}); - } - this.setState(res, resolve as () => void); - }) - .catch((err: Error) => { - reject(err); - }); - }); - } - }; - - /** - * init i18n apis - * @PRIVATE - */ - __initI18nAPIs = () => { - this.i18n = (key: string, values = {}) => { - const { locale, messages } = this.props; - return getI18n(key, values, locale, messages); - }; - this.getLocale = () => this.props.locale; - this.setLocale = (loc: string) => { - const setLocaleFn = this.appHelper?.utils?.i18n?.setLocale; - if (!setLocaleFn || typeof setLocaleFn !== 'function') { - logger.warn('initI18nAPIs Failed, i18n only works when appHelper.utils.i18n.setLocale() exists'); - return undefined; - } - return setLocaleFn(loc); - }; - }; - - /** - * write props.__schema.css to document as a style element, - * which will be added once and only once. - * @PRIVATE - */ - __writeCss = (props: IBaseRendererProps) => { - const css = getValue(props.__schema, 'css', ''); - this.__debug('create this.styleElement with css', css); - let style = this.__styleElement; - if (!this.__styleElement) { - style = document.createElement('style'); - style.type = 'text/css'; - style.setAttribute('from', 'style-sheet'); - - const head = document.head || document.getElementsByTagName('head')[0]; - head.appendChild(style); - this.__styleElement = style; - this.__debug('this.styleElement is created', this.__styleElement); - } - - if (style.innerHTML === css) { - return; - } - - style.innerHTML = css; - }; - - __render = () => { - const schema = this.props.__schema; - this.__executeLifeCycleMethod('render'); - this.__writeCss(this.props); - - const { engine } = this.context; - if (engine) { - engine.props.onCompGetCtx(schema, this); - // 画布场景才需要每次渲染bind自定义方法 - if (this.__designModeIsDesign) { - this.__bindCustomMethods(this.props); - this.dataSourceMap = this.__dataHelper?.updateConfig(schema.dataSource); - } - } - }; - - __getRef = (ref: any) => { - const { engine } = this.context; - const { __schema } = this.props; - ref && engine?.props?.onCompGetRef(__schema, ref); - this.__ref = ref; - }; - - __createDom = () => { - const { __schema, __ctx, __components = {} } = this.props; - // merge defaultProps - const scopeProps = { - ...__schema.defaultProps, - ...this.props, - }; - const scope: any = { - props: scopeProps, - }; - scope.__proto__ = __ctx || this; - - const _children = getSchemaChildren(__schema); - let Comp = __components[__schema.componentName]; - - if (!Comp) { - this.__debug(`${__schema.componentName} is invalid!`); - } - const parentNodeInfo = ({ - schema: __schema, - Comp: this.__getHOCWrappedComponent(Comp, __schema, scope), - } as INodeInfo); - return this.__createVirtualDom(_children, scope, parentNodeInfo); - }; - - /** - * 将模型结构转换成react Element - * @param originalSchema schema - * @param originalScope scope - * @param parentInfo 父组件的信息,包含schema和Comp - * @param idx 为循环渲染的循环Index - */ - __createVirtualDom = (originalSchema: IPublicTypeNodeData | IPublicTypeNodeData[] | undefined, originalScope: any, parentInfo: INodeInfo, idx: string | number = ''): any => { - if (originalSchema === null || originalSchema === undefined) { - return null; - } - let scope = originalScope; - let schema = originalSchema; - const { engine } = this.context || {}; - if (!engine) { - this.__debug('this.context.engine is invalid!'); - return null; - } - try { - const { __appHelper: appHelper, __components: components = {} } = this.props || {}; - - if (isJSExpression(schema)) { - return this.__parseExpression(schema, scope); - } - if (isI18nData(schema)) { - return parseI18n(schema, scope); - } - if (isJSSlot(schema)) { - return this.__createVirtualDom(schema.value, scope, parentInfo); - } - - if (typeof schema === 'string') { - return schema; - } - - if (typeof schema === 'number' || typeof schema === 'boolean') { - return String(schema); - } - - if (Array.isArray(schema)) { - if (schema.length === 1) { - return this.__createVirtualDom(schema[0], scope, parentInfo); - } - return schema.map((item, idy) => this.__createVirtualDom(item, scope, parentInfo, (item as IPublicTypeNodeSchema)?.__ctx?.lceKey ? '' : String(idy))); - } - - // @ts-expect-error 如果直接转换好了,可以返回 - if (schema.$$typeof) { - return schema; - } - - const _children = getSchemaChildren(schema); - if (!schema.componentName) { - logger.error('The componentName in the schema is invalid, please check the schema: ', schema); - return; - } - // 解析占位组件 - if (schema.componentName === 'Fragment' && _children) { - const tarChildren = isJSExpression(_children) ? this.__parseExpression(_children, scope) : _children; - return this.__createVirtualDom(tarChildren, scope, parentInfo); - } - - if (schema.componentName === 'Text' && typeof schema.props?.text === 'string') { - const text: string = schema.props?.text; - schema = { ...schema }; - schema.children = [text]; - } - - if (!isSchema(schema)) { - return null; - } - let Comp = components[schema.componentName] || this.props.__container?.components?.[schema.componentName]; - - // 容器类组件的上下文通过props传递,避免context传递带来的嵌套问题 - const otherProps: any = isFileSchema(schema) - ? { - __schema: schema, - __appHelper: appHelper, - __components: components, - } - : {}; - - if (!Comp) { - logger.error(`${schema.componentName} component is not found in components list! component list is:`, components || this.props.__container?.components); - return engine.createElement( - engine.getNotFoundComponent(), - { - componentName: schema.componentName, - componentId: schema.id, - enableStrictNotFoundMode: engine.props.enableStrictNotFoundMode, - ref: (ref: any) => { - ref && engine.props?.onCompGetRef(schema, ref); - }, - }, - this.__getSchemaChildrenVirtualDom(schema, scope, Comp), - ); - } - - if (schema.loop != null) { - const loop = this.__parseData(schema.loop, scope); - if (Array.isArray(loop) && loop.length === 0) return null; - const useLoop = isUseLoop(loop, this.__designModeIsDesign); - if (useLoop) { - return this.__createLoopVirtualDom( - { - ...schema, - loop, - }, - scope, - parentInfo, - idx, - ); - } - } - const condition = schema.condition == null ? true : this.__parseData(schema.condition, scope); - - // DesignMode 为 design 情况下,需要进入 leaf Hoc,进行相关事件注册 - const displayInHook = this.__designModeIsDesign; - if (!condition && !displayInHook) { - return null; - } - - let scopeKey = ''; - // 判断组件是否需要生成scope,且只生成一次,挂在this.__compScopes上 - if (Comp.generateScope) { - const key = this.__parseExpression(schema.props?.key, scope); - if (key) { - // 如果组件自己设置key则使用组件自己的key - scopeKey = key; - } else if (!schema.__ctx) { - // 在生产环境schema没有__ctx上下文,需要手动生成一个lceKey - schema.__ctx = { - lceKey: `lce${++scopeIdx}`, - }; - scopeKey = schema.__ctx.lceKey; - } else { - // 需要判断循环的情况 - scopeKey = schema.__ctx.lceKey + (idx !== undefined ? `_${idx}` : ''); - } - if (!this.__compScopes[scopeKey]) { - this.__compScopes[scopeKey] = Comp.generateScope(this, schema); - } - } - // 如果组件有设置scope,需要为组件生成一个新的scope上下文 - if (scopeKey && this.__compScopes[scopeKey]) { - const compSelf = { ...this.__compScopes[scopeKey] }; - compSelf.__proto__ = scope; - scope = compSelf; - } - - if (engine.props?.designMode) { - otherProps.__designMode = engine.props.designMode; - } - if (this.__designModeIsDesign) { - otherProps.__tag = Math.random(); - } - const componentInfo: any = {}; - const props: any = this.__getComponentProps(schema, scope, Comp, { - ...componentInfo, - props: transformArrayToMap(componentInfo.props, 'name'), - }) || {}; - - this.__componentHOCs.forEach((ComponentConstruct: IComponentConstruct) => { - Comp = ComponentConstruct(Comp, { - schema, - componentInfo, - baseRenderer: this, - scope, - }); - }); - - otherProps.ref = (ref: any) => { - this.$(props.fieldId || props.ref, ref); // 收集ref - const refProps = props.ref; - if (refProps && typeof refProps === 'string') { - this[refProps] = ref; - } - ref && engine.props?.onCompGetRef(schema, ref); - }; - - // scope需要传入到组件上 - if (scopeKey && this.__compScopes[scopeKey]) { - props.__scope = this.__compScopes[scopeKey]; - } - if (schema?.__ctx?.lceKey) { - if (!isFileSchema(schema)) { - engine.props?.onCompGetCtx(schema, scope); - } - props.key = props.key || `${schema.__ctx.lceKey}_${schema.__ctx.idx || 0}_${idx !== undefined ? idx : ''}`; - } else if ((typeof idx === 'number' || typeof idx === 'string') && !props.key) { - // 仅当循环场景走这里 - props.key = idx; - } - - props.__id = schema.id; - if (!props.key) { - props.key = props.__id; - } - - let child = this.__getSchemaChildrenVirtualDom(schema, scope, Comp, condition); - const renderComp = (innerProps: any) => engine.createElement(Comp, innerProps, child); - // 设计模式下的特殊处理 - if (engine && [DESIGN_MODE.EXTEND, DESIGN_MODE.BORDER].includes(engine.props.designMode)) { - // 对于overlay,dialog等组件为了使其在设计模式下显示,外层需要增加一个div容器 - if (OVERLAY_LIST.includes(schema.componentName)) { - const { ref, ...overlayProps } = otherProps; - return createElement(Div, { - ref, - __designMode: engine.props.designMode, - }, renderComp({ ...props, ...overlayProps })); - } - // 虚拟dom显示 - if (componentInfo?.parentRule) { - const parentList = componentInfo.parentRule.split(','); - const { schema: parentSchema = { componentName: '' }, Comp: parentComp } = parentInfo; - if ( - !parentList.includes(parentSchema.componentName) || - parentComp !== components[parentSchema.componentName] - ) { - props.__componentName = schema.componentName; - Comp = VisualDom; - } else { - // 若虚拟dom在正常的渲染上下文中,就不显示设计模式了 - props.__disableDesignMode = true; - } - } - } - return renderComp({ - ...props, - ...otherProps, - __inner__: { - hidden: schema.hidden, - condition, - }, - }); - } catch (e) { - return engine.createElement(engine.getFaultComponent(), { - error: e, - schema, - self: scope, - parentInfo, - idx, - }); - } - }; - - /** - * get Component HOCs - * - * @readonly - * @type {IComponentConstruct[]} - */ - get __componentHOCs(): IComponentConstruct[] { - if (this.__designModeIsDesign) { - return [leafWrapper, compWrapper]; - } - return [compWrapper]; - } - - __getSchemaChildrenVirtualDom = (schema: IPublicTypeNodeSchema | undefined, scope: any, Comp: any, condition = true) => { - let children = condition ? getSchemaChildren(schema) : null; - - // @todo 补完这里的 Element 定义 @承虎 - let result: any = []; - if (children) { - if (!Array.isArray(children)) { - children = [children]; - } - - children.forEach((child: any) => { - const childVirtualDom = this.__createVirtualDom( - isJSExpression(child) ? this.__parseExpression(child, scope) : child, - scope, - { - schema, - Comp, - }, - ); - - result.push(childVirtualDom); - }); - } - - if (result && result.length > 0) { - return result; - } - return null; - }; - - __getComponentProps = (schema: IPublicTypeNodeSchema | undefined, scope: any, Comp: any, componentInfo?: any) => { - if (!schema) { - return {}; - } - return this.__parseProps(schema?.props, scope, '', { - schema, - Comp, - componentInfo: { - ...(componentInfo || {}), - props: transformArrayToMap((componentInfo || {}).props, 'name'), - }, - }) || {}; - }; - - __createLoopVirtualDom = (schema: IPublicTypeNodeSchema, scope: any, parentInfo: INodeInfo, idx: number | string) => { - if (isFileSchema(schema)) { - logger.warn('file type not support Loop'); - return null; - } - if (!Array.isArray(schema.loop)) { - return null; - } - const itemArg = (schema.loopArgs && schema.loopArgs[0]) || DEFAULT_LOOP_ARG_ITEM; - const indexArg = (schema.loopArgs && schema.loopArgs[1]) || DEFAULT_LOOP_ARG_INDEX; - const { loop } = schema; - return loop.map((item: IPublicTypeJSONValue | IPublicTypeCompositeValue, i: number) => { - const loopSelf: any = { - [itemArg]: item, - [indexArg]: i, - }; - loopSelf.__proto__ = scope; - return this.__createVirtualDom( - { - ...schema, - loop: undefined, - props: { - ...schema.props, - // 循环下 key 不能为常量,这样会造成 key 值重复,渲染异常 - key: isJSExpression(schema.props?.key) ? schema.props?.key : null, - }, - }, - loopSelf, - parentInfo, - idx ? `${idx}_${i}` : i, - ); - }); - }; - - get __designModeIsDesign() { - const { engine } = this.context || {}; - return engine?.props?.designMode === 'design'; - } - - __parseProps = (originalProps: any, scope: any, path: string, info: INodeInfo): any => { - let props = originalProps; - const { schema, Comp, componentInfo = {} } = info; - const propInfo = getValue(componentInfo.props, path); - // FIXME: 将这行逻辑外置,解耦,线上环境不要验证参数,调试环境可以有,通过传参自定义 - const propType = propInfo?.extra?.propType; - - const checkProps = (value: any) => { - if (!propType) { - return value; - } - return checkPropTypes(value, path, propType, componentInfo.name) ? value : undefined; - }; - - const parseReactNode = (data: any, params: any) => { - if (isEmpty(params)) { - const virtualDom = this.__createVirtualDom(data, scope, ({ schema, Comp } as INodeInfo)); - return checkProps(virtualDom); - } - return checkProps((...argValues: any[]) => { - const args: any = {}; - if (Array.isArray(params) && params.length) { - params.forEach((item, idx) => { - if (typeof item === 'string') { - args[item] = argValues[idx]; - } else if (item && typeof item === 'object') { - args[item.name] = argValues[idx]; - } - }); - } - args.__proto__ = scope; - return scope.__createVirtualDom(data, args, ({ schema, Comp } as INodeInfo)); - }); - }; - - if (isJSExpression(props)) { - props = this.__parseExpression(props, scope); - // 只有当变量解析出来为模型结构的时候才会继续解析 - if (!isSchema(props) && !isJSSlot(props)) { - return checkProps(props); - } - } - - const handleI18nData = (innerProps: any) => innerProps[innerProps.use || (this.getLocale && this.getLocale()) || 'zh-CN']; - - // @LEGACY 兼容老平台设计态 i18n 数据 - if (isI18nData(props)) { - const i18nProp = handleI18nData(props); - if (i18nProp) { - props = i18nProp; - } else { - return parseI18n(props, scope); - } - } - - // @LEGACY 兼容老平台设计态的变量绑定 - if (isVariable(props)) { - props = props.value; - if (isI18nData(props)) { - props = handleI18nData(props); - } - } - - if (isJSFunction(props)) { - props = transformStringToFunction(props.value); - } - if (isJSSlot(props)) { - const { params, value } = props; - if (!isSchema(value) || isEmpty(value)) { - return undefined; - } - return parseReactNode(value, params); - } - - // 兼容通过componentInfo判断的情况 - if (isSchema(props)) { - const isReactNodeFunction = !!(propInfo?.type === 'ReactNode' && propInfo?.props?.type === 'function'); - - const isMixinReactNodeFunction = !!( - propInfo?.type === 'Mixin' - && propInfo?.props?.types?.indexOf('ReactNode') > -1 - && propInfo?.props?.reactNodeProps?.type === 'function' - ); - - let params = null; - if (isReactNodeFunction) { - params = propInfo?.props?.params; - } else if (isMixinReactNodeFunction) { - params = propInfo?.props?.reactNodeProps?.params; - } - return parseReactNode( - props, - params, - ); - } - if (Array.isArray(props)) { - return checkProps(props.map((item, idx) => this.__parseProps(item, scope, path ? `${path}.${idx}` : `${idx}`, info))); - } - if (typeof props === 'function') { - return checkProps(props.bind(scope)); - } - if (props && typeof props === 'object') { - if (props.$$typeof) { - return checkProps(props); - } - const res: any = {}; - forEach(props, (val: any, key: string) => { - if (key.startsWith('__')) { - res[key] = val; - return; - } - res[key] = this.__parseProps(val, scope, path ? `${path}.${key}` : key, info); - }); - return checkProps(res); - } - return checkProps(props); - }; - - $(filedId: string, instance?: any) { - this.__instanceMap = this.__instanceMap || {}; - if (!filedId || typeof filedId !== 'string') { - return this.__instanceMap; - } - if (instance) { - this.__instanceMap[filedId] = instance; - } - return this.__instanceMap[filedId]; - } - - __debug = (...args: any[]) => { logger.debug(...args); }; - - __renderContextProvider = (customProps?: object, children?: any) => { - return createElement(AppContext.Provider, { - value: { - ...this.context, - blockContext: this, - ...(customProps || {}), - }, - children: children || this.__createDom(), - }); - }; - - __renderContextConsumer = (children: any) => { - return createElement(AppContext.Consumer, {}, children); - }; - - __getHOCWrappedComponent(OriginalComp: any, schema: any, scope: any) { - let Comp = OriginalComp; - this.__componentHOCs.forEach((ComponentConstruct: IComponentConstruct) => { - Comp = ComponentConstruct(Comp || Div, { - schema, - componentInfo: {}, - baseRenderer: this, - scope, - }); - }); - - return Comp; - } - - __renderComp(OriginalComp: any, ctxProps: object) { - let Comp = OriginalComp; - const { __schema, __ctx } = this.props; - const scope: any = {}; - scope.__proto__ = __ctx || this; - Comp = this.__getHOCWrappedComponent(Comp, __schema, scope); - const data = this.__parseProps(__schema?.props, scope, '', { - schema: __schema, - Comp, - componentInfo: {}, - }); - const { className } = data; - const otherProps: any = {}; - const { engine } = this.context || {}; - if (!engine) { - return null; - } - - if (this.__designModeIsDesign) { - otherProps.__tag = Math.random(); - } - - const child = engine.createElement( - Comp, - { - ...data, - ...this.props, - ref: this.__getRef, - className: classnames(getFileCssName(__schema?.fileName), className, this.props.className), - __id: __schema?.id, - ...otherProps, - }, - this.__createDom(), - ); - return this.__renderContextProvider(ctxProps, child); - } - - __renderContent(children: any) { - const { __schema } = this.props; - const parsedProps = this.__parseData(__schema.props); - const className = classnames(`lce-${this.__namespace}`, getFileCssName(__schema.fileName), parsedProps.className, this.props.className); - const style = { ...(parsedProps.style || {}), ...(typeof this.props.style === 'object' ? this.props.style : {}) }; - const id = this.props.id || parsedProps.id; - return createElement('div', { - ref: this.__getRef, - className, - id, - style, - }, children); - } - - __checkSchema = (schema: IPublicTypeNodeSchema | undefined, originalExtraComponents: string | string[] = []) => { - let extraComponents = originalExtraComponents; - if (typeof extraComponents === 'string') { - extraComponents = [extraComponents]; - } - - const builtin = capitalizeFirstLetter(this.__namespace); - const componentNames = [builtin, ...extraComponents]; - return !isSchema(schema) || !componentNames.includes(schema?.componentName ?? ''); - }; - - get appHelper(): IRendererAppHelper { - return this.props.__appHelper; - } - - get requestHandlersMap() { - return this.appHelper?.requestHandlersMap; - } - - get utils() { - return this.appHelper?.utils; - } - - get constants() { - return this.appHelper?.constants; - } - - get history() { - return this.appHelper?.history; - } - - get location() { - return this.appHelper?.location; - } - - get match() { - return this.appHelper?.match; - } - - render() { - return null; - } - }; -} diff --git a/packages/renderer-core/src/renderer/block.tsx b/packages/renderer-core/src/renderer/block.tsx deleted file mode 100644 index 5132997f0..000000000 --- a/packages/renderer-core/src/renderer/block.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import baseRendererFactory from './base'; -import { IBaseRendererProps, IBaseRenderComponent } from '../types'; - -export default function blockRendererFactory(): IBaseRenderComponent { - const BaseRenderer = baseRendererFactory(); - return class BlockRenderer extends BaseRenderer { - static displayName = 'BlockRenderer'; - - __namespace = 'block'; - - __afterInit(props: IBaseRendererProps) { - this.__generateCtx({}); - const schema = props.__schema || {}; - this.state = this.__parseData(schema.state || {}); - this.__initDataSource(props); - this.__executeLifeCycleMethod('constructor', [...arguments]); - } - - render() { - const { __schema, __components } = this.props; - - if (this.__checkSchema(__schema, 'Div')) { - return '区块 schema 结构异常!'; - } - - this.__debug(`${BlockRenderer.displayName} render - ${__schema?.fileName}`); - this.__generateCtx({}); - this.__render(); - - const { Block } = __components; - if (Block) { - return this.__renderComp(Block, {}); - } - - return this.__renderContent(this.__renderContextProvider()); - } - }; -} diff --git a/packages/renderer-core/src/renderer/component.tsx b/packages/renderer-core/src/renderer/component.tsx deleted file mode 100644 index 3dfc1df33..000000000 --- a/packages/renderer-core/src/renderer/component.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import baseRendererFactory from './base'; -import { IBaseRendererProps, IBaseRenderComponent } from '../types'; - -export default function componentRendererFactory(): IBaseRenderComponent { - const BaseRenderer = baseRendererFactory(); - return class CompRenderer extends BaseRenderer { - static displayName = 'CompRenderer'; - - __namespace = 'component'; - - __afterInit(props: IBaseRendererProps) { - this.__generateCtx({ - component: this, - }); - const schema = props.__schema || {}; - this.state = this.__parseData(schema.state || {}); - this.__initDataSource(props); - this.__executeLifeCycleMethod('constructor', arguments as any); - } - - render() { - const { __schema, __components } = this.props; - if (this.__checkSchema(__schema)) { - return '自定义组件 schema 结构异常!'; - } - this.__debug(`${CompRenderer.displayName} render - ${__schema.fileName}`); - - this.__generateCtx({ - component: this, - }); - this.__render(); - - const noContainer = this.__parseData(__schema.props?.noContainer); - - this.__bindCustomMethods(this.props); - - if (noContainer) { - return this.__renderContextProvider({ compContext: this }); - } - - const Component = __components?.[__schema?.componentName]; - - if (!Component) { - return this.__renderContent(this.__renderContextProvider({ compContext: this })); - } - - return this.__renderComp(Component, this.__renderContextProvider({ compContext: this })); - } - }; -} diff --git a/packages/renderer-core/src/renderer/index.ts b/packages/renderer-core/src/renderer/index.ts deleted file mode 100644 index a4fee1d48..000000000 --- a/packages/renderer-core/src/renderer/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import baseRendererFactory from './base'; -import pageRendererFactory from './page'; -import componentRendererFactory from './component'; -import blockRendererFactory from './block'; -import addonRendererFactory from './addon'; -import tempRendererFactory from './temp'; -import rendererFactory from './renderer'; - -export { - baseRendererFactory, - pageRendererFactory, - componentRendererFactory, - blockRendererFactory, - addonRendererFactory, - tempRendererFactory, - rendererFactory, -}; diff --git a/packages/renderer-core/src/renderer/page.tsx b/packages/renderer-core/src/renderer/page.tsx deleted file mode 100644 index 16d55e01b..000000000 --- a/packages/renderer-core/src/renderer/page.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { getLogger } from '@alilc/lowcode-utils'; -import baseRendererFactory from './base'; -import { IBaseRendererProps, IBaseRenderComponent } from '../types'; - -const logger = getLogger({ level: 'warn', bizName: 'renderer-core:page' }); - -export default function pageRendererFactory(): IBaseRenderComponent { - const BaseRenderer = baseRendererFactory(); - return class PageRenderer extends BaseRenderer { - static displayName = 'PageRenderer'; - - __namespace = 'page'; - - __afterInit(props: IBaseRendererProps, ...rest: unknown[]) { - this.__generateCtx({ - page: this, - }); - const schema = props.__schema || {}; - this.state = this.__parseData(schema.state || {}); - this.__initDataSource(props); - this.__executeLifeCycleMethod('constructor', [props, ...rest]); - } - - async componentDidUpdate(prevProps: IBaseRendererProps, _prevState: {}, snapshot: unknown) { - const { __ctx } = this.props; - // 当编排的时候修改 schema.state 值,需要将最新 schema.state 值 setState - if (JSON.stringify(prevProps.__schema.state) != JSON.stringify(this.props.__schema.state)) { - const newState = this.__parseData(this.props.__schema.state, __ctx); - this.setState(newState); - } - - super.componentDidUpdate?.(prevProps, _prevState, snapshot); - } - - setState(state: any, callback?: () => void) { - logger.info('page set state', state); - super.setState(state, callback); - } - - render() { - const { __schema, __components } = this.props; - if (this.__checkSchema(__schema)) { - return '页面schema结构异常!'; - } - this.__debug(`${PageRenderer.displayName} render - ${__schema.fileName}`); - - this.__bindCustomMethods(this.props); - this.__initDataSource(this.props); - - this.__generateCtx({ - page: this, - }); - this.__render(); - - const { Page } = __components; - if (Page) { - return this.__renderComp(Page, { pageContext: this }); - } - - return this.__renderContent(this.__renderContextProvider({ pageContext: this })); - } - }; -} diff --git a/packages/renderer-core/src/renderer/renderer.tsx b/packages/renderer-core/src/renderer/renderer.tsx deleted file mode 100644 index 300b1cd16..000000000 --- a/packages/renderer-core/src/renderer/renderer.tsx +++ /dev/null @@ -1,182 +0,0 @@ -import Debug from 'debug'; -import adapter from '../adapter'; -import contextFactory from '../context'; -import { isFileSchema, isEmpty } from '../utils'; -import baseRendererFactory from './base'; -import divFactory from '../components/Div'; -import { IRenderComponent, IRendererProps, IRendererState } from '../types'; -import { IPublicTypeNodeSchema, IPublicTypeRootSchema } from '@alilc/lowcode-types'; -import logger from '../utils/logger'; - -export default function rendererFactory(): IRenderComponent { - const { PureComponent, Component, createElement, findDOMNode } = adapter.getRuntime(); - const RENDERER_COMPS: any = adapter.getRenderers(); - const BaseRenderer = baseRendererFactory(); - const AppContext = contextFactory(); - const Div = divFactory(); - - const ConfigProvider = adapter.getConfigProvider() || Div; - - const debug = Debug('renderer:entry'); - - class FaultComponent extends PureComponent { - render() { - logger.error(`%c${this.props.componentName || ''} 组件渲染异常, 异常原因: ${this.props.error?.message || this.props.error || '未知'}`, 'color: #ff0000;'); - return createElement(Div, { - style: { - width: '100%', - height: '50px', - lineHeight: '50px', - textAlign: 'center', - fontSize: '15px', - color: '#ff0000', - border: '2px solid #ff0000', - }, - }, `${this.props.componentName || ''} 组件渲染异常,请查看控制台日志`); - } - } - - class NotFoundComponent extends PureComponent<{ - componentName: string; - } & IRendererProps> { - render() { - if (this.props.enableStrictNotFoundMode) { - return `${this.props.componentName || ''} Component Not Found`; - } - return createElement(Div, this.props, this.props.children || `${this.props.componentName || ''} Component Not Found`); - } - } - - return class Renderer extends Component { - static displayName = 'Renderer'; - - state: Partial = {}; - - __ref: any; - - static defaultProps: IRendererProps = { - appHelper: undefined, - components: {}, - designMode: '', - suspended: false, - schema: {} as IPublicTypeRootSchema, - onCompGetRef: () => { }, - onCompGetCtx: () => { }, - thisRequiredInJSE: true, - }; - - static findDOMNode = findDOMNode; - - constructor(props: IRendererProps, context: any) { - super(props, context); - this.state = {}; - debug(`entry.constructor - ${props?.schema?.componentName}`); - } - - async componentDidMount() { - debug(`entry.componentDidMount - ${this.props.schema && this.props.schema.componentName}`); - } - - async componentDidUpdate() { - debug(`entry.componentDidUpdate - ${this.props?.schema?.componentName}`); - } - - async componentWillUnmount() { - debug(`entry.componentWillUnmount - ${this.props?.schema?.componentName}`); - } - - componentDidCatch(error: Error) { - this.state.engineRenderError = true; - this.state.error = error; - } - - shouldComponentUpdate(nextProps: IRendererProps) { - return !nextProps.suspended; - } - - __getRef = (ref: any) => { - this.__ref = ref; - if (ref) { - this.props.onCompGetRef?.(this.props.schema, ref); - } - }; - - isValidComponent(SetComponent: any) { - return SetComponent; - } - - createElement(SetComponent: any, props: any, children?: any) { - return (this.props.customCreateElement || createElement)(SetComponent, props, children); - } - - getNotFoundComponent() { - return this.props.notFoundComponent || NotFoundComponent; - } - - getFaultComponent() { - const { faultComponent, faultComponentMap, schema } = this.props; - if (faultComponentMap) { - const { componentName } = schema; - return faultComponentMap[componentName] || faultComponent || FaultComponent; - } - return faultComponent || FaultComponent; - } - - getComp() { - const { schema, components } = this.props; - const { componentName } = schema; - const allComponents = { ...RENDERER_COMPS, ...components }; - let Comp = allComponents[componentName] || RENDERER_COMPS[`${componentName}Renderer`]; - if (Comp && Comp.prototype) { - if (!(Comp.prototype instanceof BaseRenderer)) { - Comp = RENDERER_COMPS[`${componentName}Renderer`]; - } - } - return Comp; - } - - render() { - const { schema, designMode, appHelper, components } = this.props; - if (isEmpty(schema)) { - return null; - } - // 兼容乐高区块模板 - if (schema.componentName !== 'Div' && !isFileSchema(schema)) { - logger.error('The root component name needs to be one of Page、Block、Component, please check the schema: ', schema); - return '模型结构异常'; - } - debug('entry.render'); - const allComponents = { ...RENDERER_COMPS, ...components }; - let Comp = this.getComp(); - - if (this.state && this.state.engineRenderError) { - return createElement(this.getFaultComponent(), { - ...this.props, - error: this.state.error, - }); - } - - if (Comp) { - return createElement(AppContext.Provider, { - value: { - appHelper, - components: allComponents, - engine: this, - }, - }, createElement(ConfigProvider, { - device: this.props.device, - locale: this.props.locale, - }, createElement(Comp, { - key: schema.__ctx && `${schema.__ctx.lceKey}_${schema.__ctx.idx || '0'}`, - ref: this.__getRef, - __appHelper: appHelper, - __components: allComponents, - __schema: schema, - __designMode: designMode, - ...this.props, - }))); - } - return null; - } - }; -} diff --git a/packages/renderer-core/src/renderer/temp.tsx b/packages/renderer-core/src/renderer/temp.tsx deleted file mode 100644 index 1432da5fd..000000000 --- a/packages/renderer-core/src/renderer/temp.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { IBaseRenderComponent } from '../types'; -import logger from '../utils/logger'; -import baseRendererFactory from './base'; - -export default function tempRendererFactory(): IBaseRenderComponent { - const BaseRenderer = baseRendererFactory(); - - return class TempRenderer extends BaseRenderer { - static displayName = 'TempRenderer'; - - __namespace = 'temp'; - - cacheSetState?: Record; - - __init() { - this.state = {}; - this.cacheSetState = {}; - } - - async componentDidMount() { - const ctx = this.props.__ctx; - if (!ctx) return; - const { setState } = ctx; - this.cacheSetState = setState; - ctx.setState = (...args: any) => { - setState.call(ctx, ...args); - setTimeout(() => this.forceUpdate(), 0); - }; - this.__debug(`componentDidMount - ${this.props.__schema.fileName}`); - } - - async componentDidUpdate() { - this.__debug(`componentDidUpdate - ${this.props.__schema.fileName}`); - } - - async componentWillUnmount() { - const ctx = this.props.__ctx; - if (!ctx || !this.cacheSetState) return; - ctx.setState = this.cacheSetState; - delete this.cacheSetState; - this.__debug(`componentWillUnmount - ${this.props.__schema.fileName}`); - } - - async componentDidCatch(e: any) { - logger.warn(e); - this.__debug(`componentDidCatch - ${this.props.__schema.fileName}`); - } - - render() { - const { __schema, __ctx } = this.props; - if (this.__checkSchema(__schema)) { - return '下钻编辑 schema 结构异常!'; - } - - this.__debug(`${TempRenderer.displayName} render - ${__schema?.fileName}`); - - return this.__renderContent(this.__renderContextProvider({ __ctx })); - } - }; -} diff --git a/packages/renderer-core/src/types/index.ts b/packages/renderer-core/src/types/index.ts deleted file mode 100644 index afbec272a..000000000 --- a/packages/renderer-core/src/types/index.ts +++ /dev/null @@ -1,342 +0,0 @@ -import type { ComponentLifecycle, CSSProperties } from 'react'; -import { BuiltinSimulatorHost, BuiltinSimulatorRenderer } from '@alilc/lowcode-designer'; -import { RequestHandler, IPublicTypeNodeSchema, IPublicTypeRootSchema, IPublicTypeJSONObject } from '@alilc/lowcode-types'; - -export type ISchema = IPublicTypeNodeSchema | IPublicTypeRootSchema; - -/* - ** Duck typed component type supporting both react and rax - */ -interface IGeneralComponent

extends ComponentLifecycle { - readonly props: Readonly

& Readonly<{ children?: any | undefined }>; - state: Readonly; - refs: Record; - context: any; - setState( - state: ((prevState: Readonly, props: Readonly

) => (Pick | S | null)) | (Pick | S | null), - callback?: () => void - ): void; - forceUpdate(callback?: () => void): void; - render(): any; -} - -export type IGeneralConstructor< - T = { - [key: string]: any; - }, S = { - [key: string]: any; - }, D = any -> = new (props: TT, context: any) => IGeneralComponent; - -/** - * duck-typed History - * - * @see https://github.com/ReactTraining/history/tree/master/docs/api-reference.md - */ -interface IHistoryLike { - readonly action: any; - readonly location: ILocationLike; - createHref: (to: any) => string; - push: (to: any, state?: any) => void; - replace: (to: any, state?: any) => void; - go: (delta: any) => void; - back: () => void; - forward: () => void; - listen: (listener: any) => () => void; - block: (blocker: any) => () => void; -} - -/** - * duck-typed History.Location - * - * @see https://github.com/remix-run/history/blob/dev/docs/api-reference.md#location - */ -export interface ILocationLike { - pathname: any; - search: any; - state: any; - hash: any; - key?: any; -} - -export type IRendererAppHelper = Partial<{ - - /** 全局公共函数 */ - utils: Record; - - /** 全局常量 */ - constants: Record; - - /** react-router 的 location 实例 */ - location: ILocationLike; - - /** react-router 的 history 实例 */ - history: IHistoryLike; - - /** @deprecated 已无业务使用 */ - match: any; - - /** @experimental 内部使用 */ - logParams: Record; - - /** @experimental 内部使用 */ - addons: Record; - - /** @experimental 内部使用 */ - requestHandlersMap: Record>; -}>; - -/** - * 渲染模块可用配置 - * - * @see @todo @承虎 - */ -export interface IRendererProps { - - /** 符合低代码搭建协议的数据 */ - schema: IPublicTypeRootSchema | IPublicTypeNodeSchema; - - /** 组件依赖的实例 */ - components: Record; - - /** CSS 类名 */ - className?: string; - - /** style */ - style?: CSSProperties; - - /** id */ - id?: string | number; - - /** 语言 */ - locale?: string; - - /** - * 多语言语料 - * 配置规范参见《低代码搭建组件描述协议》https://lowcode-engine.cn/lowcode 中 2.6 国际化多语言支持 - * */ - messages?: Record; - - /** 主要用于设置渲染模块的全局上下文,里面定义的内容可以在低代码中通过 this 来访问,比如 this.utils */ - appHelper?: IRendererAppHelper; - - /** - * 配置规范参见《低代码搭建组件描述协议》https://lowcode-engine.cn/lowcode - * 主要在搭建场景中使用,用于提升用户搭建体验。 - * - * > 在生产环境下不需要设置 - */ - componentsMap?: { [key: string]: any }; - - /** 设计模式,可选值:live、design */ - designMode?: string; - - /** 渲染模块是否挂起,当设置为 true 时,渲染模块最外层容器的 shouldComponentUpdate 将始终返回false,在下钻编辑或者多引擎渲染的场景会用到该参数。 */ - suspended?: boolean; - - /** 组件获取 ref 时触发的钩子 */ - onCompGetRef?: (schema: IPublicTypeNodeSchema, ref: any) => void; - - /** 组件 ctx 更新回调 */ - onCompGetCtx?: (schema: IPublicTypeNodeSchema, ref: any) => void; - - /** 传入的 schema 是否有变更 */ - getSchemaChangedSymbol?: () => boolean; - - /** 设置 schema 是否有变更 */ - setSchemaChangedSymbol?: (symbol: boolean) => void; - - /** 自定义创建 element 的钩子 */ - customCreateElement?: (Component: any, props: any, children: any) => any; - - /** 渲染类型,标识当前模块是以什么类型进行渲染的 */ - rendererName?: 'LowCodeRenderer' | 'PageRenderer' | string; - - /** 当找不到组件时,显示的组件 */ - notFoundComponent?: IGeneralComponent; - - /** 当组件渲染异常时,显示的组件 */ - faultComponent?: IGeneralComponent; - - /** */ - faultComponentMap?: { - [prop: string]: IGeneralComponent; - }; - - /** 设备信息 */ - device?: string; - - /** - * @default true - * JSExpression 是否只支持使用 this 来访问上下文变量 - */ - thisRequiredInJSE?: boolean; - - /** - * @default false - * 当开启组件未找到严格模式时,渲染模块不会默认给一个容器组件 - */ - enableStrictNotFoundMode?: boolean; -} - -export interface IRendererState { - engineRenderError?: boolean; - error?: Error; -} - -/** - * 渲染内部模块可用配置 - */ -export interface IBaseRendererProps { - locale?: string; - messages: Record; - __appHelper: IRendererAppHelper; - __components: Record; - __ctx: Record; - __schema: IPublicTypeRootSchema; - __host?: BuiltinSimulatorHost; - __container?: BuiltinSimulatorRenderer; - config?: Record; - designMode?: 'design'; - className?: string; - style?: CSSProperties; - id?: string | number; - getSchemaChangedSymbol?: () => boolean; - setSchemaChangedSymbol?: (symbol: boolean) => void; - thisRequiredInJSE?: boolean; - documentId?: string; - getNode?: any; - - /** - * 设备类型,默认值:'default' - */ - device?: 'default' | 'mobile' | string; - componentName?: string; -} - -export interface INodeInfo { - schema?: IPublicTypeNodeSchema; - Comp: any; - componentInfo?: any; - componentChildren?: any; -} - -export interface JSExpression { - type: string; - value: string; -} - -export interface DataSourceItem { - id: string; - isInit?: boolean | JSExpression; - type?: string; - options?: { - uri: string | JSExpression; - params?: IPublicTypeJSONObject | JSExpression; - method?: string | JSExpression; - shouldFetch?: string; - willFetch?: string; - fit?: string; - didFetch?: string; - }; - dataHandler?: JSExpression; -} - -export interface DataSource { - list?: DataSourceItem[]; - dataHandler?: JSExpression; -} - -export interface IRuntime { - [key: string]: any; - Component: IGeneralConstructor; - PureComponent: IGeneralConstructor; - createElement: (...args: any) => any; - createContext: (...args: any) => any; - forwardRef: (...args: any) => any; - findDOMNode: (...args: any) => any; -} - -export interface IRendererModules { - BaseRenderer?: IBaseRenderComponent; - PageRenderer: IBaseRenderComponent; - ComponentRenderer: IBaseRenderComponent; - BlockRenderer?: IBaseRenderComponent; - AddonRenderer?: IBaseRenderComponent; - TempRenderer?: IBaseRenderComponent; - DivRenderer?: IBaseRenderComponent; -} - -export interface IBaseRendererContext { - appHelper: IRendererAppHelper; - components: Record; - engine: IRuntime; - pageContext?: IBaseRenderComponent; - compContext?: IBaseRenderComponent; -} - -export type IBaseRendererInstance = IGeneralComponent< - IBaseRendererProps, - Record, - any -> - & { - reloadDataSource(): Promise; - __beforeInit(props: IBaseRendererProps): void; - __init(props: IBaseRendererProps): void; - __afterInit(props: IBaseRendererProps): void; - __executeLifeCycleMethod(method: string, args?: any[]): void; - __bindCustomMethods(props: IBaseRendererProps): void; - __generateCtx(ctx: Record): void; - __parseData(data: any, ctx?: any): any; - __initDataSource(props: IBaseRendererProps): void; - __render(): void; - __getRef(ref: any): void; - __getSchemaChildrenVirtualDom( - schema: IPublicTypeNodeSchema | undefined, - Comp: any, - nodeChildrenMap?: any - ): any; - __getComponentProps(schema: IPublicTypeNodeSchema | undefined, scope: any, Comp: any, componentInfo?: any): any; - __createDom(): any; - __createVirtualDom(schema: any, self: any, parentInfo: INodeInfo, idx: string | number): any; - __createLoopVirtualDom(schema: any, self: any, parentInfo: INodeInfo, idx: number | string): any; - __parseProps(props: any, self: any, path: string, info: INodeInfo): any; - __initDebug?(): void; - __debug(...args: any[]): void; - __renderContextProvider(customProps?: object, children?: any): any; - __renderContextConsumer(children: any): any; - __renderContent(children: any): any; - __checkSchema(schema: IPublicTypeNodeSchema | undefined, extraComponents?: string | string[]): any; - __renderComp(Comp: any, ctxProps: object): any; - $(filedId: string, instance?: any): any; - }; - -export interface IBaseRenderComponent { - new( - props: IBaseRendererProps, - context: any - ): IBaseRendererInstance; -} - -export interface IRenderComponent { - displayName: string; - defaultProps: IRendererProps; - findDOMNode: (...args: any) => any; - - new(props: IRendererProps, context: any): IGeneralComponent & { - [x: string]: any; - __getRef: (ref: any) => void; - componentDidMount(): Promise | void; - componentDidUpdate(): Promise | void; - componentWillUnmount(): Promise | void; - componentDidCatch(e: any): Promise | void; - shouldComponentUpdate(nextProps: IRendererProps): boolean; - isValidComponent(SetComponent: any): any; - createElement(SetComponent: any, props: any, children?: any): any; - getNotFoundComponent(): any; - getFaultComponent(): any; - }; -} diff --git a/packages/renderer-core/src/utils/common.ts b/packages/renderer-core/src/utils/common.ts deleted file mode 100644 index 0462d358a..000000000 --- a/packages/renderer-core/src/utils/common.ts +++ /dev/null @@ -1,370 +0,0 @@ -/* eslint-disable no-console */ -/* eslint-disable no-new-func */ -import logger from './logger'; -import { IPublicTypeRootSchema, IPublicTypeNodeSchema, IPublicTypeJSSlot } from '@alilc/lowcode-types'; -import { isI18nData, isJSExpression } from '@alilc/lowcode-utils'; -import { isEmpty } from 'lodash'; -import IntlMessageFormat from 'intl-messageformat'; -import pkg from '../../package.json'; - -(window as any).sdkVersion = pkg.version; - -export { pick, isEqualWith as deepEqual, cloneDeep as clone, isEmpty, throttle, debounce } from 'lodash'; - -const EXPRESSION_TYPE = { - JSEXPRESSION: 'JSExpression', - JSFUNCTION: 'JSFunction', - JSSLOT: 'JSSlot', - JSBLOCK: 'JSBlock', - I18N: 'i18n', -}; - -/** - * check if schema passed in is a valid schema - * @name isSchema - * @returns boolean - */ -export function isSchema(schema: any): schema is IPublicTypeNodeSchema { - if (isEmpty(schema)) { - return false; - } - // Leaf and Slot should be valid - if (schema.componentName === 'Leaf' || schema.componentName === 'Slot') { - return true; - } - if (Array.isArray(schema)) { - return schema.every((item) => isSchema(item)); - } - // check if props is valid - const isValidProps = (props: any) => { - if (!props) { - return false; - } - if (isJSExpression(props)) { - return true; - } - return (typeof schema.props === 'object' && !Array.isArray(props)); - }; - return !!(schema.componentName && isValidProps(schema.props)); -} - -/** - * check if schema passed in is a container type, including : Component Block Page - * @param schema - * @returns boolean - */ -export function isFileSchema(schema: IPublicTypeNodeSchema): schema is IPublicTypeRootSchema { - if (!isSchema(schema)) { - return false; - } - return ['Page', 'Block', 'Component'].includes(schema.componentName); -} - -/** - * check if current page is nested within another page with same host - * @returns boolean - */ -export function inSameDomain() { - try { - return window.parent !== window && window.parent.location.host === window.location.host; - } catch (e) { - return false; - } -} - -/** - * get css styled name from schema`s fileName - * FileName -> lce-file-name - * @returns string - */ -export function getFileCssName(fileName: string) { - if (!fileName) { - return; - } - const name = fileName.replace(/([A-Z])/g, '-$1').toLowerCase(); - return (`lce-${name}`) - .split('-') - .filter((p) => !!p) - .join('-'); -} - -/** - * check if a object is type of JSSlot - * @returns string - */ -export function isJSSlot(obj: any): obj is IPublicTypeJSSlot { - if (!obj) { - return false; - } - if (typeof obj !== 'object' || Array.isArray(obj)) { - return false; - } - - // Compatible with the old protocol JSBlock - return [EXPRESSION_TYPE.JSSLOT, EXPRESSION_TYPE.JSBLOCK].includes(obj.type); -} - -/** - * get value from an object - * @returns string - */ -export function getValue(obj: any, path: string, defaultValue = {}) { - // array is not valid type, return default value - if (Array.isArray(obj)) { - return defaultValue; - } - - if (isEmpty(obj) || typeof obj !== 'object') { - return defaultValue; - } - - const res = path.split('.').reduce((pre, cur) => { - return pre && pre[cur]; - }, obj); - if (res === undefined) { - return defaultValue; - } - return res; -} - -/** - * 用于处理国际化字符串 - * @param {*} key 语料标识 - * @param {*} values 字符串模版变量 - * @param {*} locale 国际化标识,例如 zh-CN、en-US - * @param {*} messages 国际化语言包 - */ -export function getI18n(key: string, values = {}, locale = 'zh-CN', messages: Record = {}) { - if (!messages || !messages[locale] || !messages[locale][key]) { - return ''; - } - const formater = new IntlMessageFormat(messages[locale][key], locale); - return formater.format(values); -} - -/** - * 判断当前组件是否能够设置ref - * @param {*} Comp 需要判断的组件 - */ -export function canAcceptsRef(Comp: any) { - const hasSymbol = typeof Symbol === 'function' && Symbol.for; - const REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0; - // eslint-disable-next-line max-len - return Comp?.$$typeof === REACT_FORWARD_REF_TYPE || Comp?.prototype?.isReactComponent || Comp?.prototype?.setState || Comp._forwardRef; -} - -/** - * transform array to a object - * @param arr array to be transformed - * @param key key of array item, which`s value will be used as key in result map - * @param overwrite overwrite existing item in result or not - * @returns object result map - */ -export function transformArrayToMap(arr: any[], key: string, overwrite = true) { - if (isEmpty(arr) || !Array.isArray(arr)) { - return {}; - } - const res: any = {}; - arr.forEach((item) => { - const curKey = item[key]; - if (item[key] === undefined) { - return; - } - if (res[curKey] && !overwrite) { - return; - } - res[curKey] = item; - }); - return res; -} - -/** - * transform string to a function - * @param str function in string form - * @returns funtion - */ -export function transformStringToFunction(str: string) { - if (typeof str !== 'string') { - return str; - } - if (inSameDomain() && (window.parent as any).__newFunc) { - return (window.parent as any).__newFunc(`"use strict"; return ${str}`)(); - } else { - return new Function(`"use strict"; return ${str}`)(); - } -} - -/** - * 对象类型JSExpression,支持省略this - * @param str expression in string form - * @param self scope object - * @returns funtion - */ - -function parseExpression(options: { - str: any; self: any; thisRequired?: boolean; logScope?: string; -}): any; -function parseExpression(str: any, self: any, thisRequired?: boolean): any; -function parseExpression(a: any, b?: any, c = false) { - let str; - let self; - let thisRequired; - let logScope; - if (typeof a === 'object' && b === undefined) { - str = a.str; - self = a.self; - thisRequired = a.thisRequired; - logScope = a.logScope; - } else { - str = a; - self = b; - thisRequired = c; - } - try { - const contextArr = ['"use strict";', 'var __self = arguments[0];']; - contextArr.push('return '); - let tarStr: string; - - tarStr = (str.value || '').trim(); - - // NOTE: use __self replace 'this' in the original function str - // may be wrong in extreme case which contains '__self' already - tarStr = tarStr.replace(/this(\W|$)/g, (_a: any, b: any) => `__self${b}`); - tarStr = contextArr.join('\n') + tarStr; - - // 默认调用顶层窗口的parseObj, 保障new Function的window对象是顶层的window对象 - if (inSameDomain() && (window.parent as any).__newFunc) { - return (window.parent as any).__newFunc(tarStr)(self); - } - const code = `with(${thisRequired ? '{}' : '$scope || {}'}) { ${tarStr} }`; - return new Function('$scope', code)(self); - } catch (err) { - logger.error(`${logScope || ''} parseExpression.error`, err, str, self?.__self ?? self); - return undefined; - } -} - -export { - parseExpression, -}; - -export function parseThisRequiredExpression(str: any, self: any) { - return parseExpression(str, self, true); -} - -/** - * capitalize first letter - * @param word string to be proccessed - * @returns string capitalized string - */ -export function capitalizeFirstLetter(word: string) { - if (!word || !isString(word) || word.length === 0) { - return word; - } - return word[0].toUpperCase() + word.slice(1); -} - -/** - * check str passed in is a string type of not - * @param str obj to be checked - * @returns boolean - */ -export function isString(str: any): boolean { - return {}.toString.call(str) === '[object String]'; -} - -/** - * check if obj is type of variable structure - * @param obj object to be checked - * @returns boolean - */ -export function isVariable(obj: any) { - if (!obj || Array.isArray(obj)) { - return false; - } - return typeof obj === 'object' && obj?.type === 'variable'; -} - -/** - * 将 i18n 结构,降级解释为对 i18n 接口的调用 - * @param i18nInfo object - * @param self context - */ -export function parseI18n(i18nInfo: any, self: any) { - return parseExpression({ - type: EXPRESSION_TYPE.JSEXPRESSION, - value: `this.i18n('${i18nInfo.key}')`, - }, self); -} - -/** - * for each key in targetObj, run fn with the value of the value, and the context paased in. - * @param targetObj object that keys will be for each - * @param fn function that process each item - * @param context - */ -export function forEach(targetObj: any, fn: any, context?: any) { - if (!targetObj || Array.isArray(targetObj) || isString(targetObj) || typeof targetObj !== 'object') { - return; - } - - Object.keys(targetObj).forEach((key) => fn.call(context, targetObj[key], key)); -} - -interface IParseOptions { - thisRequiredInJSE?: boolean; - logScope?: string; -} - -export function parseData(schema: unknown, self: any, options: IParseOptions = {}): any { - if (isJSExpression(schema)) { - return parseExpression({ - str: schema, - self, - thisRequired: options.thisRequiredInJSE, - logScope: options.logScope, - }); - } else if (isI18nData(schema)) { - return parseI18n(schema, self); - } else if (typeof schema === 'string') { - return schema.trim(); - } else if (Array.isArray(schema)) { - return schema.map((item) => parseData(item, self, options)); - } else if (typeof schema === 'function') { - return schema.bind(self); - } else if (typeof schema === 'object') { - // 对于undefined及null直接返回 - if (!schema) { - return schema; - } - const res: any = {}; - forEach(schema, (val: any, key: string) => { - if (key.startsWith('__')) { - return; - } - res[key] = parseData(val, self, options); - }); - return res; - } - return schema; -} - -/** - * process params for using in a url query - * @param obj params to be processed - * @returns string - */ -export function serializeParams(obj: any) { - let result: any = []; - forEach(obj, (val: any, key: any) => { - if (val === null || val === undefined || val === '') { - return; - } - if (typeof val === 'object') { - result.push(`${key}=${encodeURIComponent(JSON.stringify(val))}`); - } else { - result.push(`${key}=${encodeURIComponent(val)}`); - } - }); - return result.join('&'); -} diff --git a/packages/renderer-core/src/utils/data-helper.ts b/packages/renderer-core/src/utils/data-helper.ts deleted file mode 100644 index 41bcb9bfa..000000000 --- a/packages/renderer-core/src/utils/data-helper.ts +++ /dev/null @@ -1,309 +0,0 @@ -/* eslint-disable no-console */ -/* eslint-disable max-len */ -/* eslint-disable object-curly-newline */ -import { isJSFunction } from '@alilc/lowcode-utils'; -import { transformArrayToMap, transformStringToFunction } from './common'; -import { jsonp, request, get, post } from './request'; -import logger from './logger'; -import { DataSource, DataSourceItem, IRendererAppHelper } from '../types'; - -const DS_STATUS = { - INIT: 'init', - LOADING: 'loading', - LOADED: 'loaded', - ERROR: 'error', -}; - -type DataSourceType = 'fetch' | 'jsonp'; - -/** - * do request for standard DataSourceType - * @param {DataSourceType} type type of DataSourceItem - * @param {any} options - */ -export function doRequest(type: DataSourceType, options: any) { - // eslint-disable-next-line prefer-const - let { uri, url, method = 'GET', headers, params, ...otherProps } = options; - otherProps = otherProps || {}; - if (type === 'jsonp') { - return jsonp(uri, params, otherProps); - } - - if (type === 'fetch') { - switch (method.toUpperCase()) { - case 'GET': - return get(uri, params, headers, otherProps); - case 'POST': - return post(uri, params, headers, otherProps); - default: - return request(uri, method, params, headers, otherProps); - } - } - - logger.log(`Engine default dataSource does not support type:[${type}] dataSource request!`, options); -} - -// TODO: according to protocol, we should implement errorHandler/shouldFetch/willFetch/requestHandler and isSync controll. -export class DataHelper { - /** - * host object that will be "this" object when excuting dataHandler - * - * @type {*} - * @memberof DataHelper - */ - host: any; - - /** - * data source config - * - * @type {DataSource} - * @memberof DataHelper - */ - config: DataSource; - - /** - * a parser function which will be called to process config data - * which eventually will call common/utils.processData() to process data - * (originalConfig) => parsedConfig - * @type {*} - * @memberof DataHelper - */ - parser: any; - - /** - * config.list - * - * @type {any[]} - * @memberof DataHelper - */ - ajaxList: any[]; - - ajaxMap: any; - - dataSourceMap: any; - - appHelper: IRendererAppHelper; - - constructor(comp: any, config: DataSource, appHelper: IRendererAppHelper, parser: any) { - this.host = comp; - this.config = config || {}; - this.parser = parser; - this.ajaxList = config?.list || []; - this.ajaxMap = transformArrayToMap(this.ajaxList, 'id'); - this.dataSourceMap = this.generateDataSourceMap(); - this.appHelper = appHelper; - } - - // 更新config,只会更新配置,状态保存; - updateConfig(config = {}) { - this.config = config as DataSource; - this.ajaxList = (config as DataSource)?.list || []; - const ajaxMap: any = transformArrayToMap(this.ajaxList, 'id'); - // 删除已经移除的接口 - Object.keys(this.ajaxMap).forEach((key) => { - if (!ajaxMap[key]) { - delete this.dataSourceMap[key]; - } - }); - this.ajaxMap = ajaxMap; - // 添加未加入到dataSourceMap中的接口 - this.ajaxList.forEach((item) => { - if (!this.dataSourceMap[item.id]) { - this.dataSourceMap[item.id] = { - status: DS_STATUS.INIT, - load: (...args: any) => { - // @ts-ignore - return this.getDataSource(item.id, ...args); - }, - }; - } - }); - return this.dataSourceMap; - } - - generateDataSourceMap() { - const res: any = {}; - this.ajaxList.forEach((item) => { - res[item.id] = { - status: DS_STATUS.INIT, - load: (...args: any) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - return this.getDataSource(item.id, ...args); - }, - }; - }); - return res; - } - - updateDataSourceMap(id: string, data: any, error: any) { - this.dataSourceMap[id].error = error || undefined; - this.dataSourceMap[id].data = data; - this.dataSourceMap[id].status = error ? DS_STATUS.ERROR : DS_STATUS.LOADED; - } - - /** - * get all dataSourceItems which marked as isInit === true - * @private - * @returns - * @memberof DataHelper - */ - getInitDataSourseConfigs() { - const initConfigs = this.parser(this.ajaxList).filter((item: DataSourceItem) => { - // according to [spec](https://lowcode-engine.cn/lowcode), isInit should be boolean true to be working - if (item.isInit === true) { - this.dataSourceMap[item.id].status = DS_STATUS.LOADING; - return true; - } - return false; - }); - return initConfigs; - } - - /** - * process all dataSourceItems which marked as isInit === true, and get dataSource request results. - * @public - * @returns - * @memberof DataHelper - */ - getInitData() { - const initSyncData = this.getInitDataSourseConfigs(); - // 所有 datasource 的 datahandler - return this.asyncDataHandler(initSyncData).then((res) => { - const { dataHandler } = this.config; - return this.handleData(null, dataHandler, res, null); - }); - } - - getDataSource(id: string, params: any, otherOptions: any, callback: any) { - const req = this.parser(this.ajaxMap[id]); - const options = req.options || {}; - let callbackFn = callback; - let otherOptionsObj = otherOptions; - if (typeof otherOptions === 'function') { - callbackFn = otherOptions; - otherOptionsObj = {}; - } - const { headers, ...otherProps } = otherOptionsObj || {}; - if (!req) { - logger.warn(`getDataSource API named ${id} not exist`); - return; - } - - return this.asyncDataHandler([ - { - ...req, - options: { - ...options, - // 支持参数为array的情况,当参数为array时,不做参数合并 - params: - Array.isArray(options.params) || Array.isArray(params) - ? params || options.params - : { - ...options.params, - ...params, - }, - headers: { - ...options.headers, - ...headers, - }, - ...otherProps, - }, - }, - ]) - .then((res: any) => { - try { - callbackFn && callbackFn(res && res[id]); - } catch (e) { - logger.error('load请求回调函数报错', e); - } - return res && res[id]; - }) - .catch((err) => { - try { - callbackFn && callbackFn(null, err); - } catch (e) { - logger.error('load请求回调函数报错', e); - } - return err; - }); - } - - asyncDataHandler(asyncDataList: any[]) { - return new Promise((resolve, reject) => { - const allReq: any[] = []; - asyncDataList.forEach((req) => { - const { id, type } = req; - // TODO: need refactoring to remove 'legao' related logic - if (!id || !type || type === 'legao') { - return; - } - allReq.push(req); - }); - - if (allReq.length === 0) { - resolve({}); - } - const res: any = {}; - Promise.all( - allReq.map((item: any) => { - return new Promise((innerResolve) => { - const { type, id, dataHandler, options } = item; - - const fetchHandler = (data: any, error: any) => { - res[id] = this.handleData(id, dataHandler, data, error); - this.updateDataSourceMap(id, res[id], error); - innerResolve({}); - }; - - const doFetch = (innerType: string, innerOptions: any) => { - doRequest(innerType as any, innerOptions) - ?.then((data: any) => { - fetchHandler(data, undefined); - }) - .catch((err: Error) => { - fetchHandler(undefined, err); - }); - }; - - this.dataSourceMap[id].status = DS_STATUS.LOADING; - doFetch(type, options); - }); - }), - ).then(() => { - resolve(res); - }).catch((e) => { - reject(e); - }); - }); - } - - /** - * process data using dataHandler - * - * @param {(string | null)} id request id, will be used in error message, can be null - * @param {*} dataHandler - * @param {*} data - * @param {*} error - * @returns - * @memberof DataHelper - */ - handleData(id: string | null, dataHandler: any, data: any, error: any) { - let dataHandlerFun = dataHandler; - if (isJSFunction(dataHandler)) { - dataHandlerFun = transformStringToFunction(dataHandler.value); - } - if (!dataHandlerFun || typeof dataHandlerFun !== 'function') { - return data; - } - try { - return dataHandlerFun.call(this.host, data, error); - } catch (e) { - if (id) { - logger.error(`[${id}]单个请求数据处理函数运行出错`, e); - } else { - logger.error('请求数据处理函数运行出错', e); - } - } - } -} diff --git a/packages/renderer-core/src/utils/index.ts b/packages/renderer-core/src/utils/index.ts deleted file mode 100644 index b22e23064..000000000 --- a/packages/renderer-core/src/utils/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './common'; -export * from './data-helper'; -export * from './request'; diff --git a/packages/renderer-core/src/utils/is-use-loop.ts b/packages/renderer-core/src/utils/is-use-loop.ts deleted file mode 100644 index b6d67a802..000000000 --- a/packages/renderer-core/src/utils/is-use-loop.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { IPublicTypeJSExpression } from '@alilc/lowcode-types'; -import { isJSExpression } from '@alilc/lowcode-utils'; - -// 1.渲染模式下,loop 是数组,则按照数组长度渲染组件 -// 2.设计模式下,loop 需要长度大于 0,按照循环模式渲染,防止无法设计的情况 -export default function isUseLoop(loop: null | any[] | IPublicTypeJSExpression, isDesignMode: boolean): boolean { - if (isJSExpression(loop)) { - return true; - } - - if (!isDesignMode) { - return true; - } - - if (!Array.isArray(loop)) { - return false; - } - - return loop.length > 0; -} diff --git a/packages/renderer-core/src/utils/logger.ts b/packages/renderer-core/src/utils/logger.ts deleted file mode 100644 index 5b7a276eb..000000000 --- a/packages/renderer-core/src/utils/logger.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Logger } from '@alilc/lowcode-utils'; - -export default new Logger({ level: 'warn', bizName: 'renderer' }); \ No newline at end of file diff --git a/packages/renderer-core/src/utils/request.ts b/packages/renderer-core/src/utils/request.ts deleted file mode 100644 index dde5ca87e..000000000 --- a/packages/renderer-core/src/utils/request.ts +++ /dev/null @@ -1,198 +0,0 @@ -import 'whatwg-fetch'; -import fetchJsonp from 'fetch-jsonp'; -import { serializeParams } from '.'; - -/** - * this is a private method, export for testing purposes only. - * - * @export - * @param {*} dataAPI - * @param {*} params - * @returns - */ -export function buildUrl(dataAPI: any, params: any) { - const paramStr = serializeParams(params); - if (paramStr) { - return dataAPI.indexOf('?') > 0 ? `${dataAPI}&${paramStr}` : `${dataAPI}?${paramStr}`; - } - return dataAPI; -} - -/** - * do Get request - * - * @export - * @param {*} dataAPI - * @param {*} [params={}] - * @param {*} [headers={}] - * @param {*} [otherProps={}] - * @returns - */ - export function get(dataAPI: any, params = {}, headers = {}, otherProps = {}) { - const processedHeaders = { - Accept: 'application/json', - ...headers, - }; - const url = buildUrl(dataAPI, params); - return request(url, 'GET', null, processedHeaders, otherProps); -} - -/** - * do Post request - * - * @export - * @param {*} dataAPI - * @param {*} [params={}] - * @param {*} [headers={}] - * @param {*} [otherProps={}] - * @returns - */ -export function post(dataAPI: any, params = {}, headers: any = {}, otherProps = {}) { - const processedHeaders = { - Accept: 'application/json', - 'Content-Type': 'application/x-www-form-urlencoded', - ...headers, - }; - const body = processedHeaders['Content-Type'].indexOf('application/json') > -1 || Array.isArray(params) - ? JSON.stringify(params) - : serializeParams(params); - - return request( - dataAPI, - 'POST', - body, - processedHeaders, - otherProps, - ); -} - -/** - * do request - * - * @export - * @param {*} dataAPI - * @param {string} [method='GET'] - * @param {*} data - * @param {*} [headers={}] - * @param {*} [otherProps={}] - * @returns - */ -export function request(dataAPI: any, method = 'GET', data: any, headers = {}, otherProps: any = {}) { - let processedHeaders = headers || {}; - let payload = data; - if (method === 'PUT' || method === 'DELETE') { - processedHeaders = { - Accept: 'application/json', - 'Content-Type': 'application/json', - ...processedHeaders, - }; - payload = JSON.stringify(payload || {}); - } - return new Promise((resolve, reject) => { - if (otherProps.timeout) { - setTimeout(() => { - reject(new Error('timeout')); - }, otherProps.timeout); - } - fetch(dataAPI, { - method, - credentials: 'include', - headers: processedHeaders, - body: payload, - ...otherProps, - }) - .then((response) => { - switch (response.status) { - case 200: - case 201: - case 202: - return response.json(); - case 204: - if (method === 'DELETE') { - return { - success: true, - }; - } else { - return { - __success: false, - code: response.status, - }; - } - case 400: - case 401: - case 403: - case 404: - case 406: - case 410: - case 422: - case 500: - return response - .json() - .then((res) => { - return { - __success: false, - code: response.status, - data: res, - }; - }) - .catch(() => { - return { - __success: false, - code: response.status, - }; - }); - default: - } - return null; - }) - .then((json) => { - if (!json) { - reject(json); - return; - } - if (json.__success !== false) { - resolve(json); - } else { - // eslint-disable-next-line no-param-reassign - delete json.__success; - reject(json); - } - }) - .catch((err) => { - reject(err); - }); - }); -} - -/** - * do jsonp request - * - * @export - * @param {*} dataAPI - * @param {*} [params={}] - * @param {*} [otherProps={}] - * @returns - */ -export function jsonp(dataAPI: any, params = {}, otherProps = {}) { - return new Promise((resolve, reject) => { - const processedOtherProps = { - timeout: 5000, - ...otherProps, - }; - const url = buildUrl(dataAPI, params); - fetchJsonp(url, processedOtherProps) - .then((response) => { - response.json(); - }) - .then((json) => { - if (json) { - resolve(json); - } else { - reject(); - } - }) - .catch((err) => { - reject(err); - }); - }); -} diff --git a/packages/renderer-core/tests/adapter/adapter.test.ts b/packages/renderer-core/tests/adapter/adapter.test.ts deleted file mode 100644 index 57d92d1d4..000000000 --- a/packages/renderer-core/tests/adapter/adapter.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -// @ts-nocheck -import adapter, { Env } from '../../src/adapter'; - - - -describe('test src/adapter ', () => { - - it('adapter basic use works', () => { - expect(adapter).toBeTruthy(); - - }); - - it('isValidRuntime works', () => { - expect(adapter.isValidRuntime([] as any)).toBeFalsy(); - - expect(adapter.isValidRuntime('' as any)).toBeFalsy(); - - let invalidRuntime = {}; - expect(() => adapter.isValidRuntime(invalidRuntime as any)).toThrowError(/Component/); - invalidRuntime = { - Component: {}, - }; - expect(() => adapter.isValidRuntime(invalidRuntime as any)).toThrowError(/PureComponent/); - invalidRuntime = { - Component: {}, - PureComponent: {}, - }; - expect(() => adapter.isValidRuntime(invalidRuntime as any)).toThrowError(/createElement/); - invalidRuntime = { - Component: {}, - PureComponent: {}, - createElement: {}, - }; - expect(() => adapter.isValidRuntime(invalidRuntime as any)).toThrowError(/createContext/); - invalidRuntime = { - Component: {}, - PureComponent: {}, - createElement: {}, - createContext: {}, - }; - expect(() => adapter.isValidRuntime(invalidRuntime as any)).toThrowError(/forwardRef/); - invalidRuntime = { - Component: {}, - PureComponent: {}, - createElement: {}, - createContext: {}, - forwardRef: {}, - }; - expect(() => adapter.isValidRuntime(invalidRuntime as any)).toThrowError(/findDOMNode/); - const validRuntime = { - Component: {}, - PureComponent: {}, - createElement: {}, - createContext: {}, - forwardRef: {}, - findDOMNode: {}, - }; - - expect(adapter.isValidRuntime(validRuntime as any)).toBeTruthy(); - }); - - it('setRuntime/getRuntime works', () => { - const validRuntime = { - Component: {}, - PureComponent: {}, - createElement: {}, - createContext: {}, - forwardRef: {}, - findDOMNode: {}, - }; - - adapter.setRuntime(validRuntime as any); - expect(adapter.getRuntime()).toBe(validRuntime); - - // won`t work when invalid runtime paased in. - adapter.setRuntime([] as any); - expect(adapter.getRuntime()).toBe(validRuntime); - - - }); - - it('setEnv/.env/isReact works', () => { - adapter.setEnv(Env.React); - expect(adapter.env).toBe(Env.React); - expect(adapter.isReact()).toBeTruthy(); - }); - - it('setRenderers/getRenderers works', () => { - const mockRenderers = { BaseRenderer: {} as IBaseRenderComponent}; - adapter.setRenderers(mockRenderers); - expect(adapter.getRenderers()).toBe(mockRenderers); - adapter.setRenderers(undefined); - expect(adapter.getRenderers()).toStrictEqual({}); - }); - - it('setConfigProvider/getConfigProvider works', () => { - const mockConfigProvider = { a: 111 }; - adapter.setConfigProvider(mockConfigProvider); - expect(adapter.getConfigProvider()).toBe(mockConfigProvider); - }); -}); \ No newline at end of file diff --git a/packages/renderer-core/tests/fixtures/schema/basic.ts b/packages/renderer-core/tests/fixtures/schema/basic.ts deleted file mode 100644 index cc587163a..000000000 --- a/packages/renderer-core/tests/fixtures/schema/basic.ts +++ /dev/null @@ -1,567 +0,0 @@ -export default { - 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 }', - }, - closeDialog: { - type: 'JSFunction', - value: 'function() {\n this.setState({\n isShowDialog: false\n })\n }', - }, - }, - 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: 'Button', - id: 'node_dockcy8n9xe10', - props: { - type: 'normal', - style: { - margin: '0 5px 0 5px', - }, - htmlType: 'reset', - children: '清空', - }, - }, - ], - }, - ], - }, - ], - }, - { - componentName: 'Box', - id: 'node_dockcy8n9xe1f', - props: { - style: { - backgroundColor: '#ffffff', - paddingBottom: '24px', - display: 'flex', - flexDirection: 'row', - justifyContent: 'flex-end', - }, - }, - children: [ - { - componentName: 'Button', - id: 'node_dockd5nrh9p4', - props: { - type: 'primary', - size: 'medium', - htmlType: 'button', - component: 'button', - children: '新建配置', - style: {}, - __events: [ - { - type: 'componentEvent', - name: 'onClick', - relatedEventName: 'onClick', - }, - ], - onClick: { - type: 'JSFunction', - value: 'function(){ this.onClick() }', - }, - }, - }, - ], - }, - { - componentName: 'Box', - id: 'node_dockd5nrh9p5', - props: {}, - children: [ - { - componentName: 'Table', - id: 'node_dockjielosj1', - props: { - showMiniPager: true, - showActionBar: true, - actionBar: [ - { - title: '新增', - type: 'primary', - }, - { - title: '编辑', - }, - ], - columns: [ - { - dataKey: 'name', - width: 200, - align: 'center', - title: '姓名', - editType: 'text', - }, - { - dataKey: 'age', - width: 200, - align: 'center', - title: '年龄', - }, - { - dataKey: 'email', - width: 200, - align: 'center', - title: '邮箱', - }, - ], - data: [ - { - name: '王小', - id: '1', - age: 15000, - email: 'aaa@abc.com', - }, - { - name: '王中', - id: '2', - age: 25000, - email: 'bbb@abc.com', - }, - { - name: '王大', - id: '3', - age: 35000, - email: 'ccc@abc.com', - }, - ], - actionTitle: '操作', - actionWidth: 180, - actionType: 'link', - actionFixed: 'right', - actionHidden: false, - maxWebShownActionCount: 2, - actionColumn: [ - { - title: '编辑', - callback: { - type: 'JSFunction', - value: '(rowData, action, table) => {\n return table.editRow(rowData).then((row) => {\n console.log(row);\n });\n }', - }, - device: [ - 'desktop', - ], - }, - { - title: '保存', - callback: { - type: 'JSFunction', - value: '(rowData, action, table) => { \nreturn table.saveRow(rowData).then((row) => { \nconsole.log(row); \n}); \n}', - }, - mode: 'EDIT', - }, - ], - }, - }, - { - componentName: 'Box', - id: 'node_dockd5nrh9pg', - props: { - style: { - display: 'flex', - flexDirection: 'row', - justifyContent: 'flex-end', - }, - }, - children: [ - { - componentName: 'Pagination', - id: 'node_dockd5nrh9pf', - props: { - prefix: 'next-', - type: 'normal', - shape: 'normal', - size: 'medium', - defaultCurrent: 1, - total: 100, - pageShowCount: 5, - pageSize: 10, - pageSizePosition: 'start', - showJump: true, - style: {}, - }, - }, - ], - }, - ], - }, - ], - }, - { - 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', - }, - title: '标题', - events: [], - __events: [ - { - type: 'componentEvent', - name: 'onCancel', - relatedEventName: 'closeDialog', - }, - { - type: 'componentEvent', - name: 'onClose', - relatedEventName: 'closeDialog', - }, - { - type: 'componentEvent', - name: 'onOk', - relatedEventName: 'testFunc', - }, - ], - onCancel: { - type: 'JSFunction', - value: 'function(){ this.closeDialog() }', - }, - onClose: { - type: 'JSFunction', - value: 'function(){ this.closeDialog() }', - }, - onOk: { - type: 'JSFunction', - value: 'function(){ this.testFunc() }', - }, - }, - children: [ - { - componentName: 'Form', - id: 'node_dockd5nrh9pi', - props: { - inline: false, - labelAlign: 'top', - labelTextAlign: 'right', - size: 'medium', - }, - children: [ - { - componentName: 'Form.Item', - id: 'node_dockd5nrh9pj', - props: { - style: { - marginBottom: '0', - minWidth: '200px', - minHeight: '28px', - }, - label: '商品类目', - }, - children: [ - { - componentName: 'Select', - id: 'node_dockd5nrh9pk', - props: { - mode: 'single', - hasArrow: true, - cacheValue: true, - }, - }, - ], - }, - { - componentName: 'Form.Item', - id: 'node_dockd5nrh9pl', - props: { - style: { - marginBottom: '0', - minWidth: '200px', - minHeight: '28px', - }, - label: '商品类目', - }, - children: [ - { - componentName: 'Select', - id: 'node_dockd5nrh9pm', - props: { - mode: 'single', - hasArrow: true, - cacheValue: true, - }, - }, - ], - }, - { - componentName: 'Form.Item', - id: 'node_dockd5nrh9pn', - props: { - style: { - marginBottom: '0', - minWidth: '200px', - minHeight: '28px', - }, - label: '商品类目', - asterisk: true, - }, - children: [ - { - componentName: 'Select', - id: 'node_dockd5nrh9po', - props: { - mode: 'single', - hasArrow: true, - cacheValue: true, - }, - }, - ], - }, - { - componentName: 'Form.Item', - id: 'node_dockd5nrh9pp', - props: { - style: { - marginBottom: '0', - minWidth: '200px', - minHeight: '28px', - }, - label: '商品类目', - }, - children: [ - { - componentName: 'Input', - id: 'node_dockd5nrh9pr', - props: { - hasBorder: true, - size: 'medium', - autoComplete: 'off', - }, - }, - ], - }, - ], - }, - ], - }, - { - componentName: 'ErrorComponent', - id: 'node_dockd5nrh9pr', - props: { - name: 'error', - }, - }, - ], -}; diff --git a/packages/renderer-core/tests/fixtures/unhandled-rejection.ts b/packages/renderer-core/tests/fixtures/unhandled-rejection.ts deleted file mode 100644 index d2ab20b0b..000000000 --- a/packages/renderer-core/tests/fixtures/unhandled-rejection.ts +++ /dev/null @@ -1,7 +0,0 @@ -if (!process.env.LISTENING_TO_UNHANDLED_REJECTION) { - process.on('unhandledRejection', reason => { - throw reason; - }); - // Avoid memory leak by adding too many listeners - process.env.LISTENING_TO_UNHANDLED_REJECTION = 'true'; -} diff --git a/packages/renderer-core/tests/hoc/__snapshots__/leaf.test.tsx.snap b/packages/renderer-core/tests/hoc/__snapshots__/leaf.test.tsx.snap deleted file mode 100644 index e0ddfa8c2..000000000 --- a/packages/renderer-core/tests/hoc/__snapshots__/leaf.test.tsx.snap +++ /dev/null @@ -1,148 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`children this.props.children is array 1`] = ` -

-
- content -
-
- content -
-
-`; - -exports[`lifecycle leaf change and make componentWillReceiveProps 1`] = ` -
-
- content new leaf -
-
-`; - -exports[`lifecycle props change and make componentWillReceiveProps 1`] = ` -
-
- content -
-
-`; - -exports[`lifecycle props change and make componentWillReceiveProps 2`] = ` -
-
- content 123 -
-
-`; - -exports[`lifecycle props change and make componentWillReceiveProps 3`] = ` -
-
- content 123 -
-
-`; - -exports[`mini unit render leaf has a loop, render from parent 1`] = ` -
- this is a new children -
-`; - -exports[`mini unit render make text props change 1`] = ` -
-
- content -
-
-`; - -exports[`mini unit render make text props change 2`] = ` -
-`; - -exports[`mini unit render parent is a mock leaf 1`] = ` -
-
- new content to mock -
-
-`; - -exports[`mini unit render props has new children 1`] = ` -
- children 01 - children 02 -
-`; - -exports[`onChildrenChange children is array string 1`] = ` -
- onChildrenChange content 01 - onChildrenChange content 02 -
-`; - -exports[`onPropChange change textNode [key:___condition___] props, but not hidden component 1`] = ` -
-
- content -
-
-`; - -exports[`onPropChange change textNode [key:___condition___] props, hide textNode component 1`] = `
`; - -exports[`onPropChange change textNode [key:content], content in this.props but not in leaf.export result 1`] = ` -
-
- content -
-
-`; - -exports[`onPropChange change textNode [key:content], content in this.props but not in leaf.export result 2`] = ` -
-
-
-`; - -exports[`onVisibleChange visible is false 1`] = `
`; - -exports[`onVisibleChange visible is true 1`] = ` -
-
- content -
-
-`; diff --git a/packages/renderer-core/tests/hoc/leaf.test.tsx b/packages/renderer-core/tests/hoc/leaf.test.tsx deleted file mode 100644 index c21a10be9..000000000 --- a/packages/renderer-core/tests/hoc/leaf.test.tsx +++ /dev/null @@ -1,604 +0,0 @@ -import renderer from 'react-test-renderer'; -import React from 'react'; -import { createElement } from 'react'; -import '../utils/react-env-init'; -import { leafWrapper } from '../../src/hoc/leaf'; -import components from '../utils/components'; -import Node from '../utils/node'; -import { parseData } from '../../src/utils'; - -let rerenderCount = 0; - -const nodeMap = new Map(); - -const makeSnapshot = (component) => { - let tree = component.toJSON(); - expect(tree).toMatchSnapshot(); -} - -const baseRenderer: any = { - __debug () {}, - __getComponentProps (schema: any) { - return schema.props; - }, - __getSchemaChildrenVirtualDom (schema: any) { - return schema.children; - }, - context: { - engine: { - createElement, - } - }, - props: { - __host: {}, - getNode: (id) => nodeMap.get(id), - __container: { - rerender: () => { - rerenderCount = 1 + rerenderCount; - }, - autoRepaintNode: true, - }, - documentId: '01' - }, - __parseData (data, scope) { - return parseData(data, scope, {}); - } -} - -let Div, DivNode, Text, TextNode, component, textSchema, divSchema; -let id = 0; - -beforeEach(() => { - textSchema = { - id: 'text' + id, - props: { - content: 'content' - }, - }; - - divSchema = { - id: 'div' + id, - }; - - id++; - - Div = leafWrapper(components.Div as any, { - schema: divSchema, - baseRenderer, - componentInfo: {}, - scope: {}, - }); - - DivNode = new Node(divSchema); - TextNode = new Node(textSchema); - - nodeMap.set(divSchema.id, DivNode); - nodeMap.set(textSchema.id, TextNode); - - Text = leafWrapper(components.Text as any, { - schema: textSchema, - baseRenderer, - componentInfo: {}, - scope: {}, - }); - - component = renderer.create( -
- -
- ); -}); - -afterEach(() => { - component.unmount(component); -}); - -describe('onPropChange', () => { - it('change textNode [key:content] props', () => { - TextNode.emitPropChange({ - key: 'content', - newValue: 'new content', - } as any); - - const root = component.root; - expect(root.findByType(components.Text).props.content).toEqual('new content') - }); - - it('change textNode [key:___condition___] props, hide textNode component', () => { - // mock leaf?.export result - TextNode.schema.condition = false; - TextNode.emitPropChange({ - key: '___condition___', - newValue: false, - } as any); - - makeSnapshot(component); - }); - - it('change textNode [key:___condition___] props, but not hidden component', () => { - TextNode.schema.condition = true; - TextNode.emitPropChange({ - key: '___condition___', - newValue: false, - } as any); - - makeSnapshot(component); - }); - - it('change textNode [key:content], content in this.props but not in leaf.export result', () => { - makeSnapshot(component); - - delete TextNode.schema.props.content; - TextNode.emitPropChange({ - key: 'content', - newValue: null, - } as any, true); - - makeSnapshot(component); - - const root = component.root; - - const TextInst = root.findByType(components.Text); - - expect(TextInst.props.content).toBeNull(); - }); - - it('change textNode [key:___loop___], make rerender', () => { - expect(leafWrapper(components.Text as any, { - schema: textSchema, - baseRenderer, - componentInfo: {}, - scope: {}, - })).toEqual(Text); - - const nextRerenderCount = rerenderCount + 1; - - TextNode.emitPropChange({ - key: '___loop___', - newValue: 'new content', - } as any); - - expect(rerenderCount).toBe(nextRerenderCount); - expect(leafWrapper(components.Text as any, { - schema: textSchema, - baseRenderer, - componentInfo: {}, - scope: {}, - })).not.toEqual(Text); - }); -}); - -describe('lifecycle', () => { - it('props change and make componentWillReceiveProps', () => { - makeSnapshot(component); - - // 没有 __tag 标识 - component.update(( -
- -
- )); - - makeSnapshot(component); - - // 有 __tag 标识 - component.update(( -
- -
- )); - - makeSnapshot(component); - }); - - it('leaf change and make componentWillReceiveProps', () => { - const newTextNodeLeaf = new Node(textSchema); - nodeMap.set(textSchema.id, newTextNodeLeaf); - component.update(( -
- -
- )); - - newTextNodeLeaf.emitPropChange({ - key: 'content', - newValue: 'content new leaf', - }); - - makeSnapshot(component); - }); -}); - -describe('mini unit render', () => { - let miniRenderSchema, MiniRenderDiv, MiniRenderDivNode; - beforeEach(() => { - miniRenderSchema = { - id: 'miniDiv' + id, - }; - - MiniRenderDiv = leafWrapper(components.MiniRenderDiv as any, { - schema: miniRenderSchema, - baseRenderer, - componentInfo: {}, - scope: {}, - }); - - MiniRenderDivNode = new Node(miniRenderSchema, { - componentMeta: { - isMinimalRenderUnit: true, - }, - }); - - TextNode = new Node(textSchema, { - parent: MiniRenderDivNode, - }); - - nodeMap.set(miniRenderSchema.id, MiniRenderDivNode); - nodeMap.set(textSchema.id, TextNode); - - component = renderer.create( - - - - ); - }) - - it('make text props change', () => { - if (!MiniRenderDivNode.schema.props) { - MiniRenderDivNode.schema.props = {}; - } - MiniRenderDivNode.schema.props['newPropKey'] = 'newPropValue'; - - makeSnapshot(component); - - const inst = component.root; - - const TextInst = inst.findByType(Text).children[0]; - - TextNode.emitPropChange({ - key: 'content', - newValue: 'new content', - } as any); - - expect((TextInst as any)?._fiber.stateNode.renderUnitInfo).toEqual({ - singleRender: false, - minimalUnitId: 'miniDiv' + id, - minimalUnitName: undefined, - }); - - makeSnapshot(component); - }); - - it('dont render mini render component', () => { - const TextNode = new Node(textSchema, { - parent: new Node({ - id: 'random', - }, { - componentMeta: { - isMinimalRenderUnit: true, - }, - }), - }); - - nodeMap.set(textSchema.id, TextNode); - - renderer.create( -
- -
- ); - - const nextCount = rerenderCount + 1; - - TextNode.emitPropChange({ - key: 'content', - newValue: 'new content', - } as any); - - expect(rerenderCount).toBe(nextCount); - }); - - it('leaf is a mock function', () => { - const TextNode = new Node(textSchema, { - parent: { - isEmpty: () => false, - } - }); - - renderer.create( -
- -
- ); - - TextNode.emitPropChange({ - key: 'content', - newValue: 'new content', - } as any); - }); - - it('change component leaf isRoot is true', () => { - const TextNode = new Node(textSchema, { - isRoot: true, - isRootNode: true, - }); - - nodeMap.set(textSchema.id, TextNode); - - const component = renderer.create( - - ); - - const inst = component.root; - - TextNode.emitPropChange({ - key: 'content', - newValue: 'new content', - } as any); - - expect((inst.children[0] as any)?._fiber.stateNode.renderUnitInfo).toEqual({ - singleRender: true, - }); - }); - - it('change component leaf parent isRoot is true', () => { - const TextNode = new Node(textSchema, { - parent: new Node({ - id: 'first-parent', - }, { - componentMeta: { - isMinimalRenderUnit: true, - }, - parent: new Node({ - id: 'rootId', - }, { - isRoot: true, - isRootNode: true - }), - }) - }); - - nodeMap.set(textSchema.id, TextNode); - - const component = renderer.create( - - ); - - const inst = component.root; - - TextNode.emitPropChange({ - key: 'content', - newValue: 'new content', - } as any); - - expect((inst.children[0] as any)?._fiber.stateNode.renderUnitInfo).toEqual({ - singleRender: false, - minimalUnitId: 'first-parent', - minimalUnitName: undefined, - }); - }); - - it('parent is a mock leaf', () => { - const MiniRenderDivNode = { - isMock: true, - }; - - const component = renderer.create( - - - - ); - - TextNode.emitPropChange({ - key: 'content', - newValue: 'new content to mock', - } as any); - - makeSnapshot(component); - }); - - it('props has new children', () => { - MiniRenderDivNode.schema.props.children = [ - 'children 01', - 'children 02', - ]; - - TextNode.emitPropChange({ - key: 'content', - newValue: 'props' - }); - - makeSnapshot(component); - }); - - it('leaf has a loop, render from parent', () => { - MiniRenderDivNode = new Node(miniRenderSchema, {}); - - TextNode = new Node(textSchema, { - parent: MiniRenderDivNode, - hasLoop: true, - }); - - nodeMap.set(textSchema.id, TextNode); - nodeMap.set(miniRenderSchema.id, MiniRenderDivNode); - - component = renderer.create( - - - - ); - - MiniRenderDivNode.schema.children = ['this is a new children']; - - TextNode.emitPropChange({ - key: 'content', - newValue: '1', - }); - - makeSnapshot(component); - }); -}); - -describe('component cache', () => { - it('get different component with same is and different doc id', () => { - const baseRenderer02 = { - ...baseRenderer, - props: { - ...baseRenderer.props, - documentId: '02', - } - } - const Div3 = leafWrapper(components.Div as any, { - schema: divSchema, - baseRenderer: baseRenderer02, - componentInfo: {}, - scope: {}, - }); - - expect(Div).not.toEqual(Div3); - }); - - it('get component again and get ths cache component', () => { - const Div2 = leafWrapper(components.Div as any, { - schema: divSchema, - baseRenderer, - componentInfo: {}, - scope: {}, - }); - - expect(Div).toEqual(Div2); - }); -}); - -describe('onVisibleChange', () => { - it('visible is false', () => { - TextNode.emitVisibleChange(false); - makeSnapshot(component); - }); - - it('visible is true', () => { - TextNode.emitVisibleChange(true); - makeSnapshot(component); - }); -}); - -describe('children', () => { - it('this.props.children is array', () => { - const component = renderer.create( -
- - -
- ); - - makeSnapshot(component); - }); -}); - -describe('onChildrenChange', () => { - it('children is array string', () => { - DivNode.schema.children = [ - 'onChildrenChange content 01', - 'onChildrenChange content 02' - ] - DivNode.emitChildrenChange(); - makeSnapshot(component); - }); - - it('children is 0', () => { - DivNode.schema.children = 0 - DivNode.emitChildrenChange(); - const componentInstance = component.root; - expect(componentInstance.findByType(components.Div).props.children).toEqual(0); - }); - - it('children is false', () => { - DivNode.schema.children = false - DivNode.emitChildrenChange(); - const componentInstance = component.root; - expect(componentInstance.findByType(components.Div).props.children).toEqual(false); - }); - - it('children is []', () => { - DivNode.schema.children = [] - DivNode.emitChildrenChange(); - const componentInstance = component.root; - expect(componentInstance.findByType(components.Div).props.children).toEqual([]); - }); - - it('children is null', () => { - DivNode.schema.children = null - DivNode.emitChildrenChange(); - const componentInstance = component.root; - expect(componentInstance.findByType(components.Div).props.children).toEqual(null); - }); - - it('children is undefined', () => { - DivNode.schema.children = undefined; - DivNode.emitChildrenChange(); - const componentInstance = component.root; - expect(componentInstance.findByType(components.Div).props.children).toEqual(undefined); - }); -}); - -describe('not render leaf', () => { - let miniRenderSchema, MiniRenderDiv, MiniRenderDivNode; - beforeEach(() => { - miniRenderSchema = { - id: 'miniDiv' + id, - }; - - MiniRenderDivNode = new Node(miniRenderSchema, { - componentMeta: { - isMinimalRenderUnit: true, - }, - }); - - nodeMap.set(miniRenderSchema.id, MiniRenderDivNode); - - MiniRenderDiv = leafWrapper(components.MiniRenderDiv as any, { - schema: miniRenderSchema, - baseRenderer, - componentInfo: {}, - scope: {}, - }); - - TextNode = new Node(textSchema, { - parent: MiniRenderDivNode, - }); - - component = renderer.create( - - ); - }); - - it('onPropsChange', () => { - const nextCount = rerenderCount + 1; - - MiniRenderDivNode.emitPropChange({ - key: 'any', - newValue: 'any', - }); - - expect(rerenderCount).toBe(nextCount); - }); - - it('onChildrenChange', () => { - const nextCount = rerenderCount + 1; - - MiniRenderDivNode.emitChildrenChange({ - key: 'any', - newValue: 'any', - }); - - expect(rerenderCount).toBe(nextCount); - }); - - it('onVisibleChange', () => { - const nextCount = rerenderCount + 1; - - MiniRenderDivNode.emitVisibleChange(true); - - expect(rerenderCount).toBe(nextCount); - }); -}); diff --git a/packages/renderer-core/tests/mock/loop.ts b/packages/renderer-core/tests/mock/loop.ts deleted file mode 100644 index 60e74378d..000000000 --- a/packages/renderer-core/tests/mock/loop.ts +++ /dev/null @@ -1,221 +0,0 @@ -const schema = { - "componentName": "Page", - "id": "node_ocl1djd9o41", - "docId": "docl1djd9o4", - "props": { - "templateVersion": "1.0.0", - "containerStyle": {}, - "pageStyle": { - "backgroundColor": "#f2f3f5" - }, - "className": "_css_pseudo_node_ocl1djd9o41" - }, - "dataSource": { - "offline": [], - "globalConfig": {}, - "online": [ - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61", - "formUuid": "FORM-3KYJN7RV-J47BPFK63W2PHAGPO1VC3-B4H1WE5K-131", - "name": "locale", - "description": "当前语种(在 window.g_config 中设置)", - "id": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61", - "protocal": "VALUE", - "shareType": "APP" - }, - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71", - "formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H", - "name": "appType", - "description": "应用的唯一 code", - "id": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71", - "protocal": "VALUE", - "shareType": "APP" - }, - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81", - "formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H", - "name": "version", - "description": "应该版本,默认 0.1.0", - "id": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81", - "protocal": "VALUE", - "shareType": "APP" - }, - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91", - "formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H", - "name": "apiPrefix", - "description": "", - "id": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91", - "protocal": "VALUE", - "shareType": "APP" - } - ], - "sync": true, - "list": [ - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61", - "formUuid": "FORM-3KYJN7RV-J47BPFK63W2PHAGPO1VC3-B4H1WE5K-131", - "name": "locale", - "description": "当前语种(在 window.g_config 中设置)", - "id": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61", - "protocal": "VALUE", - "shareType": "APP" - }, - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71", - "formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H", - "name": "appType", - "description": "应用的唯一 code", - "id": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71", - "protocal": "VALUE", - "shareType": "APP" - }, - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81", - "formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H", - "name": "version", - "description": "应该版本,默认 0.1.0", - "id": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81", - "protocal": "VALUE", - "shareType": "APP" - }, - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91", - "formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H", - "name": "apiPrefix", - "description": "", - "id": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91", - "protocal": "VALUE", - "shareType": "APP" - } - ] - }, - "methods": {}, - "hidden": false, - "title": "", - "isLocked": false, - "condition": true, - "conditionGroup": "", - "children": [ - { - "componentName": "RootHeader", - "id": "node_ocl1djd9o42", - "docId": "docl1djd9o4", - "props": {}, - "hidden": false, - "title": "", - "isLocked": false, - "condition": true, - "conditionGroup": "" - }, - { - "componentName": "RootContent", - "id": "node_ocl1djd9o43", - "docId": "docl1djd9o4", - "props": { - "contentMargin": "20", - "contentPadding": "20", - "contentBgColor": "white" - }, - "hidden": false, - "title": "", - "isLocked": false, - "condition": true, - "conditionGroup": "", - "children": [ - { - "componentName": "Div", - "id": "node_ocl1djd9o45", - "docId": "docl1djd9o4", - "props": { - "behavior": "NORMAL", - "__style__": {}, - "fieldId": "div_l1djdj1n", - "events": { - "ignored": true - }, - "useFieldIdAsDomId": false, - "customClassName": "", - "className": "_css_pseudo_node_ocl1djd9o45" - }, - "hidden": false, - "title": "", - "isLocked": false, - "condition": true, - "conditionGroup": "", - "loop": [ - 1, - 2, - 3 - ], - "loopArgs": [ - null, - null - ], - "children": [ - { - "componentName": "Div", - "id": "node_ocl1djd9o46", - "docId": "docl1djd9o4", - "props": { - "behavior": "NORMAL", - "__style__": {}, - "fieldId": "div_l1djdj1o", - "events": { - "ignored": true - }, - "useFieldIdAsDomId": false, - "customClassName": "", - "className": "_css_pseudo_node_ocl1djd9o46" - }, - "hidden": false, - "title": "", - "isLocked": false, - "condition": true, - "conditionGroup": "", - "loop": [ - 1, - 2, - 3 - ], - "loopArgs": [ - null, - null - ] - } - ] - } - ] - }, - { - "componentName": "RootFooter", - "id": "node_ocl1djd9o44", - "docId": "docl1djd9o4", - "props": {}, - "hidden": false, - "title": "", - "isLocked": false, - "condition": true, - "conditionGroup": "" - } - ] -}; - -export default schema; diff --git a/packages/renderer-core/tests/mock/sample.ts b/packages/renderer-core/tests/mock/sample.ts deleted file mode 100644 index e94572a74..000000000 --- a/packages/renderer-core/tests/mock/sample.ts +++ /dev/null @@ -1,184 +0,0 @@ -export const sampleSchema = { - "componentName": "Page", - "id": "node_ockyigdqxl1", - "docId": "dockyigdqxl", - "props": { - "templateVersion": "1.0.0", - "containerStyle": {}, - "pageStyle": { - "backgroundColor": "#f2f3f5" - }, - "className": "_css_pseudo_node_ockyigdqxl1" - }, - "dataSource": { - "offline": [], - "globalConfig": {}, - "online": [ - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61", - "formUuid": "FORM-3KYJN7RV-J47BPFK63W2PHAGPO1VC3-B4H1WE5K-131", - "name": "locale", - "description": "当前语种(在 window.g_config 中设置)", - "id": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61", - "protocal": "VALUE", - "shareType": "APP" - }, - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71", - "formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H", - "name": "appType", - "description": "应用的唯一 code", - "id": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71", - "protocal": "VALUE", - "shareType": "APP" - }, - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81", - "formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H", - "name": "version", - "description": "应该版本,默认 0.1.0", - "id": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81", - "protocal": "VALUE", - "shareType": "APP" - }, - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91", - "formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H", - "name": "apiPrefix", - "description": "", - "id": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91", - "protocal": "VALUE", - "shareType": "APP" - } - ], - "sync": true, - "list": [ - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61", - "formUuid": "FORM-3KYJN7RV-J47BPFK63W2PHAGPO1VC3-B4H1WE5K-131", - "name": "locale", - "description": "当前语种(在 window.g_config 中设置)", - "id": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61", - "protocal": "VALUE", - "shareType": "APP" - }, - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71", - "formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H", - "name": "appType", - "description": "应用的唯一 code", - "id": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71", - "protocal": "VALUE", - "shareType": "APP" - }, - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81", - "formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H", - "name": "version", - "description": "应该版本,默认 0.1.0", - "id": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81", - "protocal": "VALUE", - "shareType": "APP" - }, - { - "gmtModified": 1639385418000, - "initialData": "", - "globalUid": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91", - "formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H", - "name": "apiPrefix", - "description": "", - "id": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91", - "protocal": "VALUE", - "shareType": "APP" - } - ] - }, - "methods": {}, - "lifeCycles": {}, - "hidden": false, - "title": "", - "isLocked": false, - "condition": true, - "conditionGroup": "", - "children": [ - { - "componentName": "RootHeader", - "id": "node_ockyigdqxl2", - "docId": "dockyigdqxl", - "props": {}, - "hidden": false, - "title": "", - "isLocked": false, - "condition": true, - "conditionGroup": "" - }, - { - "componentName": "RootContent", - "id": "node_ockyigdqxl3", - "docId": "dockyigdqxl", - "props": { - "contentMargin": "20", - "contentPadding": "20", - "contentBgColor": "white" - }, - "hidden": false, - "title": "", - "isLocked": false, - "condition": true, - "conditionGroup": "", - "children": [ - { - "componentName": "Button", - "id": "node_ockyigdqxl5", - "docId": "dockyigdqxl", - "props": { - "content": "按 钮", - "type": "primary", - "size": "medium", - "behavior": "NORMAL", - "__style__": {}, - "fieldId": "button_kyige3yf", - "events": { - "ignored": true - }, - "baseIcon": "", - "otherIcon": "", - "loading": false, - "triggerEventsWhenLoading": false, - "className": "_css_pseudo_node_ockyigdqxl5" - }, - "hidden": false, - "title": "", - "isLocked": false, - "condition": true, - "conditionGroup": "" - } - ] - }, - { - "componentName": "RootFooter", - "id": "node_ockyigdqxl4", - "docId": "dockyigdqxl", - "props": {}, - "hidden": false, - "title": "", - "isLocked": false, - "condition": true, - "conditionGroup": "" - } - ] -}; \ No newline at end of file diff --git a/packages/renderer-core/tests/mock/styleMock.js b/packages/renderer-core/tests/mock/styleMock.js deleted file mode 100644 index f49164dea..000000000 --- a/packages/renderer-core/tests/mock/styleMock.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - process() { - return ''; - }, -}; \ No newline at end of file diff --git a/packages/renderer-core/tests/renderer/__snapshots__/renderer.test.tsx.snap b/packages/renderer-core/tests/renderer/__snapshots__/renderer.test.tsx.snap deleted file mode 100644 index 79c5f0f08..000000000 --- a/packages/renderer-core/tests/renderer/__snapshots__/renderer.test.tsx.snap +++ /dev/null @@ -1,1398 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Base Render renderComp 1`] = ` -
-
-
- -
-
-
-
-
- -
-
- - - - - - - - 请选择 - - -   - - - - - - - - - - - - - - - - -
-
-
-
- -
-
- - - - - - - - 请选择 - - -   - - - - - - - - - - - - - - - - -
-
-
-
- -
-
- - - - - -
-
-
- - -
- -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - -
-
- 姓名 -
-
-
- 年龄 -
-
-
- 邮箱 -
-
-
- 没有数据 -
-
-
-
-
-
- -
- - - - - - -
- - - - 1 - - / - 10 - - - 到第 - - - - - - 页 - - -
-
-
-
-
- - - - - - - - 请选择 - - -   - - - - - - - - - - - - - - -
-`; - -exports[`JSExpression JSExpression props 1`] = ` -
-
-
-`; - -exports[`JSExpression JSExpression props with loop 1`] = ` -
-
-
-
-`; - -exports[`JSExpression JSExpression props with loop, and thisRequiredInJSE is true 1`] = ` -
-
-
-
-`; - -exports[`JSExpression JSFunction props 1`] = ` -
-
-
-`; - -exports[`JSExpression JSSlot has loop 1`] = ` -
-
-
- 这是一个低代码业务组件~ -
-
-
-
- 这是一个低代码业务组件~ -
-
-
-
- 这是一个低代码业务组件~ -
-
-
-`; - -exports[`JSExpression base props 1`] = ` -
-
-
-`; - -exports[`designMode designMode:default 1`] = ` -
-
-
-
-
-`; diff --git a/packages/renderer-core/tests/renderer/base.test.tsx b/packages/renderer-core/tests/renderer/base.test.tsx deleted file mode 100644 index 3faa2bcf4..000000000 --- a/packages/renderer-core/tests/renderer/base.test.tsx +++ /dev/null @@ -1,222 +0,0 @@ - -import React, { Component, createElement, forwardRef, PureComponent, createContext } from 'react'; -const mockGetRenderers = jest.fn(); -const mockGetRuntime = jest.fn(); -const mockParseExpression = jest.fn(); -jest.mock('../../src/adapter', () => { - return { - getRenderers: () => { return mockGetRenderers();}, - getRuntime: () => { return mockGetRuntime();}, - }; -}); -jest.mock('../../src/utils', () => { - const originalUtils = jest.requireActual('../../src/utils'); - return { - ...originalUtils, - parseExpression: (...args) => { mockParseExpression(args);}, - }; -}); - - -import baseRendererFactory from '../../src/renderer/base'; -import { IBaseRendererProps } from '../../src/types'; -import TestRenderer from 'react-test-renderer'; -import components from '../utils/components'; -import schema from '../fixtures/schema/basic'; - - -describe('Base Render factory', () => { - it('customBaseRenderer logic works', () => { - mockGetRenderers.mockReturnValue({BaseRenderer: {}}); - const baseRenderer = baseRendererFactory(); - expect(mockGetRenderers).toBeCalledTimes(1); - expect(baseRenderer).toStrictEqual({}); - mockGetRenderers.mockClear(); - }); -}); - -describe('Base Render methods', () => { - let RendererClass; - const mockRendererFactory = () => { - return class extends Component { - constructor(props: IBaseRendererProps, context: any) { - super(props, context); - } - } - } - beforeEach(() => { - const mockRnederers = { - PageRenderer: mockRendererFactory(), - ComponentRenderer: mockRendererFactory(), - BlockRenderer: mockRendererFactory(), - AddonRenderer: mockRendererFactory(), - TempRenderer: mockRendererFactory(), - DivRenderer: mockRendererFactory(), - }; - mockGetRenderers.mockReturnValue(mockRnederers); - mockGetRuntime.mockReturnValue({ - Component, - createElement, - PureComponent, - createContext, - forwardRef, - }); - RendererClass = baseRendererFactory(); - }) - - afterEach(() => { - mockGetRenderers.mockClear(); - }) - - it('should excute lifecycle.getDerivedStateFromProps when defined', () => { - const mockGetDerivedStateFromProps = { - type: 'JSFunction', - value: 'function() {\n console.log(\'did mount\');\n }', - }; - const mockSchema = schema; - (mockSchema.lifeCycles as any).getDerivedStateFromProps = mockGetDerivedStateFromProps; - - // const originalUtils = jest.requireActual('../../src/utils'); - // mockParseExpression.mockImplementation(originalUtils.parseExpression); - const component = TestRenderer.create( - ); - // console.log(component.root.props.a); - // component.update(); - // console.log(component.root.props.a); - // expect(mockParseExpression).toHaveBeenCalledWith(mockGetDerivedStateFromProps, expect.anything()) - // test lifecycle.getDerivedStateFromProps is null - - // test lifecycle.getDerivedStateFromProps is JSExpression - - // test lifecycle.getDerivedStateFromProps is JSFunction - - // test lifecycle.getDerivedStateFromProps is function - - }); - - - // it('should excute lifecycle.getSnapshotBeforeUpdate when defined', () => { - // }); - - // it('should excute lifecycle.componentDidMount when defined', () => { - // }); - - // it('should excute lifecycle.componentDidUpdate when defined', () => { - // }); - - // it('should excute lifecycle.componentWillUnmount when defined', () => { - // }); - - // it('should excute lifecycle.componentDidCatch when defined', () => { - // }); - - // it('__executeLifeCycleMethod should work', () => { - // }); - - // it('reloadDataSource should work', () => { - // }); - - // it('shouldComponentUpdate should work', () => { - // }); - - - // it('_getComponentView should work', () => { - // }); - - // it('__bindCustomMethods should work', () => { - // }); - - // it('__generateCtx should work', () => { - // }); - - // it('__parseData should work', () => { - // }); - - // it('__initDataSource should work', () => { - // }); - - // it('__initI18nAPIs should work', () => { - // }); - - // it('__writeCss should work', () => { - // }); - - // it('__render should work', () => { - // }); - - // it('getSchemaChildren should work', () => { - // }); - - // it('__createDom should work', () => { - // }); - - // it('__createVirtualDom should work', () => { - // }); - - // it('__componentHOCs should work', () => { - // }); - - // it('__getSchemaChildrenVirtualDom should work', () => { - // }); - - // it('__getComponentProps should work', () => { - // }); - - // it('__createLoopVirtualDom should work', () => { - // }); - - // it('__designModeIsDesign should work', () => { - // }); - - // it('__parseProps should work', () => { - // }); - - // it('$ should work', () => { - // }); - - // it('__renderContextProvider should work', () => { - // }); - - // it('__renderContextConsumer should work', () => { - // }); - - // it('__getHOCWrappedComponent should work', () => { - // }); - - // it('__renderComp should work', () => { - // }); - - // it('__renderContent should work', () => { - // }); - - // it('__checkSchema should work', () => { - // }); - - // it('requestHandlersMap should work', () => { - // }); - - // it('utils should work', () => { - // }); - - // it('constants should work', () => { - // }); - - // it('history should work', () => { - // }); - - // it('location should work', () => { - // }); - - // it('match should work', () => { - // }); -}); \ No newline at end of file diff --git a/packages/renderer-core/tests/renderer/renderer.test.tsx b/packages/renderer-core/tests/renderer/renderer.test.tsx deleted file mode 100644 index 081cede8a..000000000 --- a/packages/renderer-core/tests/renderer/renderer.test.tsx +++ /dev/null @@ -1,447 +0,0 @@ -import React from 'react'; -import renderer from 'react-test-renderer'; -import schema from '../fixtures/schema/basic'; -import '../utils/react-env-init'; -import rendererFactory from '../../src/renderer/renderer'; -import components from '../utils/components'; - -const Renderer = rendererFactory(); - -function getComp(schema, comp = null, others = {}): Promise<{ - component, - inst, -}> { - return new Promise((resolve, reject) => { - const component = renderer.create( - ); - - const componentInstance = component.root; - - setTimeout(() => { - resolve({ - inst: comp ? componentInstance.findAllByType(comp) : null, - component, - }); - }, 20); - }) -} - -beforeEach(() => { - -}); - -let componentSnapshot; - -afterEach(() => { - if (componentSnapshot) { - let tree = componentSnapshot.toJSON(); - expect(tree).toMatchSnapshot(); - componentSnapshot = null; - } -}); - -describe('Base Render', () => { - it('renderComp', () => { - const content = ( - ); - const tree = renderer.create(content).toJSON(); - expect(tree).toMatchSnapshot(); - }); -}); - -describe('JSExpression', () => { - it('base props', (done) => { - const schema = { - componentName: 'Page', - props: {}, - children: [ - { - componentName: "Div", - props: { - className: 'div-ut', - text: "123", - visible: true, - } - } - ] - }; - - getComp(schema, components.Div).then(({ component, inst }) => { - expect(inst[0].props.text).toBe('123'); - expect(inst[0].props.visible).toBeTruthy(); - - componentSnapshot = component; - done(); - }); - }); - - it('JSExpression props', (done) => { - const schema = { - componentName: 'Page', - props: {}, - state: { - isShowDialog: true, - }, - children: [ - { - componentName: "Div", - props: { - className: "div-ut", - visible: { - type: 'JSExpression', - value: 'this.state.isShowDialog', - }, - } - } - ] - }; - - getComp(schema, components.Div).then(({ component, inst }) => { - expect(inst[0].props.visible).toBeTruthy(); - componentSnapshot = component; - done(); - }); - }); - - it('JSExpression props with loop', (done) => { - const schema = { - componentName: 'Page', - props: {}, - state: { - isShowDialog: true, - }, - children: [ - { - componentName: "Div", - loop: [ - { - name: '1', - }, - { - name: '2' - } - ], - props: { - className: "div-ut", - name1: { - type: 'JSExpression', - value: 'this.item.name', - }, - name2: { - type: 'JSExpression', - value: 'item.name', - }, - } - } - ] - }; - - getComp(schema, components.Div, { - thisRequiredInJSE: false, - }).then(({ component, inst }) => { - // expect(inst[0].props.visible).toBeTruthy(); - expect(inst.length).toEqual(2); - [1, 2].forEach((i) => { - expect(inst[0].props[`name${i}`]).toBe('1'); - expect(inst[1].props[`name${i}`]).toBe('2'); - }) - componentSnapshot = component; - done(); - }); - }); - - it('JSExpression props with loop, and thisRequiredInJSE is true', (done) => { - const schema = { - componentName: 'Page', - props: {}, - state: { - isShowDialog: true, - }, - children: [ - { - componentName: "Div", - loop: [ - { - name: '1', - }, - { - name: '2' - } - ], - props: { - className: "div-ut", - name1: { - type: 'JSExpression', - value: 'this.item.name', - }, - name2: { - type: 'JSExpression', - value: 'item.name', - }, - } - } - ] - }; - - getComp(schema, components.Div).then(({ component, inst }) => { - expect(inst.length).toEqual(2); - [0, 1].forEach((i) => { - expect(inst[i].props[`name1`]).toBe(i + 1 + ''); - expect(inst[i].props[`name2`]).toBe(undefined); - }) - componentSnapshot = component; - done(); - }); - }); - - // it('JSFunction props with loop', (done) => { - // const schema = { - // componentName: 'Page', - // props: {}, - // state: { - // isShowDialog: true, - // }, - // children: [ - // { - // componentName: "Div", - // loop: [ - // { - // name: '1', - // }, - // { - // name: '2' - // } - // ], - // props: { - // className: "div-ut", - // onClick1: { - // type: 'JSFunction', - // value: '() => this.item.name', - // }, - // onClick2: { - // type: 'JSFunction', - // value: 'function(){ return this.item.name }', - // }, - // onClick3: { - // type: 'JSFunction', - // value: 'function(){ return item.name }', - // }, - // onClick4: { - // type: 'JSFunction', - // value: '() => item.name', - // } - // } - // } - // ] - // }; - - // getComp(schema, components.Div).then(({ component, inst }) => { - // // expect(inst[0].props.visible).toBeTruthy(); - // expect(inst.length).toEqual(2); - // [1, 2, 3, 4].forEach((i) => { - // expect(inst[0].props[`onClick${i}`]()).toBe('1'); - // expect(inst[1].props[`onClick${i}`]()).toBe('2'); - // }) - // componentSnapshot = component; - // done(); - // }); - // }); - - it('JSFunction props', (done) => { - const schema = { - componentName: 'Page', - props: {}, - state: { - isShowDialog: true, - }, - children: [ - { - componentName: "Div", - props: { - className: "div-ut", - onClick: { - type: 'JSFunction', - value: 'function() {return this.state.isShowDialog}', - }, - } - } - ] - }; - - getComp(schema, components.Div).then(({ component, inst }) => { - expect(!!inst[0].props.onClick).toBeTruthy(); - expect(inst[0].props.onClick()).toBeTruthy(); - - componentSnapshot = component; - done(); - }); - }); - - it('JSSlot has loop', (done) => { - const schema = { - componentName: "Page", - props: {}, - children: [ - { - componentName: "SlotComponent", - id: "node_k8bnubvz", - props: { - mobileSlot: { - type: "JSSlot", - title: "mobile容器", - name: "mobileSlot", - value: [ - { - condition: true, - hidden: false, - children: [ - { - condition: true, - hidden: false, - loopArgs: [ - "item", - "index" - ], - isLocked: false, - conditionGroup: "", - componentName: "Text", - id: "node_ocl1ao1o7w4", - title: "", - props: { - maxLine: 0, - showTitle: false, - className: "text_l1ao7pfb", - behavior: "NORMAL", - content: "这是一个低代码业务组件~", - __style__: ":root {\n font-size: 14px;\n color: #666;\n}", - fieldId: "text_l1ao7lvp" - } - } - ], - loop: { - type: "JSExpression", - value: "this.state.content" - }, - loopArgs: [ - "item", - "index" - ], - isLocked: false, - conditionGroup: "", - componentName: "Div", - id: "node_ocl1ao1o7w3", - title: "", - props: { - useFieldIdAsDomId: false, - customClassName: "", - className: "div_l1ao7pfc", - behavior: "NORMAL", - __style__: ":root {\n padding: 12px;\n background: #f2f2f2;\n border: 1px solid #ddd;\n}", - fieldId: "div_l1ao7lvq" - } - } - ] - }, - }, - } - ], - state: { - content: { - type: "JSExpression", - value: "[{}, {}, {}]", - }, - }, - }; - - getComp(schema, components.Div).then(({ component, inst }) => { - expect(inst.length).toBe(3); - componentSnapshot = component; - done(); - }); - }) -}); - -describe("designMode", () => { - it('designMode:default', (done) => { - const schema = { - componentName: 'Page', - props: {}, - children: [ - { - componentName: "Div", - props: { - className: 'div-ut', - children: [ - { - componentName: "Div", - visible: true, - props: { - className: 'div-ut-children', - } - } - ] - } - } - ] - }; - - getComp(schema, components.Div).then(({ component, inst }) => { - expect(inst.length).toBe(2); - expect(inst[0].props.className).toBe('div-ut'); - expect(inst[1].props.className).toBe('div-ut-children'); - componentSnapshot = component; - done(); - }); - }); - it('designMode:design', (done) => { - const schema = { - componentName: 'Page', - props: {}, - children: [ - { - componentName: "Div", - id: '0', - props: { - className: 'div-ut', - children: [ - { - componentName: "Div", - id: 'hiddenId', - hidden: true, - props: { - className: 'div-ut-children', - } - } - ] - } - } - ] - }; - - getComp(schema, components.Div, { - designMode: 'design', - getNode: (id) => { - if (id === 'hiddenId') { - return { - export() { - return { - hidden: true, - }; - } - } - } - } - }).then(({ component, inst }) => { - expect(inst.length).toBe(1); - expect(inst[0].props.className).toBe('div-ut'); - done(); - }); - }); -}) \ No newline at end of file diff --git a/packages/renderer-core/tests/setup.ts b/packages/renderer-core/tests/setup.ts deleted file mode 100644 index 0d51f6bb5..000000000 --- a/packages/renderer-core/tests/setup.ts +++ /dev/null @@ -1,14 +0,0 @@ -jest.mock('lodash', () => { - const original = jest.requireActual('lodash'); - - return { - ...original, - debounce: (fn) => (...args: any[]) => fn.apply(this, args), - throttle: (fn) => (...args: any[]) => fn.apply(this, args), - } -}) - -export const mockConsoleWarn = jest.fn(); -console.warn = mockConsoleWarn; - -process.env.NODE_ENV = 'production'; \ No newline at end of file diff --git a/packages/renderer-core/tests/utils/common.test.ts b/packages/renderer-core/tests/utils/common.test.ts deleted file mode 100644 index 13b6908d5..000000000 --- a/packages/renderer-core/tests/utils/common.test.ts +++ /dev/null @@ -1,463 +0,0 @@ -import { - isSchema, - isFileSchema, - inSameDomain, - getFileCssName, - isJSSlot, - getValue, - getI18n, - transformArrayToMap, - transformStringToFunction, - isVariable, - capitalizeFirstLetter, - forEach, - isString, - serializeParams, - parseExpression, - parseThisRequiredExpression, - parseI18n, - parseData, -} from '../../src/utils/common'; -import logger from '../../src/utils/logger'; - -describe('test isSchema', () => { - it('should be false when empty value is passed', () => { - expect(isSchema(null)).toBeFalsy(); - expect(isSchema(undefined)).toBeFalsy(); - expect(isSchema('')).toBeFalsy(); - expect(isSchema({})).toBeFalsy(); - }); - - it('should be true when componentName is Leaf or Slot ', () => { - expect(isSchema({ componentName: 'Leaf' })).toBeTruthy(); - expect(isSchema({ componentName: 'Slot' })).toBeTruthy(); - }); - - it('should check each item of an array', () => { - const validArraySchema = [ - { componentName: 'Button', props: {}}, - { componentName: 'Button', props: { type: 'JSExpression' }}, - { componentName: 'Leaf' }, - { componentName: 'Slot'}, - ]; - const invalidArraySchema = [ - ...validArraySchema, - { componentName: 'ComponentWithoutProps'}, - ]; - expect(isSchema(validArraySchema)).toBeTruthy(); - expect(isSchema(invalidArraySchema)).toBeFalsy(); - }); - - it('normal valid schema should contains componentName, and props of type object or JSExpression', () => { - expect(isSchema({ componentName: 'Button', props: {}})).toBeTruthy(); - expect(isSchema({ componentName: 'Button', props: { type: 'JSExpression' }})).toBeTruthy(); - expect(isSchema({ xxxName: 'Button'})).toBeFalsy(); - expect(isSchema({ componentName: 'Button', props: null})).toBeFalsy(); - expect(isSchema({ componentName: 'Button', props: []})).toBeFalsy(); - expect(isSchema({ componentName: 'Button', props: 'props string'})).toBeFalsy(); - }); -}); - -describe('test isFileSchema ', () => { - it('should be false when invalid schema is passed', () => { - expect(isFileSchema({ xxxName: 'Button'})).toBeFalsy(); - expect(isFileSchema({ componentName: 'Button', props: null})).toBeFalsy(); - expect(isFileSchema({ componentName: 'Button', props: []})).toBeFalsy(); - expect(isFileSchema({ componentName: 'Button', props: 'props string'})).toBeFalsy(); - }); - it('should be true only when schema with root named Page || Block || Component is passed', () => { - expect(isFileSchema({ componentName: 'Page', props: {}})).toBeTruthy(); - expect(isFileSchema({ componentName: 'Block', props: {}})).toBeTruthy(); - expect(isFileSchema({ componentName: 'Component', props: {}})).toBeTruthy(); - expect(isFileSchema({ componentName: 'Button', props: {}})).toBeFalsy(); - }); -}); - -describe('test inSameDomain ', () => { - let windowSpy; - - beforeEach(() => { - windowSpy = jest.spyOn(window, "window", "get"); - }); - - afterEach(() => { - windowSpy.mockRestore(); - }); - it('should work', () => { - - windowSpy.mockImplementation(() => ({ - parent: { - location: { - host: "example.com" - }, - }, - location: { - host: "example.com" - } - })); - expect(inSameDomain()).toBeTruthy(); - - windowSpy.mockImplementation(() => ({ - parent: { - location: { - host: "example.com" - }, - }, - location: { - host: "another.com" - } - })); - expect(inSameDomain()).toBeFalsy(); - - windowSpy.mockImplementation(() => ({ - parent: null, - location: { - host: "example.com" - } - })); - - expect(inSameDomain()).toBeFalsy(); - }); -}); - - -describe('test getFileCssName ', () => { - it('should work', () => { - expect(getFileCssName(null)).toBe(undefined); - expect(getFileCssName(undefined)).toBe(undefined); - expect(getFileCssName('')).toBe(undefined); - expect(getFileCssName('FileName')).toBe('lce-file-name'); - expect(getFileCssName('Page1_abc')).toBe('lce-page1_abc'); - }); -}); - - -describe('test isJSSlot ', () => { - it('should work', () => { - expect(isJSSlot(null)).toBeFalsy(); - expect(isJSSlot(undefined)).toBeFalsy(); - expect(isJSSlot('stringValue')).toBeFalsy(); - expect(isJSSlot([1, 2, 3])).toBeFalsy(); - expect(isJSSlot({ type: 'JSSlot' })).toBeTruthy(); - expect(isJSSlot({ type: 'JSBlock' })).toBeTruthy(); - expect(isJSSlot({ type: 'anyOtherType' })).toBeFalsy(); - }); -}); - -describe('test getValue ', () => { - it('should check params', () => { - expect(getValue(null, 'somePath')).toStrictEqual({}); - expect(getValue(undefined, 'somePath')).toStrictEqual({}); - // array is not valid input, return default - expect(getValue([], 'somePath')).toStrictEqual({}); - expect(getValue([], 'somePath', 'aaa')).toStrictEqual('aaa'); - expect(getValue([1, 2, 3], 'somePath', 'aaa')).toStrictEqual('aaa'); - - expect(getValue({}, 'somePath')).toStrictEqual({}); - expect(getValue({}, 'somePath', 'default')).toStrictEqual('default'); - }); - it('should work normally', () => { - // single segment path - expect(getValue({ a: 'aValue' }, 'a')).toStrictEqual('aValue'); - expect(getValue({ a: 'aValue', f:null }, 'f')).toBeNull(); - expect(getValue({ a: { b: 'bValue' } }, 'a.b')).toStrictEqual('bValue'); - expect(getValue({ a: { b: 'bValue', c: { d: 'dValue' } } }, 'a.c.d')).toStrictEqual('dValue'); - expect(getValue({ a: { b: 'bValue', c: { d: 'dValue' } } }, 'e')).toStrictEqual({}); - }); -}); - -describe('test getI18n ', () => { - it('should work', () => { - const messages = { - 'zh-CN': { - 'key1': '啊啊啊', - 'key2': '哈哈哈', - }, - }; - expect(getI18n('keyString', {}, 'zh-CN')).toStrictEqual(''); - expect(getI18n('keyString', {}, 'zh-CN', null)).toStrictEqual(''); - expect(getI18n('keyString', {}, 'en-US', messages)).toStrictEqual(''); - expect(getI18n('key3', {}, 'zh-CN', messages)).toStrictEqual(''); - }); -}); - - -describe('test transformArrayToMap ', () => { - it('should work', () => { - expect(transformArrayToMap([])).toStrictEqual({}); - expect(transformArrayToMap('not a array')).toStrictEqual({}); - expect(transformArrayToMap({'not Array': 1})).toStrictEqual({}); - - let mockArray = [ - { - name: 'jack', - age: 2, - }, - { - name: 'jack', - age: 20, - } - ]; - // test override - expect(transformArrayToMap(mockArray, 'name', true).jack.age).toBe(20); - expect(transformArrayToMap(mockArray, 'name').jack.age).toBe(20); - expect(transformArrayToMap(mockArray, 'name', false).jack.age).toBe(2); - - mockArray = [ - { - name: 'jack', - age: 2, - }, - { - name: 'rose', - age: 20, - } - ]; - // normal case - expect(transformArrayToMap(mockArray, 'name').jack.age).toBe(2); - expect(transformArrayToMap(mockArray, 'name').jack.name).toBe('jack'); - expect(transformArrayToMap(mockArray, 'name').rose.age).toBe(20); - // key not exists - expect(transformArrayToMap(mockArray, 'nameEn')).toStrictEqual({}); - }); -}); - - - -describe('test transformStringToFunction ', () => { - it('should work', () => { - const mockFun = jest.fn(); - expect(transformStringToFunction(mockFun)).toBe(mockFun); - expect(transformStringToFunction(111)).toBe(111); - - let mockFnStr = 'function(){return 111;}'; - let fn = transformStringToFunction(mockFnStr); - expect(fn()).toBe(111); - - mockFnStr = '() => { return 222; }'; - fn = transformStringToFunction(mockFnStr); - expect(fn()).toBe(222); - - mockFnStr = 'function getValue() { return 333; }'; - fn = transformStringToFunction(mockFnStr); - expect(fn()).toBe(333); - - mockFnStr = 'function getValue(aaa) {\ - return aaa; \ - }'; - fn = transformStringToFunction(mockFnStr); - expect(fn(123)).toBe(123); - }); -}); - - -describe('test isVariable ', () => { - it('should work', () => { - expect(isVariable(null)).toBeFalsy(); - expect(isVariable(undefined)).toBeFalsy(); - expect(isVariable([1, 2, 3])).toBeFalsy(); - expect(isVariable({})).toBeFalsy(); - expect(isVariable({ type: 'any other type' })).toBeFalsy(); - expect(isVariable({ type: 'variable' })).toBeTruthy(); - }); -}); - -describe('test capitalizeFirstLetter ', () => { - it('should work', () => { - expect(capitalizeFirstLetter(null)).toBeNull(); - expect(capitalizeFirstLetter()).toBeUndefined(); - expect(capitalizeFirstLetter([1, 2, 3])).toStrictEqual([1, 2, 3]); - expect(capitalizeFirstLetter({ a: 1 })).toStrictEqual({ a: 1 }); - expect(capitalizeFirstLetter('')).toStrictEqual(''); - expect(capitalizeFirstLetter('a')).toStrictEqual('A'); - expect(capitalizeFirstLetter('abcd')).toStrictEqual('Abcd'); - }); -}); - -describe('test forEach ', () => { - it('should work', () => { - const mockFn = jest.fn(); - - forEach(null, mockFn); - expect(mockFn).toBeCalledTimes(0); - - forEach(undefined, mockFn); - expect(mockFn).toBeCalledTimes(0); - - forEach([1, 2, 3], mockFn); - expect(mockFn).toBeCalledTimes(0); - - forEach('stringValue', mockFn); - expect(mockFn).toBeCalledTimes(0); - - forEach({ a: 1, b: 2, c: 3 }, mockFn); - expect(mockFn).toBeCalledTimes(3); - - const mockFn2 = jest.fn(); - forEach({ a: 1 }, mockFn2, { b: 'bbb' }); - expect(mockFn2).toHaveBeenCalledWith(1, 'a'); - - let sum = 0; - const mockFn3 = function(value, key) { sum = value + this.b; }; - forEach({ a: 1 }, mockFn3, { b: 10 }); - expect(sum).toEqual(11); - }); -}); - -describe('test isString ', () => { - it('should work', () => { - expect(isString(123)).toBeFalsy(); - expect(isString([])).toBeFalsy(); - expect(isString({})).toBeFalsy(); - expect(isString(null)).toBeFalsy(); - expect(isString(undefined)).toBeFalsy(); - expect(isString(true)).toBeFalsy(); - expect(isString('111')).toBeTruthy(); - expect(isString(new String('111'))).toBeTruthy(); - }); -}); - -describe('test serializeParams ', () => { - it('should work', () => { - const mockParams = { a: 1, b: 2, c: 'cvalue', d:[1, 'a', {}], e: {e1: 'value1', e2: 'value2'}}; - const result = serializeParams(mockParams); - const decodedParams = decodeURIComponent(result); - expect(result).toBe('a=1&b=2&c=cvalue&d=%5B1%2C%22a%22%2C%7B%7D%5D&e=%7B%22e1%22%3A%22value1%22%2C%22e2%22%3A%22value2%22%7D'); - expect(decodedParams).toBe('a=1&b=2&c=cvalue&d=[1,"a",{}]&e={"e1":"value1","e2":"value2"}'); - }); -}); - -describe('test parseExpression ', () => { - it('can handle JSExpression', () => { - const mockExpression = { - "type": "JSExpression", - "value": "function (params) { return this.scopeValue + params.param1 + 5;}" - }; - const result = parseExpression(mockExpression, { scopeValue: 1 }); - expect(result({ param1: 2 })).toBe((1 + 2 + 5)); - }); - - it('[success] JSExpression handle without this use scopeValue', () => { - const mockExpression = { - "type": "JSExpression", - "value": "state" - }; - const result = parseExpression(mockExpression, { state: 1 }); - expect(result).toBe((1)); - }); - - it('[success] JSExpression handle without this use scopeValue', () => { - const mockExpression = { - "type": "JSExpression", - "value": "this.state" - }; - const result = parseExpression(mockExpression, { state: 1 }); - expect(result).toBe((1)); - }); -}); - -describe('test parseThisRequiredExpression', () => { - it('can handle JSExpression', () => { - const mockExpression = { - "type": "JSExpression", - "value": "function (params) { return this.scopeValue + params.param1 + 5;}" - }; - const result = parseThisRequiredExpression(mockExpression, { scopeValue: 1 }); - expect(result({ param1: 2 })).toBe((1 + 2 + 5)); - }); - - it('[error] JSExpression handle without this use scopeValue', () => { - const mockExpression = { - "type": "JSExpression", - "value": "state.text" - }; - const fn = logger.error = jest.fn(); - parseThisRequiredExpression(mockExpression, { state: { text: 'text' } }); - expect(fn).toBeCalledWith(' parseExpression.error', new ReferenceError('state is not defined'), {"type": "JSExpression", "value": "state.text"}, {"state": {"text": "text"}}); - }); - - it('[success] JSExpression handle without this use scopeValue', () => { - const mockExpression = { - "type": "JSExpression", - "value": "this.state" - }; - const result = parseThisRequiredExpression(mockExpression, { state: 1 }); - expect(result).toBe((1)); - }); -}) - -describe('test parseI18n ', () => { - it('can handle normal parseI18n', () => { - const mockI18n = { - "type": "i18n", - "key": "keyA" - }; - const mockI18nFun = (key) => { return 'hahaha' + key;}; - const result = parseI18n(mockI18n, { i18n: mockI18nFun }); - expect(result).toBe('hahahakeyA'); - }); -}); - -describe('test parseData ', () => { - it('should work when isJSExpression === true', () => { - const mockExpression = { - "type": "JSExpression", - "value": "function (params) { return this.scopeValue + params.param1 + 5;}" - }; - const result = parseData(mockExpression, { scopeValue: 1 }); - expect(result({ param1: 2 })).toBe((1 + 2 + 5)); - }); - it('should work when isI18nData === true', () => { - const mockI18n = { - "type": "i18n", - "key": "keyA" - }; - const mockI18nFun = (key) => { return 'hahaha' + key;}; - const result = parseData(mockI18n, { i18n: mockI18nFun }); - expect(result).toBe('hahahakeyA'); - }); - it('should work when schema is string', () => { - expect(parseData(' this is a normal string, will be trimmed only ')).toStrictEqual('this is a normal string, will be trimmed only'); - }); - - it('should work when schema is array', () => { - const mockData = [ - { - "type": "i18n", - "key": "keyA" - }, - ' this is a normal string, will be trimmed only ', - ]; - - const mockI18nFun = (key) => { return 'hahaha' + key;}; - const result = parseData(mockData, { i18n: mockI18nFun }); - - expect(result[0]).toStrictEqual('hahahakeyA'); - expect(result[1]).toStrictEqual('this is a normal string, will be trimmed only'); - }); - it('should work when schema is function', () => { - const mockFn = function() { return this.a; }; - const result = parseData(mockFn, { a: 111 }); - expect(result()).toBe(111); - }); - it('should work when schema is null or undefined', () => { - expect(parseData(null)).toBe(null); - expect(parseData(undefined)).toBe(undefined); - }); - it('should work when schema is normal object', () => { - expect(parseData({})).toStrictEqual({}); - const mockI18nFun = (key) => { return 'hahaha' + key;}; - const result = parseData({ - key1: { - "type": "i18n", - "key": "keyA" - }, - key2: ' this is a normal string, will be trimmed only ', - __privateKey: 'any value', - }, { i18n: mockI18nFun }); - expect(result.key1).toStrictEqual('hahahakeyA'); - expect(result.key2).toStrictEqual('this is a normal string, will be trimmed only'); - expect(result.__privateKey).toBeUndefined(); - - }); -}); diff --git a/packages/renderer-core/tests/utils/components.tsx b/packages/renderer-core/tests/utils/components.tsx deleted file mode 100644 index 639151612..000000000 --- a/packages/renderer-core/tests/utils/components.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import { Box, Breadcrumb, Form, Select, Input, Button, Table, Pagination, Dialog } from '@alifd/next'; - -const Div = ({_leaf, ...rest}: any) => (
{rest.children}
); - -const MiniRenderDiv = ({_leaf, ...rest}: any) => { - return ( -
- {rest.children} -
- ); -}; - -const Text = ({_leaf, ...rest}: any) => (
{rest.content}
); - -const SlotComponent = (props: any) => props.mobileSlot; - -const components = { - Box, - Breadcrumb, - 'Breadcrumb.Item': Breadcrumb.Item, - Form, - 'Form.Item': Form.Item, - Select, - Input, - Button, - 'Button.Group': Button.Group, - Table, - Pagination, - Dialog, - ErrorComponent: Select, - Div, - SlotComponent, - Text, - MiniRenderDiv, -}; - -export default components; diff --git a/packages/renderer-core/tests/utils/data-helper.test.ts b/packages/renderer-core/tests/utils/data-helper.test.ts deleted file mode 100644 index f4b388ce9..000000000 --- a/packages/renderer-core/tests/utils/data-helper.test.ts +++ /dev/null @@ -1,559 +0,0 @@ -// @ts-nocheck -const mockJsonp = jest.fn(); -const mockRequest = jest.fn(); -const mockGet = jest.fn(); -const mockPost = jest.fn(); -jest.mock('../../src/utils/request', () => { - return { - jsonp: (uri, params, headers, otherProps) => { - return new Promise((resolve, reject) => { - resolve(mockJsonp(uri, params, headers, otherProps)); - }); - }, - request: (uri, params, headers, otherProps) => { - return new Promise((resolve, reject) => { - resolve(mockRequest(uri, params, headers, otherProps)); - }); - }, - get: (uri, params, headers, otherProps) => { - return new Promise((resolve, reject) => { - resolve(mockGet(uri, params, headers, otherProps)); - }); - }, - post: (uri, params, headers, otherProps) => { - return new Promise((resolve, reject) => { - resolve(mockPost(uri, params, headers, otherProps)); - }); - }, - }; - }); - -import { DataHelper, doRequest } from '../../src/utils/data-helper'; -import { parseData } from '../../src/utils/common'; - -describe('test DataHelper ', () => { - beforeEach(() => { - jest.resetModules(); - }) - it('can be inited', () => { - const mockHost = {}; - let mockDataSourceConfig = {}; - const mockAppHelper = {}; - const mockParser = (config: any) => parseData(config); - let dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser); - - expect(dataHelper).toBeTruthy(); - expect(dataHelper.host).toBe(mockHost); - expect(dataHelper.config).toBe(mockDataSourceConfig); - expect(dataHelper.appHelper).toBe(mockAppHelper); - expect(dataHelper.parser).toBe(mockParser); - - - dataHelper = new DataHelper(mockHost, undefined, mockAppHelper, mockParser); - expect(dataHelper.config).toStrictEqual({}); - expect(dataHelper.ajaxList).toStrictEqual([]); - - mockDataSourceConfig = { - list: [ - { - id: 'ds1', - }, { - id: 'ds2', - }, - ] - }; - dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser); - expect(dataHelper.config).toBe(mockDataSourceConfig); - expect(dataHelper.ajaxList.length).toBe(2); - expect(dataHelper.ajaxMap.ds1).toStrictEqual({ - id: 'ds1', - }); - }); - it('should handle generateDataSourceMap properly in constructor', () => { - const mockHost = {}; - let mockDataSourceConfig = {}; - const mockAppHelper = {}; - const mockParser = (config: any) => parseData(config); - let dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser); - - // test generateDataSourceMap logic - mockDataSourceConfig = { - list: [ - { - id: 'getInfo', - isInit: true, - type: 'fetch', // fetch/mtop/jsonp/custom - options: { - uri: 'mock/info.json', - method: 'GET', - params: { a: 1 }, - timeout: 5000, - }, - }, { - id: 'postInfo', - isInit: true, - type: 'fetch', - options: { - uri: 'mock/info.json', - method: 'POST', - params: { a: 1 }, - timeout: 5000, - }, - }, - ] - }; - dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser); - expect(Object.keys(dataHelper.dataSourceMap).length).toBe(2); - expect(dataHelper.dataSourceMap.getInfo.status).toBe('init'); - expect(typeof dataHelper.dataSourceMap.getInfo.load).toBe('function'); - }); - - it('getInitDataSourseConfigs should work', () => { - const mockHost = {}; - let mockDataSourceConfig = {}; - const mockAppHelper = {}; - const mockParser = (config: any) => parseData(config); - - // test generateDataSourceMap logic - mockDataSourceConfig = { - list: [ - { - id: 'getInfo', - isInit: true, - type: 'fetch', // fetch/mtop/jsonp/custom - options: { - uri: 'mock/info.json', - method: 'GET', - params: { a: 1 }, - timeout: 5000, - }, - }, - { - id: 'postInfo', - isInit: false, - type: 'fetch', - options: { - uri: 'mock/info.json', - method: 'POST', - params: { a: 1 }, - timeout: 5000, - }, - }, - { - id: 'getInfoLater', - isInit: false, - type: 'fetch', - options: { - uri: 'mock/info.json', - method: 'POST', - params: { a: 1 }, - timeout: 5000, - }, - }, - { - id: 'getInfoLater2', - isInit: 'not a valid boolean', - type: 'fetch', - options: { - uri: 'mock/info.json', - method: 'POST', - params: { a: 1 }, - timeout: 5000, - }, - }, - ], - }; - - const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser); - expect(dataHelper.getInitDataSourseConfigs().length).toBe(1); - expect(dataHelper.getInitDataSourseConfigs()[0].id).toBe('getInfo'); - }); - it('util function doRequest should work', () => { - doRequest('jsonp', { - uri: 'https://www.baidu.com', - params: { a: 1 }, - otherStuff1: 'aaa', - }); - expect(mockJsonp).toBeCalled(); - - // test GET - doRequest('fetch', { - uri: 'https://www.baidu.com', - method: 'get', - params: { a: 1 }, - otherStuff1: 'aaa', - }); - expect(mockGet).toBeCalled(); - - mockGet.mockClear(); - doRequest('fetch', { - uri: 'https://www.baidu.com', - method: 'Get', - params: { a: 1 }, - otherStuff1: 'aaa', - }); - expect(mockGet).toBeCalled(); - - mockGet.mockClear(); - doRequest('fetch', { - uri: 'https://www.baidu.com', - method: 'GET', - params: { a: 1 }, - otherStuff1: 'aaa', - }); - expect(mockGet).toBeCalled(); - - mockGet.mockClear(); - - // test POST - doRequest('fetch', { - uri: 'https://www.baidu.com', - method: 'post', - params: { a: 1 }, - otherStuff1: 'aaa', - }); - expect(mockPost).toBeCalled(); - mockPost.mockClear(); - - doRequest('fetch', { - uri: 'https://www.baidu.com', - method: 'POST', - params: { a: 1 }, - otherStuff1: 'aaa', - }); - expect(mockPost).toBeCalled(); - mockPost.mockClear(); - doRequest('fetch', { - uri: 'https://www.baidu.com', - method: 'Post', - params: { a: 1 }, - otherStuff1: 'aaa', - }); - expect(mockPost).toBeCalled(); - mockPost.mockClear(); - - // test default - doRequest('fetch', { - uri: 'https://www.baidu.com', - method: 'whatever', - params: { a: 1 }, - otherStuff1: 'aaa', - }); - expect(mockRequest).toBeCalled(); - mockRequest.mockClear(); - mockGet.mockClear(); - - // method will be GET when not provided - doRequest('fetch', { - uri: 'https://www.baidu.com', - params: { a: 1 }, - otherStuff1: 'aaa', - }); - expect(mockRequest).toBeCalledTimes(0); - expect(mockGet).toBeCalledTimes(1); - - mockRequest.mockClear(); - mockGet.mockClear(); - mockPost.mockClear(); - mockJsonp.mockClear(); - - doRequest('someOtherType', { - uri: 'https://www.baidu.com', - params: { a: 1 }, - otherStuff1: 'aaa', - }); - expect(mockRequest).toBeCalledTimes(0); - expect(mockGet).toBeCalledTimes(0); - expect(mockPost).toBeCalledTimes(0); - expect(mockJsonp).toBeCalledTimes(0); - }); - it('updateDataSourceMap should work', () => { - const mockHost = {}; - const mockDataSourceConfig = { - list: [ - { - id: 'ds1', - }, { - id: 'ds2', - }, - ] - }; - const mockAppHelper = {}; - const mockParser = (config: any) => parseData(config); - const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser); - dataHelper.updateDataSourceMap('ds1', { a: 1 }, null); - expect(dataHelper.dataSourceMap['ds1']).toBeTruthy(); - expect(dataHelper.dataSourceMap['ds1'].data).toStrictEqual({ a: 1 }); - expect(dataHelper.dataSourceMap['ds1'].error).toBeUndefined(); - expect(dataHelper.dataSourceMap['ds1'].status).toBe('loaded'); - dataHelper.updateDataSourceMap('ds2', { b: 2 }, new Error()); - expect(dataHelper.dataSourceMap['ds2']).toBeTruthy(); - expect(dataHelper.dataSourceMap['ds2'].data).toStrictEqual({ b: 2 }); - expect(dataHelper.dataSourceMap['ds2'].status).toBe('error'); - expect(dataHelper.dataSourceMap['ds2'].error).toBeTruthy(); - }); - - it('handleData should work', () => { - const mockHost = { stateA: 'aValue'}; - const mockDataSourceConfig = { - list: [ - { - id: 'fullConfigGet', - isInit: true, - options: { - params: {}, - method: 'GET', - isCors: true, - timeout: 5000, - headers: {}, - uri: 'mock/info.json', - }, - shouldFetch: { - type: 'JSFunction', - value: 'function() { return true; }', - }, - dataHandler: { - type: 'JSFunction', - value: 'function(res) { return res.data; }', - }, - errorHandler: { - type: 'JSFunction', - value: 'function(error) {}', - }, - willFetch: { - type: 'JSFunction', - value: 'function(options) { return options; }', - }, - }, - ] - }; - const mockAppHelper = {}; - const mockParser = (config: any) => parseData(config); - const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser); - // test valid case - let mockDataHandler = { - type: 'JSFunction', - value: 'function(res) { return res.data + \'+\' + this.stateA; }', - }; - let result = dataHelper.handleData('fullConfigGet', mockDataHandler, { data: 'mockDataValue' }, null); - expect(result).toBe('mockDataValue+aValue'); - - // test invalid datahandler - mockDataHandler = { - type: 'not a JSFunction', - value: 'function(res) { return res.data + \'+\' + this.stateA; }', - }; - result = dataHelper.handleData('fullConfigGet', mockDataHandler, { data: 'mockDataValue' }, null); - expect(result).toStrictEqual({ data: 'mockDataValue' }); - - // exception with id - mockDataHandler = { - type: 'JSFunction', - value: 'function(res) { return res.data + \'+\' + JSON.parse({a:1}); }', - }; - result = dataHelper.handleData('fullConfigGet', mockDataHandler, { data: 'mockDataValue' }, null); - expect(result).toBeUndefined(); - - // exception without id - mockDataHandler = { - type: 'JSFunction', - value: 'function(res) { return res.data + \'+\' + JSON.parse({a:1}); }', - }; - result = dataHelper.handleData(null, mockDataHandler, { data: 'mockDataValue' }, null); - expect(result).toBeUndefined(); - }); - - it('updateConfig should work', () => { - const mockHost = { stateA: 'aValue'}; - const mockDataSourceConfig = { - list: [ - { - id: 'ds1', - }, { - id: 'ds2', - }, - { - id: 'fullConfigGet', - isInit: true, - options: { - params: {}, - method: 'GET', - isCors: true, - timeout: 5000, - headers: {}, - uri: 'mock/info.json', - }, - shouldFetch: { - type: 'JSFunction', - value: 'function() { return true; }', - }, - dataHandler: { - type: 'JSFunction', - value: 'function(res) { return res.data; }', - }, - errorHandler: { - type: 'JSFunction', - value: 'function(error) {}', - }, - willFetch: { - type: 'JSFunction', - value: 'function(options) { return options; }', - }, - }, - ] - }; - const mockAppHelper = {}; - const mockParser = (config: any) => parseData(config); - const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser); - - expect(dataHelper.ajaxList.length).toBe(3); - - let updatedConfig = { - list: [ - { - id: 'ds2', - }, - { - id: 'fullConfigGet', - }, - ] - }; - dataHelper.updateConfig(updatedConfig); - - expect(dataHelper.ajaxList.length).toBe(2); - expect(dataHelper.dataSourceMap.ds1).toBeUndefined(); - - updatedConfig = { - list: [ - { - id: 'ds2', - }, - { - id: 'fullConfigGet', - }, - { - id: 'ds3', - }, - ] - }; - dataHelper.updateConfig(updatedConfig); - expect(dataHelper.ajaxList.length).toBe(3); - expect(dataHelper.dataSourceMap.ds3).toBeTruthy(); - }); - - it('getInitData should work', () => { - const mockHost = { stateA: 'aValue'}; - const mockDataSourceConfig = { - list: [ - { - id: 'ds1', - }, { - id: 'ds2', - }, - { - id: 'fullConfigGet', - isInit: true, - type: 'fetch', - options: { - params: {}, - method: 'GET', - isCors: true, - timeout: 5000, - headers: { - headerA: 1, - }, - uri: 'mock/info.json', - }, - shouldFetch: { - type: 'JSFunction', - value: 'function() { return true; }', - }, - dataHandler: { - type: 'JSFunction', - value: 'function(res) { return 123; }', - }, - errorHandler: { - type: 'JSFunction', - value: 'function(error) {}', - }, - willFetch: { - type: 'JSFunction', - value: 'function(options) { return options; }', - }, - }, - ] - }; - const mockAppHelper = {}; - const mockParser = (config: any) => parseData(config); - const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser); - - expect(dataHelper.ajaxList.length).toBe(3); - expect(mockGet).toBeCalledTimes(0); - dataHelper.getInitData().then(res => { - expect(mockGet).toBeCalledTimes(1); - expect(mockGet).toBeCalledWith('mock/info.json', {}, { - headerA: 1, - }, expect.anything()); - mockGet.mockClear(); - }); - }); - - it('getDataSource should work', () => { - const mockHost = { stateA: 'aValue'}; - const mockDataSourceConfig = { - list: [ - { - id: 'ds1', - }, { - id: 'ds2', - }, - { - id: 'fullConfigGet', - isInit: true, - type: 'fetch', - options: { - params: {}, - method: 'GET', - isCors: true, - timeout: 5000, - headers: { - headerA: 1, - }, - uri: 'mock/info.json', - }, - shouldFetch: { - type: 'JSFunction', - value: 'function() { return true; }', - }, - dataHandler: { - type: 'JSFunction', - value: 'function(res) { return 123; }', - }, - errorHandler: { - type: 'JSFunction', - value: 'function(error) {}', - }, - willFetch: { - type: 'JSFunction', - value: 'function(options) { return options; }', - }, - }, - ] - }; - const mockAppHelper = {}; - const mockParser = (config: any) => parseData(config); - const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser); - - expect(dataHelper.ajaxList.length).toBe(3); - expect(mockGet).toBeCalledTimes(0); - const callbackFn = jest.fn(); - dataHelper.getDataSource('fullConfigGet', { param1: 'value1' }, {}, callbackFn).then(res => { - expect(mockGet).toBeCalledTimes(1); - expect(mockGet).toBeCalledWith('mock/info.json', { param1: 'value1' }, { - headerA: 1, - }, expect.anything()); - mockGet.mockClear(); - expect(callbackFn).toBeCalledTimes(1); - }); - }); -}); diff --git a/packages/renderer-core/tests/utils/is-use-loop.test.ts b/packages/renderer-core/tests/utils/is-use-loop.test.ts deleted file mode 100644 index b0a614f2e..000000000 --- a/packages/renderer-core/tests/utils/is-use-loop.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -// @ts-nocheck -import isUseLoop from '../../src/utils/is-use-loop'; - -describe('base test', () => { - it('designMode is true', () => { - expect(isUseLoop([], true)).toBeFalsy(); - expect(isUseLoop([{}], true)).toBeTruthy(); - expect(isUseLoop(null, true)).toBeFalsy(); - expect(isUseLoop(undefined, true)).toBeFalsy(); - expect(isUseLoop(0, true)).toBeFalsy(); - }); - - it('loop is expression', () => { - expect(isUseLoop({ - "type": "JSExpression", - "value": "function() { console.log('componentDidMount'); }" - }, true)).toBeTruthy(); - expect(isUseLoop({ - "type": "JSExpression", - "value": "function() { console.log('componentDidMount'); }" - }, false)).toBeTruthy(); - }); - - it('designMode is false', () => { - expect(isUseLoop([], false)).toBeTruthy(); - expect(isUseLoop([{}], false)).toBeTruthy(); - expect(isUseLoop(null, false)).toBeTruthy(); - expect(isUseLoop(undefined, false)).toBeTruthy(); - expect(isUseLoop(0, false)).toBeTruthy(); - }); -}); diff --git a/packages/renderer-core/tests/utils/node.ts b/packages/renderer-core/tests/utils/node.ts deleted file mode 100644 index 01c6ab507..000000000 --- a/packages/renderer-core/tests/utils/node.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { IPublicTypePropChangeOptions } from "@ali/lowcode-designer"; -import EventEmitter from "events"; - -export default class Node { - private emitter: EventEmitter; - schema: any = { - props: {}, - }; - - componentMeta = {}; - - parent; - - hasLoop = () => this._hasLoop; - - id; - - _isRoot: false; - - _hasLoop: false; - - constructor(schema: any, info: any = {}) { - this.emitter = new EventEmitter(); - const { - componentMeta, - parent, - isRoot, - hasLoop, - } = info; - this.schema = { - props: {}, - ...schema, - }; - this.componentMeta = componentMeta || {}; - this.parent = parent; - this.id = schema.id; - this._isRoot = isRoot; - this._hasLoop = hasLoop; - } - - isRoot = () => this._isRoot; - - get isRootNode () { - return this._isRoot; - }; - - // componentMeta() { - // return this.componentMeta; - // } - - // mockLoop() { - // // this.hasLoop = true; - // } - - onChildrenChange(fn: any) { - this.emitter.on('onChildrenChange', fn); - return () => { - this.emitter.off('onChildrenChange', fn); - } - } - - emitChildrenChange() { - this.emitter?.emit('onChildrenChange', {}); - } - - onPropChange(fn: any) { - this.emitter.on('onPropChange', fn); - return () => { - this.emitter.off('onPropChange', fn); - } - } - - emitPropChange(val: IPublicTypePropChangeOptions, skip?: boolean) { - if (!skip) { - this.schema.props = { - ...this.schema.props, - [val.key + '']: val.newValue, - } - } - - this.emitter?.emit('onPropChange', val); - } - - onVisibleChange(fn: any) { - this.emitter.on('onVisibleChange', fn); - return () => { - this.emitter.off('onVisibleChange', fn); - } - } - - emitVisibleChange(val: boolean) { - this.emitter?.emit('onVisibleChange', val); - } - export() { - return this.schema; - } -} \ No newline at end of file diff --git a/packages/renderer-core/tests/utils/react-env-init.ts b/packages/renderer-core/tests/utils/react-env-init.ts deleted file mode 100644 index 78fbb2fa5..000000000 --- a/packages/renderer-core/tests/utils/react-env-init.ts +++ /dev/null @@ -1,66 +0,0 @@ -import React, { Component, PureComponent, createElement, createContext, forwardRef, ReactInstance, ContextType } from 'react'; -import ReactDOM from 'react-dom'; -import { - adapter, - pageRendererFactory, - componentRendererFactory, - blockRendererFactory, - addonRendererFactory, - tempRendererFactory, - // rendererFactory, - // types, -} from '../../src'; -import ConfigProvider from '@alifd/next/lib/config-provider'; - -(window as any).React = React; -(window as any).ReactDom = ReactDOM; - -adapter.setRuntime({ - Component, - PureComponent, - createContext, - createElement, - forwardRef, - findDOMNode: ReactDOM.findDOMNode, -}); - -adapter.setRenderers({ - PageRenderer: pageRendererFactory(), - ComponentRenderer: componentRendererFactory(), - BlockRenderer: blockRendererFactory(), - AddonRenderer: addonRendererFactory(), - TempRenderer: tempRendererFactory(), - DivRenderer: blockRendererFactory(), -}); - -adapter.setConfigProvider(ConfigProvider); - -// function factory(): types.IRenderComponent { -// const Renderer = rendererFactory(); -// return class ReactRenderer extends Renderer implements Component { -// readonly props: types.IRendererProps; - -// context: ContextType; - -// setState: ( -// state: types.IRendererState, -// callback?: () => void, -// ) => void; - -// forceUpdate: (callback?: () => void) => void; - -// refs: { -// [key: string]: ReactInstance; -// }; - -// constructor(props: types.IRendererProps, context: ContextType) { -// super(props, context); -// } - -// isValidComponent(obj: any) { -// return obj?.prototype?.isReactComponent || obj?.prototype instanceof Component; -// } -// }; -// } - -// export default factory(); diff --git a/packages/renderer-core/tests/utils/request.test.ts b/packages/renderer-core/tests/utils/request.test.ts deleted file mode 100644 index d0bfeb5aa..000000000 --- a/packages/renderer-core/tests/utils/request.test.ts +++ /dev/null @@ -1,159 +0,0 @@ -// @ts-nocheck -const mockSerializeParams = jest.fn(); -jest.mock('../../src/utils/common', () => { - return { - serializeParams: (params) => { - return mockSerializeParams(params); - }, - }; - }); -const mockFetchJsonp = jest.fn(); -jest.mock('fetch-jsonp', () => { - return (uri, otherProps) => { - mockFetchJsonp(uri, otherProps); - return Promise.resolve({ - json: () => { - return Promise.resolve({ data: [1, 2, 3]}); - } , - ok: true, - }); - } -}); - -import { get, post, buildUrl, request, jsonp } from '../../src/utils/request'; - - -describe('test utils/request.ts ', () => { - - it('buildUrl should be working properly', () => { - mockSerializeParams.mockImplementation((params) => { - return 'serializedParams=serializedParams'; - }); - expect(buildUrl('mockDataApi', { a: 1, b: 'a', c: []})).toBe('mockDataApi?serializedParams=serializedParams'); - expect(buildUrl('mockDataApi?existingParamA=valueA', { a: 1, b: 'a', c: []})).toBe('mockDataApi?existingParamA=valueA&serializedParams=serializedParams'); - mockSerializeParams.mockClear(); - - mockSerializeParams.mockImplementation((params) => { - return undefined; - }); - expect(buildUrl('mockDataApi', { a: 1, b: 'a', c: []})).toBe('mockDataApi'); - mockSerializeParams.mockClear(); - }); - - it('request should be working properly', () => { - const fetchMock = jest - .spyOn(global, 'fetch') - .mockImplementation(() => - Promise.resolve({ - json: () => Promise.resolve([]) , - status: 200, - }) - ); - - request('https://someradomurl/api/list', 'GET', {}, {}, {}).then((response) => { - expect(fetchMock).toBeCalledWith('https://someradomurl/api/list', { body: {}, credentials: 'include', headers: {}, method: 'GET'}); - }).catch((error) => { - console.error(error); - }); - - }); - - it('get should be working properly', () => { - const fetchMock = jest - .spyOn(global, 'fetch') - .mockImplementation(() => - Promise.resolve({ - json: () => Promise.resolve([]) , - status: 200, - }) - ); - - get('https://someradomurl/api/list', {}, {}, {}).then((response) => { - expect(fetchMock).toBeCalledWith( - 'https://someradomurl/api/list', - { - body: null, - headers: { Accept: 'application/json' }, - method: 'GET', - credentials: 'include', - }); - }).catch((error) => { - console.error(error); - }); - - }); - - it('post should be working properly', () => { - const fetchMock = jest - .spyOn(global, 'fetch') - .mockImplementation(() => - Promise.resolve({ - json: () => Promise.resolve([]) , - status: 200, - }) - ); - - post('https://someradomurl/api/list', { a: 1, b: 'a', c: [] }, { 'Content-Type': 'application/json' }, {}).then((response) => { - expect(fetchMock).toBeCalledWith( - 'https://someradomurl/api/list', - { - body: '{"a":1,"b":"a","c":[]}', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - method: 'POST', - credentials: 'include', - }); - }).catch((error) => { - console.error(error); - }); - - - post('https://someradomurl/api/list', [ 1, 2, 3, 4 ], {}, {}).then((response) => { - expect(fetchMock).toBeCalledWith( - 'https://someradomurl/api/list', - { - body: '[1,2,3,4]', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/x-www-form-urlencoded', - }, - method: 'POST', - credentials: 'include', - }); - }).catch((error) => { - console.error(error); - }); - - mockSerializeParams.mockImplementation((params) => { - return 'serializedParams=serializedParams'; - }); - post('https://someradomurl/api/list', { a: 1, b: 'a', c: [] }, {}, {}).then((response) => { - expect(fetchMock).toBeCalledWith( - 'https://someradomurl/api/list', - { - body: 'serializedParams=serializedParams', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/x-www-form-urlencoded', - }, - method: 'POST', - credentials: 'include', - }); - mockSerializeParams.mockClear(); - }).catch((error) => { - console.error(error); - }); - - }); - it('jsonp should be working properly', () => { - mockSerializeParams.mockImplementation((params) => { - return 'params'; - }); - jsonp('https://someradomurl/api/list', {}, { otherParam1: '123'}).catch(() => { - expect(mockFetchJsonp).toBeCalledWith('https://someradomurl/api/list?params', { timeout: 5000, otherParam1: '123' }); - mockSerializeParams.mockClear(); - }); - }); -}); diff --git a/packages/renderer-core/tsconfig.json b/packages/renderer-core/tsconfig.json deleted file mode 100644 index aee32500e..000000000 --- a/packages/renderer-core/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "lib", - "jsx": "react" - }, - "include": ["./src/"], -} diff --git a/runtime/router/src/history.ts b/runtime/router/src/history.ts index 83a0b98ba..8ccd4455a 100644 --- a/runtime/router/src/history.ts +++ b/runtime/router/src/history.ts @@ -1,6 +1,6 @@ import { useEvent } from '@alilc/renderer-core'; -export type HistoryState = Record; +export type HistoryState = History['state']; export type HistoryLocation = string; export enum NavigationType {