mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-14 12:59:38 +00:00
fix: fix some bugs
This commit is contained in:
parent
f1711e0fc9
commit
0157bbd68f
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 (
|
||||
<RouterContext.Provider value={router}>
|
||||
<RouteLocationContext.Provider value={location}>
|
||||
<PageConfigContext.Provider value={pageSchema}>{children}</PageConfigContext.Provider>
|
||||
<PageConfigContext.Provider value={pageConfig}>{children}</PageConfigContext.Provider>
|
||||
</RouteLocationContext.Provider>
|
||||
</RouterContext.Provider>
|
||||
);
|
||||
|
||||
@ -1 +1,5 @@
|
||||
export const dataSourceCreator = () => ({}) as any;
|
||||
export const dataSourceCreator = () =>
|
||||
({
|
||||
dataSourceMap: {},
|
||||
reloadDataSource: () => {},
|
||||
}) as any;
|
||||
|
||||
@ -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),
|
||||
);
|
||||
|
||||
@ -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<HTMLElement> {
|
||||
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<HTMLElement> {
|
||||
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<HTMLElement> {
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@ -3,5 +3,5 @@
|
||||
"compilerOptions": {
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src"]
|
||||
"include": ["src", "../../playground/renderer/src/plugin/remote/element.ts"]
|
||||
}
|
||||
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
@ -1 +0,0 @@
|
||||
import {} from 'vitest';
|
||||
@ -1,2 +0,0 @@
|
||||
import { expect } from 'vitest';
|
||||
import { createPackageManager } from '../src/package';
|
||||
@ -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 });
|
||||
});
|
||||
});
|
||||
@ -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', () => {});
|
||||
});
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
@ -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') ?? {};
|
||||
|
||||
@ -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<R = unknown>(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<ICodeRuntimeService>('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<BeforeResolveCb>();
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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<Component, ComponentInstance = unknown>
|
||||
);
|
||||
|
||||
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,13 +133,11 @@ export class ComponentTreeModel<Component, ComponentInstance = unknown>
|
||||
|
||||
const lifeCycleSchema = this.componentsTree.lifeCycles[lifeCycleName];
|
||||
|
||||
if (isJSFunction(lifeCycleSchema)) {
|
||||
const lifeCycleFn = this.codeRuntime.run<AnyFunction>(lifeCycleSchema.value, this.codeScope);
|
||||
if (lifeCycleFn) {
|
||||
const lifeCycleFn = this.codeRuntime.resolve(lifeCycleSchema, this.codeScope);
|
||||
if (typeof lifeCycleFn === 'function') {
|
||||
lifeCycleFn.apply(this.codeScope.value, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setComponentRef(ref: string, ins: ComponentInstance) {
|
||||
let insArr = this.instanceMap.get(ref);
|
||||
|
||||
@ -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<string, any> = ((window as any).__PACKAGE_STORE__ ??= new Map());
|
||||
|
||||
private packagesRef: Spec.Package[] = [];
|
||||
private packagesMap: Map<string, NormalizedPackage> = new Map();
|
||||
|
||||
private lowCodeComponentPackages: Map<string, LowCodeComponent> = 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;
|
||||
});
|
||||
|
||||
if (!isExist) {
|
||||
this.packagesRef.push(item);
|
||||
|
||||
if (!this.packageStore.has(newId)) {
|
||||
const loader = this.packageLoaders.find((loader) => loader.active(item));
|
||||
if (!loader) continue;
|
||||
|
||||
const result = await loader.load.call(this, item);
|
||||
if (result) this.packageStore.set(newId, result);
|
||||
}
|
||||
// low code component not need load
|
||||
if (isLowCodeComponentPackage(item)) {
|
||||
this.lowCodeComponentPackages.set(item.id, item);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isProCodeComponentPackage(item)) continue;
|
||||
|
||||
const normalized = this.normalizePackage(item);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<Render = unknown> = {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -56,7 +56,7 @@ export interface Package {
|
||||
/**
|
||||
* 作为全局变量引用时的名称,和 webpack output.library 字段含义一样,用来定义全局变量名
|
||||
*/
|
||||
library: string;
|
||||
library?: string | undefined;
|
||||
/**
|
||||
* 组件描述导出名字,可以通过 window[exportName] 获取到组件描述的 Object 内容;
|
||||
*/
|
||||
|
||||
@ -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<string, string | number | JSExpression>;
|
||||
}
|
||||
|
||||
export type JSNode = JSSlot | JSExpression | JSExpression | JSI18n;
|
||||
|
||||
export type NodeType = string | JSExpression | JSI18n | ComponentNode;
|
||||
|
||||
@ -1 +1,2 @@
|
||||
export * from './spec';
|
||||
export * from './material';
|
||||
|
||||
10
packages/shared/src/utils/type-guards/material.ts
Normal file
10
packages/shared/src/utils/type-guards/material.ts
Normal file
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user