mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-12 03:01:16 +00:00
fix: lifeCycle events and router plugin
This commit is contained in:
parent
37d07f1db6
commit
61bc8e6c3d
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,7 +13,6 @@ pnpm-lock.yaml
|
|||||||
deploy-space/packages
|
deploy-space/packages
|
||||||
deploy-space/.env
|
deploy-space/.env
|
||||||
|
|
||||||
|
|
||||||
# IDE
|
# IDE
|
||||||
.vscode
|
.vscode
|
||||||
.idea
|
.idea
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { createRenderer, type AppOptions } from '@alilc/lowcode-renderer-core';
|
import { createRenderer, type AppOptions } from '@alilc/lowcode-renderer-core';
|
||||||
import { type ComponentType } from 'react';
|
import { type ComponentType } from 'react';
|
||||||
import { type Root, createRoot } from 'react-dom/client';
|
import { type Root, createRoot } from 'react-dom/client';
|
||||||
import { ApplicationView, RendererContext, extension } from '../app';
|
import { ApplicationView, RendererContext, boosts } from '../app';
|
||||||
|
|
||||||
export interface ReactAppOptions extends AppOptions {
|
export interface ReactAppOptions extends AppOptions {
|
||||||
faultComponent?: ComponentType<any>;
|
faultComponent?: ComponentType<any>;
|
||||||
@ -17,7 +17,7 @@ export const createApp = async (options: ReactAppOptions) => {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// extends boosts
|
// extends boosts
|
||||||
extension.install(boostsManager);
|
boostsManager.extend(boosts.toExpose());
|
||||||
|
|
||||||
let root: Root | undefined;
|
let root: Root | undefined;
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { createRenderer, type AppOptions } from '@alilc/lowcode-renderer-core';
|
import { createRenderer, type AppOptions } from '@alilc/lowcode-renderer-core';
|
||||||
import { FunctionComponent } from 'react';
|
import { FunctionComponent } from 'react';
|
||||||
import { type LowCodeComponentProps, createComponentBySchema } from '../runtime/component';
|
import { type LowCodeComponentProps, createComponentBySchema } from '../runtime/schema';
|
||||||
import { RendererContext } from '../app/context';
|
import { RendererContext } from '../app/context';
|
||||||
|
|
||||||
interface Render {
|
interface Render {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { type Plugin, type IBoostsService } from '@alilc/lowcode-renderer-core';
|
import { type Plugin } from '@alilc/lowcode-renderer-core';
|
||||||
import { type ComponentType, type PropsWithChildren } from 'react';
|
import { type ComponentType, type PropsWithChildren } from 'react';
|
||||||
|
|
||||||
export type WrapperComponent = ComponentType<PropsWithChildren<any>>;
|
export type WrapperComponent = ComponentType<PropsWithChildren<any>>;
|
||||||
@ -9,13 +9,13 @@ export interface OutletProps {
|
|||||||
|
|
||||||
export type Outlet = ComponentType<OutletProps>;
|
export type Outlet = ComponentType<OutletProps>;
|
||||||
|
|
||||||
export interface ReactRendererExtensionApi {
|
export interface ReactRendererBoostsApi {
|
||||||
addAppWrapper(appWrapper: WrapperComponent): void;
|
addAppWrapper(appWrapper: WrapperComponent): void;
|
||||||
|
|
||||||
setOutlet(outlet: Outlet): void;
|
setOutlet(outlet: Outlet): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReactRendererExtension {
|
class ReactRendererBoosts {
|
||||||
private wrappers: WrapperComponent[] = [];
|
private wrappers: WrapperComponent[] = [];
|
||||||
|
|
||||||
private outlet: Outlet | null = null;
|
private outlet: Outlet | null = null;
|
||||||
@ -28,7 +28,7 @@ class ReactRendererExtension {
|
|||||||
return this.outlet;
|
return this.outlet;
|
||||||
}
|
}
|
||||||
|
|
||||||
toExpose(): ReactRendererExtensionApi {
|
toExpose(): ReactRendererBoostsApi {
|
||||||
return {
|
return {
|
||||||
addAppWrapper: (appWrapper) => {
|
addAppWrapper: (appWrapper) => {
|
||||||
if (appWrapper) this.wrappers.push(appWrapper);
|
if (appWrapper) this.wrappers.push(appWrapper);
|
||||||
@ -38,14 +38,10 @@ class ReactRendererExtension {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
install(boostsService: IBoostsService) {
|
|
||||||
boostsService.extend(this.toExpose());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const extension = new ReactRendererExtension();
|
export const boosts = new ReactRendererBoosts();
|
||||||
|
|
||||||
export function defineRendererPlugin(plugin: Plugin<ReactRendererExtensionApi>) {
|
export function defineRendererPlugin(plugin: Plugin<ReactRendererBoostsApi>) {
|
||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
@ -1,3 +1,3 @@
|
|||||||
export * from './context';
|
export * from './context';
|
||||||
export * from './extension';
|
export * from './boosts';
|
||||||
export * from './view';
|
export * from './view';
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { useRenderContext } from './context';
|
import { useRenderContext } from './context';
|
||||||
import { getComponentByName } from '../runtime/component';
|
import { getComponentByName } from '../runtime/schema';
|
||||||
import { extension } from './extension';
|
import { boosts } from './boosts';
|
||||||
|
|
||||||
export function ApplicationView() {
|
export function ApplicationView() {
|
||||||
const renderContext = useRenderContext();
|
const renderContext = useRenderContext();
|
||||||
const { schema } = renderContext;
|
const { schema } = renderContext;
|
||||||
const appWrappers = extension.getAppWrappers();
|
const appWrappers = boosts.getAppWrappers();
|
||||||
const Outlet = extension.getOutlet();
|
const Outlet = boosts.getOutlet();
|
||||||
|
|
||||||
if (!Outlet) return null;
|
if (!Outlet) return null;
|
||||||
|
|
||||||
|
|||||||
@ -5,4 +5,4 @@ export * from './router';
|
|||||||
|
|
||||||
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 } from '@alilc/lowcode-renderer-core';
|
||||||
export type { RendererExtends } from './app/extension';
|
export type { ReactRendererBoostsApi } from './app/boosts';
|
||||||
|
|||||||
@ -1,2 +1,3 @@
|
|||||||
export * from './context';
|
export * from './context';
|
||||||
export * from './plugin';
|
export * from './plugin';
|
||||||
|
export type * from '@alilc/lowcode-renderer-router';
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { defineRendererPlugin } from '../app/extension';
|
import { defineRendererPlugin } from '../app/boosts';
|
||||||
import { LifecyclePhase } from '@alilc/lowcode-renderer-core';
|
import { LifecyclePhase } from '@alilc/lowcode-renderer-core';
|
||||||
import { createRouter, type RouterOptions } from '@alilc/lowcode-renderer-router';
|
import { createRouter, type RouterOptions } from '@alilc/lowcode-renderer-router';
|
||||||
import { createRouterView } from './routerView';
|
import { createRouterView } from './routerView';
|
||||||
@ -29,13 +29,14 @@ export const routerPlugin = defineRendererPlugin({
|
|||||||
const router = createRouter(routerConfig);
|
const router = createRouter(routerConfig);
|
||||||
|
|
||||||
boosts.codeRuntime.getScope().set('router', router);
|
boosts.codeRuntime.getScope().set('router', router);
|
||||||
|
boosts.temporaryUse('router', router);
|
||||||
|
|
||||||
const RouterView = createRouterView(router);
|
const RouterView = createRouterView(router);
|
||||||
|
|
||||||
boosts.addAppWrapper(RouterView);
|
boosts.addAppWrapper(RouterView);
|
||||||
boosts.setOutlet(RouteOutlet);
|
boosts.setOutlet(RouteOutlet);
|
||||||
|
|
||||||
whenLifeCylePhaseChange(LifecyclePhase.Ready).then(() => {
|
whenLifeCylePhaseChange(LifecyclePhase.AfterInitPackageLoad).then(() => {
|
||||||
return router.isReady();
|
return router.isReady();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useRenderContext } from '../app/context';
|
import { useRenderContext } from '../app/context';
|
||||||
import { OutletProps } from '../app/extension';
|
import { OutletProps } from '../app/boosts';
|
||||||
import { useRouteLocation } from './context';
|
import { useRouteLocation } from './context';
|
||||||
import { createComponentBySchema } from '../runtime/component';
|
import { createComponentBySchema } from '../runtime/schema';
|
||||||
|
|
||||||
export function RouteOutlet(props: OutletProps) {
|
export function RouteOutlet(props: OutletProps) {
|
||||||
const context = useRenderContext();
|
const context = useRenderContext();
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import { type ComponentType, type ReactInstance, useMemo, createElement } from '
|
|||||||
import { useRenderContext } from '../app/context';
|
import { useRenderContext } from '../app/context';
|
||||||
import { useReactiveStore } from './hooks/useReactiveStore';
|
import { useReactiveStore } from './hooks/useReactiveStore';
|
||||||
import { useModel } from './context';
|
import { useModel } from './context';
|
||||||
import { getComponentByName } from './component';
|
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>;
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { IComponentTreeModel } from '@alilc/lowcode-renderer-core';
|
import { IComponentTreeModel } from '@alilc/lowcode-renderer-core';
|
||||||
import { createContext, useContext, type ReactInstance } from 'react';
|
import { createContext, useContext, type ReactInstance } from 'react';
|
||||||
import { type ReactComponent } from './render';
|
import { type ReactComponent } from './components';
|
||||||
|
|
||||||
export const ModelContext = createContext<IComponentTreeModel<ReactComponent, ReactInstance>>(
|
export const ModelContext = createContext<IComponentTreeModel<ReactComponent, ReactInstance>>(
|
||||||
undefined!,
|
undefined!,
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export * from './component';
|
export * from './schema';
|
||||||
export * from './render';
|
export * from './components';
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { isValidElementType } from 'react-is';
|
|||||||
import { useRenderContext } from '../app/context';
|
import { useRenderContext } from '../app/context';
|
||||||
import { reactiveStateFactory } from './reactiveState';
|
import { reactiveStateFactory } from './reactiveState';
|
||||||
import { dataSourceCreator } from './dataSource';
|
import { dataSourceCreator } from './dataSource';
|
||||||
import { type ReactComponent, type ReactWidget, createElementByWidget } from './render';
|
import { type ReactComponent, type ReactWidget, createElementByWidget } from './components';
|
||||||
import { ModelContextProvider } from './context';
|
import { ModelContextProvider } from './context';
|
||||||
import { appendExternalStyle } from '../utils/element';
|
import { appendExternalStyle } from '../utils/element';
|
||||||
|
|
||||||
@ -1,5 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* @vitest-environment jsdom
|
||||||
|
*/
|
||||||
import { describe, it, expect } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
import { CodeScope } from '../../../src/parts/code-runtime';
|
import { CodeScope } from '../../../src/services/code-runtime';
|
||||||
|
|
||||||
describe('CodeScope', () => {
|
describe('CodeScope', () => {
|
||||||
it('should return initial values', () => {
|
it('should return initial values', () => {
|
||||||
@ -18,9 +21,8 @@ describe('CodeScope', () => {
|
|||||||
it('inject should not overwrite existing values without force', () => {
|
it('inject should not overwrite existing values without force', () => {
|
||||||
const initValue = { a: 1 };
|
const initValue = { a: 1 };
|
||||||
const scope = new CodeScope(initValue);
|
const scope = new CodeScope(initValue);
|
||||||
scope.set('a', 2);
|
expect(scope.value.a).not.toBe(2);
|
||||||
expect(scope.value.a).toBe(1);
|
scope.set('a', 3);
|
||||||
scope.set('a', 3, true);
|
|
||||||
expect(scope.value.a).toBe(3);
|
expect(scope.value.a).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
36
packages/renderer-core/__tests__/services/lifeCycle.spec.ts
Normal file
36
packages/renderer-core/__tests__/services/lifeCycle.spec.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { LifeCycleService, LifecyclePhase } from '../../src/services/lifeCycleService';
|
||||||
|
|
||||||
|
const sleep = () => new Promise((r) => setTimeout(r, 500));
|
||||||
|
|
||||||
|
describe('LifeCycleService', () => {
|
||||||
|
it('it works', async () => {
|
||||||
|
let result = '';
|
||||||
|
const lifeCycle = new LifeCycleService();
|
||||||
|
|
||||||
|
lifeCycle.when(LifecyclePhase.Ready).then(() => {
|
||||||
|
result += '1';
|
||||||
|
});
|
||||||
|
lifeCycle.when(LifecyclePhase.Ready).finally(() => {
|
||||||
|
result += '2';
|
||||||
|
});
|
||||||
|
lifeCycle.when(LifecyclePhase.AfterInitPackageLoad).then(() => {
|
||||||
|
result += '3';
|
||||||
|
});
|
||||||
|
lifeCycle.when(LifecyclePhase.AfterInitPackageLoad).finally(() => {
|
||||||
|
result += '4';
|
||||||
|
});
|
||||||
|
|
||||||
|
lifeCycle.phase = LifecyclePhase.Ready;
|
||||||
|
|
||||||
|
await sleep();
|
||||||
|
|
||||||
|
expect(result).toEqual('12');
|
||||||
|
|
||||||
|
lifeCycle.phase = LifecyclePhase.AfterInitPackageLoad;
|
||||||
|
|
||||||
|
await sleep();
|
||||||
|
|
||||||
|
expect(result).toEqual('1234');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -31,7 +31,7 @@ export class RendererMain<RenderObject> {
|
|||||||
@IBoostsService private boostsService: IBoostsService,
|
@IBoostsService private boostsService: IBoostsService,
|
||||||
@ILifeCycleService private lifeCycleService: ILifeCycleService,
|
@ILifeCycleService private lifeCycleService: ILifeCycleService,
|
||||||
) {
|
) {
|
||||||
this.lifeCycleService.when(LifecyclePhase.OptionsResolved).finally(async () => {
|
this.lifeCycleService.when(LifecyclePhase.OptionsResolved).then(async () => {
|
||||||
const renderContext = {
|
const renderContext = {
|
||||||
schema: this.schemaService,
|
schema: this.schemaService,
|
||||||
packageManager: this.packageManagementService,
|
packageManager: this.packageManagementService,
|
||||||
@ -42,8 +42,6 @@ export class RendererMain<RenderObject> {
|
|||||||
|
|
||||||
this.renderObject = await this.adapter(renderContext);
|
this.renderObject = await this.adapter(renderContext);
|
||||||
|
|
||||||
await this.packageManagementService.loadPackages(this.initOptions.packages ?? []);
|
|
||||||
|
|
||||||
this.lifeCycleService.phase = LifecyclePhase.Ready;
|
this.lifeCycleService.phase = LifecyclePhase.Ready;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -60,13 +58,19 @@ export class RendererMain<RenderObject> {
|
|||||||
|
|
||||||
this.codeRuntimeService.initialize(options.codeRuntime ?? {});
|
this.codeRuntimeService.initialize(options.codeRuntime ?? {});
|
||||||
|
|
||||||
this.extensionHostService.registerPlugin(plugins);
|
|
||||||
|
|
||||||
this.lifeCycleService.phase = LifecyclePhase.OptionsResolved;
|
this.lifeCycleService.phase = LifecyclePhase.OptionsResolved;
|
||||||
|
|
||||||
|
await this.lifeCycleService.when(LifecyclePhase.Ready);
|
||||||
|
|
||||||
|
await this.extensionHostService.registerPlugin(plugins);
|
||||||
|
|
||||||
|
await this.packageManagementService.loadPackages(this.initOptions.packages ?? []);
|
||||||
|
|
||||||
|
this.lifeCycleService.phase = LifecyclePhase.AfterInitPackageLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getApp(): Promise<RendererApplication<RenderObject>> {
|
async getApp(): Promise<RendererApplication<RenderObject>> {
|
||||||
await this.lifeCycleService.when(LifecyclePhase.Ready);
|
await this.lifeCycleService.when(LifecyclePhase.AfterInitPackageLoad);
|
||||||
|
|
||||||
// construct application
|
// construct application
|
||||||
return Object.freeze<RendererApplication<RenderObject>>({
|
return Object.freeze<RendererApplication<RenderObject>>({
|
||||||
@ -79,8 +83,7 @@ export class RendererMain<RenderObject> {
|
|||||||
...this.renderObject,
|
...this.renderObject,
|
||||||
|
|
||||||
use: (plugin) => {
|
use: (plugin) => {
|
||||||
this.extensionHostService.registerPlugin(plugin);
|
return this.extensionHostService.registerPlugin(plugin);
|
||||||
return this.extensionHostService.doSetupPlugin(plugin);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { 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;
|
export type IBoosts<Extends> = IBoostsApi & Extends & { [key: string]: any };
|
||||||
|
|
||||||
export interface IBoostsApi {
|
export interface IBoostsApi {
|
||||||
readonly codeRuntime: ICodeRuntimeService;
|
readonly codeRuntime: ICodeRuntimeService;
|
||||||
@ -12,6 +12,10 @@ export interface IBoostsApi {
|
|||||||
readonly intl: Pick<IRuntimeIntlService, 't' | 'setLocale' | 'getLocale' | 'addTranslations'>;
|
readonly intl: Pick<IRuntimeIntlService, 't' | 'setLocale' | 'getLocale' | 'addTranslations'>;
|
||||||
|
|
||||||
readonly util: Pick<IRuntimeUtilService, 'add' | 'remove'>;
|
readonly util: Pick<IRuntimeUtilService, 'add' | 'remove'>;
|
||||||
|
/**
|
||||||
|
* 允许插件挂载额外的对象在 boosts 上,方便其他插件使用
|
||||||
|
*/
|
||||||
|
temporaryUse(name: string, value: any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,6 +47,9 @@ export class BoostsService implements IBoostsService {
|
|||||||
codeRuntime: this.codeRuntimeService,
|
codeRuntime: this.codeRuntimeService,
|
||||||
intl: this.runtimeIntlService,
|
intl: this.runtimeIntlService,
|
||||||
util: this.runtimeUtilService,
|
util: this.runtimeUtilService,
|
||||||
|
temporaryUse: (name, value) => {
|
||||||
|
this.extend(name, value);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +62,8 @@ export class BoostsService implements IBoostsService {
|
|||||||
} else {
|
} else {
|
||||||
if (!this.extendsValue[name]) {
|
if (!this.extendsValue[name]) {
|
||||||
this.extendsValue[name] = value;
|
this.extendsValue[name] = value;
|
||||||
|
} else {
|
||||||
|
console.warn(`${name} is exist`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (isObject(name)) {
|
} else if (isObject(name)) {
|
||||||
|
|||||||
@ -3,8 +3,6 @@ import { type Plugin, type PluginContext } from './plugin';
|
|||||||
import { IBoostsService } from './boosts';
|
import { IBoostsService } from './boosts';
|
||||||
import { IPackageManagementService } from '../package';
|
import { IPackageManagementService } from '../package';
|
||||||
import { ISchemaService } from '../schema';
|
import { ISchemaService } from '../schema';
|
||||||
import { type RenderAdapter } from './render';
|
|
||||||
import { IComponentTreeModelService } from '../model';
|
|
||||||
import { ILifeCycleService, LifecyclePhase } from '../lifeCycleService';
|
import { ILifeCycleService, LifecyclePhase } from '../lifeCycleService';
|
||||||
|
|
||||||
interface IPluginRuntime extends Plugin {
|
interface IPluginRuntime extends Plugin {
|
||||||
@ -12,9 +10,7 @@ interface IPluginRuntime extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IExtensionHostService {
|
export interface IExtensionHostService {
|
||||||
registerPlugin(plugin: Plugin | Plugin[]): void;
|
registerPlugin(plugin: Plugin | Plugin[]): Promise<void>;
|
||||||
|
|
||||||
doSetupPlugin(plugin: Plugin): Promise<void>;
|
|
||||||
|
|
||||||
getPlugin(name: string): Plugin | undefined;
|
getPlugin(name: string): Plugin | undefined;
|
||||||
|
|
||||||
@ -50,15 +46,9 @@ export class ExtensionHostService implements IExtensionHostService {
|
|||||||
return this.lifeCycleService.when(phase);
|
return this.lifeCycleService.when(phase);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.lifeCycleService.when(LifecyclePhase.OptionsResolved).then(async () => {
|
|
||||||
for (const plugin of this.pluginRuntimes) {
|
|
||||||
await this.doSetupPlugin(plugin);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerPlugin(plugins: Plugin | Plugin[]) {
|
async registerPlugin(plugins: Plugin | Plugin[]) {
|
||||||
plugins = Array.isArray(plugins) ? plugins : [plugins];
|
plugins = Array.isArray(plugins) ? plugins : [plugins];
|
||||||
|
|
||||||
for (const plugin of plugins) {
|
for (const plugin of plugins) {
|
||||||
@ -67,19 +57,17 @@ export class ExtensionHostService implements IExtensionHostService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pluginRuntimes.push({
|
await this.doSetupPlugin(plugin);
|
||||||
...plugin,
|
|
||||||
status: 'ready',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async doSetupPlugin(plugin: Plugin) {
|
private async doSetupPlugin(plugin: Plugin) {
|
||||||
const pluginRuntime = plugin as IPluginRuntime;
|
const pluginRuntime = plugin as IPluginRuntime;
|
||||||
|
|
||||||
if (!this.pluginRuntimes.some((item) => item.name !== pluginRuntime.name)) {
|
this.pluginRuntimes.push({
|
||||||
return;
|
...pluginRuntime,
|
||||||
}
|
status: 'ready',
|
||||||
|
});
|
||||||
|
|
||||||
const isSetup = (name: string) => {
|
const isSetup = (name: string) => {
|
||||||
const setupPlugins = this.pluginRuntimes.filter((item) => item.status === 'setup');
|
const setupPlugins = this.pluginRuntimes.filter((item) => item.status === 'setup');
|
||||||
|
|||||||
@ -7,11 +7,7 @@ export const enum LifecyclePhase {
|
|||||||
|
|
||||||
Ready = 3,
|
Ready = 3,
|
||||||
|
|
||||||
BeforeMount = 4,
|
AfterInitPackageLoad = 4,
|
||||||
|
|
||||||
Mounted = 5,
|
|
||||||
|
|
||||||
BeforeUnmount = 6,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILifeCycleService {
|
export interface ILifeCycleService {
|
||||||
@ -40,7 +36,7 @@ export class LifeCycleService implements ILifeCycleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set phase(value: LifecyclePhase) {
|
set phase(value: LifecyclePhase) {
|
||||||
if (value < this.phase) {
|
if (value < this._phase) {
|
||||||
throw new Error('Lifecycle cannot go backwards');
|
throw new Error('Lifecycle cannot go backwards');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -68,7 +68,7 @@ export class PackageManagementService implements IPackageManagementService {
|
|||||||
@ISchemaService private schemaService: ISchemaService,
|
@ISchemaService private schemaService: ISchemaService,
|
||||||
@ILifeCycleService private lifeCycleService: ILifeCycleService,
|
@ILifeCycleService private lifeCycleService: ILifeCycleService,
|
||||||
) {
|
) {
|
||||||
this.lifeCycleService.when(LifecyclePhase.Ready).then(() => {
|
this.lifeCycleService.when(LifecyclePhase.AfterInitPackageLoad).then(() => {
|
||||||
const componentsMaps = this.schemaService.get('componentsMap');
|
const componentsMaps = this.schemaService.get('componentsMap');
|
||||||
this.resolveComponentMaps(componentsMaps);
|
this.resolveComponentMaps(componentsMaps);
|
||||||
});
|
});
|
||||||
@ -85,7 +85,6 @@ export class PackageManagementService implements IPackageManagementService {
|
|||||||
if (!isProCodeComponentPackage(item)) continue;
|
if (!isProCodeComponentPackage(item)) continue;
|
||||||
|
|
||||||
const normalized = this.normalizePackage(item);
|
const normalized = this.normalizePackage(item);
|
||||||
|
|
||||||
await this.loadPackageByNormalized(normalized);
|
await this.loadPackageByNormalized(normalized);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {
|
|||||||
type Spec,
|
type Spec,
|
||||||
type Locale,
|
type Locale,
|
||||||
type Translations,
|
type Translations,
|
||||||
|
platformLocale,
|
||||||
} from '@alilc/lowcode-shared';
|
} from '@alilc/lowcode-shared';
|
||||||
import { ILifeCycleService, LifecyclePhase } from './lifeCycleService';
|
import { ILifeCycleService, LifecyclePhase } from './lifeCycleService';
|
||||||
import { ICodeRuntimeService } from './code-runtime';
|
import { ICodeRuntimeService } from './code-runtime';
|
||||||
@ -34,7 +35,7 @@ export const IRuntimeIntlService = createDecorator<IRuntimeIntlService>('IRuntim
|
|||||||
export class RuntimeIntlService implements IRuntimeIntlService {
|
export class RuntimeIntlService implements IRuntimeIntlService {
|
||||||
private intl: Intl = new Intl();
|
private intl: Intl = new Intl();
|
||||||
|
|
||||||
public locale: string = navigator.language;
|
public locale: string = platformLocale;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ILifeCycleService private lifeCycleService: ILifeCycleService,
|
@ILifeCycleService private lifeCycleService: ILifeCycleService,
|
||||||
|
|||||||
@ -23,12 +23,11 @@ export class RuntimeUtilService implements IRuntimeUtilService {
|
|||||||
@ILifeCycleService private lifeCycleService: ILifeCycleService,
|
@ILifeCycleService private lifeCycleService: ILifeCycleService,
|
||||||
@ISchemaService private schemaService: ISchemaService,
|
@ISchemaService private schemaService: ISchemaService,
|
||||||
) {
|
) {
|
||||||
this.lifeCycleService.when(LifecyclePhase.Ready).then(() => {
|
this.lifeCycleService.when(LifecyclePhase.AfterInitPackageLoad).then(() => {
|
||||||
const utils = this.schemaService.get('utils') ?? [];
|
const utils = this.schemaService.get('utils') ?? [];
|
||||||
for (const util of utils) {
|
for (const util of utils) {
|
||||||
this.add(util);
|
this.add(util);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.toExpose();
|
this.toExpose();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { defineConfig } from 'vitest/config'
|
import { defineProject } from 'vitest/config';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineProject({
|
||||||
test: {
|
test: {
|
||||||
include: ['tests/**/*.spec.ts']
|
environment: 'jsdom',
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
||||||
import { EventEmitter } from '../../src';
|
|
||||||
|
|
||||||
describe('hookable', () => {
|
|
||||||
let eventEmitter: EventEmitter;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
eventEmitter = new EventEmitter();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('on', async () => {
|
|
||||||
const spy = vi.fn();
|
|
||||||
eventEmitter.on('test', spy);
|
|
||||||
await eventEmitter.emit('test');
|
|
||||||
|
|
||||||
expect(spy).toBeCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('prependListener', () => {
|
|
||||||
// const spy = vi.fn();
|
|
||||||
// expect(spy).toC
|
|
||||||
});
|
|
||||||
});
|
|
||||||
56
packages/shared/__tests__/utils/async.spec.ts
Normal file
56
packages/shared/__tests__/utils/async.spec.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { Barrier } from '../../src';
|
||||||
|
|
||||||
|
describe('Barrier', () => {
|
||||||
|
it('waits for barrier to open', async () => {
|
||||||
|
const barrier = new Barrier();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
barrier.open();
|
||||||
|
}, 500); // Simulate opening the barrier after 500ms
|
||||||
|
|
||||||
|
const start = Date.now();
|
||||||
|
await barrier.wait(); // Async operation should await here
|
||||||
|
const duration = Date.now() - start;
|
||||||
|
|
||||||
|
expect(barrier.isOpen()).toBeTruthy();
|
||||||
|
expect(duration).toBeGreaterThanOrEqual(500); // Ensures we waited for at least 500ms
|
||||||
|
});
|
||||||
|
|
||||||
|
it('mutiple', async () => {
|
||||||
|
let result = '';
|
||||||
|
const b1 = new Barrier();
|
||||||
|
const b2 = new Barrier();
|
||||||
|
|
||||||
|
async function run1() {
|
||||||
|
await b1.wait();
|
||||||
|
}
|
||||||
|
async function run2() {
|
||||||
|
await b2.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
run1().then(() => {
|
||||||
|
result += 1;
|
||||||
|
});
|
||||||
|
run1().finally(() => {
|
||||||
|
result += 2;
|
||||||
|
});
|
||||||
|
|
||||||
|
run2().then(() => {
|
||||||
|
result += 3;
|
||||||
|
});
|
||||||
|
run2().finally(() => {
|
||||||
|
result += 4;
|
||||||
|
});
|
||||||
|
|
||||||
|
b1.open();
|
||||||
|
|
||||||
|
await run1();
|
||||||
|
|
||||||
|
b2.open();
|
||||||
|
|
||||||
|
await run2();
|
||||||
|
|
||||||
|
expect(result).toBe('1234');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -5,6 +5,13 @@
|
|||||||
"main": "dist/low-code-shared.cjs",
|
"main": "dist/low-code-shared.cjs",
|
||||||
"module": "dist/low-code-shared.js",
|
"module": "dist/low-code-shared.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./dist/low-code-shared.js",
|
||||||
|
"require": "./dist/low-code-shared.cjs",
|
||||||
|
"types": "./dist/index.d.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
"src",
|
"src",
|
||||||
|
|||||||
@ -15,7 +15,7 @@ export type Constructor<T = any> = new (...args: any[]) => T;
|
|||||||
export function createDecorator<T>(serviceId: string): ServiceIdentifier<T> {
|
export function createDecorator<T>(serviceId: string): ServiceIdentifier<T> {
|
||||||
const id = <any>(
|
const id = <any>(
|
||||||
function (target: Constructor, targetKey: string, indexOrPropertyDescriptor: any): any {
|
function (target: Constructor, targetKey: string, indexOrPropertyDescriptor: any): any {
|
||||||
return set(serviceId)(target, targetKey, indexOrPropertyDescriptor);
|
return inject(serviceId)(target, targetKey, indexOrPropertyDescriptor);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
id.toString = () => serviceId;
|
id.toString = () => serviceId;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { createIntl, createIntlCache, type IntlShape as IntlFormatter } from '@formatjs/intl';
|
import { createIntl, createIntlCache, type IntlShape as IntlFormatter } from '@formatjs/intl';
|
||||||
import { mapKeys } from 'lodash-es';
|
import { mapKeys } from 'lodash-es';
|
||||||
import { signal, computed, effect, type Signal, type ComputedSignal } from '../signals';
|
import { signal, computed, effect, type Signal, type ComputedSignal } from '../signals';
|
||||||
|
import { platformLocale } from '../utils';
|
||||||
|
|
||||||
export { IntlFormatter };
|
export { IntlFormatter };
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ export class Intl {
|
|||||||
private currentMessage: ComputedSignal<Translations>;
|
private currentMessage: ComputedSignal<Translations>;
|
||||||
private intlShape: IntlFormatter;
|
private intlShape: IntlFormatter;
|
||||||
|
|
||||||
constructor(defaultLocale: string = navigator.language, messages: LocaleTranslationsRecord = {}) {
|
constructor(defaultLocale: string = platformLocale, messages: LocaleTranslationsRecord = {}) {
|
||||||
if (defaultLocale) {
|
if (defaultLocale) {
|
||||||
defaultLocale = nomarlizeLocale(defaultLocale);
|
defaultLocale = nomarlizeLocale(defaultLocale);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
const userAgent: string = navigator.userAgent;
|
const userAgent: string = window.navigator.userAgent;
|
||||||
|
|
||||||
export const isWindows = userAgent.indexOf('Windows') >= 0;
|
export const isWindows = userAgent.indexOf('Windows') >= 0;
|
||||||
export const isMacintosh = userAgent.indexOf('Macintosh') >= 0;
|
export const isMacintosh = userAgent.indexOf('Macintosh') >= 0;
|
||||||
export const isLinux = userAgent.indexOf('Linux') >= 0;
|
export const isLinux = userAgent.indexOf('Linux') >= 0;
|
||||||
export const isIOS =
|
export const isIOS =
|
||||||
(isMacintosh || userAgent.indexOf('iPad') >= 0 || userAgent.indexOf('iPhone') >= 0) &&
|
(isMacintosh || userAgent.indexOf('iPad') >= 0 || userAgent.indexOf('iPhone') >= 0) &&
|
||||||
!!navigator.maxTouchPoints &&
|
!!window.navigator.maxTouchPoints &&
|
||||||
navigator.maxTouchPoints > 0;
|
window.navigator.maxTouchPoints > 0;
|
||||||
export const isMobile = userAgent?.indexOf('Mobi') >= 0;
|
export const isMobile = userAgent?.indexOf('Mobi') >= 0;
|
||||||
export const locale: string | undefined = undefined;
|
|
||||||
export const platformLocale = navigator.language;
|
export const platformLocale = window.navigator.language;
|
||||||
|
|
||||||
export const enum Platform {
|
export const enum Platform {
|
||||||
Web,
|
Web,
|
||||||
|
|||||||
3
packages/shared/vitest.config.ts
Normal file
3
packages/shared/vitest.config.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { defineProject } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineProject({});
|
||||||
@ -1,5 +1,5 @@
|
|||||||
packages:
|
packages:
|
||||||
- 'packages/*'
|
- 'packages/*'
|
||||||
- 'playground'
|
- 'playground/*'
|
||||||
- 'docs'
|
- 'docs'
|
||||||
- 'scripts'
|
- 'scripts'
|
||||||
|
|||||||
@ -39,5 +39,11 @@
|
|||||||
},
|
},
|
||||||
"types": ["vite/client", "vitest/globals", "node"]
|
"types": ["vite/client", "vitest/globals", "node"]
|
||||||
},
|
},
|
||||||
"exclude": ["**/dist", "node_modules"]
|
"include": [
|
||||||
|
"packages/global.d.ts",
|
||||||
|
"packages/*/src",
|
||||||
|
"packages/*/__tests__",
|
||||||
|
"playground/*/src",
|
||||||
|
"scripts/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,12 @@ export default defineWorkspace([
|
|||||||
{
|
{
|
||||||
test: {
|
test: {
|
||||||
include: ['**/__tests__/**/*.spec.{ts?(x),js?(x)}'],
|
include: ['**/__tests__/**/*.spec.{ts?(x),js?(x)}'],
|
||||||
|
alias: {
|
||||||
|
'@alilc/lowcode-shared': 'packages/shared',
|
||||||
|
},
|
||||||
name: 'unit test',
|
name: 'unit test',
|
||||||
environment: 'jsdom',
|
environmentMatchGlobs: [['packages/**', 'jsdom']],
|
||||||
globals: true
|
globals: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
])
|
]);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user