diff --git a/packages/common/infra/src/framework/core/index.ts b/packages/common/infra/src/framework/core/index.ts index b0edde95c1..0d67d74d2f 100644 --- a/packages/common/infra/src/framework/core/index.ts +++ b/packages/common/infra/src/framework/core/index.ts @@ -7,5 +7,5 @@ export { createEvent, type FrameworkEvent, OnEvent } from './event'; export { Framework } from './framework'; export { createIdentifier } from './identifier'; export type { ResolveOptions } from './provider'; -export { FrameworkProvider } from './provider'; +export { FrameworkProvider, FrameworkStackProvider } from './provider'; export type { GeneralIdentifier, Identifier } from './types'; diff --git a/packages/common/infra/src/framework/core/provider.ts b/packages/common/infra/src/framework/core/provider.ts index 2c8cd662e2..4322cf64ea 100644 --- a/packages/common/infra/src/framework/core/provider.ts +++ b/packages/common/infra/src/framework/core/provider.ts @@ -322,3 +322,61 @@ export class BasicFrameworkProvider extends FrameworkProvider { this.eventBus.dispose(); } } + +export class FrameworkStackProvider extends FrameworkProvider { + public readonly stack: FrameworkProvider[]; + public readonly collection: Framework; + public readonly eventBus: EventBus; + + constructor(providers: FrameworkProvider[]) { + if (providers.length === 0) { + throw new Error('FrameworkStackProvider must have at least one provider'); + } + super(); + this.stack = [...providers]; + + // use the collection and eventBus from the first provider + this.collection = this.stack[0].collection; + this.eventBus = this.stack[0].eventBus; + } + + get scope(): FrameworkScopeStack { + return this.stack[0]?.scope || []; + } + + getRaw(identifier: IdentifierValue, options?: ResolveOptions): any { + for (const provider of this.stack) { + const service = provider.getRaw(identifier, { + ...options, + optional: true, + }); + if (service) { + return service; + } + } + + if (options?.optional) { + return undefined; + } + + throw new ComponentNotFoundError(identifier); + } + + getAllRaw( + identifier: IdentifierValue, + options?: ResolveOptions + ): Map { + for (const provider of this.stack) { + const components = provider.getAllRaw(identifier, options); + if (components.size > 0) { + return components; + } + } + + return new Map(); + } + + dispose(): void { + // No need to handle the disposal of providers in the stack, as they are passed in externally + } +} diff --git a/packages/common/infra/src/framework/react/index.tsx b/packages/common/infra/src/framework/react/index.tsx index 8916fe2a34..8577bca001 100644 --- a/packages/common/infra/src/framework/react/index.tsx +++ b/packages/common/infra/src/framework/react/index.tsx @@ -1,43 +1,21 @@ import React, { useContext, useMemo } from 'react'; import type { FrameworkProvider, Scope, Service } from '../core'; -import { ComponentNotFoundError, Framework } from '../core'; -import { parseIdentifier } from '../core/identifier'; +import { Framework, FrameworkStackProvider } from '../core'; import type { GeneralIdentifier, IdentifierType, Type } from '../core/types'; -export const FrameworkStackContext = React.createContext([ - Framework.EMPTY.provider(), -]); +export const FrameworkProviderContext = React.createContext( + Framework.EMPTY.provider() +); export function useFramework(): FrameworkProvider { - const stack = useContext(FrameworkStackContext); - - return stack[stack.length - 1]; // never null, because the default value + return useContext(FrameworkProviderContext); // never null, because the default value } export function useService( identifier: GeneralIdentifier ): T { - // eslint-disable-next-line react-hooks/rules-of-hooks - const stack = useContext(FrameworkStackContext); - - let service: T | undefined = undefined; - - for (let i = stack.length - 1; i >= 0; i--) { - service = stack[i].getOptional(identifier, { - sameScope: true, - }); - - if (service) { - break; - } - } - - if (!service) { - throw new ComponentNotFoundError(parseIdentifier(identifier)); - } - - return service; + return useContext(FrameworkProviderContext).get(identifier); } /** @@ -57,27 +35,12 @@ export function useServices< ): keyof T extends string ? { [key in Uncapitalize]: IdentifierType]> } : never { - const stack = useContext(FrameworkStackContext); + const provider = useContext(FrameworkProviderContext); const services: any = {}; for (const [key, value] of Object.entries(identifiers)) { - let service; - for (let i = stack.length - 1; i >= 0; i--) { - service = stack[i].getOptional(value, { - sameScope: true, - }); - - if (service) { - break; - } - } - - if (!service) { - throw new ComponentNotFoundError(parseIdentifier(value)); - } - - services[key.charAt(0).toLowerCase() + key.slice(1)] = service; + services[key.charAt(0).toLowerCase() + key.slice(1)] = provider.get(value); } return services; @@ -86,22 +49,7 @@ export function useServices< export function useServiceOptional( identifier: Type ): T | undefined { - // eslint-disable-next-line react-hooks/rules-of-hooks - const stack = useContext(FrameworkStackContext); - - let service: T | undefined = undefined; - - for (let i = stack.length - 1; i >= 0; i--) { - service = stack[i].getOptional(identifier, { - sameScope: true, - }); - - if (service) { - break; - } - } - - return service; + return useContext(FrameworkProviderContext).getOptional(identifier); } export const FrameworkRoot = ({ @@ -109,9 +57,9 @@ export const FrameworkRoot = ({ children, }: React.PropsWithChildren<{ framework: FrameworkProvider }>) => { return ( - + {children} - + ); }; @@ -119,16 +67,16 @@ export const FrameworkScope = ({ scope, children, }: React.PropsWithChildren<{ scope?: Scope }>) => { - const stack = useContext(FrameworkStackContext); + const provider = useContext(FrameworkProviderContext); const nextStack = useMemo(() => { - if (!scope) return stack; - return [...stack, scope.framework]; - }, [stack, scope]); + if (!scope) return provider; + return new FrameworkStackProvider([provider, scope.framework]); + }, [scope, provider]); return ( - + {children} - + ); };