From badc857bcde4e7951c7bb258eb554e90198c4bf4 Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Tue, 16 Sep 2025 16:28:23 +0530 Subject: [PATCH] Move modules to moduleLoader format (#23740) --- packages/lib/di/containers/LuckyUser.ts | 19 +---- packages/lib/di/di.ts | 94 ++++++++++++++++++++++--- packages/lib/di/modules/Attribute.ts | 20 ++++-- packages/lib/di/modules/Host.ts | 18 ++++- packages/lib/di/modules/LuckyUser.ts | 36 +++++++--- packages/lib/di/modules/Ooo.ts | 18 ++++- packages/lib/di/modules/User.ts | 18 ++++- 7 files changed, 180 insertions(+), 43 deletions(-) diff --git a/packages/lib/di/containers/LuckyUser.ts b/packages/lib/di/containers/LuckyUser.ts index 51d635a09b..953ddeaae1 100644 --- a/packages/lib/di/containers/LuckyUser.ts +++ b/packages/lib/di/containers/LuckyUser.ts @@ -1,24 +1,11 @@ -import { DI_TOKENS } from "@calcom/lib/di/tokens"; import type { LuckyUserService } from "@calcom/lib/server/getLuckyUser"; -import { prismaModule } from "@calcom/prisma/prisma.module"; import { createContainer } from "../di"; -import { attributeRepositoryModule } from "../modules/Attribute"; -import { bookingRepositoryModule } from "../modules/Booking"; -import { hostRepositoryModule } from "../modules/Host"; -import { luckyUserServiceModule } from "../modules/LuckyUser"; -import { oooRepositoryModule } from "../modules/Ooo"; -import { userRepositoryModule } from "../modules/User"; +import { moduleLoader as luckyUserServiceModuleLoader } from "../modules/LuckyUser"; const container = createContainer(); -container.load(DI_TOKENS.PRISMA_MODULE, prismaModule); -container.load(DI_TOKENS.BOOKING_REPOSITORY_MODULE, bookingRepositoryModule); -container.load(DI_TOKENS.HOST_REPOSITORY_MODULE, hostRepositoryModule); -container.load(DI_TOKENS.OOO_REPOSITORY_MODULE, oooRepositoryModule); -container.load(DI_TOKENS.USER_REPOSITORY_MODULE, userRepositoryModule); -container.load(DI_TOKENS.ATTRIBUTE_REPOSITORY_MODULE, attributeRepositoryModule); -container.load(DI_TOKENS.LUCKY_USER_SERVICE_MODULE, luckyUserServiceModule); export function getLuckyUserService() { - return container.get(DI_TOKENS.LUCKY_USER_SERVICE); + luckyUserServiceModuleLoader.loadModule(container); + return container.get(luckyUserServiceModuleLoader.token); } diff --git a/packages/lib/di/di.ts b/packages/lib/di/di.ts index 1db2355e2f..bb02a13d06 100644 --- a/packages/lib/di/di.ts +++ b/packages/lib/di/di.ts @@ -1,14 +1,16 @@ import type { Container, Module } from "@evyweb/ioctopus"; import { createContainer, createModule } from "@evyweb/ioctopus"; +export type ModuleLoader = { token: string | symbol; loadModule: (container: Container) => void }; + /** * A type-safe alternative to module.bind(token).toClass(classs, deps) that automatically ensures that all dependencies required by the Class are provided. - * It assumes that dependencies are stored under the `deps` property of the Class, which is a good convention to follow anyway + * It assumes that dependencies are passed to the constructor as an object map(e.g. new MyService(deps)) * * @returns A function that can be used to load the dependencies into the container automatically. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -export function bindModuleToClassOnToken any>({ +export function bindModuleToClassOnToken any>({ module, token, classs, @@ -20,18 +22,92 @@ export function bindModuleToClassOnToken token: string | symbol; classs: TClass; depsMap: Record< - keyof InstanceType["deps"], - { token: string | symbol; loadModule: (container: Container) => void } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + keyof (TClass extends new (deps: infer TDeps) => any ? TDeps : never), + ModuleLoader >; +}): (container: Container) => void; + +/** + * A type-safe alternative to module.bind(token).toClass(classs, deps) that automatically ensures that all dependencies required by the Class are provided. + * It assumes that there is a single dependency, passed as argument to the constructor + * + * @returns A function that can be used to load the dependencies into the container automatically. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function bindModuleToClassOnToken any>({ + module, + token, + classs, + dep, + moduleToken, +}: { + module: Module; + moduleToken: string | symbol; + token: string | symbol; + classs: TClass; + dep: ModuleLoader; +}): (container: Container) => void; + +/** + * A type-safe alternative to module.bind(token).toClass(classs, deps) that automatically ensures that all dependencies required by the Class are provided. + * @returns A function that can be used to load the dependencies into the container automatically. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function bindModuleToClassOnToken any>({ + module, + token, + classs, + depsMap, + dep, + moduleToken, +}: { + module: Module; + moduleToken: string | symbol; + token: string | symbol; + classs: TClass; + /** + * When the constructor of the class accept deps as the argument which is a Record of many dependencies + */ + depsMap?: Record< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + keyof (TClass extends new (deps: infer TDeps) => any ? TDeps : never), + ModuleLoader + >; + /** + * When the constructor of the class accept a single dependency and is the only argument to constructor. + */ + dep?: ModuleLoader; }) { - const depsObject = Object.fromEntries(Object.entries(depsMap).map(([key, value]) => [key, value.token])); - module.bind(token).toClass(classs, depsObject); + if (dep && depsMap) { + throw new Error( + "Cannot provide both 'dep' and 'depsMap'. Use 'dep' for single dependency or 'depsMap' for multiple dependencies." + ); + } + + if (!dep && !depsMap) { + throw new Error( + "Must provide either 'dep' for single dependency or 'depsMap' for multiple dependencies." + ); + } + + if (dep) { + module.bind(token).toClass(classs, [dep.token]); + } else if (depsMap) { + const depsObject = Object.fromEntries(Object.entries(depsMap).map(([key, value]) => [key, value.token])); + module.bind(token).toClass(classs, depsObject); + } return function loadModule(container: Container) { container.load(moduleToken, module); - for (const key in depsMap) { - const loadModule = depsMap[key as keyof typeof depsMap].loadModule; - loadModule(container); + + if (dep) { + dep.loadModule(container); + } else if (depsMap) { + for (const key in depsMap) { + const loadModule = depsMap[key as keyof typeof depsMap].loadModule; + loadModule(container); + } } }; } diff --git a/packages/lib/di/modules/Attribute.ts b/packages/lib/di/modules/Attribute.ts index b3b581be76..11f06dd1bd 100644 --- a/packages/lib/di/modules/Attribute.ts +++ b/packages/lib/di/modules/Attribute.ts @@ -1,9 +1,21 @@ import { DI_TOKENS } from "@calcom/lib/di/tokens"; import { PrismaAttributeRepository } from "@calcom/lib/server/repository/PrismaAttributeRepository"; +import { moduleLoader as prismaModuleLoader } from "@calcom/prisma/prisma.module"; -import { createModule } from "../di"; +import { createModule, bindModuleToClassOnToken, type ModuleLoader } from "../di"; export const attributeRepositoryModule = createModule(); -attributeRepositoryModule - .bind(DI_TOKENS.ATTRIBUTE_REPOSITORY) - .toClass(PrismaAttributeRepository, [DI_TOKENS.PRISMA_CLIENT]); +const token = DI_TOKENS.ATTRIBUTE_REPOSITORY; +const moduleToken = DI_TOKENS.ATTRIBUTE_REPOSITORY_MODULE; +const loadModule = bindModuleToClassOnToken({ + module: attributeRepositoryModule, + moduleToken, + token, + classs: PrismaAttributeRepository, + dep: prismaModuleLoader, +}); + +export const moduleLoader: ModuleLoader = { + token, + loadModule, +}; diff --git a/packages/lib/di/modules/Host.ts b/packages/lib/di/modules/Host.ts index 8810533eb4..4bd97cdd18 100644 --- a/packages/lib/di/modules/Host.ts +++ b/packages/lib/di/modules/Host.ts @@ -1,7 +1,21 @@ import { DI_TOKENS } from "@calcom/lib/di/tokens"; import { HostRepository } from "@calcom/lib/server/repository/host"; +import { moduleLoader as prismaModuleLoader } from "@calcom/prisma/prisma.module"; -import { createModule } from "../di"; +import { createModule, bindModuleToClassOnToken, type ModuleLoader } from "../di"; export const hostRepositoryModule = createModule(); -hostRepositoryModule.bind(DI_TOKENS.HOST_REPOSITORY).toClass(HostRepository, [DI_TOKENS.PRISMA_CLIENT]); +const token = DI_TOKENS.HOST_REPOSITORY; +const moduleToken = DI_TOKENS.HOST_REPOSITORY_MODULE; +const loadModule = bindModuleToClassOnToken({ + module: hostRepositoryModule, + moduleToken, + token, + classs: HostRepository, + dep: prismaModuleLoader, +}); + +export const moduleLoader: ModuleLoader = { + token, + loadModule, +}; diff --git a/packages/lib/di/modules/LuckyUser.ts b/packages/lib/di/modules/LuckyUser.ts index 0017b485bf..96cf779e31 100644 --- a/packages/lib/di/modules/LuckyUser.ts +++ b/packages/lib/di/modules/LuckyUser.ts @@ -1,13 +1,33 @@ import { DI_TOKENS } from "@calcom/lib/di/tokens"; import { LuckyUserService } from "@calcom/lib/server/getLuckyUser"; -import { createModule } from "../di"; +import { bindModuleToClassOnToken, createModule, type ModuleLoader } from "../di"; +import { moduleLoader as attributeRepositoryModuleLoader } from "./Attribute"; +import { moduleLoader as bookingRepositoryModuleLoader } from "./Booking"; +import { moduleLoader as hostRepositoryModuleLoader } from "./Host"; +import { moduleLoader as oooRepositoryModuleLoader } from "./Ooo"; +import { moduleLoader as userRepositoryModuleLoader } from "./User"; -export const luckyUserServiceModule = createModule(); -luckyUserServiceModule.bind(DI_TOKENS.LUCKY_USER_SERVICE).toClass(LuckyUserService, { - bookingRepository: DI_TOKENS.BOOKING_REPOSITORY, - hostRepository: DI_TOKENS.HOST_REPOSITORY, - oooRepository: DI_TOKENS.OOO_REPOSITORY, - userRepository: DI_TOKENS.USER_REPOSITORY, - attributeRepository: DI_TOKENS.ATTRIBUTE_REPOSITORY, +const thisModule = createModule(); +const token = DI_TOKENS.LUCKY_USER_SERVICE; +const moduleToken = DI_TOKENS.LUCKY_USER_SERVICE_MODULE; +const loadModule = bindModuleToClassOnToken({ + module: thisModule, + moduleToken, + token, + classs: LuckyUserService, + depsMap: { + bookingRepository: bookingRepositoryModuleLoader, + hostRepository: hostRepositoryModuleLoader, + oooRepository: oooRepositoryModuleLoader, + userRepository: userRepositoryModuleLoader, + attributeRepository: attributeRepositoryModuleLoader, + }, }); + +export const moduleLoader: ModuleLoader = { + token, + loadModule, +}; + +export type { LuckyUserService }; diff --git a/packages/lib/di/modules/Ooo.ts b/packages/lib/di/modules/Ooo.ts index e2b9722017..523c7e9b0f 100644 --- a/packages/lib/di/modules/Ooo.ts +++ b/packages/lib/di/modules/Ooo.ts @@ -1,7 +1,21 @@ import { DI_TOKENS } from "@calcom/lib/di/tokens"; import { PrismaOOORepository } from "@calcom/lib/server/repository/ooo"; +import { moduleLoader as prismaModuleLoader } from "@calcom/prisma/prisma.module"; -import { createModule } from "../di"; +import { createModule, bindModuleToClassOnToken, type ModuleLoader } from "../di"; export const oooRepositoryModule = createModule(); -oooRepositoryModule.bind(DI_TOKENS.OOO_REPOSITORY).toClass(PrismaOOORepository, [DI_TOKENS.PRISMA_CLIENT]); +const token = DI_TOKENS.OOO_REPOSITORY; +const moduleToken = DI_TOKENS.OOO_REPOSITORY_MODULE; +const loadModule = bindModuleToClassOnToken({ + module: oooRepositoryModule, + moduleToken, + token, + classs: PrismaOOORepository, + dep: prismaModuleLoader, +}); + +export const moduleLoader: ModuleLoader = { + token, + loadModule, +}; diff --git a/packages/lib/di/modules/User.ts b/packages/lib/di/modules/User.ts index f89fd16d8e..f8daa69e2d 100644 --- a/packages/lib/di/modules/User.ts +++ b/packages/lib/di/modules/User.ts @@ -1,7 +1,21 @@ import { DI_TOKENS } from "@calcom/lib/di/tokens"; import { UserRepository } from "@calcom/lib/server/repository/user"; +import { moduleLoader as prismaModuleLoader } from "@calcom/prisma/prisma.module"; -import { createModule } from "../di"; +import { createModule, bindModuleToClassOnToken, type ModuleLoader } from "../di"; export const userRepositoryModule = createModule(); -userRepositoryModule.bind(DI_TOKENS.USER_REPOSITORY).toClass(UserRepository, [DI_TOKENS.PRISMA_CLIENT]); +const token = DI_TOKENS.USER_REPOSITORY; +const moduleToken = DI_TOKENS.USER_REPOSITORY_MODULE; +const loadModule = bindModuleToClassOnToken({ + module: userRepositoryModule, + moduleToken, + token, + classs: UserRepository, + dep: prismaModuleLoader, +}); + +export const moduleLoader: ModuleLoader = { + token, + loadModule, +};