diff --git a/jsconfig.json b/jsconfig.json
index 9011eef09..3f7722f09 100644
--- a/jsconfig.json
+++ b/jsconfig.json
@@ -7,6 +7,7 @@
"@opentiny/tiny-engine": ["packages/design-core/index.js"],
"@opentiny/tiny-engine-meta-register": ["packages/register/src/index.js"],
"@opentiny/tiny-engine-canvas": ["packages/canvas/src/index.js"],
+ "@opentiny/tiny-engine-renderer": ["packages/renderer/src/index.js"],
"@opentiny/tiny-engine-plugin-materials": ["packages/plugins/materials/index"],
"@opentiny/tiny-engine-plugin-state": ["packages/plugins/state/index"],
"@opentiny/tiny-engine-plugin-script": ["packages/plugins/script/index"],
diff --git a/mockServer/src/mock/get/app-center/v1/apps/schema/918.json b/mockServer/src/mock/get/app-center/v1/apps/schema/918.json
index 17656258e..6a49846a8 100644
--- a/mockServer/src/mock/get/app-center/v1/apps/schema/918.json
+++ b/mockServer/src/mock/get/app-center/v1/apps/schema/918.json
@@ -2130,14 +2130,6 @@
"main": ""
}
},
- {
- "name": "npm",
- "type": "function",
- "content": {
- "type": "JSFunction",
- "value": "''"
- }
- },
{
"name": "Pager",
"type": "npm",
diff --git a/packages/build/vite-config/src/vite-plugins/devAliasPlugin.js b/packages/build/vite-config/src/vite-plugins/devAliasPlugin.js
index 2f2cd3b58..9eb8e87b4 100644
--- a/packages/build/vite-config/src/vite-plugins/devAliasPlugin.js
+++ b/packages/build/vite-config/src/vite-plugins/devAliasPlugin.js
@@ -55,6 +55,7 @@ const getDevAlias = (useSourceAlias) => {
'@opentiny/tiny-engine-svgs': path.resolve(basePath, 'packages/svgs/index.js'),
'@opentiny/tiny-engine-canvas/render': path.resolve(basePath, 'packages/canvas/render/index.js'),
'@opentiny/tiny-engine-canvas': path.resolve(basePath, 'packages/canvas/index.js'),
+ '@opentiny/tiny-engine-renderer': path.resolve(basePath, 'packages/renderer/src/index.js'),
'@opentiny/tiny-engine-utils': path.resolve(basePath, 'packages/utils/src/index.js'),
'@opentiny/tiny-engine-webcomponent-core': path.resolve(basePath, 'packages/webcomponent/src/lib.js'),
'@opentiny/tiny-engine-i18n-host': path.resolve(basePath, 'packages/i18n/src/lib.js'),
diff --git a/packages/canvas/container/src/container.js b/packages/canvas/container/src/container.js
index d758c61a4..a46f0e83b 100644
--- a/packages/canvas/container/src/container.js
+++ b/packages/canvas/container/src/container.js
@@ -21,7 +21,7 @@ import {
} from '../../common'
import { useCanvas, useLayout, useResource, useTranslate, useMaterial } from '@opentiny/tiny-engine-meta-register'
import { isVsCodeEnv } from '@opentiny/tiny-engine-common/js/environments'
-import Builtin from '../../render/src/builtin/builtin.json' //TODO 画布内外应该分开
+import { BuiltinBundle } from '@opentiny/tiny-engine-renderer'
export const POSITION = Object.freeze({
TOP: 'top',
@@ -901,7 +901,7 @@ export const canvasApi = {
setDesignMode,
getDocument,
canvasDispatch,
- Builtin,
+ BuiltinBundle,
setDataSourceMap: (...args) => {
return canvasState.renderer.setDataSourceMap(...args)
},
diff --git a/packages/canvas/package.json b/packages/canvas/package.json
index bb80e1e1c..f1a74596f 100644
--- a/packages/canvas/package.json
+++ b/packages/canvas/package.json
@@ -36,12 +36,12 @@
"type": "module",
"dependencies": {
"@babel/core": "7.18.13",
- "@opentiny/tiny-engine-builtin-component": "workspace:*",
"@opentiny/tiny-engine-common": "workspace:*",
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@opentiny/tiny-engine-meta-register": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/tiny-engine-webcomponent-core": "workspace:*",
+ "@opentiny/tiny-engine-renderer": "workspace:*",
"@vue/babel-plugin-jsx": "1.1.1",
"@vue/shared": "^3.3.4",
"@vueuse/core": "^9.6.0"
diff --git a/packages/canvas/render/src/lowcode.js b/packages/canvas/render/src/lowcode.js
index 27214c2e7..d533c8723 100644
--- a/packages/canvas/render/src/lowcode.js
+++ b/packages/canvas/render/src/lowcode.js
@@ -12,8 +12,9 @@
import { getCurrentInstance, nextTick, provide, inject } from 'vue'
import { I18nInjectionKey } from 'vue-i18n'
-import { api } from './RenderMain'
-import { collectionMethodsMap, generateFn, globalNotify } from './render'
+import { api } from '@opentiny/tiny-engine-renderer'
+
+const { getCollectionMethodsMap, generateFn, globalNotify, getUtils, getDataSourceMap } = api
export const lowcodeWrap = (props, context) => {
const global = {}
@@ -49,10 +50,10 @@ export const lowcodeWrap = (props, context) => {
location: { get: location },
history: { get: history },
utils: {
- get: api.getUtils
+ get: getUtils
},
bridge: { get: () => ({}) },
- dataSourceMap: { get: api.getDataSourceMap },
+ dataSourceMap: { get: getDataSourceMap },
$: { get: () => ref }
})
@@ -61,7 +62,7 @@ export const lowcodeWrap = (props, context) => {
const fnName = fn.name
if (fn.toString().includes('return this')) {
return () => global
- } else if (fnName && collectionMethodsMap[fnName.slice(0, -1)]) {
+ } else if (fnName && getCollectionMethodsMap()[fnName.slice(0, -1)]) {
// 这里区块打包的时候会在方法名称后面多加一个字符串,所以此处需要截取下函数名称
fn.realName = fnName.slice(0, -1)
return generateFn(fn)
diff --git a/packages/canvas/render/src/runner.js b/packages/canvas/render/src/runner.js
index deb1ce1b9..d088102d9 100644
--- a/packages/canvas/render/src/runner.js
+++ b/packages/canvas/render/src/runner.js
@@ -13,7 +13,7 @@
import { createApp } from 'vue'
import { addScript, addStyle, dynamicImportComponents, updateDependencies } from '../../common'
import TinyI18nHost, { I18nInjectionKey } from '@opentiny/tiny-engine-common/js/i18n'
-import Main, { api } from './RenderMain'
+import Main, { api } from '@opentiny/tiny-engine-renderer'
import lowcode from './lowcode'
import { supportUmdBlock } from './supportUmdBlock'
diff --git a/packages/canvas/render/src/supportUmdBlock.js b/packages/canvas/render/src/supportUmdBlock.js
index b07d50658..60c8a3dcf 100644
--- a/packages/canvas/render/src/supportUmdBlock.js
+++ b/packages/canvas/render/src/supportUmdBlock.js
@@ -5,7 +5,9 @@ import * as TinyVueIcon from '@opentiny/vue-icon'
import TinyVue from '@opentiny/vue'
import TinyI18nHost from '@opentiny/tiny-engine-common/js/i18n'
import { camelize, capitalize } from '@vue/shared'
-import { blockSlotDataMap, getComponent } from './render'
+import { api } from '@opentiny/tiny-engine-renderer'
+
+const { getBlockSlotDataMap, getComponent } = api
// 和 @opentiny/tiny-engine-block-build 打包umd方式相适配
export function supportUmdBlock() {
@@ -33,6 +35,8 @@ export function supportUmdBlock() {
// 如果是作用域插槽,则获取作用域插槽传递过来的参数
if (slotData) {
+ const blockSlotDataMap = getBlockSlotDataMap()
+
if (blockSlotDataMap[blockName]) {
blockSlotDataMap[blockName][slotName] = slotData
} else {
diff --git a/packages/plugins/materials/src/composable/useMaterial.js b/packages/plugins/materials/src/composable/useMaterial.js
index 91365bfea..8c3dd971e 100644
--- a/packages/plugins/materials/src/composable/useMaterial.js
+++ b/packages/plugins/materials/src/composable/useMaterial.js
@@ -404,9 +404,9 @@ const updateCanvasDependencies = (blocks) => {
}
const initBuiltinMaterial = () => {
- const { Builtin } = useCanvas().canvasApi.value
+ const { BuiltinBundle } = useCanvas().canvasApi.value
// 添加画布物料
- addMaterials(Builtin.data.materials)
+ addMaterials(BuiltinBundle.data.materials)
// 添加builtin-component NPM包物料
addMaterials(BuiltinComponentMaterials)
}
diff --git a/packages/renderer/.eslintrc.cjs b/packages/renderer/.eslintrc.cjs
new file mode 100644
index 000000000..7e042cff2
--- /dev/null
+++ b/packages/renderer/.eslintrc.cjs
@@ -0,0 +1,42 @@
+/**
+* Copyright (c) 2023 - present TinyEngine Authors.
+* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
+*
+* Use of this source code is governed by an MIT-style license.
+*
+* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+*
+*/
+
+module.exports = {
+ env: {
+ browser: true,
+ es2015: true,
+ node: true,
+ jest: true
+ },
+ extends: ['eslint:recommended', 'plugin:vue/vue3-essential'],
+ parser: 'vue-eslint-parser',
+ parserOptions: {
+ parser: '@babel/eslint-parser',
+ ecmaVersion: 'latest',
+ sourceType: 'module',
+ requireConfigFile: false,
+ babelOptions: {
+ parserOpts: {
+ plugins: ['jsx']
+ }
+ }
+ },
+ plugins: ['vue'],
+ rules: {
+ 'no-console': 'error',
+ 'no-debugger': 'error',
+ 'space-before-function-paren': 'off',
+ 'vue/multi-word-component-names': 'off',
+ 'no-use-before-define': 'error',
+ 'no-unused-vars': ['error', { ignoreRestSiblings: true, varsIgnorePattern: '^_', argsIgnorePattern: '^_' }]
+ }
+}
diff --git a/packages/renderer/.gitignore b/packages/renderer/.gitignore
new file mode 100644
index 000000000..a547bf36d
--- /dev/null
+++ b/packages/renderer/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/packages/renderer/README.md b/packages/renderer/README.md
new file mode 100644
index 000000000..9a22e301c
--- /dev/null
+++ b/packages/renderer/README.md
@@ -0,0 +1,45 @@
+# tiny-engine-renderer
+
+## Introduction
+
+A Vue3 renderer for tiny-engine.
+
+## Usage
+
+```javascript
+// xxx.vue
+import { h } from 'vue'
+import Main, { api } from '@opentiny/tiny-engine-renderer'
+
+const getSchema = () => {
+ return newPromise((resolve) => {
+ setTimeout(() => {
+ const data = {
+ state: {},
+ children: [
+ {
+ componentName: 'Text',
+ props: {
+ text: 'Title'
+ }
+ }
+ ]
+ }
+ resolve(data)
+ }, 100)
+ })
+}
+
+export default {
+ setup() {
+ onMounted(async () => {
+ const schema = await getSchema()
+
+ api.setSchema(schema)
+ })
+ },
+ render() {
+ return h(Main)
+ }
+}
+```
diff --git a/packages/renderer/README.zh-CN.md b/packages/renderer/README.zh-CN.md
new file mode 100644
index 000000000..360ae0378
--- /dev/null
+++ b/packages/renderer/README.zh-CN.md
@@ -0,0 +1,45 @@
+# tiny-engine-renderer
+
+## 简介
+
+基于 Vue 3 的低代码渲染引擎。
+
+## 用法
+
+```javascript
+// xxx.vue
+import { h } from 'vue'
+import Main, { api } from '@opentiny/tiny-engine-renderer'
+
+const getSchema = () => {
+ return newPromise((resolve) => {
+ setTimeout(() => {
+ const data = {
+ state: {},
+ children: [
+ {
+ componentName: 'Text',
+ props: {
+ text: '标题'
+ }
+ }
+ ]
+ }
+ resolve(data)
+ }, 100)
+ })
+}
+
+export default {
+ setup() {
+ onMounted(async () => {
+ const schema = await getSchema()
+
+ api.setSchema(schema)
+ })
+ },
+ render() {
+ return h(Main)
+ }
+}
+```
diff --git a/packages/renderer/package.json b/packages/renderer/package.json
new file mode 100644
index 000000000..8ee891fec
--- /dev/null
+++ b/packages/renderer/package.json
@@ -0,0 +1,58 @@
+{
+ "name": "@opentiny/tiny-engine-renderer",
+ "version": "2.0.0-alpha.4",
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "main": "dist/index.js",
+ "module": "dist/index.js",
+ "files": [
+ "dist"
+ ],
+ "exports": {
+ ".": {
+ "import": "./dist/index.js"
+ }
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/opentiny/tiny-engine",
+ "directory": "packages/renderer"
+ },
+ "bugs": {
+ "url": "https://github.com/opentiny/tiny-engine/issues"
+ },
+ "author": "OpenTiny Team",
+ "license": "MIT",
+ "homepage": "https://opentiny.design/tiny-engine",
+ "type": "module",
+ "dependencies": {
+ "@babel/core": "7.18.13",
+ "@opentiny/tiny-engine-builtin-component": "workspace:*",
+ "@opentiny/tiny-engine-i18n-host": "workspace:*",
+ "@opentiny/tiny-engine-meta-register": "workspace:*",
+ "@opentiny/tiny-engine-utils": "workspace:*",
+ "@vue/babel-plugin-jsx": "1.1.1",
+ "@vue/shared": "^3.3.4",
+ "@vueuse/core": "^9.6.0"
+ },
+ "devDependencies": {
+ "@opentiny/tiny-engine-vite-plugin-meta-comments": "workspace:*",
+ "@vitejs/plugin-vue": "^5.1.2",
+ "@vitejs/plugin-vue-jsx": "^4.0.1",
+ "rollup-plugin-polyfill-node": "^0.13.0",
+ "vite": "^5.4.2"
+ },
+ "peerDependencies": {
+ "@opentiny/vue": "^3.14.0",
+ "@opentiny/vue-icon": "^3.14.0",
+ "@opentiny/vue-renderless": "^3.14.0",
+ "vue": "^3.4.15",
+ "vue-i18n": "^9.9.0"
+ }
+}
diff --git a/packages/renderer/src/CanvasEmpty.vue b/packages/renderer/src/CanvasEmpty.vue
new file mode 100644
index 000000000..c6dc2c454
--- /dev/null
+++ b/packages/renderer/src/CanvasEmpty.vue
@@ -0,0 +1,14 @@
+
+ 从左侧面板拖入组件,以构建页面
+
+
+
diff --git a/packages/canvas/render/src/RenderMain.js b/packages/renderer/src/RenderMain.js
similarity index 89%
rename from packages/canvas/render/src/RenderMain.js
rename to packages/renderer/src/RenderMain.js
index bb41b5fc7..e238ffb0d 100644
--- a/packages/canvas/render/src/RenderMain.js
+++ b/packages/renderer/src/RenderMain.js
@@ -16,7 +16,17 @@ import TinyVue from '@opentiny/vue'
import * as TinyVueIcon from '@opentiny/vue-icon'
import { useBroadcastChannel } from '@vueuse/core'
import { constants, utils as commonUtils } from '@opentiny/tiny-engine-utils'
-import renderer, { parseData, setConfigure, setController, globalNotify, isStateAccessor } from './render'
+import renderer, {
+ parseData,
+ setConfigure,
+ setController,
+ globalNotify,
+ isStateAccessor,
+ generateFn,
+ getCollectionMethodsMap,
+ getBlockSlotDataMap,
+ getComponent
+} from './render'
import {
getNode as getNodeById,
clearNodes,
@@ -43,7 +53,7 @@ const reset = (obj) => {
const refreshKey = ref(0)
const methods = {}
const schema = reactive({})
-const state = shallowReactive({})
+const state = reactive({})
const bridge = {}
const utils = {}
const props = {}
@@ -67,13 +77,27 @@ watchEffect(() => {
})
})
+const dynamicImport = async ({ name, content }) => {
+ const { cdnLink, destructuring, exportName } = content
+
+ if (!cdnLink) return
+
+ const module = await import(/* @vite-ignore */ cdnLink)
+
+ return {
+ name,
+ module: destructuring ? module[exportName] : module
+ }
+}
+
const getUtils = () => utils
-const setUtils = (data, clear, isForceRefresh) => {
+const setUtils = async (data, clear, isForceRefresh) => {
if (clear) {
reset(utils)
}
const utilsCollection = {}
+ const npmUtilPromises = []
// 目前画布还不具备远程加载utils工具类的功能,目前只能加载TinyVue组件库中的组件工具
data?.forEach((item) => {
const util = TinyVue[item.content.exportName]
@@ -92,7 +116,18 @@ const setUtils = (data, clear, isForceRefresh) => {
const defaultFn = () => {}
utilsCollection[item.name] = generateFunction(item.content.value, context) || defaultFn
}
+
+ if (item.type === 'npm' && item.content.cdnLink) {
+ npmUtilPromises.push(dynamicImport(item))
+ }
+ })
+
+ const npmUtils = await Promise.all(npmUtilPromises)
+
+ npmUtils.forEach(({ name, module }) => {
+ utilsCollection[name] = module
})
+
Object.assign(utils, utilsCollection)
// 因为工具类并不具有响应式行为,所以需要通过修改key来强制刷新画布
@@ -358,6 +393,7 @@ const setSchema = async (data) => {
const getNode = (id, parent) => (id ? getNodeById(id, parent) : schema)
+// 设置自定义渲染器
let canvasRenderer = null
const defaultRenderer = function () {
@@ -441,6 +477,7 @@ export const api = {
getProps,
setProps,
getContext,
+ setContext,
getNode,
getRoot,
setPagecss,
@@ -454,6 +491,34 @@ export const api = {
setNode,
getRenderer,
setRenderer,
+ globalNotify,
+ generateFn,
+ getCollectionMethodsMap,
+ getBlockSlotDataMap,
+ getComponent,
getDesignMode,
setDesignMode
}
+
+/**
+ * schema和元数据组装context
+ * @param {*} schema
+ * @param {*} metaData
+ */
+export const generateContext = async (schema, metaData) => {
+ const { globalState, utils, dataSource } = metaData
+
+ if (Array.isArray(globalState)) {
+ setGlobalState(globalState)
+ }
+
+ if (Array.isArray(utils)) {
+ await setUtils(utils)
+ }
+
+ if (Array.isArray(dataSource.list)) {
+ await setDataSourceMap(dataSource.list)
+ }
+
+ await setSchema(schema)
+}
diff --git a/packages/canvas/render/src/builtin/CanvasBox.vue b/packages/renderer/src/builtin/CanvasBox.vue
similarity index 100%
rename from packages/canvas/render/src/builtin/CanvasBox.vue
rename to packages/renderer/src/builtin/CanvasBox.vue
diff --git a/packages/canvas/render/src/builtin/CanvasCollection.js b/packages/renderer/src/builtin/CanvasCollection.js
similarity index 97%
rename from packages/canvas/render/src/builtin/CanvasCollection.js
rename to packages/renderer/src/builtin/CanvasCollection.js
index b5cb22762..75502aa16 100644
--- a/packages/canvas/render/src/builtin/CanvasCollection.js
+++ b/packages/renderer/src/builtin/CanvasCollection.js
@@ -10,7 +10,6 @@
*
*/
-import { getController } from '../render'
import { api } from '../RenderMain'
import { useModal } from '@opentiny/tiny-engine-meta-register'
@@ -45,7 +44,7 @@ const genRemoteMethodToLifeSetup = (variableName, sourceRef, pageSchema) => {
const removeState = (pageSchema, variableName) => {
delete pageSchema.state[variableName]
- const { parse, traverse, generate } = getController().ast
+ const { parse, traverse, generate } = api.getController().ast
const setupFn = pageSchema.lifeCycles?.setup?.value
try {
@@ -76,7 +75,7 @@ const defaultHandlerTemplate = ({ node, sourceRef, schemaId, pageSchema }) => {
const genVarName = (schemaId) => `${NAME_PREFIX.loop}${schemaId}`
const updateNode = () => {
- const { configure } = getController().getMaterial(node?.componentName)
+ const { configure } = api.getController().getMaterial(node?.componentName)
if (!configure?.loop) {
return
diff --git a/packages/canvas/render/src/builtin/CanvasCollection.vue b/packages/renderer/src/builtin/CanvasCollection.vue
similarity index 94%
rename from packages/canvas/render/src/builtin/CanvasCollection.vue
rename to packages/renderer/src/builtin/CanvasCollection.vue
index d033924f3..1242a879f 100644
--- a/packages/canvas/render/src/builtin/CanvasCollection.vue
+++ b/packages/renderer/src/builtin/CanvasCollection.vue
@@ -8,13 +8,13 @@