diff --git a/package.json b/package.json
index 3d3a7b816..f471dd0ce 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,6 @@
"private": true,
"type": "module",
"scripts": {
- "playground": "pnpm --filter playground dev",
"build": "node ./scripts/build.js",
"test": "vitest",
"clean": "rimraf ./packages/*/dist",
@@ -35,6 +34,7 @@
"eslint-plugin-react-hooks": "^4.6.0",
"globals": "^15.0.0",
"husky": "^9.0.11",
+ "jsdom": "^24.1.0",
"lint-staged": "^15.2.2",
"prettier": "^3.2.5",
"rimraf": "^5.0.2",
diff --git a/packages/react-renderer/src/components/route.tsx b/packages/react-renderer/src/components/route.tsx
index c2c132cca..246f70669 100644
--- a/packages/react-renderer/src/components/route.tsx
+++ b/packages/react-renderer/src/components/route.tsx
@@ -26,6 +26,11 @@ function RouteOutlet({ pageConfig }: OutletProps) {
const context = useRenderContext();
const { schema, packageManager } = context;
const { type = 'lowCode', mappingId } = pageConfig;
+ console.log(
+ '%c [ pageConfig ]-29',
+ 'font-size:13px; background:pink; color:#bf2c9f;',
+ pageConfig,
+ );
if (type === 'lowCode') {
// 在页面渲染时重新获取 componentsMap
diff --git a/packages/react-renderer/src/components/routerView.tsx b/packages/react-renderer/src/components/routerView.tsx
index 4aaa90317..bb76722f7 100644
--- a/packages/react-renderer/src/components/routerView.tsx
+++ b/packages/react-renderer/src/components/routerView.tsx
@@ -13,12 +13,12 @@ export const createRouterProvider = (router: Router) => {
return () => remove();
}, []);
- const pageSchema = useMemo(() => {
+ const pageConfig = useMemo(() => {
const pages = schema.get('pages') ?? [];
const matched = location.matched[location.matched.length - 1];
if (matched) {
- const page = pages.find((item) => matched.page === item.id);
+ const page = pages.find((item) => matched.page === item.mappingId);
return page;
}
@@ -28,7 +28,7 @@ export const createRouterProvider = (router: Router) => {
return (
- {children}
+ {children}
);
diff --git a/packages/react-renderer/src/runtime/dataSource.ts b/packages/react-renderer/src/runtime/dataSource.ts
index eadc5050f..daa27c093 100644
--- a/packages/react-renderer/src/runtime/dataSource.ts
+++ b/packages/react-renderer/src/runtime/dataSource.ts
@@ -1 +1,5 @@
-export const dataSourceCreator = () => ({}) as any;
+export const dataSourceCreator = () =>
+ ({
+ dataSourceMap: {},
+ reloadDataSource: () => {},
+ }) as any;
diff --git a/packages/react-renderer/src/runtime/index.tsx b/packages/react-renderer/src/runtime/index.tsx
index 866472b7d..26d756f87 100644
--- a/packages/react-renderer/src/runtime/index.tsx
+++ b/packages/react-renderer/src/runtime/index.tsx
@@ -1,15 +1,14 @@
import { processValue, someValue } from '@alilc/lowcode-renderer-core';
import {
- watch,
isJSExpression,
isJSFunction,
isJSSlot,
invariant,
- isLowCodeComponentSchema,
+ isLowCodeComponentPackage,
isJSI18nNode,
} from '@alilc/lowcode-shared';
import { forwardRef, useRef, useEffect, createElement, memo } from 'react';
-import { appendExternalStyle } from '../utils/element';
+import { appendExternalStyle } from '../../../../playground/renderer/src/plugin/remote/element';
import { reactive } from '../utils/reactive';
import { useRenderContext } from '../context/render';
import { reactiveStateCreator } from './reactiveState';
@@ -63,7 +62,7 @@ export function getComponentByName(
invariant(result, `${name} component not found in componentsRecord`);
- if (isLowCodeComponentSchema(result)) {
+ if (isLowCodeComponentPackage(result)) {
const { componentsMap, componentsTree, utils, i18n } = result.schema;
if (componentsMap.length > 0) {
@@ -120,6 +119,7 @@ export function createComponentBySchema(
}
const model = modelRef.current;
+ console.log('%c [ model ]-123', 'font-size:13px; background:pink; color:#bf2c9f;', model);
const isConstructed = useRef(false);
const isMounted = useRef(false);
@@ -133,7 +133,7 @@ export function createComponentBySchema(
const scopeValue = model.codeScope.value;
// init dataSource
- scopeValue.reloadDataSource();
+ scopeValue.reloadDataSource?.();
let styleEl: HTMLElement | undefined;
const cssText = model.getCssText();
@@ -148,11 +148,11 @@ export function createComponentBySchema(
model.triggerLifeCycle('componentDidMount');
// 当 state 改变之后调用
- const unwatch = watch(scopeValue.state, (_, oldVal) => {
- if (isMounted.current) {
- model.triggerLifeCycle('componentDidUpdate', props, oldVal);
- }
- });
+ // const unwatch = watch(scopeValue.state, (_, oldVal) => {
+ // if (isMounted.current) {
+ // model.triggerLifeCycle('componentDidUpdate', props, oldVal);
+ // }
+ // });
isMounted.current = true;
@@ -160,7 +160,7 @@ export function createComponentBySchema(
// componentWillUnmount?.();
model.triggerLifeCycle('componentWillUnmount');
styleEl?.parentNode?.removeChild(styleEl);
- unwatch();
+ // unwatch();
isMounted.current = false;
};
}, []);
@@ -247,7 +247,7 @@ function createElementByWidget(
};
// 先将 jsslot, jsFunction 对象转换
- const finalProps = processValue(
+ let finalProps = processValue(
componentProps,
(node) => isJSFunction(node) || isJSSlot(node),
(node: Spec.JSSlot | Spec.JSFunction) => {
@@ -288,6 +288,17 @@ function createElementByWidget(
},
);
+ finalProps = processValue(
+ finalProps,
+ (value) => {
+ return value.type === 'JSSlot' && !value.value;
+ },
+ (node) => {
+ console.log('%c [ node ]-303', 'font-size:13px; background:pink; color:#bf2c9f;', node);
+ return null;
+ },
+ );
+
const childElements = children?.map((child) =>
createElementByWidget(child, codeScope, renderContext, componentRefAttached),
);
diff --git a/packages/react-renderer/src/utils/element.ts b/packages/react-renderer/src/utils/element.ts
deleted file mode 100644
index 1ac0942d6..000000000
--- a/packages/react-renderer/src/utils/element.ts
+++ /dev/null
@@ -1,158 +0,0 @@
-const addLeadingSlash = (path: string): string => {
- return path.charAt(0) === '/' ? path : `/${path}`;
-};
-
-export function getElementById(id: string, tag: string = 'div') {
- let el = document.getElementById(id);
- if (!el) {
- el = document.createElement(tag);
- el.id = id;
- }
-
- return el;
-}
-
-const enum AssetType {
- STYLE = 'style',
- SCRIPT = 'script',
- UNKONWN = 'unkonwn',
-}
-
-function getAssetTypeByUrl(url: string): AssetType {
- const IS_CSS_REGEX = /\.css(\?((?!\.js$).)+)?$/;
- const IS_JS_REGEX = /\.[t|j]sx?(\?((?!\.css$).)+)?$/;
- const IS_JSON_REGEX = /\.json$/;
-
- if (IS_CSS_REGEX.test(url)) {
- return AssetType.STYLE;
- } else if (IS_JS_REGEX.test(url) || IS_JSON_REGEX.test(url)) {
- return AssetType.SCRIPT;
- }
-
- return AssetType.UNKONWN;
-}
-
-export async function loadPackageUrls(urls: string[]) {
- const styles: string[] = [];
- const scripts: string[] = [];
-
- for (const url of urls) {
- const type = getAssetTypeByUrl(url);
-
- if (type === AssetType.SCRIPT) {
- scripts.push(url);
- } else if (type === AssetType.STYLE) {
- styles.push(url);
- }
- }
-
- await Promise.all(styles.map((item) => appendExternalCss(item)));
- await Promise.all(scripts.map((item) => appendExternalScript(item)));
-}
-
-async function appendExternalScript(
- url: string,
- root: HTMLElement = document.body,
-): Promise {
- if (url) {
- const el = getIfExistAssetByUrl(url, 'script');
- if (el) return el;
- }
-
- return new Promise((resolve, reject) => {
- const scriptElement = document.createElement('script');
-
- // scriptElement.type = moduleType === 'module' ? 'module' : 'text/javascript';
- /**
- * `async=false` is required to make sure all js resources execute sequentially.
- */
- scriptElement.async = false;
- scriptElement.crossOrigin = 'anonymous';
- scriptElement.src = url;
-
- scriptElement.addEventListener(
- 'load',
- () => {
- resolve(scriptElement);
- },
- false,
- );
- scriptElement.addEventListener('error', (error) => {
- if (root.contains(scriptElement)) {
- root.removeChild(scriptElement);
- }
- reject(error);
- });
-
- root.appendChild(scriptElement);
- });
-}
-
-async function appendExternalCss(
- url: string,
- root: HTMLElement = document.head,
-): Promise {
- if (url) {
- const el = getIfExistAssetByUrl(url, 'link');
- if (el) return el;
- }
-
- return new Promise((resolve, reject) => {
- const el: HTMLLinkElement = document.createElement('link');
- el.rel = 'stylesheet';
- el.href = url;
-
- el.addEventListener(
- 'load',
- () => {
- resolve(el);
- },
- false,
- );
- el.addEventListener('error', (error) => {
- reject(error);
- });
-
- root.appendChild(el);
- });
-}
-
-export async function appendExternalStyle(
- cssText: string,
- root: HTMLElement = document.head,
-): Promise {
- return new Promise((resolve, reject) => {
- const el: HTMLStyleElement = document.createElement('style');
- el.innerText = cssText;
-
- el.addEventListener(
- 'load',
- () => {
- resolve(el);
- },
- false,
- );
- el.addEventListener('error', (error) => {
- reject(error);
- });
-
- root.appendChild(el);
- });
-}
-
-function getIfExistAssetByUrl(
- url: string,
- tag: 'link' | 'script',
-): HTMLLinkElement | HTMLScriptElement | undefined {
- return Array.from(document.getElementsByTagName(tag)).find((item) => {
- const elUrl = (item as HTMLLinkElement).href || (item as HTMLScriptElement).src;
-
- if (/^(https?:)?\/\/([\w.]+\/?)\S*/gi.test(url)) {
- // if url === http://xxx.xxx
- return url === elUrl;
- } else {
- // if url === /xx/xx/xx.xx
- return `${location.origin}${addLeadingSlash(url)}` === elUrl;
- }
- });
-}
diff --git a/packages/react-renderer/src/utils/node.ts b/packages/react-renderer/src/utils/node.ts
index 554374c22..e455e8bdc 100644
--- a/packages/react-renderer/src/utils/node.ts
+++ b/packages/react-renderer/src/utils/node.ts
@@ -10,5 +10,6 @@ export function normalizeComponentNode(node: Spec.ComponentNode): NormalizedComp
...node,
loopArgs: node.loopArgs ?? ['item', 'index'],
props: node.props ?? {},
+ condition: node.condition || node.condition === false ? node.condition : true,
};
}
diff --git a/packages/react-renderer/tsconfig.json b/packages/react-renderer/tsconfig.json
index 039e0b4d1..946b55298 100644
--- a/packages/react-renderer/tsconfig.json
+++ b/packages/react-renderer/tsconfig.json
@@ -3,5 +3,5 @@
"compilerOptions": {
"outDir": "dist"
},
- "include": ["src"]
+ "include": ["src", "../../playground/renderer/src/plugin/remote/element.ts"]
}
diff --git a/packages/renderer-core/__tests__/boosts.spec.ts b/packages/renderer-core/__tests__/boosts.spec.ts
deleted file mode 100644
index 3486eb29e..000000000
--- a/packages/renderer-core/__tests__/boosts.spec.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { describe, expect, it } from 'vitest';
-import { appBoosts } from '../src/boosts';
-
-describe('appBoosts', () => {
- it('should add value successfully', () => {
- appBoosts.add('test', 1);
- expect(appBoosts.value.test).toBe(1);
- });
-
- it('should clear removed value', () => {
- appBoosts.add('test', 1);
- expect(appBoosts.value.test).toBe(1);
-
- appBoosts.remove('test');
- expect(appBoosts.value.test).toBeUndefined();
- });
-});
diff --git a/packages/renderer-core/__tests__/code-runtime.spec.ts b/packages/renderer-core/__tests__/code-runtime.spec.ts
deleted file mode 100644
index 81470aec3..000000000
--- a/packages/renderer-core/__tests__/code-runtime.spec.ts
+++ /dev/null
@@ -1 +0,0 @@
-import {} from 'vitest';
diff --git a/packages/renderer-core/__tests__/package.spec.ts b/packages/renderer-core/__tests__/package.spec.ts
deleted file mode 100644
index e130a9d59..000000000
--- a/packages/renderer-core/__tests__/package.spec.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-import { expect } from 'vitest';
-import { createPackageManager } from '../src/package';
diff --git a/packages/renderer-core/__tests__/parts/code-runtime/codeScope.spec.ts b/packages/renderer-core/__tests__/parts/code-runtime/codeScope.spec.ts
new file mode 100644
index 000000000..3c56eeda9
--- /dev/null
+++ b/packages/renderer-core/__tests__/parts/code-runtime/codeScope.spec.ts
@@ -0,0 +1,46 @@
+import { describe, it, expect, beforeAll } from 'vitest';
+import { ICodeScope, CodeScope } from '../../../src/parts/code-runtime';
+
+describe('codeScope', () => {
+ let scope: ICodeScope;
+
+ beforeAll(() => {
+ scope = new CodeScope({});
+ });
+
+ it('should inject a new value', () => {
+ scope.inject('username', 'Alice');
+ expect(scope.value).toEqual({ username: 'Alice' });
+ });
+
+ it('should not overwrite an existing value without force', () => {
+ scope.inject('username', 'Bob');
+ expect(scope.value).toEqual({ username: 'Alice' });
+ });
+
+ it('should overwrite an existing value with force', () => {
+ scope.inject('username', 'Bob', true);
+ expect(scope.value).toEqual({ username: 'Bob' });
+ });
+
+ it('should set new value without replacing existing values', () => {
+ scope.setValue({ age: 25 });
+ expect(scope.value).toEqual({ username: 'Bob', age: 25 });
+ });
+
+ it('should set new value and replace all existing values', () => {
+ scope.setValue({ loggedIn: true }, true);
+ expect(scope.value).toEqual({ loggedIn: true });
+ });
+
+ it('should create a child scope with initial values', () => {
+ const childScope = scope.createChild({ sessionId: 'abc123' });
+ expect(childScope.value).toEqual({ loggedIn: true, sessionId: 'abc123' });
+ });
+
+ it('should set new values in the child scope without affecting the parent scope', () => {
+ const childScope = scope.createChild({ theme: 'dark' });
+ expect(childScope.value).toEqual({ loggedIn: true, sessionId: 'abc123', theme: 'dark' });
+ expect(scope.value).toEqual({ loggedIn: true });
+ });
+});
diff --git a/packages/renderer-core/__tests__/plugin.spec.ts b/packages/renderer-core/__tests__/plugin.spec.ts
deleted file mode 100644
index 0af367ee7..000000000
--- a/packages/renderer-core/__tests__/plugin.spec.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { describe, it, expect, expectTypeOf } from 'vitest';
-import { definePlugin, type Plugin, createPluginManager } from '../src/plugin';
-
-describe('createPluginManager', () => {
- it('should install plugin successfully', () => {});
-
- it('should install plugins when deps installed', () => {});
-});
-
-describe('definePlugin', () => {
- it('should return a plugin', () => {});
-});
diff --git a/packages/renderer-core/__tests__/utils/non-setter-proxy.spec.ts b/packages/renderer-core/__tests__/utils/non-setter-proxy.spec.ts
deleted file mode 100644
index 3e6f871c4..000000000
--- a/packages/renderer-core/__tests__/utils/non-setter-proxy.spec.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { describe, it, expect } from 'vitest';
-import { nonSetterProxy } from '../../src/utils/non-setter-proxy';
-
-describe('nonSetterProxy', () => {
- it('should non setter on proxy', () => {
- const target = { a: 1 };
- const proxy = nonSetterProxy(target);
-
- expect(() => ((proxy as any).b = 1)).toThrowError(/trap returned falsish for property 'b'/);
- });
-
- it('should correct value when getter', () => {
- const target = { a: 1 };
- const proxy = nonSetterProxy(target);
-
- expect(proxy.a).toBe(1);
- expect('a' in proxy).toBeTruthy();
- });
-});
diff --git a/packages/renderer-core/src/main.ts b/packages/renderer-core/src/main.ts
index 426d602c5..541e61d21 100644
--- a/packages/renderer-core/src/main.ts
+++ b/packages/renderer-core/src/main.ts
@@ -32,6 +32,8 @@ export class RendererMain {
// valid schema
this.schemaService.initialize(schema);
+ this.codeRuntimeService.initialize(options);
+
// init intl
const finalLocale = options.locale ?? navigator.language;
const i18nTranslations = this.schemaService.get('i18n') ?? {};
diff --git a/packages/renderer-core/src/parts/code-runtime/codeRuntimeService.ts b/packages/renderer-core/src/parts/code-runtime/codeRuntimeService.ts
index 1df8c2d68..300433492 100644
--- a/packages/renderer-core/src/parts/code-runtime/codeRuntimeService.ts
+++ b/packages/renderer-core/src/parts/code-runtime/codeRuntimeService.ts
@@ -11,25 +11,43 @@ import {
import { type ICodeScope, CodeScope } from './codeScope';
import { processValue } from '../../utils/value';
+type BeforeResolveCb = (
+ code: Spec.JSExpression | Spec.JSFunction,
+) => Spec.JSExpression | Spec.JSFunction;
+
export interface ICodeRuntimeService {
+ initialize({ evalCodeFunction }: CodeRuntimeInitializeOptions): void;
+
getScope(): ICodeScope;
run(code: string, scope?: ICodeScope): R | undefined;
resolve(value: PlainObject, scope?: ICodeScope): any;
- beforeRun(fn: (code: string) => string): EventDisposable;
+ beforeResolve(fn: BeforeResolveCb): EventDisposable;
createChildScope(value: PlainObject): ICodeScope;
}
export const ICodeRuntimeService = createDecorator('codeRuntimeService');
+export type EvalCodeFunction = (code: string, scope: any) => any;
+
+export interface CodeRuntimeInitializeOptions {
+ evalCodeFunction?: EvalCodeFunction;
+}
+
@Provide(ICodeRuntimeService)
export class CodeRuntimeService implements ICodeRuntimeService {
private codeScope: ICodeScope = new CodeScope({});
- private callbacks = createCallback<(code: string) => string>();
+ private callbacks = createCallback();
+
+ private evalCodeFunction: EvalCodeFunction = evaluate;
+
+ initialize({ evalCodeFunction }: CodeRuntimeInitializeOptions) {
+ if (evalCodeFunction) this.evalCodeFunction = evalCodeFunction;
+ }
getScope() {
return this.codeScope;
@@ -39,13 +57,7 @@ export class CodeRuntimeService implements ICodeRuntimeService {
if (!code) return undefined;
try {
- const cbs = this.callbacks.list();
- const finalCode = cbs.reduce((code, cb) => cb(code), code);
-
- let result = new Function(
- 'scope',
- `"use strict";return (function(){return (${finalCode})}).bind(scope)();`,
- )(scope.value);
+ let result = this.evalCodeFunction(code, scope.value);
if (typeof result === 'function') {
result = result.bind(scope.value);
@@ -66,17 +78,20 @@ export class CodeRuntimeService implements ICodeRuntimeService {
return isJSExpression(data) || isJSFunction(data);
},
(node: Spec.JSExpression | Spec.JSFunction) => {
- const v = this.run(node.value, scope);
+ const cbs = this.callbacks.list();
+ const finalNode = cbs.reduce((node, cb) => cb(node), node);
- if (typeof v === 'undefined' && (node as any).mock) {
- return (node as any).mock;
+ const v = this.run(finalNode.value, scope);
+
+ if (typeof v === 'undefined' && finalNode.mock) {
+ return this.resolve(finalNode.mock, scope);
}
return v;
},
);
}
- beforeRun(fn: (code: string) => string): EventDisposable {
+ beforeResolve(fn: BeforeResolveCb): EventDisposable {
return this.callbacks.add(fn);
}
@@ -84,3 +99,9 @@ export class CodeRuntimeService implements ICodeRuntimeService {
return this.codeScope.createChild(value);
}
}
+
+function evaluate(code: string, scope: any) {
+ return new Function('scope', `"use strict";return (function(){return (${code})}).bind(scope)();`)(
+ scope,
+ );
+}
diff --git a/packages/renderer-core/src/parts/code-runtime/codeScope.ts b/packages/renderer-core/src/parts/code-runtime/codeScope.ts
index c08edeeeb..1e9a24c8c 100644
--- a/packages/renderer-core/src/parts/code-runtime/codeScope.ts
+++ b/packages/renderer-core/src/parts/code-runtime/codeScope.ts
@@ -38,11 +38,23 @@ export class CodeScope implements ICodeScope {
if (Reflect.has(valueTarget.current, p)) {
return Reflect.get(valueTarget.current, p, receiver);
}
- valueTarget = this.__node.prev;
+ valueTarget = valueTarget.prev;
}
return Reflect.get(target, p, receiver);
},
+ has: (target, p) => {
+ let valueTarget: IScopeNode | undefined = this.__node;
+
+ while (valueTarget) {
+ if (Reflect.has(valueTarget.current, p)) {
+ return true;
+ }
+ valueTarget = valueTarget.prev;
+ }
+
+ return Reflect.has(target, p);
+ },
});
}
diff --git a/packages/renderer-core/src/parts/component-tree-model/treeModel.ts b/packages/renderer-core/src/parts/component-tree-model/treeModel.ts
index 5590d1d70..853d52ca6 100644
--- a/packages/renderer-core/src/parts/component-tree-model/treeModel.ts
+++ b/packages/renderer-core/src/parts/component-tree-model/treeModel.ts
@@ -1,11 +1,4 @@
-import {
- type Spec,
- type PlainObject,
- isJSFunction,
- isComponentNode,
- invariant,
- type AnyFunction,
-} from '@alilc/lowcode-shared';
+import { type Spec, type PlainObject, isComponentNode, invariant } from '@alilc/lowcode-shared';
import { type ICodeScope, type ICodeRuntimeService } from '../code-runtime';
import { IWidget, Widget } from '../widget';
@@ -118,8 +111,8 @@ export class ComponentTreeModel
);
for (const [key, fn] of Object.entries(methods)) {
- const customMethod = this.codeRuntime.run(fn.value, this.codeScope);
- if (customMethod) {
+ const customMethod = this.codeRuntime.resolve(fn, this.codeScope);
+ if (typeof customMethod === 'function') {
this.codeScope.inject(key, customMethod);
}
}
@@ -140,11 +133,9 @@ export class ComponentTreeModel
const lifeCycleSchema = this.componentsTree.lifeCycles[lifeCycleName];
- if (isJSFunction(lifeCycleSchema)) {
- const lifeCycleFn = this.codeRuntime.run(lifeCycleSchema.value, this.codeScope);
- if (lifeCycleFn) {
- lifeCycleFn.apply(this.codeScope.value, args);
- }
+ const lifeCycleFn = this.codeRuntime.resolve(lifeCycleSchema, this.codeScope);
+ if (typeof lifeCycleFn === 'function') {
+ lifeCycleFn.apply(this.codeScope.value, args);
}
}
diff --git a/packages/renderer-core/src/parts/package/managementService.ts b/packages/renderer-core/src/parts/package/managementService.ts
index 7d815d7d0..6b59f0938 100644
--- a/packages/renderer-core/src/parts/package/managementService.ts
+++ b/packages/renderer-core/src/parts/package/managementService.ts
@@ -1,6 +1,23 @@
-import { type Spec, type LowCodeComponent, createDecorator, Provide } from '@alilc/lowcode-shared';
+import {
+ type Spec,
+ type LowCodeComponent,
+ type ProCodeComponent,
+ createDecorator,
+ Provide,
+ isLowCodeComponentPackage,
+ isProCodeComponentPackage,
+} from '@alilc/lowcode-shared';
+import { get as lodashGet } from 'lodash-es';
import { PackageLoader } from './loader';
+export interface NormalizedPackage {
+ id: string;
+ package: string;
+ library: string;
+ exportSource?: NormalizedPackage | undefined;
+ raw: ProCodeComponent;
+}
+
export interface IPackageManagementService {
/**
* 新增资产包
@@ -39,36 +56,30 @@ export class PackageManagementService implements IPackageManagementService {
private packageStore: Map = ((window as any).__PACKAGE_STORE__ ??= new Map());
- private packagesRef: Spec.Package[] = [];
+ private packagesMap: Map = new Map();
+
+ private lowCodeComponentPackages: Map = new Map();
private packageLoaders: PackageLoader[] = [];
async loadPackages(packages: Spec.Package[]) {
for (const item of packages) {
- if (!item.package && !item.id) continue;
-
- const newId = item.package ?? item.id!;
- const isExist = this.packagesRef.some((_) => {
- const itemId = _.package ?? _.id;
- return itemId === newId;
- });
+ // low code component not need load
+ if (isLowCodeComponentPackage(item)) {
+ this.lowCodeComponentPackages.set(item.id, item);
+ continue;
+ }
- if (!isExist) {
- this.packagesRef.push(item);
+ if (!isProCodeComponentPackage(item)) continue;
- if (!this.packageStore.has(newId)) {
- const loader = this.packageLoaders.find((loader) => loader.active(item));
- if (!loader) continue;
+ const normalized = this.normalizePackage(item);
- const result = await loader.load.call(this, item);
- if (result) this.packageStore.set(newId, result);
- }
- }
+ await this.loadPackageByNormalized(normalized);
}
}
getPackageInfo(packageName: string) {
- return this.packagesRef.find((p) => p.package === packageName);
+ return this.packagesMap.get(packageName)?.raw;
}
getLibraryByPackageName(packageName: string) {
@@ -86,9 +97,7 @@ export class PackageManagementService implements IPackageManagementService {
resolveComponentMaps(componentMaps: Spec.ComponentMap[]) {
for (const map of componentMaps) {
if (map.devMode === 'lowCode') {
- const packageInfo = this.packagesRef.find((_) => {
- return _.id === (map as LowCodeComponent).id;
- });
+ const packageInfo = this.lowCodeComponentPackages.get((map as LowCodeComponent).id);
if (packageInfo) {
this.componentsRecord[map.componentName] = packageInfo;
@@ -123,7 +132,6 @@ export class PackageManagementService implements IPackageManagementService {
getComponentsNameRecord(componentMaps?: Spec.ComponentMap[]) {
if (componentMaps) {
const newMaps = componentMaps.filter((item) => !this.componentsRecord[item.componentName]);
-
this.resolveComponentMaps(newMaps);
}
@@ -143,4 +151,72 @@ export class PackageManagementService implements IPackageManagementService {
this.packageLoaders.push(loader);
}
}
+
+ private normalizePackage(packageInfo: ProCodeComponent): NormalizedPackage {
+ if (this.packagesMap.has(packageInfo.package)) {
+ return this.packagesMap.get(packageInfo.package)!;
+ }
+
+ const normalized: NormalizedPackage = {
+ package: packageInfo.package,
+ id: packageInfo.id ?? packageInfo.package,
+ library: packageInfo.library,
+ raw: packageInfo,
+ };
+
+ this.packagesMap.set(normalized.package, normalized);
+
+ // add normalized to package exports dependency graph
+ const packagesRef = [...this.packagesMap.values()];
+
+ // set export source
+ if (packageInfo.exportSourceId || packageInfo.exportSourceLibrary) {
+ const found = packagesRef.find((item) => {
+ if (!packageInfo.exportSourceId) {
+ return item.library === packageInfo.exportSourceLibrary;
+ } else {
+ return item.package === packageInfo.exportSourceId;
+ }
+ });
+
+ if (found) {
+ normalized.exportSource = found;
+ }
+ }
+
+ return normalized;
+ }
+
+ private async loadPackageByNormalized(normalized: NormalizedPackage) {
+ if (this.packageStore.has(normalized.package)) return;
+
+ // if it has export source package, wait export source package loaded
+ if (normalized.exportSource) {
+ if (this.packageStore.has(normalized.package)) {
+ const library = lodashGet(window, normalized.library);
+ if (library) {
+ this.packageStore.set(normalized.package, library);
+ }
+ }
+ } else {
+ const loader = this.packageLoaders.find((loader) => loader.active(normalized.raw));
+ if (!loader) return;
+
+ const result = await loader.load.call(this, normalized.raw);
+ if (result) {
+ this.packageStore.set(normalized.package, result);
+ }
+ }
+
+ // if current package loaded, set the value of the dependency on this package
+ if (this.packageStore.has(normalized.package)) {
+ const chilren = [...this.packagesMap.values()].filter((item) => {
+ return item.exportSource?.package === normalized.package;
+ });
+
+ for (const child of chilren) {
+ await this.loadPackageByNormalized(child);
+ }
+ }
+ }
}
diff --git a/packages/renderer-core/src/parts/runtimeUtil.ts b/packages/renderer-core/src/parts/runtimeUtil.ts
index 789680168..7785efb59 100644
--- a/packages/renderer-core/src/parts/runtimeUtil.ts
+++ b/packages/renderer-core/src/parts/runtimeUtil.ts
@@ -33,7 +33,7 @@ export class RuntimeUtilService implements IRuntimeUtilService {
}
} else {
const fn = this.parseUtil(name);
- this.utilsMap.set(name.name, fn);
+ if (fn) this.utilsMap.set(name.name, fn);
}
}
diff --git a/packages/renderer-core/src/types.ts b/packages/renderer-core/src/types.ts
index 95144eee5..d1f304f2f 100644
--- a/packages/renderer-core/src/types.ts
+++ b/packages/renderer-core/src/types.ts
@@ -3,6 +3,7 @@ import { type Plugin } from './parts/extension';
import { type ISchemaService } from './parts/schema';
import { type IPackageManagementService } from './parts/package';
import { type IExtensionHostService } from './parts/extension';
+import { type EvalCodeFunction } from './parts/code-runtime';
export interface AppOptions {
schema: Spec.Project;
@@ -18,6 +19,8 @@ export interface AppOptions {
* 运行模式
*/
mode?: 'development' | 'production';
+
+ evalCodeFunction?: EvalCodeFunction;
}
export type RendererApplication = {
diff --git a/packages/shared/src/abilities/intl.ts b/packages/shared/src/abilities/intl.ts
index 6362f62c3..27a205342 100644
--- a/packages/shared/src/abilities/intl.ts
+++ b/packages/shared/src/abilities/intl.ts
@@ -100,9 +100,10 @@ function nomarlizeLocale(target: Locale) {
return navigatorLanguageMapping[target];
}
- const replaced = target.replace('_', '-');
- const splited = replaced.split('-').slice(0, 2);
- splited[1] = splited[1].toUpperCase();
+ // const replaced = target.replace('_', '-');
+ // const splited = replaced.split('-').slice(0, 2);
+ // splited[1] = splited[1].toUpperCase();
- return splited.join('-');
+ // return splited.join('-');
+ return target;
}
diff --git a/packages/shared/src/types/material.ts b/packages/shared/src/types/material.ts
index d9b781f59..e4eba25e8 100644
--- a/packages/shared/src/types/material.ts
+++ b/packages/shared/src/types/material.ts
@@ -4,6 +4,7 @@ import { Project } from './specs/lowcode-spec';
export interface ProCodeComponent extends Package {
package: string;
type: 'proCode';
+ library: string;
}
export interface LowCodeComponent extends Package {
diff --git a/packages/shared/src/types/specs/asset-spec.ts b/packages/shared/src/types/specs/asset-spec.ts
index 97e5ef691..28cb51e16 100644
--- a/packages/shared/src/types/specs/asset-spec.ts
+++ b/packages/shared/src/types/specs/asset-spec.ts
@@ -56,7 +56,7 @@ export interface Package {
/**
* 作为全局变量引用时的名称,和 webpack output.library 字段含义一样,用来定义全局变量名
*/
- library: string;
+ library?: string | undefined;
/**
* 组件描述导出名字,可以通过 window[exportName] 获取到组件描述的 Object 内容;
*/
diff --git a/packages/shared/src/types/specs/lowcode-spec.ts b/packages/shared/src/types/specs/lowcode-spec.ts
index ce878371b..b24650677 100644
--- a/packages/shared/src/types/specs/lowcode-spec.ts
+++ b/packages/shared/src/types/specs/lowcode-spec.ts
@@ -423,6 +423,8 @@ export interface JSSlot {
type: 'JSSlot';
value: ComponentNode | ComponentNode[];
params?: string[];
+
+ [key: string]: any;
}
/**
@@ -431,6 +433,8 @@ export interface JSSlot {
export interface JSFunction {
type: 'JSFunction';
value: string;
+
+ [key: string]: any;
}
/**
@@ -439,6 +443,8 @@ export interface JSFunction {
export interface JSExpression {
type: 'JSExpression';
value: string;
+
+ [key: string]: any;
}
/**
@@ -456,4 +462,6 @@ export interface JSI18n {
params?: Record;
}
+export type JSNode = JSSlot | JSExpression | JSExpression | JSI18n;
+
export type NodeType = string | JSExpression | JSI18n | ComponentNode;
diff --git a/packages/shared/src/utils/type-guards/index.ts b/packages/shared/src/utils/type-guards/index.ts
index 97bec9d72..aa58622ee 100644
--- a/packages/shared/src/utils/type-guards/index.ts
+++ b/packages/shared/src/utils/type-guards/index.ts
@@ -1 +1,2 @@
export * from './spec';
+export * from './material';
diff --git a/packages/shared/src/utils/type-guards/material.ts b/packages/shared/src/utils/type-guards/material.ts
new file mode 100644
index 000000000..5e58b72c7
--- /dev/null
+++ b/packages/shared/src/utils/type-guards/material.ts
@@ -0,0 +1,10 @@
+import { LowCodeComponent, ProCodeComponent } from '../../types';
+import { isPlainObject } from 'lodash-es';
+
+export function isLowCodeComponentPackage(v: unknown): v is LowCodeComponent {
+ return isPlainObject(v) && (v as any).type === 'lowCode' && (v as any).schema;
+}
+
+export function isProCodeComponentPackage(v: unknown): v is ProCodeComponent {
+ return isPlainObject(v) && (v as any).package && (v as any).library;
+}
diff --git a/packages/shared/src/utils/type-guards/spec.ts b/packages/shared/src/utils/type-guards/spec.ts
index 61b65a648..da4ca4089 100644
--- a/packages/shared/src/utils/type-guards/spec.ts
+++ b/packages/shared/src/utils/type-guards/spec.ts
@@ -1,4 +1,4 @@
-import type { Spec, LowCodeComponent } from '../../types';
+import type { Spec } from '../../types';
import { isPlainObject } from 'lodash-es';
export function isJSExpression(v: unknown): v is Spec.JSExpression {
@@ -24,7 +24,3 @@ export function isJSI18nNode(v: unknown): v is Spec.JSI18n {
export function isComponentNode(v: unknown): v is Spec.ComponentNode {
return isPlainObject(v) && (v as any).componentName;
}
-
-export function isLowCodeComponentSchema(v: unknown): v is LowCodeComponent {
- return isPlainObject(v) && (v as any).type === 'lowCode' && (v as any).schema;
-}