mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-12 19:52:51 +00:00
fix: renderer bugs fix
This commit is contained in:
parent
a855c05d67
commit
450d08e500
@ -1,7 +1,8 @@
|
|||||||
import { createRenderer } from '@alilc/lowcode-renderer-core';
|
import { createRenderer } from '@alilc/lowcode-renderer-core';
|
||||||
import { type Root, createRoot } from 'react-dom/client';
|
import { type Root, createRoot } from 'react-dom/client';
|
||||||
import { type ReactAppOptions, RendererContext } from './context';
|
import { RendererContext } from './context';
|
||||||
import { ApplicationView, boosts } from '../app';
|
import { ApplicationView, boosts } from '../app';
|
||||||
|
import { type ReactAppOptions } from './types';
|
||||||
|
|
||||||
export const createApp = async (options: ReactAppOptions) => {
|
export const createApp = async (options: ReactAppOptions) => {
|
||||||
return createRenderer(async (context) => {
|
return createRenderer(async (context) => {
|
||||||
|
|||||||
@ -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 { FunctionComponent } from 'react';
|
||||||
import { type LowCodeComponentProps, createComponentBySchema } from '../runtime/schema';
|
import {
|
||||||
import { RendererContext } from '../api/context';
|
type LowCodeComponentProps,
|
||||||
|
createComponent as createSchemaComponent,
|
||||||
|
} from '../runtime/createComponent';
|
||||||
|
import { RendererContext } from './context';
|
||||||
|
import { type ReactAppOptions } from './types';
|
||||||
|
|
||||||
interface Render {
|
interface Render {
|
||||||
toComponent(): FunctionComponent<LowCodeComponentProps>;
|
toComponent(): FunctionComponent<LowCodeComponentProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createComponent(options: AppOptions) {
|
export async function createComponent(options: ReactAppOptions) {
|
||||||
const creator = createRenderer<Render>((context) => {
|
const creator = createRenderer<Render>((context) => {
|
||||||
const { schema } = 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 };
|
const contextValue = { ...context, options };
|
||||||
|
|
||||||
function Component(props: LowCodeComponentProps) {
|
function Component(props: LowCodeComponentProps) {
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
import { type ComponentType, createContext, useContext } from 'react';
|
import { createContext, useContext } from 'react';
|
||||||
import { type AppOptions, type RenderContext } from '@alilc/lowcode-renderer-core';
|
import { type RenderContext } from '@alilc/lowcode-renderer-core';
|
||||||
|
import { type ReactAppOptions } from './types';
|
||||||
export interface ReactAppOptions extends AppOptions {
|
|
||||||
faultComponent?: ComponentType<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const RendererContext = createContext<RenderContext & { options: ReactAppOptions }>(
|
export const RendererContext = createContext<RenderContext & { options: ReactAppOptions }>(
|
||||||
undefined!,
|
undefined!,
|
||||||
|
|||||||
11
packages/react-renderer/src/api/types.ts
Normal file
11
packages/react-renderer/src/api/types.ts
Normal file
@ -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<any>;
|
||||||
|
}
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import { useRendererContext } from '../api/context';
|
import { useRendererContext } from '../api/context';
|
||||||
import { getComponentByName } from '../runtime/schema';
|
import { getComponentByName } from '../runtime/createComponent';
|
||||||
import { boosts } from './boosts';
|
import { boosts } from './boosts';
|
||||||
|
|
||||||
export function ApplicationView() {
|
export function ApplicationView() {
|
||||||
const rendererContext = useRendererContext();
|
const rendererContext = useRendererContext();
|
||||||
const { schema } = rendererContext;
|
const { schema, options } = rendererContext;
|
||||||
const appWrappers = boosts.getAppWrappers();
|
const appWrappers = boosts.getAppWrappers();
|
||||||
const Outlet = boosts.getOutlet();
|
const Outlet = boosts.getOutlet();
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ export function ApplicationView() {
|
|||||||
|
|
||||||
if (layoutConfig) {
|
if (layoutConfig) {
|
||||||
const componentName = layoutConfig.componentName;
|
const componentName = layoutConfig.componentName;
|
||||||
const Layout = getComponentByName(componentName, rendererContext);
|
const Layout = getComponentByName(componentName, rendererContext, options.component);
|
||||||
|
|
||||||
if (Layout) {
|
if (Layout) {
|
||||||
const layoutProps: any = layoutConfig.props ?? {};
|
const layoutProps: any = layoutConfig.props ?? {};
|
||||||
|
|||||||
@ -6,5 +6,11 @@ export * from './router';
|
|||||||
export { LifecyclePhase } from '@alilc/lowcode-renderer-core';
|
export { LifecyclePhase } from '@alilc/lowcode-renderer-core';
|
||||||
|
|
||||||
export type { Spec, ProCodeComponent, LowCodeComponent } from '@alilc/lowcode-shared';
|
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';
|
export type { ReactRendererBoostsApi } from './app/boosts';
|
||||||
|
|||||||
@ -2,12 +2,12 @@ import { useMemo } from 'react';
|
|||||||
import { useRendererContext } from '../api/context';
|
import { useRendererContext } from '../api/context';
|
||||||
import { OutletProps } from '../app/boosts';
|
import { OutletProps } from '../app/boosts';
|
||||||
import { useRouteLocation } from './context';
|
import { useRouteLocation } from './context';
|
||||||
import { createComponentBySchema } from '../runtime/schema';
|
import { createComponent } from '../runtime/createComponent';
|
||||||
|
|
||||||
export function RouteOutlet(props: OutletProps) {
|
export function RouteOutlet(props: OutletProps) {
|
||||||
const context = useRendererContext();
|
const context = useRendererContext();
|
||||||
const location = useRouteLocation();
|
const location = useRouteLocation();
|
||||||
const { schema, packageManager } = context;
|
const { schema, packageManager, options } = context;
|
||||||
|
|
||||||
const pageConfig = useMemo(() => {
|
const pageConfig = useMemo(() => {
|
||||||
const pages = schema.get('pages') ?? [];
|
const pages = schema.get('pages') ?? [];
|
||||||
@ -27,11 +27,12 @@ export function RouteOutlet(props: OutletProps) {
|
|||||||
const componentsMap = schema.get('componentsMap');
|
const componentsMap = schema.get('componentsMap');
|
||||||
packageManager.resolveComponentMaps(componentsMap);
|
packageManager.resolveComponentMaps(componentsMap);
|
||||||
|
|
||||||
const LowCodeComponent = createComponentBySchema(pageConfig.mappingId, {
|
const LowCodeComponent = createComponent(pageConfig.mappingId, {
|
||||||
displayName: pageConfig.id,
|
displayName: pageConfig.id,
|
||||||
modelOptions: {
|
modelOptions: {
|
||||||
metadata: pageConfig,
|
metadata: pageConfig,
|
||||||
},
|
},
|
||||||
|
...options.component,
|
||||||
});
|
});
|
||||||
|
|
||||||
return <LowCodeComponent {...props} />;
|
return <LowCodeComponent {...props} />;
|
||||||
|
|||||||
@ -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<IComponentTreeModel<ReactComponent, ReactInstance>>(
|
|
||||||
undefined!,
|
|
||||||
);
|
|
||||||
|
|
||||||
export const useModel = () => useContext(ModelContext);
|
|
||||||
|
|
||||||
export const ModelContextProvider = ModelContext.Provider;
|
|
||||||
@ -3,23 +3,23 @@ import { forwardRef, useRef, useEffect } from 'react';
|
|||||||
import { isValidElementType } from 'react-is';
|
import { isValidElementType } from 'react-is';
|
||||||
import { useRendererContext } from '../api/context';
|
import { useRendererContext } from '../api/context';
|
||||||
import { reactiveStateFactory } from './reactiveState';
|
import { reactiveStateFactory } from './reactiveState';
|
||||||
import { type ReactComponent, type ReactWidget, createElementByWidget } from './components';
|
import { type ReactComponent, type ReactWidget, createElementByWidget } from './elements';
|
||||||
import { ModelContextProvider } from './context';
|
|
||||||
import { appendExternalStyle } from '../utils/element';
|
import { appendExternalStyle } from '../utils/element';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
RenderContext,
|
RenderContext,
|
||||||
IComponentTreeModel,
|
IComponentTreeModel,
|
||||||
ComponentTreeModelOptions,
|
CreateComponentTreeModelOptions,
|
||||||
} from '@alilc/lowcode-renderer-core';
|
} from '@alilc/lowcode-renderer-core';
|
||||||
import type { ReactInstance, CSSProperties, ForwardedRef } from 'react';
|
import type { ReactInstance, CSSProperties, ForwardedRef, ReactNode } from 'react';
|
||||||
|
|
||||||
export interface ComponentOptions {
|
export interface ComponentOptions {
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
modelOptions?: ComponentTreeModelOptions;
|
modelOptions?: Pick<CreateComponentTreeModelOptions, 'id' | 'metadata'>;
|
||||||
|
|
||||||
widgetCreated?(widget: ReactWidget): void;
|
beforeElementCreate?(widget: ReactWidget): ReactWidget;
|
||||||
componentRefAttached?(widget: ReactWidget, instance: ReactInstance): void;
|
elementCreated?(widget: ReactWidget, element: ReactNode): ReactNode;
|
||||||
|
componentRefAttached?(widget: ReactWidget, instance: ReactInstance | null): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LowCodeComponentProps {
|
export interface LowCodeComponentProps {
|
||||||
@ -37,6 +37,7 @@ const lowCodeComponentsCache = new Map<string, ReactComponent>();
|
|||||||
export function getComponentByName(
|
export function getComponentByName(
|
||||||
name: string,
|
name: string,
|
||||||
{ packageManager, boostsManager }: RenderContext,
|
{ packageManager, boostsManager }: RenderContext,
|
||||||
|
componentOptions: ComponentOptions = {},
|
||||||
): ReactComponent {
|
): ReactComponent {
|
||||||
const result = lowCodeComponentsCache.get(name) || packageManager.getComponent(name);
|
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,
|
displayName: name,
|
||||||
modelOptions: {
|
modelOptions: {
|
||||||
id: metadata.id,
|
id: metadata.id,
|
||||||
@ -76,40 +78,49 @@ export function getComponentByName(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createComponentBySchema(
|
export function createComponent(
|
||||||
schema: string | Spec.ComponentTreeRoot,
|
schema: string | Spec.ComponentTreeRoot,
|
||||||
{ displayName = '__LowCodeComponent__', modelOptions }: ComponentOptions = {},
|
componentOptions: ComponentOptions = {},
|
||||||
) {
|
) {
|
||||||
|
const { displayName = '__LowCodeComponent__', modelOptions } = componentOptions;
|
||||||
|
|
||||||
const LowCodeComponent = forwardRef(function (
|
const LowCodeComponent = forwardRef(function (
|
||||||
props: LowCodeComponentProps,
|
props: LowCodeComponentProps,
|
||||||
ref: ForwardedRef<any>,
|
ref: ForwardedRef<any>,
|
||||||
) {
|
) {
|
||||||
const renderContext = useRendererContext();
|
const context = useRendererContext();
|
||||||
const { options, componentTreeModel } = renderContext;
|
const { options: globalOptions, componentTreeModel } = context;
|
||||||
|
|
||||||
const modelRef = useRef<IComponentTreeModel<ReactComponent, ReactInstance>>();
|
const modelRef = useRef<IComponentTreeModel<ReactComponent, ReactInstance>>();
|
||||||
|
|
||||||
if (!modelRef.current) {
|
if (!modelRef.current) {
|
||||||
|
const finalOptions: CreateComponentTreeModelOptions = {
|
||||||
|
...modelOptions,
|
||||||
|
codeScopeValue: {
|
||||||
|
props,
|
||||||
|
},
|
||||||
|
stateCreator: reactiveStateFactory,
|
||||||
|
dataSourceCreator: globalOptions.dataSourceCreator,
|
||||||
|
};
|
||||||
|
|
||||||
if (typeof schema === 'string') {
|
if (typeof schema === 'string') {
|
||||||
modelRef.current = componentTreeModel.createById(schema, modelOptions);
|
modelRef.current = componentTreeModel.createById(schema, finalOptions);
|
||||||
} else {
|
} 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!;
|
const model = modelRef.current!;
|
||||||
console.log('%c [ model ]-103', 'font-size:13px; background:pink; color:#bf2c9f;', model);
|
|
||||||
|
|
||||||
const isConstructed = useRef(false);
|
const isConstructed = useRef(false);
|
||||||
const isMounted = useRef(false);
|
const isMounted = useRef(false);
|
||||||
|
|
||||||
if (!isConstructed.current) {
|
if (!isConstructed.current) {
|
||||||
model.initialize({
|
|
||||||
defaultProps: props,
|
|
||||||
stateCreator: reactiveStateFactory,
|
|
||||||
dataSourceCreator: options.dataSourceCreator,
|
|
||||||
});
|
|
||||||
|
|
||||||
model.triggerLifeCycle('constructor');
|
model.triggerLifeCycle('constructor');
|
||||||
isConstructed.current = true;
|
isConstructed.current = true;
|
||||||
|
|
||||||
@ -142,11 +153,9 @@ export function createComponentBySchema(
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModelContextProvider value={model}>
|
|
||||||
<div id={props.id} className={props.className} style={props.style} ref={ref}>
|
<div id={props.id} className={props.className} style={props.style} ref={ref}>
|
||||||
{model.widgets.map((w) => createElementByWidget(w, model.codeScope))}
|
{model.widgets.map((w) => createElementByWidget(w, w.model.codeRuntime, componentOptions))}
|
||||||
</div>
|
</div>
|
||||||
</ModelContextProvider>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
type IWidget,
|
type IWidget,
|
||||||
type ICodeScope,
|
type ICodeRuntime,
|
||||||
type NormalizedComponentNode,
|
type NormalizedComponentNode,
|
||||||
mapValue,
|
mapValue,
|
||||||
} from '@alilc/lowcode-renderer-core';
|
} from '@alilc/lowcode-renderer-core';
|
||||||
@ -15,38 +15,40 @@ import {
|
|||||||
import { type ComponentType, type ReactInstance, useMemo, createElement } from 'react';
|
import { type ComponentType, type ReactInstance, useMemo, createElement } from 'react';
|
||||||
import { useRendererContext } from '../api/context';
|
import { useRendererContext } from '../api/context';
|
||||||
import { useReactiveStore } from './hooks/useReactiveStore';
|
import { useReactiveStore } from './hooks/useReactiveStore';
|
||||||
import { useModel } from './context';
|
import { getComponentByName, type ComponentOptions } from './createComponent';
|
||||||
import { getComponentByName } from './schema';
|
|
||||||
|
|
||||||
export type ReactComponent = ComponentType<any>;
|
export type ReactComponent = ComponentType<any>;
|
||||||
export type ReactWidget = IWidget<ReactComponent, ReactInstance>;
|
export type ReactWidget = IWidget<ReactComponent, ReactInstance>;
|
||||||
|
|
||||||
interface WidgetRendererProps {
|
interface WidgetRendererProps {
|
||||||
widget: ReactWidget;
|
widget: ReactWidget;
|
||||||
codeScope: ICodeScope;
|
codeRuntime: ICodeRuntime;
|
||||||
|
options: ComponentOptions;
|
||||||
|
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createElementByWidget(
|
export function createElementByWidget(
|
||||||
widget: IWidget<ReactComponent, ReactInstance>,
|
widget: ReactWidget,
|
||||||
codeScope: ICodeScope,
|
codeRuntime: ICodeRuntime,
|
||||||
|
options: ComponentOptions,
|
||||||
) {
|
) {
|
||||||
const { key, node } = widget;
|
const getElement = (widget: ReactWidget) => {
|
||||||
|
const { key, rawNode } = widget;
|
||||||
|
|
||||||
if (typeof node === 'string') {
|
if (typeof rawNode === 'string') {
|
||||||
return node;
|
return rawNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isJSExpression(node)) {
|
if (isJSExpression(rawNode)) {
|
||||||
return <Text key={key} expr={node} codeScope={codeScope} />;
|
return <Text key={key} expr={rawNode} codeRuntime={codeRuntime} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isJSI18nNode(node)) {
|
if (isJSI18nNode(rawNode)) {
|
||||||
return <I18nText key={key} i18n={node} codeScope={codeScope} />;
|
return <I18nText key={key} i18n={rawNode} codeRuntime={codeRuntime} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { condition, loop } = widget.node as NormalizedComponentNode;
|
const { condition, loop } = widget.rawNode as NormalizedComponentNode;
|
||||||
|
|
||||||
// condition为 Falsy 的情况下 不渲染
|
// condition为 Falsy 的情况下 不渲染
|
||||||
if (!condition) return null;
|
if (!condition) return null;
|
||||||
@ -54,31 +56,98 @@ export function createElementByWidget(
|
|||||||
if (Array.isArray(loop) && loop.length === 0) return null;
|
if (Array.isArray(loop) && loop.length === 0) return null;
|
||||||
|
|
||||||
if (isJSExpression(loop)) {
|
if (isJSExpression(loop)) {
|
||||||
return <LoopWidgetRenderer key={key} loop={loop} widget={widget} codeScope={codeScope} />;
|
return (
|
||||||
|
<LoopWidgetRenderer
|
||||||
|
key={key}
|
||||||
|
loop={loop}
|
||||||
|
widget={widget}
|
||||||
|
codeRuntime={codeRuntime}
|
||||||
|
options={options}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <WidgetComponent key={key} widget={widget} codeScope={codeScope} />;
|
return (
|
||||||
|
<WidgetComponent key={key} widget={widget} codeRuntime={codeRuntime} options={options} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options.beforeElementCreate) {
|
||||||
|
widget = options.beforeElementCreate(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
const element = getElement(widget);
|
||||||
|
|
||||||
|
if (options.elementCreated) {
|
||||||
|
return options.elementCreated(widget, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function WidgetComponent(props: WidgetRendererProps) {
|
export function WidgetComponent(props: WidgetRendererProps) {
|
||||||
const { widget, codeScope, ...otherProps } = props;
|
const { widget, codeRuntime, options, ...otherProps } = props;
|
||||||
const componentNode = widget.node as NormalizedComponentNode;
|
const componentNode = widget.rawNode as NormalizedComponentNode;
|
||||||
const { ref, ...componentProps } = componentNode.props;
|
const { ref, ...componentProps } = componentNode.props;
|
||||||
|
|
||||||
const rendererContext = useRendererContext();
|
const context = useRendererContext();
|
||||||
|
|
||||||
const Component = useMemo(
|
const Component = useMemo(
|
||||||
() => getComponentByName(componentNode.componentName, rendererContext),
|
() => getComponentByName(componentNode.componentName, context, options),
|
||||||
[widget],
|
[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({
|
const state = useReactiveStore({
|
||||||
target: {
|
target: {
|
||||||
condition: componentNode.condition,
|
condition: componentNode.condition,
|
||||||
props: preprocessProps(componentProps, widget, codeScope),
|
props: processedProps,
|
||||||
},
|
},
|
||||||
valueGetter(expr) {
|
valueGetter(expr) {
|
||||||
return widget.model.codeRuntime.resolve(expr, { scope: codeScope });
|
return codeRuntime.resolve(expr);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -88,6 +157,8 @@ export function WidgetComponent(props: WidgetRendererProps) {
|
|||||||
} else {
|
} else {
|
||||||
if (ref) widget.model.removeComponentRef(ref);
|
if (ref) widget.model.removeComponentRef(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options.componentRefAttached?.(widget, ins);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!state.condition) {
|
if (!state.condition) {
|
||||||
@ -107,58 +178,15 @@ export function WidgetComponent(props: WidgetRendererProps) {
|
|||||||
key: widget.key,
|
key: widget.key,
|
||||||
ref: attachRef,
|
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) {
|
function Text(props: { expr: Spec.JSExpression; codeRuntime: ICodeRuntime }) {
|
||||||
// 先将 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();
|
|
||||||
const text: string = useReactiveStore({
|
const text: string = useReactiveStore({
|
||||||
target: props.expr,
|
target: props.expr,
|
||||||
getter: (obj) => {
|
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';
|
Text.displayName = 'Text';
|
||||||
|
|
||||||
function I18nText(props: { i18n: Spec.JSI18n; codeScope: ICodeScope }) {
|
function I18nText(props: { i18n: Spec.JSI18n; codeRuntime: ICodeRuntime }) {
|
||||||
const model = useModel();
|
|
||||||
const text: string = useReactiveStore({
|
const text: string = useReactiveStore({
|
||||||
target: props.i18n,
|
target: props.i18n,
|
||||||
getter: (obj) => {
|
getter: (obj) => {
|
||||||
return model.codeRuntime.resolve(obj, { scope: props.codeScope });
|
return props.codeRuntime.resolve(obj);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -184,32 +211,34 @@ I18nText.displayName = 'I18nText';
|
|||||||
function LoopWidgetRenderer({
|
function LoopWidgetRenderer({
|
||||||
loop,
|
loop,
|
||||||
widget,
|
widget,
|
||||||
codeScope,
|
codeRuntime,
|
||||||
|
options,
|
||||||
...otherProps
|
...otherProps
|
||||||
}: {
|
}: {
|
||||||
loop: Spec.JSExpression;
|
loop: Spec.JSExpression;
|
||||||
widget: ReactWidget;
|
widget: ReactWidget;
|
||||||
codeScope: ICodeScope;
|
codeRuntime: ICodeRuntime;
|
||||||
|
options: ComponentOptions;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}) {
|
}) {
|
||||||
const { condition, loopArgs } = widget.node as NormalizedComponentNode;
|
const { condition, loopArgs } = widget.rawNode as NormalizedComponentNode;
|
||||||
const state = useReactiveStore({
|
const state = useReactiveStore({
|
||||||
target: {
|
target: {
|
||||||
loop,
|
loop,
|
||||||
condition,
|
condition,
|
||||||
},
|
},
|
||||||
valueGetter(expr) {
|
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) {
|
if (state.condition && Array.isArray(state.loop) && state.loop.length > 0) {
|
||||||
return state.loop.map((item: any, idx: number) => {
|
return state.loop.map((item: any, idx: number) => {
|
||||||
const childScope = codeScope.createChild({
|
const childRuntime = codeRuntime.createChild({
|
||||||
|
initScopeValue: {
|
||||||
[loopArgs[0]]: item,
|
[loopArgs[0]]: item,
|
||||||
[loopArgs[1]]: idx,
|
[loopArgs[1]]: idx,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -217,7 +246,8 @@ function LoopWidgetRenderer({
|
|||||||
{...otherProps}
|
{...otherProps}
|
||||||
key={`loop-${widget.key}-${idx}`}
|
key={`loop-${widget.key}-${idx}`}
|
||||||
widget={widget}
|
widget={widget}
|
||||||
codeScope={childScope}
|
codeRuntime={childRuntime}
|
||||||
|
options={options}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -1,2 +1,2 @@
|
|||||||
export * from './schema';
|
export * from './createComponent';
|
||||||
export * from './components';
|
export * from './elements';
|
||||||
|
|||||||
128
packages/renderer-core/src/services/code-runtime/codeRuntime.ts
Normal file
128
packages/renderer-core/src/services/code-runtime/codeRuntime.ts
Normal file
@ -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<T extends PlainObject = PlainObject> {
|
||||||
|
initScopeValue?: Partial<T>;
|
||||||
|
parentScope?: ICodeScope;
|
||||||
|
|
||||||
|
evalCodeFunction?: EvalCodeFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICodeRuntime<T extends PlainObject = PlainObject> {
|
||||||
|
getScope(): ICodeScope<T>;
|
||||||
|
|
||||||
|
run<R = unknown>(code: string, scope?: ICodeScope): R | undefined;
|
||||||
|
|
||||||
|
resolve(value: PlainObject): any;
|
||||||
|
|
||||||
|
onResolve(handler: NodeResolverHandler): EventDisposable;
|
||||||
|
|
||||||
|
createChild<V extends PlainObject = PlainObject>(
|
||||||
|
options: Omit<CodeRuntimeOptions<V>, 'parentScope'>,
|
||||||
|
): ICodeRuntime<V>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NodeResolverHandler = (node: Spec.JSNode) => Spec.JSNode | false | undefined;
|
||||||
|
|
||||||
|
let onResolveHandlers: NodeResolverHandler[] = [];
|
||||||
|
|
||||||
|
export type EvalCodeFunction = (code: string, scope: any) => any;
|
||||||
|
|
||||||
|
export class CodeRuntime<T extends PlainObject = PlainObject> implements ICodeRuntime<T> {
|
||||||
|
private codeScope: ICodeScope<T>;
|
||||||
|
|
||||||
|
private evalCodeFunction: EvalCodeFunction = evaluate;
|
||||||
|
|
||||||
|
constructor(options: CodeRuntimeOptions<T> = {}) {
|
||||||
|
if (options.evalCodeFunction) this.evalCodeFunction = options.evalCodeFunction;
|
||||||
|
|
||||||
|
if (options.parentScope) {
|
||||||
|
this.codeScope = options.parentScope.createChild<T>(options.initScopeValue ?? {});
|
||||||
|
} else {
|
||||||
|
this.codeScope = new CodeScope(options.initScopeValue ?? {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getScope() {
|
||||||
|
return this.codeScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
run<R = unknown>(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<V extends PlainObject = PlainObject>(
|
||||||
|
options?: Omit<CodeRuntimeOptions<V>, 'parentScope'>,
|
||||||
|
): ICodeRuntime<V> {
|
||||||
|
return new CodeRuntime({
|
||||||
|
initScopeValue: options?.initScopeValue,
|
||||||
|
parentScope: this.codeScope,
|
||||||
|
evalCodeFunction: options?.evalCodeFunction ?? this.evalCodeFunction,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,126 +1,33 @@
|
|||||||
import {
|
import { createDecorator, invariant, Provide, type PlainObject } from '@alilc/lowcode-shared';
|
||||||
type PlainObject,
|
import { type ICodeRuntime, type CodeRuntimeOptions, CodeRuntime } from './codeRuntime';
|
||||||
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;
|
|
||||||
|
|
||||||
export interface ICodeRuntimeService {
|
export interface ICodeRuntimeService {
|
||||||
initialize(options: CodeRuntimeInitializeOptions): void;
|
readonly rootRuntime: ICodeRuntime;
|
||||||
|
|
||||||
getScope(): ICodeScope;
|
initialize(options: CodeRuntimeOptions): void;
|
||||||
|
|
||||||
run<R = unknown>(code: string, scope?: ICodeScope): R | undefined;
|
createCodeRuntime<T extends PlainObject = PlainObject>(
|
||||||
|
options: CodeRuntimeOptions<T>,
|
||||||
resolve(value: PlainObject, options?: ResolveOptions): any;
|
): ICodeRuntime<T>;
|
||||||
|
|
||||||
onResolve(handler: NodeResolverHandler): EventDisposable;
|
|
||||||
|
|
||||||
createChildScope(value: PlainObject): ICodeScope;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ICodeRuntimeService = createDecorator<ICodeRuntimeService>('codeRuntimeService');
|
export const ICodeRuntimeService = createDecorator<ICodeRuntimeService>('codeRuntimeService');
|
||||||
|
|
||||||
export interface CodeRuntimeInitializeOptions {
|
|
||||||
evalCodeFunction?: (code: string, scope: any) => any;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provide(ICodeRuntimeService)
|
@Provide(ICodeRuntimeService)
|
||||||
export class CodeRuntimeService implements ICodeRuntimeService {
|
export class CodeRuntimeService implements ICodeRuntimeService {
|
||||||
private codeScope: ICodeScope = new CodeScope({});
|
rootRuntime: ICodeRuntime;
|
||||||
|
|
||||||
private evalCodeFunction = evaluate;
|
initialize(options?: CodeRuntimeOptions) {
|
||||||
|
this.rootRuntime = new CodeRuntime(options);
|
||||||
private onResolveHandlers: NodeResolverHandler[] = [];
|
|
||||||
|
|
||||||
initialize(options: CodeRuntimeInitializeOptions) {
|
|
||||||
if (options.evalCodeFunction) this.evalCodeFunction = options.evalCodeFunction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getScope() {
|
createCodeRuntime<T extends PlainObject = PlainObject>(
|
||||||
return this.codeScope;
|
options: CodeRuntimeOptions<T> = {},
|
||||||
}
|
): ICodeRuntime<T> {
|
||||||
|
invariant(this.rootRuntime, `please initialize codeRuntimeService on renderer starting!`);
|
||||||
|
|
||||||
run<R = unknown>(code: string, scope: ICodeScope = this.codeScope): R | undefined {
|
return options.parentScope
|
||||||
if (!code) return undefined;
|
? new CodeRuntime(options)
|
||||||
|
: this.rootRuntime.createChild<T>(options);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
createChildScope(value: PlainObject): ICodeScope {
|
|
||||||
return this.codeScope.createChild(value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,27 +9,28 @@ const unscopables = trustedGlobals.reduce((acc, key) => ({ ...acc, [key]: true }
|
|||||||
__proto__: null,
|
__proto__: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface ICodeScope {
|
export interface ICodeScope<T extends PlainObject = PlainObject> {
|
||||||
readonly value: PlainObject;
|
readonly value: T;
|
||||||
set(name: string, value: any): void;
|
|
||||||
setValue(value: PlainObject, replace?: boolean): void;
|
set(name: keyof T, value: any): void;
|
||||||
createChild(initValue: PlainObject): ICodeScope;
|
setValue(value: Partial<T>, replace?: boolean): void;
|
||||||
|
createChild<V extends PlainObject = PlainObject>(initValue: Partial<V>): ICodeScope<V>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 双链表实现父域值的获取
|
* 双链表实现父域值的获取
|
||||||
*/
|
*/
|
||||||
interface IScopeNode {
|
interface IScopeNode<T extends PlainObject> {
|
||||||
parent?: IScopeNode;
|
parent?: IScopeNode<PlainObject>;
|
||||||
current: PlainObject;
|
current: Partial<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CodeScope implements ICodeScope {
|
export class CodeScope<T extends PlainObject = PlainObject> implements ICodeScope<T> {
|
||||||
__node: IScopeNode;
|
__node: IScopeNode<T>;
|
||||||
|
|
||||||
private proxyValue: PlainObject;
|
private proxyValue: T;
|
||||||
|
|
||||||
constructor(initValue: PlainObject) {
|
constructor(initValue: Partial<T>) {
|
||||||
this.__node = {
|
this.__node = {
|
||||||
current: initValue,
|
current: initValue,
|
||||||
};
|
};
|
||||||
@ -37,15 +38,15 @@ export class CodeScope implements ICodeScope {
|
|||||||
this.proxyValue = this.createProxy();
|
this.proxyValue = this.createProxy();
|
||||||
}
|
}
|
||||||
|
|
||||||
get value() {
|
get value(): T {
|
||||||
return this.proxyValue;
|
return this.proxyValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
set(name: string, value: any): void {
|
set(name: keyof T, value: any): void {
|
||||||
this.__node.current[name] = value;
|
this.__node.current[name] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue(value: PlainObject, replace = false) {
|
setValue(value: Partial<T>, replace = false) {
|
||||||
if (replace) {
|
if (replace) {
|
||||||
this.__node.current = { ...value };
|
this.__node.current = { ...value };
|
||||||
} else {
|
} else {
|
||||||
@ -53,15 +54,15 @@ export class CodeScope implements ICodeScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createChild(initValue: PlainObject): ICodeScope {
|
createChild<V extends PlainObject = PlainObject>(initValue: Partial<V>): ICodeScope<V> {
|
||||||
const childScope = new CodeScope(initValue);
|
const childScope = new CodeScope(initValue);
|
||||||
childScope.__node.parent = this.__node;
|
childScope.__node.parent = this.__node;
|
||||||
|
|
||||||
return childScope;
|
return childScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createProxy(): PlainObject {
|
private createProxy(): T {
|
||||||
return new Proxy(Object.create(null) as PlainObject, {
|
return new Proxy(Object.create(null) as T, {
|
||||||
set: (target, p, newValue) => {
|
set: (target, p, newValue) => {
|
||||||
this.set(p as string, newValue);
|
this.set(p as string, newValue);
|
||||||
return true;
|
return true;
|
||||||
@ -74,7 +75,7 @@ export class CodeScope implements ICodeScope {
|
|||||||
private findValue(prop: PropertyKey) {
|
private findValue(prop: PropertyKey) {
|
||||||
if (prop === Symbol.unscopables) return unscopables;
|
if (prop === Symbol.unscopables) return unscopables;
|
||||||
|
|
||||||
let node: IScopeNode | undefined = this.__node;
|
let node: IScopeNode<PlainObject> | undefined = this.__node;
|
||||||
while (node) {
|
while (node) {
|
||||||
if (Object.hasOwnProperty.call(node.current, prop)) {
|
if (Object.hasOwnProperty.call(node.current, prop)) {
|
||||||
return node.current[prop as string];
|
return node.current[prop as string];
|
||||||
@ -86,7 +87,7 @@ export class CodeScope implements ICodeScope {
|
|||||||
private hasProperty(prop: PropertyKey): boolean {
|
private hasProperty(prop: PropertyKey): boolean {
|
||||||
if (prop in unscopables) return true;
|
if (prop in unscopables) return true;
|
||||||
|
|
||||||
let node: IScopeNode | undefined = this.__node;
|
let node: IScopeNode<PlainObject> | undefined = this.__node;
|
||||||
while (node) {
|
while (node) {
|
||||||
if (prop in node.current) {
|
if (prop in node.current) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -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,
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,2 +1,3 @@
|
|||||||
export * from './codeScope';
|
export * from './codeScope';
|
||||||
export * from './codeRuntimeService';
|
export * from './codeRuntimeService';
|
||||||
|
export * from './codeRuntime';
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { createDecorator, Provide, type PlainObject } from '@alilc/lowcode-shared';
|
import { createDecorator, Provide, type PlainObject } from '@alilc/lowcode-shared';
|
||||||
import { isObject } from 'lodash-es';
|
import { isObject } from 'lodash-es';
|
||||||
import { ICodeRuntimeService } from '../code-runtime';
|
import { ICodeRuntime, ICodeRuntimeService } from '../code-runtime';
|
||||||
import { IRuntimeUtilService } from '../runtimeUtilService';
|
import { IRuntimeUtilService } from '../runtimeUtilService';
|
||||||
import { IRuntimeIntlService } from '../runtimeIntlService';
|
import { IRuntimeIntlService } from '../runtimeIntlService';
|
||||||
|
|
||||||
export type IBoosts<Extends> = IBoostsApi & Extends & { [key: string]: any };
|
export type IBoosts<Extends> = IBoostsApi & Extends & { [key: string]: any };
|
||||||
|
|
||||||
export interface IBoostsApi {
|
export interface IBoostsApi {
|
||||||
readonly codeRuntime: ICodeRuntimeService;
|
readonly codeRuntime: ICodeRuntime;
|
||||||
|
|
||||||
readonly intl: Pick<IRuntimeIntlService, 't' | 'setLocale' | 'getLocale' | 'addTranslations'>;
|
readonly intl: Pick<IRuntimeIntlService, 't' | 'setLocale' | 'getLocale' | 'addTranslations'>;
|
||||||
|
|
||||||
@ -39,12 +39,14 @@ export class BoostsService implements IBoostsService {
|
|||||||
private _expose: any;
|
private _expose: any;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ICodeRuntimeService private codeRuntimeService: ICodeRuntimeService,
|
@ICodeRuntimeService codeRuntimeService: ICodeRuntimeService,
|
||||||
@IRuntimeIntlService private runtimeIntlService: IRuntimeIntlService,
|
@IRuntimeIntlService private runtimeIntlService: IRuntimeIntlService,
|
||||||
@IRuntimeUtilService private runtimeUtilService: IRuntimeUtilService,
|
@IRuntimeUtilService private runtimeUtilService: IRuntimeUtilService,
|
||||||
) {
|
) {
|
||||||
this.builtInApis = {
|
this.builtInApis = {
|
||||||
codeRuntime: this.codeRuntimeService,
|
get codeRuntime() {
|
||||||
|
return codeRuntimeService.rootRuntime;
|
||||||
|
},
|
||||||
intl: this.runtimeIntlService,
|
intl: this.runtimeIntlService,
|
||||||
util: this.runtimeUtilService,
|
util: this.runtimeUtilService,
|
||||||
temporaryUse: (name, value) => {
|
temporaryUse: (name, value) => {
|
||||||
@ -75,7 +77,7 @@ export class BoostsService implements IBoostsService {
|
|||||||
|
|
||||||
toExpose<Extends>(): IBoosts<Extends> {
|
toExpose<Extends>(): IBoosts<Extends> {
|
||||||
if (!this._expose) {
|
if (!this._expose) {
|
||||||
this._expose = new Proxy(Object.create(null), {
|
this._expose = new Proxy(this.builtInApis, {
|
||||||
get: (_, p, receiver) => {
|
get: (_, p, receiver) => {
|
||||||
return (
|
return (
|
||||||
Reflect.get(this.builtInApis, p, receiver) ||
|
Reflect.get(this.builtInApis, p, receiver) ||
|
||||||
|
|||||||
@ -25,6 +25,19 @@ export interface ILifeCycleService {
|
|||||||
when(phase: LifecyclePhase, listener: () => void | Promise<void>): EventDisposable;
|
when(phase: LifecyclePhase, listener: () => void | Promise<void>): 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<ILifeCycleService>('lifeCycleService');
|
export const ILifeCycleService = createDecorator<ILifeCycleService>('lifeCycleService');
|
||||||
|
|
||||||
@Provide(ILifeCycleService)
|
@Provide(ILifeCycleService)
|
||||||
@ -55,18 +68,3 @@ export class LifeCycleService implements ILifeCycleService {
|
|||||||
return this.phaseWhen.on(LifecyclePhaseToString(phase), listener);
|
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';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
invariant,
|
invariant,
|
||||||
uniqueId,
|
uniqueId,
|
||||||
} from '@alilc/lowcode-shared';
|
} from '@alilc/lowcode-shared';
|
||||||
import { type ICodeScope, type ICodeRuntimeService } from '../code-runtime';
|
import { type ICodeRuntime } from '../code-runtime';
|
||||||
import { IWidget, Widget } from '../widget';
|
import { IWidget, Widget } from '../widget';
|
||||||
|
|
||||||
export interface NormalizedComponentNode extends Spec.ComponentNode {
|
export interface NormalizedComponentNode extends Spec.ComponentNode {
|
||||||
@ -13,26 +13,16 @@ export interface NormalizedComponentNode extends Spec.ComponentNode {
|
|||||||
props: Spec.ComponentNodeProps;
|
props: Spec.ComponentNodeProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InitializeModelOptions {
|
|
||||||
defaultProps?: PlainObject | undefined;
|
|
||||||
stateCreator: ModelScopeStateCreator;
|
|
||||||
dataSourceCreator?: ModelScopeDataSourceCreator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据低代码搭建协议的容器组件描述生成的容器模型
|
* 根据低代码搭建协议的容器组件描述生成的容器模型
|
||||||
*/
|
*/
|
||||||
export interface IComponentTreeModel<Component, ComponentInstance = unknown> {
|
export interface IComponentTreeModel<Component, ComponentInstance = unknown> {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
|
|
||||||
readonly codeScope: ICodeScope;
|
readonly codeRuntime: ICodeRuntime;
|
||||||
|
|
||||||
readonly codeRuntime: ICodeRuntimeService;
|
|
||||||
|
|
||||||
readonly widgets: IWidget<Component, ComponentInstance>[];
|
readonly widgets: IWidget<Component, ComponentInstance>[];
|
||||||
|
|
||||||
initialize(options: InitializeModelOptions): void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取协议中的 css 内容
|
* 获取协议中的 css 内容
|
||||||
*/
|
*/
|
||||||
@ -56,12 +46,18 @@ export interface IComponentTreeModel<Component, ComponentInstance = unknown> {
|
|||||||
buildWidgets(nodes: Spec.NodeType[]): IWidget<Component, ComponentInstance>[];
|
buildWidgets(nodes: Spec.NodeType[]): IWidget<Component, ComponentInstance>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ModelScopeStateCreator = (initalState: PlainObject) => Spec.InstanceStateApi;
|
export type ModelStateCreator = (initalState: PlainObject) => Spec.InstanceStateApi;
|
||||||
export type ModelScopeDataSourceCreator = (...args: any[]) => Spec.InstanceDataSourceApi;
|
export type ModelDataSourceCreator = (
|
||||||
|
dataSourceSchema: Spec.ComponentDataSource,
|
||||||
|
codeRuntime: ICodeRuntime<Spec.InstanceApi>,
|
||||||
|
) => Spec.InstanceDataSourceApi;
|
||||||
|
|
||||||
export interface ComponentTreeModelOptions {
|
export interface ComponentTreeModelOptions {
|
||||||
id?: string;
|
id?: string;
|
||||||
metadata?: PlainObject;
|
metadata?: PlainObject;
|
||||||
|
|
||||||
|
stateCreator: ModelStateCreator;
|
||||||
|
dataSourceCreator?: ModelDataSourceCreator;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ComponentTreeModel<Component, ComponentInstance = unknown>
|
export class ComponentTreeModel<Component, ComponentInstance = unknown>
|
||||||
@ -71,16 +67,14 @@ export class ComponentTreeModel<Component, ComponentInstance = unknown>
|
|||||||
|
|
||||||
public id: string;
|
public id: string;
|
||||||
|
|
||||||
public codeScope: ICodeScope;
|
|
||||||
|
|
||||||
public widgets: IWidget<Component>[] = [];
|
public widgets: IWidget<Component>[] = [];
|
||||||
|
|
||||||
public metadata: PlainObject = {};
|
public metadata: PlainObject = {};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public componentsTree: Spec.ComponentTree,
|
public componentsTree: Spec.ComponentTree,
|
||||||
public codeRuntime: ICodeRuntimeService,
|
public codeRuntime: ICodeRuntime<Spec.InstanceApi>,
|
||||||
options?: ComponentTreeModelOptions,
|
options: ComponentTreeModelOptions,
|
||||||
) {
|
) {
|
||||||
invariant(componentsTree, 'componentsTree must to provide', 'ComponentTreeModel');
|
invariant(componentsTree, 'componentsTree must to provide', 'ComponentTreeModel');
|
||||||
|
|
||||||
@ -92,39 +86,28 @@ export class ComponentTreeModel<Component, ComponentInstance = unknown>
|
|||||||
if (componentsTree.children) {
|
if (componentsTree.children) {
|
||||||
this.widgets = this.buildWidgets(componentsTree.children);
|
this.widgets = this.buildWidgets(componentsTree.children);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.initialize(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize({ defaultProps, stateCreator, dataSourceCreator }: InitializeModelOptions) {
|
private initialize({ stateCreator, dataSourceCreator }: ComponentTreeModelOptions) {
|
||||||
const {
|
const { state = {}, defaultProps, props = {}, dataSource, methods = {} } = this.componentsTree;
|
||||||
state = {},
|
const codeScope = this.codeRuntime.getScope();
|
||||||
defaultProps: defaultSchemaProps,
|
|
||||||
props = {},
|
|
||||||
dataSource,
|
|
||||||
methods = {},
|
|
||||||
} = this.componentsTree;
|
|
||||||
|
|
||||||
this.codeScope = this.codeRuntime.createChildScope({
|
const initalProps = this.codeRuntime.resolve(props);
|
||||||
props: {
|
codeScope.setValue({ props: { ...defaultProps, ...codeScope.value.props, ...initalProps } });
|
||||||
...props,
|
|
||||||
...defaultSchemaProps,
|
|
||||||
...defaultProps,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const initalProps = this.codeRuntime.resolve(props, { scope: this.codeScope });
|
const initalState = this.codeRuntime.resolve(state);
|
||||||
this.codeScope.setValue({ props: { ...defaultProps, ...initalProps } });
|
|
||||||
|
|
||||||
const initalState = this.codeRuntime.resolve(state, { scope: this.codeScope });
|
|
||||||
const stateApi = stateCreator(initalState);
|
const stateApi = stateCreator(initalState);
|
||||||
this.codeScope.setValue(stateApi);
|
codeScope.setValue(stateApi);
|
||||||
|
|
||||||
let dataSourceApi: Spec.InstanceDataSourceApi | undefined;
|
let dataSourceApi: Spec.InstanceDataSourceApi | undefined;
|
||||||
if (dataSource && dataSourceCreator) {
|
if (dataSource && dataSourceCreator) {
|
||||||
const dataSourceProps = this.codeRuntime.resolve(dataSource, { scope: this.codeScope });
|
const dataSourceProps = this.codeRuntime.resolve(dataSource);
|
||||||
dataSourceApi = dataSourceCreator(dataSourceProps, stateApi);
|
dataSourceApi = dataSourceCreator(dataSourceProps, this.codeRuntime);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.codeScope.setValue(
|
codeScope.setValue(
|
||||||
Object.assign(
|
Object.assign(
|
||||||
{
|
{
|
||||||
$: (ref: string) => {
|
$: (ref: string) => {
|
||||||
@ -141,9 +124,9 @@ export class ComponentTreeModel<Component, ComponentInstance = unknown>
|
|||||||
);
|
);
|
||||||
|
|
||||||
for (const [key, fn] of Object.entries(methods)) {
|
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') {
|
if (typeof customMethod === 'function') {
|
||||||
this.codeScope.set(key, customMethod);
|
codeScope.set(key, customMethod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,9 +146,9 @@ export class ComponentTreeModel<Component, ComponentInstance = unknown>
|
|||||||
|
|
||||||
const lifeCycleSchema = this.componentsTree.lifeCycles[lifeCycleName];
|
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') {
|
if (typeof lifeCycleFn === 'function') {
|
||||||
lifeCycleFn.apply(this.codeScope.value, args);
|
lifeCycleFn.apply(this.codeRuntime.getScope().value, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 { ICodeRuntimeService } from '../code-runtime';
|
||||||
import {
|
import {
|
||||||
type IComponentTreeModel,
|
type IComponentTreeModel,
|
||||||
@ -7,15 +13,19 @@ import {
|
|||||||
} from './componentTreeModel';
|
} from './componentTreeModel';
|
||||||
import { ISchemaService } from '../schema';
|
import { ISchemaService } from '../schema';
|
||||||
|
|
||||||
|
export interface CreateComponentTreeModelOptions extends ComponentTreeModelOptions {
|
||||||
|
codeScopeValue?: PlainObject;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IComponentTreeModelService {
|
export interface IComponentTreeModelService {
|
||||||
create<Component>(
|
create<Component>(
|
||||||
componentsTree: Spec.ComponentTree,
|
componentsTree: Spec.ComponentTree,
|
||||||
options?: ComponentTreeModelOptions,
|
options?: CreateComponentTreeModelOptions,
|
||||||
): IComponentTreeModel<Component>;
|
): IComponentTreeModel<Component>;
|
||||||
|
|
||||||
createById<Component>(
|
createById<Component>(
|
||||||
id: string,
|
id: string,
|
||||||
options?: ComponentTreeModelOptions,
|
options?: CreateComponentTreeModelOptions,
|
||||||
): IComponentTreeModel<Component>;
|
): IComponentTreeModel<Component>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,20 +42,32 @@ export class ComponentTreeModelService implements IComponentTreeModelService {
|
|||||||
|
|
||||||
create<Component>(
|
create<Component>(
|
||||||
componentsTree: Spec.ComponentTree,
|
componentsTree: Spec.ComponentTree,
|
||||||
options?: ComponentTreeModelOptions,
|
options: CreateComponentTreeModelOptions,
|
||||||
): IComponentTreeModel<Component> {
|
): IComponentTreeModel<Component> {
|
||||||
return new ComponentTreeModel(componentsTree, this.codeRuntimeService, options);
|
return new ComponentTreeModel(
|
||||||
|
componentsTree,
|
||||||
|
this.codeRuntimeService.createCodeRuntime({
|
||||||
|
initScopeValue: options?.codeScopeValue,
|
||||||
|
}),
|
||||||
|
options,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
createById<Component>(
|
createById<Component>(
|
||||||
id: string,
|
id: string,
|
||||||
options?: ComponentTreeModelOptions,
|
options: CreateComponentTreeModelOptions,
|
||||||
): IComponentTreeModel<Component> {
|
): IComponentTreeModel<Component> {
|
||||||
const componentsTrees = this.schemaService.get('componentsTree');
|
const componentsTrees = this.schemaService.get('componentsTree');
|
||||||
const componentsTree = componentsTrees.find((item) => item.id === id);
|
const componentsTree = componentsTrees.find((item) => item.id === id);
|
||||||
|
|
||||||
invariant(componentsTree, 'componentsTree not found');
|
invariant(componentsTree, 'componentsTree not found');
|
||||||
|
|
||||||
return new ComponentTreeModel(componentsTree, this.codeRuntimeService, options);
|
return new ComponentTreeModel(
|
||||||
|
componentsTree,
|
||||||
|
this.codeRuntimeService.createCodeRuntime({
|
||||||
|
initScopeValue: options?.codeScopeValue,
|
||||||
|
}),
|
||||||
|
options,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,8 +42,6 @@ export class RuntimeIntlService implements IRuntimeIntlService {
|
|||||||
@ICodeRuntimeService private codeRuntimeService: ICodeRuntimeService,
|
@ICodeRuntimeService private codeRuntimeService: ICodeRuntimeService,
|
||||||
@ISchemaService private schemaService: ISchemaService,
|
@ISchemaService private schemaService: ISchemaService,
|
||||||
) {
|
) {
|
||||||
this.injectScope();
|
|
||||||
|
|
||||||
this.lifeCycleService.when(LifecyclePhase.OptionsResolved, () => {
|
this.lifeCycleService.when(LifecyclePhase.OptionsResolved, () => {
|
||||||
const config = this.schemaService.get('config');
|
const config = this.schemaService.get('config');
|
||||||
const i18nTranslations = this.schemaService.get('i18n');
|
const i18nTranslations = this.schemaService.get('i18n');
|
||||||
@ -56,6 +54,8 @@ export class RuntimeIntlService implements IRuntimeIntlService {
|
|||||||
this.addTranslations(key, i18nTranslations[key]);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { isPlainObject } from 'lodash-es';
|
|||||||
import { IPackageManagementService } from './package';
|
import { IPackageManagementService } from './package';
|
||||||
import { ICodeRuntimeService } from './code-runtime';
|
import { ICodeRuntimeService } from './code-runtime';
|
||||||
import { ISchemaService } from './schema';
|
import { ISchemaService } from './schema';
|
||||||
|
import { ILifeCycleService, LifecyclePhase } from './lifeCycleService';
|
||||||
|
|
||||||
export interface IRuntimeUtilService {
|
export interface IRuntimeUtilService {
|
||||||
add(utilItem: Spec.Util, force?: boolean): void;
|
add(utilItem: Spec.Util, force?: boolean): void;
|
||||||
@ -27,8 +28,11 @@ export class RuntimeUtilService implements IRuntimeUtilService {
|
|||||||
@ICodeRuntimeService private codeRuntimeService: ICodeRuntimeService,
|
@ICodeRuntimeService private codeRuntimeService: ICodeRuntimeService,
|
||||||
@IPackageManagementService private packageManagementService: IPackageManagementService,
|
@IPackageManagementService private packageManagementService: IPackageManagementService,
|
||||||
@ISchemaService private schemaService: ISchemaService,
|
@ISchemaService private schemaService: ISchemaService,
|
||||||
|
@ILifeCycleService private lifeCycleService: ILifeCycleService,
|
||||||
) {
|
) {
|
||||||
|
this.lifeCycleService.when(LifecyclePhase.OptionsResolved, () => {
|
||||||
this.injectScope();
|
this.injectScope();
|
||||||
|
});
|
||||||
|
|
||||||
this.schemaService.onChange('utils', (utils = []) => {
|
this.schemaService.onChange('utils', (utils = []) => {
|
||||||
for (const util of utils) {
|
for (const util of utils) {
|
||||||
@ -93,7 +97,7 @@ export class RuntimeUtilService implements IRuntimeUtilService {
|
|||||||
const { content } = utilItem;
|
const { content } = utilItem;
|
||||||
return {
|
return {
|
||||||
key: utilItem.name,
|
key: utilItem.name,
|
||||||
value: this.codeRuntimeService.run(content.value),
|
value: this.codeRuntimeService.rootRuntime.run(content.value),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return this.packageManagementService.getLibraryByComponentMap(utilItem.content);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import { type Spec, uniqueId } from '@alilc/lowcode-shared';
|
import { type Spec, uniqueId } from '@alilc/lowcode-shared';
|
||||||
import { clone } from 'lodash-es';
|
|
||||||
import { IComponentTreeModel } from '../model';
|
import { IComponentTreeModel } from '../model';
|
||||||
|
|
||||||
export interface IWidget<Component, ComponentInstance = unknown> {
|
export interface IWidget<Component, ComponentInstance = unknown> {
|
||||||
readonly key: string;
|
readonly key: string;
|
||||||
|
|
||||||
readonly node: Spec.NodeType;
|
readonly rawNode: Spec.NodeType;
|
||||||
|
|
||||||
model: IComponentTreeModel<Component, ComponentInstance>;
|
model: IComponentTreeModel<Component, ComponentInstance>;
|
||||||
|
|
||||||
@ -15,9 +14,7 @@ export interface IWidget<Component, ComponentInstance = unknown> {
|
|||||||
export class Widget<Component, ComponentInstance = unknown>
|
export class Widget<Component, ComponentInstance = unknown>
|
||||||
implements IWidget<Component, ComponentInstance>
|
implements IWidget<Component, ComponentInstance>
|
||||||
{
|
{
|
||||||
public __raw: Spec.NodeType;
|
public rawNode: Spec.NodeType;
|
||||||
|
|
||||||
public node: Spec.NodeType;
|
|
||||||
|
|
||||||
public key: string;
|
public key: string;
|
||||||
|
|
||||||
@ -27,8 +24,7 @@ export class Widget<Component, ComponentInstance = unknown>
|
|||||||
node: Spec.NodeType,
|
node: Spec.NodeType,
|
||||||
public model: IComponentTreeModel<Component, ComponentInstance>,
|
public model: IComponentTreeModel<Component, ComponentInstance>,
|
||||||
) {
|
) {
|
||||||
this.node = clone(node);
|
this.rawNode = node;
|
||||||
this.__raw = node;
|
|
||||||
this.key = (node as Spec.ComponentNode)?.id ?? uniqueId();
|
this.key = (node as Spec.ComponentNode)?.id ?? uniqueId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import { type Spec } from '@alilc/lowcode-shared';
|
|||||||
import { type Plugin } from './services/extension';
|
import { type Plugin } from './services/extension';
|
||||||
import { type ISchemaService } from './services/schema';
|
import { type ISchemaService } from './services/schema';
|
||||||
import { type IPackageManagementService } from './services/package';
|
import { type IPackageManagementService } from './services/package';
|
||||||
import { type CodeRuntimeInitializeOptions } from './services/code-runtime';
|
import { type CodeRuntimeOptions } from './services/code-runtime';
|
||||||
import { type ModelScopeDataSourceCreator } from './services/model';
|
import { type ModelDataSourceCreator } from './services/model';
|
||||||
|
|
||||||
export interface AppOptions {
|
export interface AppOptions {
|
||||||
schema: Spec.Project;
|
schema: Spec.Project;
|
||||||
@ -16,11 +16,11 @@ export interface AppOptions {
|
|||||||
/**
|
/**
|
||||||
* code runtime 设置选项
|
* code runtime 设置选项
|
||||||
*/
|
*/
|
||||||
codeRuntime?: CodeRuntimeInitializeOptions;
|
codeRuntime?: CodeRuntimeOptions;
|
||||||
/**
|
/**
|
||||||
* 数据源创建工厂函数
|
* 数据源创建工厂函数
|
||||||
*/
|
*/
|
||||||
dataSourceCreator?: ModelScopeDataSourceCreator;
|
dataSourceCreator?: ModelDataSourceCreator;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RendererApplication<Render = unknown> = {
|
export type RendererApplication<Render = unknown> = {
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
export function evaluate(code: string, scope: any) {
|
|
||||||
return new Function('scope', `"use strict";return (function(){return (${code})}).bind(scope)();`)(
|
|
||||||
scope,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,5 +1,4 @@
|
|||||||
import { AnyFunction, PlainObject } from '../index';
|
import { AnyFunction, PlainObject } from '../index';
|
||||||
import { JSExpression } from './lowcode-spec';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在上述事件类型描述和变量类型描述中,在函数或 JS 表达式内,均可以通过 this 对象获取当前组件所在容器的实例化对象
|
* 在上述事件类型描述和变量类型描述中,在函数或 JS 表达式内,均可以通过 this 对象获取当前组件所在容器的实例化对象
|
||||||
@ -62,7 +61,7 @@ export interface DataSourceMapItem<T = any> {
|
|||||||
* 调用单个数据源
|
* 调用单个数据源
|
||||||
* @param params 替换 ComponentDataSourceItemOptions 对象描述中的 params
|
* @param params 替换 ComponentDataSourceItemOptions 对象描述中的 params
|
||||||
*/
|
*/
|
||||||
load(params: any): Promise<T>;
|
load(params?: any): Promise<T>;
|
||||||
/**
|
/**
|
||||||
* 数据源请求的返回状态
|
* 数据源请求的返回状态
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user