Files
medusa-store/packages/modules-sdk/src/loaders/utils/load-internal.ts
Adrien de Peretti 8fd1488938 chore: medusa shutdown (#6865)
* chore: medusa shutdown

* continue

* use shutdown

* on application shutdown

* consume shutdown

* more connection close

* more cleanup

* more cleanup

* update lock

* revert package

* graceful shutdown

* Create yellow-apples-attack.md

* graceful shutdown

* graceful shutdown

---------

Co-authored-by: Sebastian Rindom <skrindom@gmail.com>
Co-authored-by: Riqwan Thamir <rmthamir@gmail.com>
2024-03-29 09:22:29 -03:00

161 lines
4.4 KiB
TypeScript

import {
InternalModuleDeclaration,
Logger,
MedusaContainer,
MODULE_RESOURCE_TYPE,
ModuleExports,
ModuleResolution,
} from "@medusajs/types"
import {
ContainerRegistrationKeys,
createMedusaContainer,
MedusaModuleType,
} from "@medusajs/utils"
import { asFunction, asValue } from "awilix"
export async function loadInternalModule(
container: MedusaContainer,
resolution: ModuleResolution,
logger: Logger,
migrationOnly?: boolean,
loaderOnly?: boolean
): Promise<{ error?: Error } | void> {
const registrationName = !loaderOnly
? resolution.definition.registrationName
: resolution.definition.registrationName + "__loaderOnly"
const { resources } =
resolution.moduleDeclaration as InternalModuleDeclaration
let loadedModule: ModuleExports
try {
// When loading manually, we pass the exports to be loaded, meaning that we do not need to import the package to find
// the exports. This is useful when a package export an initialize function which will bootstrap itself and therefore
// does not need to import the package that is currently being loaded as it would create a
// circular reference.
const modulePath = resolution.resolutionPath as string
if (resolution.moduleExports) {
loadedModule = resolution.moduleExports
} else {
loadedModule = await import(modulePath)
loadedModule = (loadedModule as any).default
}
} catch (error) {
if (
resolution.definition.isRequired &&
resolution.definition.defaultPackage
) {
return {
error: new Error(
`Make sure you have installed the default package: ${resolution.definition.defaultPackage}`
),
}
}
return { error }
}
if (!loadedModule?.service) {
container.register({
[registrationName]: asValue(undefined),
})
return {
error: new Error(
"No service found in module. Make sure your module exports a service."
),
}
}
if (migrationOnly) {
// Partially loaded module, only register the service __joinerConfig function to be able to resolve it later
const moduleService = {
__joinerConfig: loadedModule.service.prototype.__joinerConfig,
}
container.register({
[registrationName]: asValue(moduleService),
})
return
}
const localContainer = createMedusaContainer()
const dependencies = resolution?.dependencies ?? []
if (resources === MODULE_RESOURCE_TYPE.SHARED) {
dependencies.push(
ContainerRegistrationKeys.MANAGER,
ContainerRegistrationKeys.CONFIG_MODULE,
ContainerRegistrationKeys.LOGGER,
ContainerRegistrationKeys.PG_CONNECTION
)
}
for (const dependency of dependencies) {
localContainer.register(
dependency,
asFunction(() => {
return container.resolve(dependency, { allowUnregistered: true })
})
)
}
const moduleLoaders = loadedModule?.loaders ?? []
try {
for (const loader of moduleLoaders) {
await loader(
{
container: localContainer,
logger,
options: resolution.options,
},
resolution.moduleDeclaration as InternalModuleDeclaration
)
}
} catch (err) {
container.register({
[registrationName]: asValue(undefined),
})
return {
error: new Error(
`Loaders for module ${resolution.definition.label} failed: ${err.message}`
),
}
}
const moduleService = loadedModule.service
container.register({
[registrationName]: asFunction((cradle) => {
;(moduleService as any).__type = MedusaModuleType
return new moduleService(
localContainer.cradle,
resolution.options,
resolution.moduleDeclaration
)
}).singleton(),
})
if (loaderOnly) {
// The expectation is only to run the loader as standalone, so we do not need to register the service and we need to cleanup all services
const service = container.resolve(registrationName)
await service.__hooks?.onApplicationShutdown()
}
}
export async function loadModuleMigrations(
resolution: ModuleResolution,
moduleExports?: ModuleExports
): Promise<[Function | undefined, Function | undefined]> {
let loadedModule: ModuleExports
try {
loadedModule =
moduleExports ?? (await import(resolution.resolutionPath as string))
return [loadedModule.runMigrations, loadedModule.revertMigration]
} catch {
return [undefined, undefined]
}
}