feat(modules-sdk): Module provider plugin loader (#6286)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
const service = class TestService {}
|
||||
|
||||
export default {
|
||||
services: [service],
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
const service = class TestService {}
|
||||
|
||||
export const defaultExport = {
|
||||
services: [service],
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default {
|
||||
loaders: [],
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import { createMedusaContainer } from "@medusajs/utils"
|
||||
import { Lifetime, asFunction } from "awilix"
|
||||
import { moduleProviderLoader } from "../module-provider-loader"
|
||||
|
||||
const logger = {
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
} as any
|
||||
|
||||
describe("modules loader", () => {
|
||||
let container
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
container = createMedusaContainer()
|
||||
})
|
||||
|
||||
it("should register the provider service", async () => {
|
||||
const moduleProviders = [
|
||||
{
|
||||
resolve: "@plugins/default",
|
||||
options: {},
|
||||
},
|
||||
]
|
||||
|
||||
await moduleProviderLoader({ container, providers: moduleProviders })
|
||||
|
||||
const testService = container.resolve("testService")
|
||||
expect(testService).toBeTruthy()
|
||||
expect(testService.constructor.name).toEqual("TestService")
|
||||
})
|
||||
|
||||
it("should register the provider service with custom register fn", async () => {
|
||||
const fn = async (klass, container, details) => {
|
||||
container.register({
|
||||
[`testServiceCustomRegistration`]: asFunction(
|
||||
(cradle) => new klass(cradle, details.options),
|
||||
{
|
||||
lifetime: Lifetime.SINGLETON,
|
||||
}
|
||||
),
|
||||
})
|
||||
}
|
||||
const moduleProviders = [
|
||||
{
|
||||
resolve: "@plugins/default",
|
||||
options: {},
|
||||
},
|
||||
]
|
||||
|
||||
await moduleProviderLoader({
|
||||
container,
|
||||
providers: moduleProviders,
|
||||
registerServiceFn: fn,
|
||||
})
|
||||
|
||||
const testService = container.resolve("testServiceCustomRegistration")
|
||||
expect(testService).toBeTruthy()
|
||||
expect(testService.constructor.name).toEqual("TestService")
|
||||
})
|
||||
|
||||
it("should log the errors if no service is defined", async () => {
|
||||
const moduleProviders = [
|
||||
{
|
||||
resolve: "@plugins/no-service",
|
||||
options: {},
|
||||
},
|
||||
]
|
||||
|
||||
try {
|
||||
await moduleProviderLoader({ container, providers: moduleProviders })
|
||||
} catch (error) {
|
||||
expect(error.message).toBe(
|
||||
"No services found in plugin @plugins/no-service -- make sure your plugin has a default export of services."
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("should throw if no default export is defined", async () => {
|
||||
const moduleProviders = [
|
||||
{
|
||||
resolve: "@plugins/no-default",
|
||||
options: {},
|
||||
},
|
||||
]
|
||||
|
||||
try {
|
||||
await moduleProviderLoader({ container, providers: moduleProviders })
|
||||
} catch (error) {
|
||||
expect(error.message).toBe(
|
||||
"No services found in plugin @plugins/no-default -- make sure your plugin has a default export of services."
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1,2 +1,4 @@
|
||||
export * from "./module-loader"
|
||||
export * from "./module-provider-loader"
|
||||
export * from "./register-modules"
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { MedusaContainer, ModuleProvider } from "@medusajs/types"
|
||||
import { isString, lowerCaseFirst, promiseAll } from "@medusajs/utils"
|
||||
import { Lifetime, asFunction } from "awilix"
|
||||
|
||||
export async function moduleProviderLoader({
|
||||
container,
|
||||
providers,
|
||||
registerServiceFn,
|
||||
}: {
|
||||
container: MedusaContainer
|
||||
providers: ModuleProvider[]
|
||||
registerServiceFn?: (
|
||||
klass,
|
||||
container: MedusaContainer,
|
||||
pluginDetails: any
|
||||
) => Promise<void>
|
||||
}) {
|
||||
if (!providers?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await promiseAll(
|
||||
providers.map(async (pluginDetails) => {
|
||||
await loadModuleProvider(container, pluginDetails, registerServiceFn)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export async function loadModuleProvider(
|
||||
container: MedusaContainer,
|
||||
provider: ModuleProvider,
|
||||
registerServiceFn?: (klass, container, pluginDetails) => Promise<void>
|
||||
) {
|
||||
let loadedProvider: any
|
||||
|
||||
const pluginName = provider.resolve ?? provider.provider_name ?? ""
|
||||
|
||||
try {
|
||||
loadedProvider = provider.resolve
|
||||
|
||||
if (isString(provider.resolve)) {
|
||||
loadedProvider = await import(provider.resolve)
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Unable to find plugin ${pluginName} -- perhaps you need to install its package?`
|
||||
)
|
||||
}
|
||||
|
||||
loadedProvider = (loadedProvider as any).default ?? loadedProvider
|
||||
|
||||
if (!loadedProvider?.services?.length) {
|
||||
throw new Error(
|
||||
`No services found in plugin ${provider.resolve} -- make sure your plugin has a default export of services.`
|
||||
)
|
||||
}
|
||||
|
||||
const services = await promiseAll(
|
||||
loadedProvider.services.map(async (service) => {
|
||||
const name = lowerCaseFirst(service.name)
|
||||
if (registerServiceFn) {
|
||||
// Used to register the specific type of service in the provider
|
||||
await registerServiceFn(service, container, provider.options)
|
||||
} else {
|
||||
container.register({
|
||||
[name]: asFunction(
|
||||
(cradle) => new service(cradle, provider.options),
|
||||
{
|
||||
lifetime: service.LIFE_TIME || Lifetime.SCOPED,
|
||||
}
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
return service
|
||||
})
|
||||
)
|
||||
|
||||
return services
|
||||
}
|
||||
Reference in New Issue
Block a user