From 450d08e5008300bd3b4af46be56a08f979aff8e7 Mon Sep 17 00:00:00 2001 From: 1ncounter <1ncounter.100@gmail.com> Date: Mon, 1 Jul 2024 19:03:35 +0800 Subject: [PATCH] fix: renderer bugs fix --- packages/react-renderer/src/api/app.tsx | 3 +- packages/react-renderer/src/api/component.tsx | 19 +- packages/react-renderer/src/api/context.ts | 9 +- packages/react-renderer/src/api/types.ts | 11 + packages/react-renderer/src/app/view.tsx | 6 +- packages/react-renderer/src/index.ts | 8 +- packages/react-renderer/src/router/route.tsx | 7 +- .../react-renderer/src/runtime/context.ts | 11 - .../{schema.tsx => createComponent.tsx} | 61 ++--- .../runtime/{components.tsx => elements.tsx} | 208 ++++++++++-------- packages/react-renderer/src/runtime/index.ts | 4 +- .../src/services/code-runtime/codeRuntime.ts | 128 +++++++++++ .../code-runtime/codeRuntimeService.ts | 127 ++--------- .../src/services/code-runtime/codeScope.ts | 41 ++-- .../src/services/code-runtime/evaluate.ts | 7 + .../src/services/code-runtime/index.ts | 1 + .../src/services/extension/boosts.ts | 12 +- .../src/services/lifeCycleService.ts | 28 ++- .../src/services/model/componentTreeModel.ts | 73 +++--- .../model/componentTreeModelService.ts | 36 ++- .../src/services/runtimeIntlService.ts | 6 +- .../src/services/runtimeUtilService.ts | 10 +- .../src/services/widget/widget.ts | 10 +- packages/renderer-core/src/types.ts | 8 +- packages/renderer-core/src/utils/evaluate.ts | 5 - packages/shared/src/types/specs/runtime.ts | 3 +- 26 files changed, 469 insertions(+), 373 deletions(-) create mode 100644 packages/react-renderer/src/api/types.ts delete mode 100644 packages/react-renderer/src/runtime/context.ts rename packages/react-renderer/src/runtime/{schema.tsx => createComponent.tsx} (70%) rename packages/react-renderer/src/runtime/{components.tsx => elements.tsx} (53%) create mode 100644 packages/renderer-core/src/services/code-runtime/codeRuntime.ts create mode 100644 packages/renderer-core/src/services/code-runtime/evaluate.ts delete mode 100644 packages/renderer-core/src/utils/evaluate.ts diff --git a/packages/react-renderer/src/api/app.tsx b/packages/react-renderer/src/api/app.tsx index 0e1e112f0..a5c9007ac 100644 --- a/packages/react-renderer/src/api/app.tsx +++ b/packages/react-renderer/src/api/app.tsx @@ -1,7 +1,8 @@ import { createRenderer } from '@alilc/lowcode-renderer-core'; import { type Root, createRoot } from 'react-dom/client'; -import { type ReactAppOptions, RendererContext } from './context'; +import { RendererContext } from './context'; import { ApplicationView, boosts } from '../app'; +import { type ReactAppOptions } from './types'; export const createApp = async (options: ReactAppOptions) => { return createRenderer(async (context) => { diff --git a/packages/react-renderer/src/api/component.tsx b/packages/react-renderer/src/api/component.tsx index f829c2901..ca20cbd24 100644 --- a/packages/react-renderer/src/api/component.tsx +++ b/packages/react-renderer/src/api/component.tsx @@ -1,17 +1,26 @@ -import { createRenderer, type AppOptions } from '@alilc/lowcode-renderer-core'; +import { createRenderer } from '@alilc/lowcode-renderer-core'; import { FunctionComponent } from 'react'; -import { type LowCodeComponentProps, createComponentBySchema } from '../runtime/schema'; -import { RendererContext } from '../api/context'; +import { + type LowCodeComponentProps, + createComponent as createSchemaComponent, +} from '../runtime/createComponent'; +import { RendererContext } from './context'; +import { type ReactAppOptions } from './types'; interface Render { toComponent(): FunctionComponent; } -export async function createComponent(options: AppOptions) { +export async function createComponent(options: ReactAppOptions) { const creator = createRenderer((context) => { const { schema } = context; + const componentsTree = schema.get('componentsTree')[0]; + + const LowCodeComponent = createSchemaComponent(componentsTree, { + displayName: componentsTree.componentName, + ...options.component, + }); - const LowCodeComponent = createComponentBySchema(schema.get('componentsTree')[0]); const contextValue = { ...context, options }; function Component(props: LowCodeComponentProps) { diff --git a/packages/react-renderer/src/api/context.ts b/packages/react-renderer/src/api/context.ts index ccd666ee7..cfc9c2de9 100644 --- a/packages/react-renderer/src/api/context.ts +++ b/packages/react-renderer/src/api/context.ts @@ -1,9 +1,6 @@ -import { type ComponentType, createContext, useContext } from 'react'; -import { type AppOptions, type RenderContext } from '@alilc/lowcode-renderer-core'; - -export interface ReactAppOptions extends AppOptions { - faultComponent?: ComponentType; -} +import { createContext, useContext } from 'react'; +import { type RenderContext } from '@alilc/lowcode-renderer-core'; +import { type ReactAppOptions } from './types'; export const RendererContext = createContext( undefined!, diff --git a/packages/react-renderer/src/api/types.ts b/packages/react-renderer/src/api/types.ts new file mode 100644 index 000000000..2c73be30f --- /dev/null +++ b/packages/react-renderer/src/api/types.ts @@ -0,0 +1,11 @@ +import { type AppOptions } from '@alilc/lowcode-renderer-core'; +import { type ComponentType } from 'react'; +import { type ComponentOptions } from '../runtime/createComponent'; + +export interface ReactAppOptions extends AppOptions { + component?: Pick< + ComponentOptions, + 'beforeElementCreate' | 'elementCreated' | 'componentRefAttached' + >; + faultComponent?: ComponentType; +} diff --git a/packages/react-renderer/src/app/view.tsx b/packages/react-renderer/src/app/view.tsx index 687101fd4..e8731b3c8 100644 --- a/packages/react-renderer/src/app/view.tsx +++ b/packages/react-renderer/src/app/view.tsx @@ -1,10 +1,10 @@ import { useRendererContext } from '../api/context'; -import { getComponentByName } from '../runtime/schema'; +import { getComponentByName } from '../runtime/createComponent'; import { boosts } from './boosts'; export function ApplicationView() { const rendererContext = useRendererContext(); - const { schema } = rendererContext; + const { schema, options } = rendererContext; const appWrappers = boosts.getAppWrappers(); const Outlet = boosts.getOutlet(); @@ -16,7 +16,7 @@ export function ApplicationView() { if (layoutConfig) { const componentName = layoutConfig.componentName; - const Layout = getComponentByName(componentName, rendererContext); + const Layout = getComponentByName(componentName, rendererContext, options.component); if (Layout) { const layoutProps: any = layoutConfig.props ?? {}; diff --git a/packages/react-renderer/src/index.ts b/packages/react-renderer/src/index.ts index fda978f64..04f2f6bf3 100644 --- a/packages/react-renderer/src/index.ts +++ b/packages/react-renderer/src/index.ts @@ -6,5 +6,11 @@ export * from './router'; export { LifecyclePhase } from '@alilc/lowcode-renderer-core'; export type { Spec, ProCodeComponent, LowCodeComponent } from '@alilc/lowcode-shared'; -export type { PackageLoader, CodeScope, Plugin } from '@alilc/lowcode-renderer-core'; +export type { + PackageLoader, + CodeScope, + Plugin, + ModelDataSourceCreator, + ModelStateCreator, +} from '@alilc/lowcode-renderer-core'; export type { ReactRendererBoostsApi } from './app/boosts'; diff --git a/packages/react-renderer/src/router/route.tsx b/packages/react-renderer/src/router/route.tsx index 615008aef..abf345479 100644 --- a/packages/react-renderer/src/router/route.tsx +++ b/packages/react-renderer/src/router/route.tsx @@ -2,12 +2,12 @@ import { useMemo } from 'react'; import { useRendererContext } from '../api/context'; import { OutletProps } from '../app/boosts'; import { useRouteLocation } from './context'; -import { createComponentBySchema } from '../runtime/schema'; +import { createComponent } from '../runtime/createComponent'; export function RouteOutlet(props: OutletProps) { const context = useRendererContext(); const location = useRouteLocation(); - const { schema, packageManager } = context; + const { schema, packageManager, options } = context; const pageConfig = useMemo(() => { const pages = schema.get('pages') ?? []; @@ -27,11 +27,12 @@ export function RouteOutlet(props: OutletProps) { const componentsMap = schema.get('componentsMap'); packageManager.resolveComponentMaps(componentsMap); - const LowCodeComponent = createComponentBySchema(pageConfig.mappingId, { + const LowCodeComponent = createComponent(pageConfig.mappingId, { displayName: pageConfig.id, modelOptions: { metadata: pageConfig, }, + ...options.component, }); return ; diff --git a/packages/react-renderer/src/runtime/context.ts b/packages/react-renderer/src/runtime/context.ts deleted file mode 100644 index ecdc79365..000000000 --- a/packages/react-renderer/src/runtime/context.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IComponentTreeModel } from '@alilc/lowcode-renderer-core'; -import { createContext, useContext, type ReactInstance } from 'react'; -import { type ReactComponent } from './components'; - -export const ModelContext = createContext>( - undefined!, -); - -export const useModel = () => useContext(ModelContext); - -export const ModelContextProvider = ModelContext.Provider; diff --git a/packages/react-renderer/src/runtime/schema.tsx b/packages/react-renderer/src/runtime/createComponent.tsx similarity index 70% rename from packages/react-renderer/src/runtime/schema.tsx rename to packages/react-renderer/src/runtime/createComponent.tsx index 336c11d27..06ba5dfa3 100644 --- a/packages/react-renderer/src/runtime/schema.tsx +++ b/packages/react-renderer/src/runtime/createComponent.tsx @@ -3,23 +3,23 @@ import { forwardRef, useRef, useEffect } from 'react'; import { isValidElementType } from 'react-is'; import { useRendererContext } from '../api/context'; import { reactiveStateFactory } from './reactiveState'; -import { type ReactComponent, type ReactWidget, createElementByWidget } from './components'; -import { ModelContextProvider } from './context'; +import { type ReactComponent, type ReactWidget, createElementByWidget } from './elements'; import { appendExternalStyle } from '../utils/element'; import type { RenderContext, IComponentTreeModel, - ComponentTreeModelOptions, + CreateComponentTreeModelOptions, } from '@alilc/lowcode-renderer-core'; -import type { ReactInstance, CSSProperties, ForwardedRef } from 'react'; +import type { ReactInstance, CSSProperties, ForwardedRef, ReactNode } from 'react'; export interface ComponentOptions { displayName?: string; - modelOptions?: ComponentTreeModelOptions; + modelOptions?: Pick; - widgetCreated?(widget: ReactWidget): void; - componentRefAttached?(widget: ReactWidget, instance: ReactInstance): void; + beforeElementCreate?(widget: ReactWidget): ReactWidget; + elementCreated?(widget: ReactWidget, element: ReactNode): ReactNode; + componentRefAttached?(widget: ReactWidget, instance: ReactInstance | null): void; } export interface LowCodeComponentProps { @@ -37,6 +37,7 @@ const lowCodeComponentsCache = new Map(); export function getComponentByName( name: string, { packageManager, boostsManager }: RenderContext, + componentOptions: ComponentOptions = {}, ): ReactComponent { const result = lowCodeComponentsCache.get(name) || packageManager.getComponent(name); @@ -58,7 +59,8 @@ export function getComponentByName( }); } - const lowCodeComponent = createComponentBySchema(componentsTree[0], { + const lowCodeComponent = createComponent(componentsTree[0], { + ...componentOptions, displayName: name, modelOptions: { id: metadata.id, @@ -76,40 +78,49 @@ export function getComponentByName( return result; } -export function createComponentBySchema( +export function createComponent( schema: string | Spec.ComponentTreeRoot, - { displayName = '__LowCodeComponent__', modelOptions }: ComponentOptions = {}, + componentOptions: ComponentOptions = {}, ) { + const { displayName = '__LowCodeComponent__', modelOptions } = componentOptions; + const LowCodeComponent = forwardRef(function ( props: LowCodeComponentProps, ref: ForwardedRef, ) { - const renderContext = useRendererContext(); - const { options, componentTreeModel } = renderContext; + const context = useRendererContext(); + const { options: globalOptions, componentTreeModel } = context; const modelRef = useRef>(); if (!modelRef.current) { + const finalOptions: CreateComponentTreeModelOptions = { + ...modelOptions, + codeScopeValue: { + props, + }, + stateCreator: reactiveStateFactory, + dataSourceCreator: globalOptions.dataSourceCreator, + }; + if (typeof schema === 'string') { - modelRef.current = componentTreeModel.createById(schema, modelOptions); + modelRef.current = componentTreeModel.createById(schema, finalOptions); } else { - modelRef.current = componentTreeModel.create(schema, modelOptions); + modelRef.current = componentTreeModel.create(schema, finalOptions); } + console.log( + '%c [ model ]-103', + 'font-size:13px; background:pink; color:#bf2c9f;', + modelRef.current, + ); } const model = modelRef.current!; - console.log('%c [ model ]-103', 'font-size:13px; background:pink; color:#bf2c9f;', model); const isConstructed = useRef(false); const isMounted = useRef(false); if (!isConstructed.current) { - model.initialize({ - defaultProps: props, - stateCreator: reactiveStateFactory, - dataSourceCreator: options.dataSourceCreator, - }); - model.triggerLifeCycle('constructor'); isConstructed.current = true; @@ -142,11 +153,9 @@ export function createComponentBySchema( }, []); return ( - -
- {model.widgets.map((w) => createElementByWidget(w, model.codeScope))} -
-
+
+ {model.widgets.map((w) => createElementByWidget(w, w.model.codeRuntime, componentOptions))} +
); }); diff --git a/packages/react-renderer/src/runtime/components.tsx b/packages/react-renderer/src/runtime/elements.tsx similarity index 53% rename from packages/react-renderer/src/runtime/components.tsx rename to packages/react-renderer/src/runtime/elements.tsx index 5aa45d610..702be02e8 100644 --- a/packages/react-renderer/src/runtime/components.tsx +++ b/packages/react-renderer/src/runtime/elements.tsx @@ -1,6 +1,6 @@ import { type IWidget, - type ICodeScope, + type ICodeRuntime, type NormalizedComponentNode, mapValue, } from '@alilc/lowcode-renderer-core'; @@ -15,70 +15,139 @@ import { import { type ComponentType, type ReactInstance, useMemo, createElement } from 'react'; import { useRendererContext } from '../api/context'; import { useReactiveStore } from './hooks/useReactiveStore'; -import { useModel } from './context'; -import { getComponentByName } from './schema'; +import { getComponentByName, type ComponentOptions } from './createComponent'; export type ReactComponent = ComponentType; export type ReactWidget = IWidget; interface WidgetRendererProps { widget: ReactWidget; - codeScope: ICodeScope; + codeRuntime: ICodeRuntime; + options: ComponentOptions; [key: string]: any; } export function createElementByWidget( - widget: IWidget, - codeScope: ICodeScope, + widget: ReactWidget, + codeRuntime: ICodeRuntime, + options: ComponentOptions, ) { - const { key, node } = widget; + const getElement = (widget: ReactWidget) => { + const { key, rawNode } = widget; - if (typeof node === 'string') { - return node; - } + if (typeof rawNode === 'string') { + return rawNode; + } - if (isJSExpression(node)) { - return ; - } + if (isJSExpression(rawNode)) { + return ; + } - if (isJSI18nNode(node)) { - return ; - } + if (isJSI18nNode(rawNode)) { + return ; + } + + const { condition, loop } = widget.rawNode as NormalizedComponentNode; + + // condition为 Falsy 的情况下 不渲染 + if (!condition) return null; + // loop 为数组且为空的情况下 不渲染 + if (Array.isArray(loop) && loop.length === 0) return null; + + if (isJSExpression(loop)) { + return ( + + ); + } - const { condition, loop } = widget.node as NormalizedComponentNode; + return ( + + ); + }; + + if (options.beforeElementCreate) { + widget = options.beforeElementCreate(widget); + } - // condition为 Falsy 的情况下 不渲染 - if (!condition) return null; - // loop 为数组且为空的情况下 不渲染 - if (Array.isArray(loop) && loop.length === 0) return null; + const element = getElement(widget); - if (isJSExpression(loop)) { - return ; + if (options.elementCreated) { + return options.elementCreated(widget, element); } - return ; + return element; } export function WidgetComponent(props: WidgetRendererProps) { - const { widget, codeScope, ...otherProps } = props; - const componentNode = widget.node as NormalizedComponentNode; + const { widget, codeRuntime, options, ...otherProps } = props; + const componentNode = widget.rawNode as NormalizedComponentNode; const { ref, ...componentProps } = componentNode.props; - const rendererContext = useRendererContext(); + const context = useRendererContext(); const Component = useMemo( - () => getComponentByName(componentNode.componentName, rendererContext), + () => getComponentByName(componentNode.componentName, context, options), [widget], ); + // 先将 jsslot, jsFunction 对象转换 + const processedProps = mapValue( + componentProps, + (node) => isJSFunction(node) || isJSSlot(node), + (node: Spec.JSSlot | Spec.JSFunction) => { + if (isJSSlot(node)) { + const slot = node as Spec.JSSlot; + + if (slot.value) { + const widgets = widget.model.buildWidgets( + Array.isArray(node.value) ? node.value : [node.value], + ); + + if (slot.params?.length) { + return (...args: any[]) => { + const params = slot.params!.reduce((prev, cur, idx) => { + return (prev[cur] = args[idx]); + }, {} as PlainObject); + + return widgets.map((n) => + createElementByWidget( + n, + codeRuntime.createChild({ initScopeValue: params }), + options, + ), + ); + }; + } else { + return widgets.map((n) => createElementByWidget(n, codeRuntime, options)); + } + } + } else if (isJSFunction(node)) { + return widget.model.codeRuntime.resolve(node); + } + + return null; + }, + ); + + if (process.env.NODE_ENV === 'development') { + // development 模式下 把 widget 的内容作为 prop ,便于排查问题 + processedProps.widget = widget; + } + const state = useReactiveStore({ target: { condition: componentNode.condition, - props: preprocessProps(componentProps, widget, codeScope), + props: processedProps, }, valueGetter(expr) { - return widget.model.codeRuntime.resolve(expr, { scope: codeScope }); + return codeRuntime.resolve(expr); }, }); @@ -88,6 +157,8 @@ export function WidgetComponent(props: WidgetRendererProps) { } else { if (ref) widget.model.removeComponentRef(ref); } + + options.componentRefAttached?.(widget, ins); }; if (!state.condition) { @@ -107,58 +178,15 @@ export function WidgetComponent(props: WidgetRendererProps) { key: widget.key, ref: attachRef, }, - widget.children?.map((item) => createElementByWidget(item, codeScope)) ?? [], + widget.children?.map((item) => createElementByWidget(item, codeRuntime, options)) ?? [], ); } -function preprocessProps(props: PlainObject, widget: ReactWidget, codeScope: ICodeScope) { - // 先将 jsslot, jsFunction 对象转换 - const finalProps = mapValue( - props, - (node) => isJSFunction(node) || isJSSlot(node), - (node: Spec.JSSlot | Spec.JSFunction) => { - if (isJSSlot(node)) { - const slot = node as Spec.JSSlot; - - if (slot.value) { - const widgets = widget.model.buildWidgets( - Array.isArray(node.value) ? node.value : [node.value], - ); - - if (slot.params?.length) { - return (...args: any[]) => { - const params = slot.params!.reduce((prev, cur, idx) => { - return (prev[cur] = args[idx]); - }, {} as PlainObject); - - return widgets.map((n) => createElementByWidget(n, codeScope.createChild(params))); - }; - } else { - return widgets.map((n) => createElementByWidget(n, codeScope)); - } - } - } else if (isJSFunction(node)) { - return widget.model.codeRuntime.resolve(node, { scope: codeScope }); - } - - return null; - }, - ); - - if (process.env.NODE_ENV === 'development') { - // development 模式下 把 widget 的内容作为 prop ,便于排查问题 - finalProps.widget = widget; - } - - return finalProps; -} - -function Text(props: { expr: Spec.JSExpression; codeScope: ICodeScope }) { - const model = useModel(); +function Text(props: { expr: Spec.JSExpression; codeRuntime: ICodeRuntime }) { const text: string = useReactiveStore({ target: props.expr, getter: (obj) => { - return model.codeRuntime.resolve(obj, { scope: props.codeScope }); + return props.codeRuntime.resolve(obj); }, }); @@ -167,12 +195,11 @@ function Text(props: { expr: Spec.JSExpression; codeScope: ICodeScope }) { Text.displayName = 'Text'; -function I18nText(props: { i18n: Spec.JSI18n; codeScope: ICodeScope }) { - const model = useModel(); +function I18nText(props: { i18n: Spec.JSI18n; codeRuntime: ICodeRuntime }) { const text: string = useReactiveStore({ target: props.i18n, getter: (obj) => { - return model.codeRuntime.resolve(obj, { scope: props.codeScope }); + return props.codeRuntime.resolve(obj); }, }); @@ -184,32 +211,34 @@ I18nText.displayName = 'I18nText'; function LoopWidgetRenderer({ loop, widget, - codeScope, - + codeRuntime, + options, ...otherProps }: { loop: Spec.JSExpression; widget: ReactWidget; - codeScope: ICodeScope; - + codeRuntime: ICodeRuntime; + options: ComponentOptions; [key: string]: any; }) { - const { condition, loopArgs } = widget.node as NormalizedComponentNode; + const { condition, loopArgs } = widget.rawNode as NormalizedComponentNode; const state = useReactiveStore({ target: { loop, condition, }, valueGetter(expr) { - return widget.model.codeRuntime.resolve(expr, { scope: codeScope }); + return codeRuntime.resolve(expr); }, }); if (state.condition && Array.isArray(state.loop) && state.loop.length > 0) { return state.loop.map((item: any, idx: number) => { - const childScope = codeScope.createChild({ - [loopArgs[0]]: item, - [loopArgs[1]]: idx, + const childRuntime = codeRuntime.createChild({ + initScopeValue: { + [loopArgs[0]]: item, + [loopArgs[1]]: idx, + }, }); return ( @@ -217,7 +246,8 @@ function LoopWidgetRenderer({ {...otherProps} key={`loop-${widget.key}-${idx}`} widget={widget} - codeScope={childScope} + codeRuntime={childRuntime} + options={options} /> ); }); diff --git a/packages/react-renderer/src/runtime/index.ts b/packages/react-renderer/src/runtime/index.ts index 334368f1f..c49e6c9b2 100644 --- a/packages/react-renderer/src/runtime/index.ts +++ b/packages/react-renderer/src/runtime/index.ts @@ -1,2 +1,2 @@ -export * from './schema'; -export * from './components'; +export * from './createComponent'; +export * from './elements'; diff --git a/packages/renderer-core/src/services/code-runtime/codeRuntime.ts b/packages/renderer-core/src/services/code-runtime/codeRuntime.ts new file mode 100644 index 000000000..6bc82551f --- /dev/null +++ b/packages/renderer-core/src/services/code-runtime/codeRuntime.ts @@ -0,0 +1,128 @@ +import { + type PlainObject, + type Spec, + type EventDisposable, + isJSExpression, + isJSFunction, +} from '@alilc/lowcode-shared'; +import { type ICodeScope, CodeScope } from './codeScope'; +import { isNode } from '../../utils/node'; +import { mapValue } from '../../utils/value'; +import { evaluate } from './evaluate'; + +export interface CodeRuntimeOptions { + initScopeValue?: Partial; + parentScope?: ICodeScope; + + evalCodeFunction?: EvalCodeFunction; +} + +export interface ICodeRuntime { + getScope(): ICodeScope; + + run(code: string, scope?: ICodeScope): R | undefined; + + resolve(value: PlainObject): any; + + onResolve(handler: NodeResolverHandler): EventDisposable; + + createChild( + options: Omit, 'parentScope'>, + ): ICodeRuntime; +} + +export type NodeResolverHandler = (node: Spec.JSNode) => Spec.JSNode | false | undefined; + +let onResolveHandlers: NodeResolverHandler[] = []; + +export type EvalCodeFunction = (code: string, scope: any) => any; + +export class CodeRuntime implements ICodeRuntime { + private codeScope: ICodeScope; + + private evalCodeFunction: EvalCodeFunction = evaluate; + + constructor(options: CodeRuntimeOptions = {}) { + if (options.evalCodeFunction) this.evalCodeFunction = options.evalCodeFunction; + + if (options.parentScope) { + this.codeScope = options.parentScope.createChild(options.initScopeValue ?? {}); + } else { + this.codeScope = new CodeScope(options.initScopeValue ?? {}); + } + } + + getScope() { + return this.codeScope; + } + + run(code: string): R | undefined { + if (!code) return undefined; + + try { + const result = this.evalCodeFunction(code, this.codeScope.value); + + return result as R; + } catch (err) { + // todo replace logger + console.error('eval error', code, this.codeScope.value, err); + return undefined; + } + } + + resolve(data: PlainObject): any { + if (onResolveHandlers.length > 0) { + data = mapValue(data, isNode, (node: Spec.JSNode) => { + let newNode: Spec.JSNode | false | undefined = node; + + for (const handler of onResolveHandlers) { + newNode = handler(newNode as Spec.JSNode); + if (newNode === false || typeof newNode === 'undefined') { + break; + } + } + + return newNode; + }); + } + + return mapValue( + data, + (data) => { + return isJSExpression(data) || isJSFunction(data); + }, + (node: Spec.JSExpression | Spec.JSFunction) => { + return this.resolveExprOrFunction(node); + }, + ); + } + + private resolveExprOrFunction(node: Spec.JSExpression | Spec.JSFunction) { + const v = this.run(node.value) as any; + + if (typeof v === 'undefined' && node.mock) { + return this.resolve(node.mock); + } + return v; + } + + /** + * 顺序执行 handler + */ + onResolve(handler: NodeResolverHandler): EventDisposable { + onResolveHandlers.push(handler); + return () => { + onResolveHandlers = onResolveHandlers.filter((h) => h !== handler); + }; + } + + createChild( + options?: Omit, 'parentScope'>, + ): ICodeRuntime { + return new CodeRuntime({ + initScopeValue: options?.initScopeValue, + parentScope: this.codeScope, + evalCodeFunction: options?.evalCodeFunction ?? this.evalCodeFunction, + }); + } +} diff --git a/packages/renderer-core/src/services/code-runtime/codeRuntimeService.ts b/packages/renderer-core/src/services/code-runtime/codeRuntimeService.ts index a56485fe4..321542e11 100644 --- a/packages/renderer-core/src/services/code-runtime/codeRuntimeService.ts +++ b/packages/renderer-core/src/services/code-runtime/codeRuntimeService.ts @@ -1,126 +1,33 @@ -import { - type PlainObject, - type Spec, - type EventDisposable, - createDecorator, - Provide, - isJSExpression, - isJSFunction, -} from '@alilc/lowcode-shared'; -import { type ICodeScope, CodeScope } from './codeScope'; -import { evaluate } from '../../utils/evaluate'; -import { isNode } from '../../utils/node'; -import { mapValue } from '../../utils/value'; - -export interface ResolveOptions { - scope?: ICodeScope; -} - -export type NodeResolverHandler = (node: Spec.JSNode) => Spec.JSNode | false | undefined; +import { createDecorator, invariant, Provide, type PlainObject } from '@alilc/lowcode-shared'; +import { type ICodeRuntime, type CodeRuntimeOptions, CodeRuntime } from './codeRuntime'; export interface ICodeRuntimeService { - initialize(options: CodeRuntimeInitializeOptions): void; - - getScope(): ICodeScope; + readonly rootRuntime: ICodeRuntime; - run(code: string, scope?: ICodeScope): R | undefined; + initialize(options: CodeRuntimeOptions): void; - resolve(value: PlainObject, options?: ResolveOptions): any; - - onResolve(handler: NodeResolverHandler): EventDisposable; - - createChildScope(value: PlainObject): ICodeScope; + createCodeRuntime( + options: CodeRuntimeOptions, + ): ICodeRuntime; } export const ICodeRuntimeService = createDecorator('codeRuntimeService'); -export interface CodeRuntimeInitializeOptions { - evalCodeFunction?: (code: string, scope: any) => any; -} - @Provide(ICodeRuntimeService) export class CodeRuntimeService implements ICodeRuntimeService { - private codeScope: ICodeScope = new CodeScope({}); - - private evalCodeFunction = evaluate; - - private onResolveHandlers: NodeResolverHandler[] = []; - - initialize(options: CodeRuntimeInitializeOptions) { - if (options.evalCodeFunction) this.evalCodeFunction = options.evalCodeFunction; - } - - getScope() { - return this.codeScope; - } - - run(code: string, scope: ICodeScope = this.codeScope): R | undefined { - if (!code) return undefined; + rootRuntime: ICodeRuntime; - try { - const result = this.evalCodeFunction(code, scope.value); - - return result as R; - } catch (err) { - // todo replace logger - console.error('eval error', code, scope.value, err); - return undefined; - } + initialize(options?: CodeRuntimeOptions) { + this.rootRuntime = new CodeRuntime(options); } - resolve(data: PlainObject, options: ResolveOptions = {}): any { - const handlers = this.onResolveHandlers; - - if (handlers.length > 0) { - data = mapValue(data, isNode, (node: Spec.JSNode) => { - let newNode: Spec.JSNode | false | undefined = node; - - for (const handler of handlers) { - newNode = handler(newNode as Spec.JSNode); - if (newNode === false || typeof newNode === 'undefined') { - break; - } - } - - return newNode; - }); - } - - return mapValue( - data, - (data) => { - return isJSExpression(data) || isJSFunction(data); - }, - (node: Spec.JSExpression | Spec.JSFunction) => { - return this.resolveExprOrFunction(node, options); - }, - ); - } - - private resolveExprOrFunction( - node: Spec.JSExpression | Spec.JSFunction, - options: ResolveOptions, - ) { - const scope = options.scope || this.codeScope; - const v = this.run(node.value, scope) as any; - - if (typeof v === 'undefined' && node.mock) { - return this.resolve(node.mock, options); - } - return v; - } - - /** - * 顺序执行 handler - */ - onResolve(handler: NodeResolverHandler): EventDisposable { - this.onResolveHandlers.push(handler); - return () => { - this.onResolveHandlers = this.onResolveHandlers.filter((h) => h !== handler); - }; - } + createCodeRuntime( + options: CodeRuntimeOptions = {}, + ): ICodeRuntime { + invariant(this.rootRuntime, `please initialize codeRuntimeService on renderer starting!`); - createChildScope(value: PlainObject): ICodeScope { - return this.codeScope.createChild(value); + return options.parentScope + ? new CodeRuntime(options) + : this.rootRuntime.createChild(options); } } diff --git a/packages/renderer-core/src/services/code-runtime/codeScope.ts b/packages/renderer-core/src/services/code-runtime/codeScope.ts index 104b2bf1c..6bf57bcf7 100644 --- a/packages/renderer-core/src/services/code-runtime/codeScope.ts +++ b/packages/renderer-core/src/services/code-runtime/codeScope.ts @@ -9,27 +9,28 @@ const unscopables = trustedGlobals.reduce((acc, key) => ({ ...acc, [key]: true } __proto__: null, }); -export interface ICodeScope { - readonly value: PlainObject; - set(name: string, value: any): void; - setValue(value: PlainObject, replace?: boolean): void; - createChild(initValue: PlainObject): ICodeScope; +export interface ICodeScope { + readonly value: T; + + set(name: keyof T, value: any): void; + setValue(value: Partial, replace?: boolean): void; + createChild(initValue: Partial): ICodeScope; } /** * 双链表实现父域值的获取 */ -interface IScopeNode { - parent?: IScopeNode; - current: PlainObject; +interface IScopeNode { + parent?: IScopeNode; + current: Partial; } -export class CodeScope implements ICodeScope { - __node: IScopeNode; +export class CodeScope implements ICodeScope { + __node: IScopeNode; - private proxyValue: PlainObject; + private proxyValue: T; - constructor(initValue: PlainObject) { + constructor(initValue: Partial) { this.__node = { current: initValue, }; @@ -37,15 +38,15 @@ export class CodeScope implements ICodeScope { this.proxyValue = this.createProxy(); } - get value() { + get value(): T { return this.proxyValue; } - set(name: string, value: any): void { + set(name: keyof T, value: any): void { this.__node.current[name] = value; } - setValue(value: PlainObject, replace = false) { + setValue(value: Partial, replace = false) { if (replace) { this.__node.current = { ...value }; } else { @@ -53,15 +54,15 @@ export class CodeScope implements ICodeScope { } } - createChild(initValue: PlainObject): ICodeScope { + createChild(initValue: Partial): ICodeScope { const childScope = new CodeScope(initValue); childScope.__node.parent = this.__node; return childScope; } - private createProxy(): PlainObject { - return new Proxy(Object.create(null) as PlainObject, { + private createProxy(): T { + return new Proxy(Object.create(null) as T, { set: (target, p, newValue) => { this.set(p as string, newValue); return true; @@ -74,7 +75,7 @@ export class CodeScope implements ICodeScope { private findValue(prop: PropertyKey) { if (prop === Symbol.unscopables) return unscopables; - let node: IScopeNode | undefined = this.__node; + let node: IScopeNode | undefined = this.__node; while (node) { if (Object.hasOwnProperty.call(node.current, prop)) { return node.current[prop as string]; @@ -86,7 +87,7 @@ export class CodeScope implements ICodeScope { private hasProperty(prop: PropertyKey): boolean { if (prop in unscopables) return true; - let node: IScopeNode | undefined = this.__node; + let node: IScopeNode | undefined = this.__node; while (node) { if (prop in node.current) { return true; diff --git a/packages/renderer-core/src/services/code-runtime/evaluate.ts b/packages/renderer-core/src/services/code-runtime/evaluate.ts new file mode 100644 index 000000000..465b6a2b3 --- /dev/null +++ b/packages/renderer-core/src/services/code-runtime/evaluate.ts @@ -0,0 +1,7 @@ +import { type EvalCodeFunction } from './codeRuntime'; + +export const evaluate: EvalCodeFunction = (code: string, scope: any) => { + return new Function('scope', `"use strict";return (function(){return (${code})}).bind(scope)();`)( + scope, + ); +}; diff --git a/packages/renderer-core/src/services/code-runtime/index.ts b/packages/renderer-core/src/services/code-runtime/index.ts index 74b951ea1..abebe49af 100644 --- a/packages/renderer-core/src/services/code-runtime/index.ts +++ b/packages/renderer-core/src/services/code-runtime/index.ts @@ -1,2 +1,3 @@ export * from './codeScope'; export * from './codeRuntimeService'; +export * from './codeRuntime'; diff --git a/packages/renderer-core/src/services/extension/boosts.ts b/packages/renderer-core/src/services/extension/boosts.ts index 6f8742495..92bd1392d 100644 --- a/packages/renderer-core/src/services/extension/boosts.ts +++ b/packages/renderer-core/src/services/extension/boosts.ts @@ -1,13 +1,13 @@ import { createDecorator, Provide, type PlainObject } from '@alilc/lowcode-shared'; import { isObject } from 'lodash-es'; -import { ICodeRuntimeService } from '../code-runtime'; +import { ICodeRuntime, ICodeRuntimeService } from '../code-runtime'; import { IRuntimeUtilService } from '../runtimeUtilService'; import { IRuntimeIntlService } from '../runtimeIntlService'; export type IBoosts = IBoostsApi & Extends & { [key: string]: any }; export interface IBoostsApi { - readonly codeRuntime: ICodeRuntimeService; + readonly codeRuntime: ICodeRuntime; readonly intl: Pick; @@ -39,12 +39,14 @@ export class BoostsService implements IBoostsService { private _expose: any; constructor( - @ICodeRuntimeService private codeRuntimeService: ICodeRuntimeService, + @ICodeRuntimeService codeRuntimeService: ICodeRuntimeService, @IRuntimeIntlService private runtimeIntlService: IRuntimeIntlService, @IRuntimeUtilService private runtimeUtilService: IRuntimeUtilService, ) { this.builtInApis = { - codeRuntime: this.codeRuntimeService, + get codeRuntime() { + return codeRuntimeService.rootRuntime; + }, intl: this.runtimeIntlService, util: this.runtimeUtilService, temporaryUse: (name, value) => { @@ -75,7 +77,7 @@ export class BoostsService implements IBoostsService { toExpose(): IBoosts { if (!this._expose) { - this._expose = new Proxy(Object.create(null), { + this._expose = new Proxy(this.builtInApis, { get: (_, p, receiver) => { return ( Reflect.get(this.builtInApis, p, receiver) || diff --git a/packages/renderer-core/src/services/lifeCycleService.ts b/packages/renderer-core/src/services/lifeCycleService.ts index 6378b59a3..37ce905f0 100644 --- a/packages/renderer-core/src/services/lifeCycleService.ts +++ b/packages/renderer-core/src/services/lifeCycleService.ts @@ -25,6 +25,19 @@ export interface ILifeCycleService { when(phase: LifecyclePhase, listener: () => void | Promise): EventDisposable; } +export function LifecyclePhaseToString(phase: LifecyclePhase): string { + switch (phase) { + case LifecyclePhase.Starting: + return 'Starting'; + case LifecyclePhase.OptionsResolved: + return 'OptionsResolved'; + case LifecyclePhase.Ready: + return 'Ready'; + case LifecyclePhase.Destroying: + return 'Destroying'; + } +} + export const ILifeCycleService = createDecorator('lifeCycleService'); @Provide(ILifeCycleService) @@ -55,18 +68,3 @@ export class LifeCycleService implements ILifeCycleService { return this.phaseWhen.on(LifecyclePhaseToString(phase), listener); } } - -export function LifecyclePhaseToString(phase: LifecyclePhase): string { - switch (phase) { - case LifecyclePhase.Starting: - return 'Starting'; - case LifecyclePhase.OptionsResolved: - return 'OptionsResolved'; - case LifecyclePhase.Ready: - return 'Ready'; - case LifecyclePhase.Inited: - return 'Inited'; - case LifecyclePhase.Destroying: - return 'Destroying'; - } -} diff --git a/packages/renderer-core/src/services/model/componentTreeModel.ts b/packages/renderer-core/src/services/model/componentTreeModel.ts index dc4810e3a..555c29054 100644 --- a/packages/renderer-core/src/services/model/componentTreeModel.ts +++ b/packages/renderer-core/src/services/model/componentTreeModel.ts @@ -5,7 +5,7 @@ import { invariant, uniqueId, } from '@alilc/lowcode-shared'; -import { type ICodeScope, type ICodeRuntimeService } from '../code-runtime'; +import { type ICodeRuntime } from '../code-runtime'; import { IWidget, Widget } from '../widget'; export interface NormalizedComponentNode extends Spec.ComponentNode { @@ -13,26 +13,16 @@ export interface NormalizedComponentNode extends Spec.ComponentNode { props: Spec.ComponentNodeProps; } -export interface InitializeModelOptions { - defaultProps?: PlainObject | undefined; - stateCreator: ModelScopeStateCreator; - dataSourceCreator?: ModelScopeDataSourceCreator; -} - /** * 根据低代码搭建协议的容器组件描述生成的容器模型 */ export interface IComponentTreeModel { readonly id: string; - readonly codeScope: ICodeScope; - - readonly codeRuntime: ICodeRuntimeService; + readonly codeRuntime: ICodeRuntime; readonly widgets: IWidget[]; - initialize(options: InitializeModelOptions): void; - /** * 获取协议中的 css 内容 */ @@ -56,12 +46,18 @@ export interface IComponentTreeModel { buildWidgets(nodes: Spec.NodeType[]): IWidget[]; } -export type ModelScopeStateCreator = (initalState: PlainObject) => Spec.InstanceStateApi; -export type ModelScopeDataSourceCreator = (...args: any[]) => Spec.InstanceDataSourceApi; +export type ModelStateCreator = (initalState: PlainObject) => Spec.InstanceStateApi; +export type ModelDataSourceCreator = ( + dataSourceSchema: Spec.ComponentDataSource, + codeRuntime: ICodeRuntime, +) => Spec.InstanceDataSourceApi; export interface ComponentTreeModelOptions { id?: string; metadata?: PlainObject; + + stateCreator: ModelStateCreator; + dataSourceCreator?: ModelDataSourceCreator; } export class ComponentTreeModel @@ -71,16 +67,14 @@ export class ComponentTreeModel public id: string; - public codeScope: ICodeScope; - public widgets: IWidget[] = []; public metadata: PlainObject = {}; constructor( public componentsTree: Spec.ComponentTree, - public codeRuntime: ICodeRuntimeService, - options?: ComponentTreeModelOptions, + public codeRuntime: ICodeRuntime, + options: ComponentTreeModelOptions, ) { invariant(componentsTree, 'componentsTree must to provide', 'ComponentTreeModel'); @@ -92,39 +86,28 @@ export class ComponentTreeModel if (componentsTree.children) { this.widgets = this.buildWidgets(componentsTree.children); } + + this.initialize(options); } - initialize({ defaultProps, stateCreator, dataSourceCreator }: InitializeModelOptions) { - const { - state = {}, - defaultProps: defaultSchemaProps, - props = {}, - dataSource, - methods = {}, - } = this.componentsTree; - - this.codeScope = this.codeRuntime.createChildScope({ - props: { - ...props, - ...defaultSchemaProps, - ...defaultProps, - }, - }); + private initialize({ stateCreator, dataSourceCreator }: ComponentTreeModelOptions) { + const { state = {}, defaultProps, props = {}, dataSource, methods = {} } = this.componentsTree; + const codeScope = this.codeRuntime.getScope(); - const initalProps = this.codeRuntime.resolve(props, { scope: this.codeScope }); - this.codeScope.setValue({ props: { ...defaultProps, ...initalProps } }); + const initalProps = this.codeRuntime.resolve(props); + codeScope.setValue({ props: { ...defaultProps, ...codeScope.value.props, ...initalProps } }); - const initalState = this.codeRuntime.resolve(state, { scope: this.codeScope }); + const initalState = this.codeRuntime.resolve(state); const stateApi = stateCreator(initalState); - this.codeScope.setValue(stateApi); + codeScope.setValue(stateApi); let dataSourceApi: Spec.InstanceDataSourceApi | undefined; if (dataSource && dataSourceCreator) { - const dataSourceProps = this.codeRuntime.resolve(dataSource, { scope: this.codeScope }); - dataSourceApi = dataSourceCreator(dataSourceProps, stateApi); + const dataSourceProps = this.codeRuntime.resolve(dataSource); + dataSourceApi = dataSourceCreator(dataSourceProps, this.codeRuntime); } - this.codeScope.setValue( + codeScope.setValue( Object.assign( { $: (ref: string) => { @@ -141,9 +124,9 @@ export class ComponentTreeModel ); for (const [key, fn] of Object.entries(methods)) { - const customMethod = this.codeRuntime.resolve(fn, { scope: this.codeScope }); + const customMethod = this.codeRuntime.resolve(fn); if (typeof customMethod === 'function') { - this.codeScope.set(key, customMethod); + codeScope.set(key, customMethod); } } } @@ -163,9 +146,9 @@ export class ComponentTreeModel const lifeCycleSchema = this.componentsTree.lifeCycles[lifeCycleName]; - const lifeCycleFn = this.codeRuntime.resolve(lifeCycleSchema, { scope: this.codeScope }); + const lifeCycleFn = this.codeRuntime.resolve(lifeCycleSchema); if (typeof lifeCycleFn === 'function') { - lifeCycleFn.apply(this.codeScope.value, args); + lifeCycleFn.apply(this.codeRuntime.getScope().value, args); } } diff --git a/packages/renderer-core/src/services/model/componentTreeModelService.ts b/packages/renderer-core/src/services/model/componentTreeModelService.ts index bd61900af..c5097e337 100644 --- a/packages/renderer-core/src/services/model/componentTreeModelService.ts +++ b/packages/renderer-core/src/services/model/componentTreeModelService.ts @@ -1,4 +1,10 @@ -import { createDecorator, Provide, invariant, type Spec } from '@alilc/lowcode-shared'; +import { + createDecorator, + Provide, + invariant, + type Spec, + type PlainObject, +} from '@alilc/lowcode-shared'; import { ICodeRuntimeService } from '../code-runtime'; import { type IComponentTreeModel, @@ -7,15 +13,19 @@ import { } from './componentTreeModel'; import { ISchemaService } from '../schema'; +export interface CreateComponentTreeModelOptions extends ComponentTreeModelOptions { + codeScopeValue?: PlainObject; +} + export interface IComponentTreeModelService { create( componentsTree: Spec.ComponentTree, - options?: ComponentTreeModelOptions, + options?: CreateComponentTreeModelOptions, ): IComponentTreeModel; createById( id: string, - options?: ComponentTreeModelOptions, + options?: CreateComponentTreeModelOptions, ): IComponentTreeModel; } @@ -32,20 +42,32 @@ export class ComponentTreeModelService implements IComponentTreeModelService { create( componentsTree: Spec.ComponentTree, - options?: ComponentTreeModelOptions, + options: CreateComponentTreeModelOptions, ): IComponentTreeModel { - return new ComponentTreeModel(componentsTree, this.codeRuntimeService, options); + return new ComponentTreeModel( + componentsTree, + this.codeRuntimeService.createCodeRuntime({ + initScopeValue: options?.codeScopeValue, + }), + options, + ); } createById( id: string, - options?: ComponentTreeModelOptions, + options: CreateComponentTreeModelOptions, ): IComponentTreeModel { const componentsTrees = this.schemaService.get('componentsTree'); const componentsTree = componentsTrees.find((item) => item.id === id); invariant(componentsTree, 'componentsTree not found'); - return new ComponentTreeModel(componentsTree, this.codeRuntimeService, options); + return new ComponentTreeModel( + componentsTree, + this.codeRuntimeService.createCodeRuntime({ + initScopeValue: options?.codeScopeValue, + }), + options, + ); } } diff --git a/packages/renderer-core/src/services/runtimeIntlService.ts b/packages/renderer-core/src/services/runtimeIntlService.ts index 90dc9724f..d8e1029c6 100644 --- a/packages/renderer-core/src/services/runtimeIntlService.ts +++ b/packages/renderer-core/src/services/runtimeIntlService.ts @@ -42,8 +42,6 @@ export class RuntimeIntlService implements IRuntimeIntlService { @ICodeRuntimeService private codeRuntimeService: ICodeRuntimeService, @ISchemaService private schemaService: ISchemaService, ) { - this.injectScope(); - this.lifeCycleService.when(LifecyclePhase.OptionsResolved, () => { const config = this.schemaService.get('config'); const i18nTranslations = this.schemaService.get('i18n'); @@ -56,6 +54,8 @@ export class RuntimeIntlService implements IRuntimeIntlService { this.addTranslations(key, i18nTranslations[key]); }); } + + this.injectScope(); }); } @@ -96,6 +96,6 @@ export class RuntimeIntlService implements IRuntimeIntlService { }, }; - this.codeRuntimeService.getScope().setValue(exposed); + this.codeRuntimeService.rootRuntime.getScope().setValue(exposed); } } diff --git a/packages/renderer-core/src/services/runtimeUtilService.ts b/packages/renderer-core/src/services/runtimeUtilService.ts index 9b5fcfad5..b59b6f4f3 100644 --- a/packages/renderer-core/src/services/runtimeUtilService.ts +++ b/packages/renderer-core/src/services/runtimeUtilService.ts @@ -9,6 +9,7 @@ import { isPlainObject } from 'lodash-es'; import { IPackageManagementService } from './package'; import { ICodeRuntimeService } from './code-runtime'; import { ISchemaService } from './schema'; +import { ILifeCycleService, LifecyclePhase } from './lifeCycleService'; export interface IRuntimeUtilService { add(utilItem: Spec.Util, force?: boolean): void; @@ -27,8 +28,11 @@ export class RuntimeUtilService implements IRuntimeUtilService { @ICodeRuntimeService private codeRuntimeService: ICodeRuntimeService, @IPackageManagementService private packageManagementService: IPackageManagementService, @ISchemaService private schemaService: ISchemaService, + @ILifeCycleService private lifeCycleService: ILifeCycleService, ) { - this.injectScope(); + this.lifeCycleService.when(LifecyclePhase.OptionsResolved, () => { + this.injectScope(); + }); this.schemaService.onChange('utils', (utils = []) => { for (const util of utils) { @@ -93,7 +97,7 @@ export class RuntimeUtilService implements IRuntimeUtilService { const { content } = utilItem; return { key: utilItem.name, - value: this.codeRuntimeService.run(content.value), + value: this.codeRuntimeService.rootRuntime.run(content.value), }; } else { return this.packageManagementService.getLibraryByComponentMap(utilItem.content); @@ -113,6 +117,6 @@ export class RuntimeUtilService implements IRuntimeUtilService { }, }); - this.codeRuntimeService.getScope().set('utils', exposed); + this.codeRuntimeService.rootRuntime.getScope().set('utils', exposed); } } diff --git a/packages/renderer-core/src/services/widget/widget.ts b/packages/renderer-core/src/services/widget/widget.ts index 825165e00..42fe619f3 100644 --- a/packages/renderer-core/src/services/widget/widget.ts +++ b/packages/renderer-core/src/services/widget/widget.ts @@ -1,11 +1,10 @@ import { type Spec, uniqueId } from '@alilc/lowcode-shared'; -import { clone } from 'lodash-es'; import { IComponentTreeModel } from '../model'; export interface IWidget { readonly key: string; - readonly node: Spec.NodeType; + readonly rawNode: Spec.NodeType; model: IComponentTreeModel; @@ -15,9 +14,7 @@ export interface IWidget { export class Widget implements IWidget { - public __raw: Spec.NodeType; - - public node: Spec.NodeType; + public rawNode: Spec.NodeType; public key: string; @@ -27,8 +24,7 @@ export class Widget node: Spec.NodeType, public model: IComponentTreeModel, ) { - this.node = clone(node); - this.__raw = node; + this.rawNode = node; this.key = (node as Spec.ComponentNode)?.id ?? uniqueId(); } } diff --git a/packages/renderer-core/src/types.ts b/packages/renderer-core/src/types.ts index 769dd5e55..b31505fa5 100644 --- a/packages/renderer-core/src/types.ts +++ b/packages/renderer-core/src/types.ts @@ -2,8 +2,8 @@ import { type Spec } from '@alilc/lowcode-shared'; import { type Plugin } from './services/extension'; import { type ISchemaService } from './services/schema'; import { type IPackageManagementService } from './services/package'; -import { type CodeRuntimeInitializeOptions } from './services/code-runtime'; -import { type ModelScopeDataSourceCreator } from './services/model'; +import { type CodeRuntimeOptions } from './services/code-runtime'; +import { type ModelDataSourceCreator } from './services/model'; export interface AppOptions { schema: Spec.Project; @@ -16,11 +16,11 @@ export interface AppOptions { /** * code runtime 设置选项 */ - codeRuntime?: CodeRuntimeInitializeOptions; + codeRuntime?: CodeRuntimeOptions; /** * 数据源创建工厂函数 */ - dataSourceCreator?: ModelScopeDataSourceCreator; + dataSourceCreator?: ModelDataSourceCreator; } export type RendererApplication = { diff --git a/packages/renderer-core/src/utils/evaluate.ts b/packages/renderer-core/src/utils/evaluate.ts deleted file mode 100644 index 4be366110..000000000 --- a/packages/renderer-core/src/utils/evaluate.ts +++ /dev/null @@ -1,5 +0,0 @@ -export function evaluate(code: string, scope: any) { - return new Function('scope', `"use strict";return (function(){return (${code})}).bind(scope)();`)( - scope, - ); -} diff --git a/packages/shared/src/types/specs/runtime.ts b/packages/shared/src/types/specs/runtime.ts index 6b900d9e2..0290ffae8 100644 --- a/packages/shared/src/types/specs/runtime.ts +++ b/packages/shared/src/types/specs/runtime.ts @@ -1,5 +1,4 @@ import { AnyFunction, PlainObject } from '../index'; -import { JSExpression } from './lowcode-spec'; /** * 在上述事件类型描述和变量类型描述中,在函数或 JS 表达式内,均可以通过 this 对象获取当前组件所在容器的实例化对象 @@ -62,7 +61,7 @@ export interface DataSourceMapItem { * 调用单个数据源 * @param params 替换 ComponentDataSourceItemOptions 对象描述中的 params */ - load(params: any): Promise; + load(params?: any): Promise; /** * 数据源请求的返回状态 */