From 61bc8e6c3d4c11fcb9919b144e4a1ecf8d468c8e Mon Sep 17 00:00:00 2001 From: 1ncounter <1ncounter.100@gmail.com> Date: Wed, 19 Jun 2024 14:12:15 +0800 Subject: [PATCH] fix: lifeCycle events and router plugin --- .gitignore | 1 - packages/react-renderer/src/api/app.tsx | 4 +- packages/react-renderer/src/api/component.tsx | 2 +- .../src/app/{extension.ts => boosts.ts} | 16 ++---- packages/react-renderer/src/app/index.ts | 2 +- packages/react-renderer/src/app/view.tsx | 8 +-- packages/react-renderer/src/index.ts | 2 +- packages/react-renderer/src/router/index.ts | 1 + packages/react-renderer/src/router/plugin.ts | 5 +- packages/react-renderer/src/router/route.tsx | 4 +- .../runtime/{render.tsx => components.tsx} | 2 +- .../react-renderer/src/runtime/context.ts | 2 +- packages/react-renderer/src/runtime/index.ts | 4 +- .../src/runtime/{component.tsx => schema.tsx} | 2 +- .../code-runtime/codeScope.spec.ts | 10 ++-- .../__tests__/services/lifeCycle.spec.ts | 36 ++++++++++++ packages/renderer-core/src/main.ts | 19 ++++--- .../src/services/extension/boosts.ts | 11 +++- .../extension/extensionHostService.ts | 28 +++------- .../src/services/lifeCycleService.ts | 8 +-- .../src/services/package/managementService.ts | 3 +- .../src/services/runtimeIntlService.ts | 3 +- .../src/services/runtimeUtilService.ts | 3 +- packages/renderer-core/vitest.config.ts | 10 ++-- .../shared/__tests__/helper/hookable.spec.ts | 23 -------- packages/shared/__tests__/utils/async.spec.ts | 56 +++++++++++++++++++ packages/shared/package.json | 7 +++ .../src/abilities/instantiation/index.ts | 2 +- packages/shared/src/abilities/intl.ts | 3 +- packages/shared/src/utils/platform.ts | 10 ++-- packages/shared/vitest.config.ts | 3 + pnpm-workspace.yaml | 2 +- tsconfig.json | 8 ++- vitest.workspace.ts | 11 ++-- 34 files changed, 197 insertions(+), 114 deletions(-) rename packages/react-renderer/src/app/{extension.ts => boosts.ts} (69%) rename packages/react-renderer/src/runtime/{render.tsx => components.tsx} (99%) rename packages/react-renderer/src/runtime/{component.tsx => schema.tsx} (99%) rename packages/renderer-core/__tests__/{parts => services}/code-runtime/codeScope.spec.ts (91%) create mode 100644 packages/renderer-core/__tests__/services/lifeCycle.spec.ts delete mode 100644 packages/shared/__tests__/helper/hookable.spec.ts create mode 100644 packages/shared/__tests__/utils/async.spec.ts create mode 100644 packages/shared/vitest.config.ts diff --git a/.gitignore b/.gitignore index 804755b3d..314af056f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,6 @@ pnpm-lock.yaml deploy-space/packages deploy-space/.env - # IDE .vscode .idea diff --git a/packages/react-renderer/src/api/app.tsx b/packages/react-renderer/src/api/app.tsx index 14f775091..360c9fda7 100644 --- a/packages/react-renderer/src/api/app.tsx +++ b/packages/react-renderer/src/api/app.tsx @@ -1,7 +1,7 @@ import { createRenderer, type AppOptions } from '@alilc/lowcode-renderer-core'; import { type ComponentType } from 'react'; 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 { faultComponent?: ComponentType; @@ -17,7 +17,7 @@ export const createApp = async (options: ReactAppOptions) => { // } // extends boosts - extension.install(boostsManager); + boostsManager.extend(boosts.toExpose()); let root: Root | undefined; diff --git a/packages/react-renderer/src/api/component.tsx b/packages/react-renderer/src/api/component.tsx index 364e07ed3..4ce9e37d6 100644 --- a/packages/react-renderer/src/api/component.tsx +++ b/packages/react-renderer/src/api/component.tsx @@ -1,6 +1,6 @@ import { createRenderer, type AppOptions } from '@alilc/lowcode-renderer-core'; import { FunctionComponent } from 'react'; -import { type LowCodeComponentProps, createComponentBySchema } from '../runtime/component'; +import { type LowCodeComponentProps, createComponentBySchema } from '../runtime/schema'; import { RendererContext } from '../app/context'; interface Render { diff --git a/packages/react-renderer/src/app/extension.ts b/packages/react-renderer/src/app/boosts.ts similarity index 69% rename from packages/react-renderer/src/app/extension.ts rename to packages/react-renderer/src/app/boosts.ts index 2723ff1ff..d38798cf7 100644 --- a/packages/react-renderer/src/app/extension.ts +++ b/packages/react-renderer/src/app/boosts.ts @@ -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'; export type WrapperComponent = ComponentType>; @@ -9,13 +9,13 @@ export interface OutletProps { export type Outlet = ComponentType; -export interface ReactRendererExtensionApi { +export interface ReactRendererBoostsApi { addAppWrapper(appWrapper: WrapperComponent): void; setOutlet(outlet: Outlet): void; } -class ReactRendererExtension { +class ReactRendererBoosts { private wrappers: WrapperComponent[] = []; private outlet: Outlet | null = null; @@ -28,7 +28,7 @@ class ReactRendererExtension { return this.outlet; } - toExpose(): ReactRendererExtensionApi { + toExpose(): ReactRendererBoostsApi { return { addAppWrapper: (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) { +export function defineRendererPlugin(plugin: Plugin) { return plugin; } diff --git a/packages/react-renderer/src/app/index.ts b/packages/react-renderer/src/app/index.ts index 4224c77e5..83465b5bc 100644 --- a/packages/react-renderer/src/app/index.ts +++ b/packages/react-renderer/src/app/index.ts @@ -1,3 +1,3 @@ export * from './context'; -export * from './extension'; +export * from './boosts'; export * from './view'; diff --git a/packages/react-renderer/src/app/view.tsx b/packages/react-renderer/src/app/view.tsx index f7ac43f02..f7aff3c45 100644 --- a/packages/react-renderer/src/app/view.tsx +++ b/packages/react-renderer/src/app/view.tsx @@ -1,12 +1,12 @@ import { useRenderContext } from './context'; -import { getComponentByName } from '../runtime/component'; -import { extension } from './extension'; +import { getComponentByName } from '../runtime/schema'; +import { boosts } from './boosts'; export function ApplicationView() { const renderContext = useRenderContext(); const { schema } = renderContext; - const appWrappers = extension.getAppWrappers(); - const Outlet = extension.getOutlet(); + const appWrappers = boosts.getAppWrappers(); + const Outlet = boosts.getOutlet(); if (!Outlet) return null; diff --git a/packages/react-renderer/src/index.ts b/packages/react-renderer/src/index.ts index c9dea7920..8203ff383 100644 --- a/packages/react-renderer/src/index.ts +++ b/packages/react-renderer/src/index.ts @@ -5,4 +5,4 @@ export * from './router'; export type { Spec, ProCodeComponent, LowCodeComponent } from '@alilc/lowcode-shared'; export type { PackageLoader, CodeScope, Plugin } from '@alilc/lowcode-renderer-core'; -export type { RendererExtends } from './app/extension'; +export type { ReactRendererBoostsApi } from './app/boosts'; diff --git a/packages/react-renderer/src/router/index.ts b/packages/react-renderer/src/router/index.ts index 2964948b4..1f2f8f6eb 100644 --- a/packages/react-renderer/src/router/index.ts +++ b/packages/react-renderer/src/router/index.ts @@ -1,2 +1,3 @@ export * from './context'; export * from './plugin'; +export type * from '@alilc/lowcode-renderer-router'; diff --git a/packages/react-renderer/src/router/plugin.ts b/packages/react-renderer/src/router/plugin.ts index 0bd155822..7dc3c4b99 100644 --- a/packages/react-renderer/src/router/plugin.ts +++ b/packages/react-renderer/src/router/plugin.ts @@ -1,4 +1,4 @@ -import { defineRendererPlugin } from '../app/extension'; +import { defineRendererPlugin } from '../app/boosts'; import { LifecyclePhase } from '@alilc/lowcode-renderer-core'; import { createRouter, type RouterOptions } from '@alilc/lowcode-renderer-router'; import { createRouterView } from './routerView'; @@ -29,13 +29,14 @@ export const routerPlugin = defineRendererPlugin({ const router = createRouter(routerConfig); boosts.codeRuntime.getScope().set('router', router); + boosts.temporaryUse('router', router); const RouterView = createRouterView(router); boosts.addAppWrapper(RouterView); boosts.setOutlet(RouteOutlet); - whenLifeCylePhaseChange(LifecyclePhase.Ready).then(() => { + whenLifeCylePhaseChange(LifecyclePhase.AfterInitPackageLoad).then(() => { return router.isReady(); }); }, diff --git a/packages/react-renderer/src/router/route.tsx b/packages/react-renderer/src/router/route.tsx index a7c93d27e..03a926dca 100644 --- a/packages/react-renderer/src/router/route.tsx +++ b/packages/react-renderer/src/router/route.tsx @@ -1,8 +1,8 @@ import { useMemo } from 'react'; import { useRenderContext } from '../app/context'; -import { OutletProps } from '../app/extension'; +import { OutletProps } from '../app/boosts'; import { useRouteLocation } from './context'; -import { createComponentBySchema } from '../runtime/component'; +import { createComponentBySchema } from '../runtime/schema'; export function RouteOutlet(props: OutletProps) { const context = useRenderContext(); diff --git a/packages/react-renderer/src/runtime/render.tsx b/packages/react-renderer/src/runtime/components.tsx similarity index 99% rename from packages/react-renderer/src/runtime/render.tsx rename to packages/react-renderer/src/runtime/components.tsx index 416b0ef08..57fd1afcd 100644 --- a/packages/react-renderer/src/runtime/render.tsx +++ b/packages/react-renderer/src/runtime/components.tsx @@ -16,7 +16,7 @@ import { type ComponentType, type ReactInstance, useMemo, createElement } from ' import { useRenderContext } from '../app/context'; import { useReactiveStore } from './hooks/useReactiveStore'; import { useModel } from './context'; -import { getComponentByName } from './component'; +import { getComponentByName } from './schema'; export type ReactComponent = ComponentType; export type ReactWidget = IWidget; diff --git a/packages/react-renderer/src/runtime/context.ts b/packages/react-renderer/src/runtime/context.ts index 32a303d4b..ecdc79365 100644 --- a/packages/react-renderer/src/runtime/context.ts +++ b/packages/react-renderer/src/runtime/context.ts @@ -1,6 +1,6 @@ import { IComponentTreeModel } from '@alilc/lowcode-renderer-core'; import { createContext, useContext, type ReactInstance } from 'react'; -import { type ReactComponent } from './render'; +import { type ReactComponent } from './components'; export const ModelContext = createContext>( undefined!, diff --git a/packages/react-renderer/src/runtime/index.ts b/packages/react-renderer/src/runtime/index.ts index 618f76e52..334368f1f 100644 --- a/packages/react-renderer/src/runtime/index.ts +++ b/packages/react-renderer/src/runtime/index.ts @@ -1,2 +1,2 @@ -export * from './component'; -export * from './render'; +export * from './schema'; +export * from './components'; diff --git a/packages/react-renderer/src/runtime/component.tsx b/packages/react-renderer/src/runtime/schema.tsx similarity index 99% rename from packages/react-renderer/src/runtime/component.tsx rename to packages/react-renderer/src/runtime/schema.tsx index 8df82c91a..9122cbc09 100644 --- a/packages/react-renderer/src/runtime/component.tsx +++ b/packages/react-renderer/src/runtime/schema.tsx @@ -4,7 +4,7 @@ import { isValidElementType } from 'react-is'; import { useRenderContext } from '../app/context'; import { reactiveStateFactory } from './reactiveState'; 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 { appendExternalStyle } from '../utils/element'; diff --git a/packages/renderer-core/__tests__/parts/code-runtime/codeScope.spec.ts b/packages/renderer-core/__tests__/services/code-runtime/codeScope.spec.ts similarity index 91% rename from packages/renderer-core/__tests__/parts/code-runtime/codeScope.spec.ts rename to packages/renderer-core/__tests__/services/code-runtime/codeScope.spec.ts index 5811c47b0..550b47eaa 100644 --- a/packages/renderer-core/__tests__/parts/code-runtime/codeScope.spec.ts +++ b/packages/renderer-core/__tests__/services/code-runtime/codeScope.spec.ts @@ -1,5 +1,8 @@ +/** + * @vitest-environment jsdom + */ import { describe, it, expect } from 'vitest'; -import { CodeScope } from '../../../src/parts/code-runtime'; +import { CodeScope } from '../../../src/services/code-runtime'; describe('CodeScope', () => { it('should return initial values', () => { @@ -18,9 +21,8 @@ describe('CodeScope', () => { it('inject should not overwrite existing values without force', () => { const initValue = { a: 1 }; const scope = new CodeScope(initValue); - scope.set('a', 2); - expect(scope.value.a).toBe(1); - scope.set('a', 3, true); + expect(scope.value.a).not.toBe(2); + scope.set('a', 3); expect(scope.value.a).toBe(3); }); diff --git a/packages/renderer-core/__tests__/services/lifeCycle.spec.ts b/packages/renderer-core/__tests__/services/lifeCycle.spec.ts new file mode 100644 index 000000000..139073192 --- /dev/null +++ b/packages/renderer-core/__tests__/services/lifeCycle.spec.ts @@ -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'); + }); +}); diff --git a/packages/renderer-core/src/main.ts b/packages/renderer-core/src/main.ts index 59e454669..23642168e 100644 --- a/packages/renderer-core/src/main.ts +++ b/packages/renderer-core/src/main.ts @@ -31,7 +31,7 @@ export class RendererMain { @IBoostsService private boostsService: IBoostsService, @ILifeCycleService private lifeCycleService: ILifeCycleService, ) { - this.lifeCycleService.when(LifecyclePhase.OptionsResolved).finally(async () => { + this.lifeCycleService.when(LifecyclePhase.OptionsResolved).then(async () => { const renderContext = { schema: this.schemaService, packageManager: this.packageManagementService, @@ -42,8 +42,6 @@ export class RendererMain { this.renderObject = await this.adapter(renderContext); - await this.packageManagementService.loadPackages(this.initOptions.packages ?? []); - this.lifeCycleService.phase = LifecyclePhase.Ready; }); } @@ -60,13 +58,19 @@ export class RendererMain { this.codeRuntimeService.initialize(options.codeRuntime ?? {}); - this.extensionHostService.registerPlugin(plugins); - 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> { - await this.lifeCycleService.when(LifecyclePhase.Ready); + await this.lifeCycleService.when(LifecyclePhase.AfterInitPackageLoad); // construct application return Object.freeze>({ @@ -79,8 +83,7 @@ export class RendererMain { ...this.renderObject, use: (plugin) => { - this.extensionHostService.registerPlugin(plugin); - return this.extensionHostService.doSetupPlugin(plugin); + return this.extensionHostService.registerPlugin(plugin); }, }); } diff --git a/packages/renderer-core/src/services/extension/boosts.ts b/packages/renderer-core/src/services/extension/boosts.ts index 25065aaee..6f8742495 100644 --- a/packages/renderer-core/src/services/extension/boosts.ts +++ b/packages/renderer-core/src/services/extension/boosts.ts @@ -4,7 +4,7 @@ import { ICodeRuntimeService } from '../code-runtime'; import { IRuntimeUtilService } from '../runtimeUtilService'; import { IRuntimeIntlService } from '../runtimeIntlService'; -export type IBoosts = IBoostsApi & Extends; +export type IBoosts = IBoostsApi & Extends & { [key: string]: any }; export interface IBoostsApi { readonly codeRuntime: ICodeRuntimeService; @@ -12,6 +12,10 @@ export interface IBoostsApi { readonly intl: Pick; readonly util: Pick; + /** + * 允许插件挂载额外的对象在 boosts 上,方便其他插件使用 + */ + temporaryUse(name: string, value: any): void; } /** @@ -43,6 +47,9 @@ export class BoostsService implements IBoostsService { codeRuntime: this.codeRuntimeService, intl: this.runtimeIntlService, util: this.runtimeUtilService, + temporaryUse: (name, value) => { + this.extend(name, value); + }, }; } @@ -55,6 +62,8 @@ export class BoostsService implements IBoostsService { } else { if (!this.extendsValue[name]) { this.extendsValue[name] = value; + } else { + console.warn(`${name} is exist`); } } } else if (isObject(name)) { diff --git a/packages/renderer-core/src/services/extension/extensionHostService.ts b/packages/renderer-core/src/services/extension/extensionHostService.ts index 2a2388e22..b911b7d9d 100644 --- a/packages/renderer-core/src/services/extension/extensionHostService.ts +++ b/packages/renderer-core/src/services/extension/extensionHostService.ts @@ -3,8 +3,6 @@ import { type Plugin, type PluginContext } from './plugin'; import { IBoostsService } from './boosts'; import { IPackageManagementService } from '../package'; import { ISchemaService } from '../schema'; -import { type RenderAdapter } from './render'; -import { IComponentTreeModelService } from '../model'; import { ILifeCycleService, LifecyclePhase } from '../lifeCycleService'; interface IPluginRuntime extends Plugin { @@ -12,9 +10,7 @@ interface IPluginRuntime extends Plugin { } export interface IExtensionHostService { - registerPlugin(plugin: Plugin | Plugin[]): void; - - doSetupPlugin(plugin: Plugin): Promise; + registerPlugin(plugin: Plugin | Plugin[]): Promise; getPlugin(name: string): Plugin | undefined; @@ -50,15 +46,9 @@ export class ExtensionHostService implements IExtensionHostService { 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]; for (const plugin of plugins) { @@ -67,19 +57,17 @@ export class ExtensionHostService implements IExtensionHostService { continue; } - this.pluginRuntimes.push({ - ...plugin, - status: 'ready', - }); + await this.doSetupPlugin(plugin); } } - async doSetupPlugin(plugin: Plugin) { + private async doSetupPlugin(plugin: Plugin) { const pluginRuntime = plugin as IPluginRuntime; - if (!this.pluginRuntimes.some((item) => item.name !== pluginRuntime.name)) { - return; - } + this.pluginRuntimes.push({ + ...pluginRuntime, + status: 'ready', + }); const isSetup = (name: string) => { const setupPlugins = this.pluginRuntimes.filter((item) => item.status === 'setup'); diff --git a/packages/renderer-core/src/services/lifeCycleService.ts b/packages/renderer-core/src/services/lifeCycleService.ts index 52e2f546f..8670dfaf3 100644 --- a/packages/renderer-core/src/services/lifeCycleService.ts +++ b/packages/renderer-core/src/services/lifeCycleService.ts @@ -7,11 +7,7 @@ export const enum LifecyclePhase { Ready = 3, - BeforeMount = 4, - - Mounted = 5, - - BeforeUnmount = 6, + AfterInitPackageLoad = 4, } export interface ILifeCycleService { @@ -40,7 +36,7 @@ export class LifeCycleService implements ILifeCycleService { } set phase(value: LifecyclePhase) { - if (value < this.phase) { + if (value < this._phase) { throw new Error('Lifecycle cannot go backwards'); } diff --git a/packages/renderer-core/src/services/package/managementService.ts b/packages/renderer-core/src/services/package/managementService.ts index e96af6189..0c96dca18 100644 --- a/packages/renderer-core/src/services/package/managementService.ts +++ b/packages/renderer-core/src/services/package/managementService.ts @@ -68,7 +68,7 @@ export class PackageManagementService implements IPackageManagementService { @ISchemaService private schemaService: ISchemaService, @ILifeCycleService private lifeCycleService: ILifeCycleService, ) { - this.lifeCycleService.when(LifecyclePhase.Ready).then(() => { + this.lifeCycleService.when(LifecyclePhase.AfterInitPackageLoad).then(() => { const componentsMaps = this.schemaService.get('componentsMap'); this.resolveComponentMaps(componentsMaps); }); @@ -85,7 +85,6 @@ export class PackageManagementService implements IPackageManagementService { if (!isProCodeComponentPackage(item)) continue; const normalized = this.normalizePackage(item); - await this.loadPackageByNormalized(normalized); } } diff --git a/packages/renderer-core/src/services/runtimeIntlService.ts b/packages/renderer-core/src/services/runtimeIntlService.ts index df4bf7f2b..1aa532c23 100644 --- a/packages/renderer-core/src/services/runtimeIntlService.ts +++ b/packages/renderer-core/src/services/runtimeIntlService.ts @@ -5,6 +5,7 @@ import { type Spec, type Locale, type Translations, + platformLocale, } from '@alilc/lowcode-shared'; import { ILifeCycleService, LifecyclePhase } from './lifeCycleService'; import { ICodeRuntimeService } from './code-runtime'; @@ -34,7 +35,7 @@ export const IRuntimeIntlService = createDecorator('IRuntim export class RuntimeIntlService implements IRuntimeIntlService { private intl: Intl = new Intl(); - public locale: string = navigator.language; + public locale: string = platformLocale; constructor( @ILifeCycleService private lifeCycleService: ILifeCycleService, diff --git a/packages/renderer-core/src/services/runtimeUtilService.ts b/packages/renderer-core/src/services/runtimeUtilService.ts index daca3bcc8..2e67ec09c 100644 --- a/packages/renderer-core/src/services/runtimeUtilService.ts +++ b/packages/renderer-core/src/services/runtimeUtilService.ts @@ -23,12 +23,11 @@ export class RuntimeUtilService implements IRuntimeUtilService { @ILifeCycleService private lifeCycleService: ILifeCycleService, @ISchemaService private schemaService: ISchemaService, ) { - this.lifeCycleService.when(LifecyclePhase.Ready).then(() => { + this.lifeCycleService.when(LifecyclePhase.AfterInitPackageLoad).then(() => { const utils = this.schemaService.get('utils') ?? []; for (const util of utils) { this.add(util); } - this.toExpose(); }); } diff --git a/packages/renderer-core/vitest.config.ts b/packages/renderer-core/vitest.config.ts index 116f3131e..e712c7baa 100644 --- a/packages/renderer-core/vitest.config.ts +++ b/packages/renderer-core/vitest.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vitest/config' +import { defineProject } from 'vitest/config'; -export default defineConfig({ +export default defineProject({ test: { - include: ['tests/**/*.spec.ts'] - } -}) + environment: 'jsdom', + }, +}); diff --git a/packages/shared/__tests__/helper/hookable.spec.ts b/packages/shared/__tests__/helper/hookable.spec.ts deleted file mode 100644 index cb3b532b0..000000000 --- a/packages/shared/__tests__/helper/hookable.spec.ts +++ /dev/null @@ -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 - }); -}); diff --git a/packages/shared/__tests__/utils/async.spec.ts b/packages/shared/__tests__/utils/async.spec.ts new file mode 100644 index 000000000..8f5838909 --- /dev/null +++ b/packages/shared/__tests__/utils/async.spec.ts @@ -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'); + }); +}); diff --git a/packages/shared/package.json b/packages/shared/package.json index 62518bb2e..dace9d2ab 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -5,6 +5,13 @@ "main": "dist/low-code-shared.cjs", "module": "dist/low-code-shared.js", "types": "dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/low-code-shared.js", + "require": "./dist/low-code-shared.cjs", + "types": "./dist/index.d.ts" + } + }, "files": [ "dist", "src", diff --git a/packages/shared/src/abilities/instantiation/index.ts b/packages/shared/src/abilities/instantiation/index.ts index c4693ed39..50196be9c 100644 --- a/packages/shared/src/abilities/instantiation/index.ts +++ b/packages/shared/src/abilities/instantiation/index.ts @@ -15,7 +15,7 @@ export type Constructor = new (...args: any[]) => T; export function createDecorator(serviceId: string): ServiceIdentifier { const id = ( function (target: Constructor, targetKey: string, indexOrPropertyDescriptor: any): any { - return set(serviceId)(target, targetKey, indexOrPropertyDescriptor); + return inject(serviceId)(target, targetKey, indexOrPropertyDescriptor); } ); id.toString = () => serviceId; diff --git a/packages/shared/src/abilities/intl.ts b/packages/shared/src/abilities/intl.ts index 836ea6377..612aea6f3 100644 --- a/packages/shared/src/abilities/intl.ts +++ b/packages/shared/src/abilities/intl.ts @@ -1,6 +1,7 @@ import { createIntl, createIntlCache, type IntlShape as IntlFormatter } from '@formatjs/intl'; import { mapKeys } from 'lodash-es'; import { signal, computed, effect, type Signal, type ComputedSignal } from '../signals'; +import { platformLocale } from '../utils'; export { IntlFormatter }; @@ -14,7 +15,7 @@ export class Intl { private currentMessage: ComputedSignal; private intlShape: IntlFormatter; - constructor(defaultLocale: string = navigator.language, messages: LocaleTranslationsRecord = {}) { + constructor(defaultLocale: string = platformLocale, messages: LocaleTranslationsRecord = {}) { if (defaultLocale) { defaultLocale = nomarlizeLocale(defaultLocale); } else { diff --git a/packages/shared/src/utils/platform.ts b/packages/shared/src/utils/platform.ts index 2cacf58b9..53cb308fc 100644 --- a/packages/shared/src/utils/platform.ts +++ b/packages/shared/src/utils/platform.ts @@ -1,15 +1,15 @@ -const userAgent: string = navigator.userAgent; +const userAgent: string = window.navigator.userAgent; export const isWindows = userAgent.indexOf('Windows') >= 0; export const isMacintosh = userAgent.indexOf('Macintosh') >= 0; export const isLinux = userAgent.indexOf('Linux') >= 0; export const isIOS = (isMacintosh || userAgent.indexOf('iPad') >= 0 || userAgent.indexOf('iPhone') >= 0) && - !!navigator.maxTouchPoints && - navigator.maxTouchPoints > 0; + !!window.navigator.maxTouchPoints && + window.navigator.maxTouchPoints > 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 { Web, diff --git a/packages/shared/vitest.config.ts b/packages/shared/vitest.config.ts new file mode 100644 index 000000000..ebb0784a7 --- /dev/null +++ b/packages/shared/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineProject } from 'vitest/config'; + +export default defineProject({}); diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3162b4d65..90c913b5a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,5 @@ packages: - 'packages/*' - - 'playground' + - 'playground/*' - 'docs' - 'scripts' diff --git a/tsconfig.json b/tsconfig.json index 2173f6981..f5483473b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -39,5 +39,11 @@ }, "types": ["vite/client", "vitest/globals", "node"] }, - "exclude": ["**/dist", "node_modules"] + "include": [ + "packages/global.d.ts", + "packages/*/src", + "packages/*/__tests__", + "playground/*/src", + "scripts/*" + ] } diff --git a/vitest.workspace.ts b/vitest.workspace.ts index 631a18210..c68331fd8 100644 --- a/vitest.workspace.ts +++ b/vitest.workspace.ts @@ -5,9 +5,12 @@ export default defineWorkspace([ { test: { include: ['**/__tests__/**/*.spec.{ts?(x),js?(x)}'], + alias: { + '@alilc/lowcode-shared': 'packages/shared', + }, name: 'unit test', - environment: 'jsdom', - globals: true - } + environmentMatchGlobs: [['packages/**', 'jsdom']], + globals: true, + }, }, -]) +]);