feat(medusa): Module Resolution API (#2597)
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(medusa): Expose Module Resolution API
|
||||||
@@ -131,6 +131,7 @@ Object {
|
|||||||
"id": Any<String>,
|
"id": Any<String>,
|
||||||
"invite_link_template": null,
|
"invite_link_template": null,
|
||||||
"metadata": null,
|
"metadata": null,
|
||||||
|
"modules": Any<Array>,
|
||||||
"name": "Medusa Store",
|
"name": "Medusa Store",
|
||||||
"payment_link_template": null,
|
"payment_link_template": null,
|
||||||
"payment_providers": Array [
|
"payment_providers": Array [
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ describe("/admin/store", () => {
|
|||||||
code: "usd",
|
code: "usd",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
modules: expect.any(Array),
|
||||||
feature_flags: expect.any(Array),
|
feature_flags: expect.any(Array),
|
||||||
default_currency_code: "usd",
|
default_currency_code: "usd",
|
||||||
created_at: expect.any(String),
|
created_at: expect.any(String),
|
||||||
|
|||||||
@@ -21,4 +21,15 @@ export function trackFeatureFlag(flag) {
|
|||||||
telemeter.trackFeatureFlag(flag)
|
telemeter.trackFeatureFlag(flag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function trackInstallation(installation, type) {
|
||||||
|
switch (type) {
|
||||||
|
case `plugin`:
|
||||||
|
telemeter.trackPlugin(installation)
|
||||||
|
break
|
||||||
|
case `module`:
|
||||||
|
telemeter.trackModule(installation)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export { default as Telemeter } from "./telemeter"
|
export { default as Telemeter } from "./telemeter"
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ class Telemeter {
|
|||||||
this.queueCount_ = this.store_.getQueueCount()
|
this.queueCount_ = this.store_.getQueueCount()
|
||||||
|
|
||||||
this.featureFlags_ = new Set()
|
this.featureFlags_ = new Set()
|
||||||
|
this.modules_ = new Set()
|
||||||
|
this.plugins_ = []
|
||||||
}
|
}
|
||||||
|
|
||||||
getMachineId() {
|
getMachineId() {
|
||||||
@@ -133,6 +135,8 @@ class Telemeter {
|
|||||||
medusa_version: this.getMedusaVersion(),
|
medusa_version: this.getMedusaVersion(),
|
||||||
cli_version: this.getCliVersion(),
|
cli_version: this.getCliVersion(),
|
||||||
feature_flags: Array.from(this.featureFlags_),
|
feature_flags: Array.from(this.featureFlags_),
|
||||||
|
modules: Array.from(this.modules_),
|
||||||
|
plugins: this.plugins_,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.store_.addEvent(event)
|
this.store_.addEvent(event)
|
||||||
@@ -161,6 +165,18 @@ class Telemeter {
|
|||||||
this.featureFlags_.add(flag)
|
this.featureFlags_.add(flag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackModule(module) {
|
||||||
|
if (module) {
|
||||||
|
this.modules_.add(module)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trackPlugin(plugin) {
|
||||||
|
if (plugin) {
|
||||||
|
this.plugins_.push(plugin)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Telemeter
|
export default Telemeter
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { NextFunction, Request, Response } from "express"
|
import { NextFunction, Request, Response } from "express"
|
||||||
import { MedusaError } from "medusa-core-utils"
|
import { MedusaError } from "medusa-core-utils"
|
||||||
import { Logger } from "../../types/global"
|
import { Logger } from "../../types/global"
|
||||||
import { formatException } from "../../utils";
|
import { formatException } from "../../utils"
|
||||||
|
|
||||||
const QUERY_RUNNER_RELEASED = "QueryRunnerAlreadyReleasedError"
|
const QUERY_RUNNER_RELEASED = "QueryRunnerAlreadyReleasedError"
|
||||||
const TRANSACTION_STARTED = "TransactionAlreadyStartedError"
|
const TRANSACTION_STARTED = "TransactionAlreadyStartedError"
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import {
|
|||||||
StoreService,
|
StoreService,
|
||||||
} from "../../../../services"
|
} from "../../../../services"
|
||||||
import { FeatureFlagsResponse } from "../../../../types/feature-flags"
|
import { FeatureFlagsResponse } from "../../../../types/feature-flags"
|
||||||
|
import { ModulesResponse } from "../../../../types/modules"
|
||||||
import { FlagRouter } from "../../../../utils/flag-router"
|
import { FlagRouter } from "../../../../utils/flag-router"
|
||||||
|
import { ModulesHelper } from "../../../../utils/module-helper"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /store
|
* @oas [get] /store
|
||||||
@@ -60,6 +62,7 @@ export default async (req, res) => {
|
|||||||
const storeService: StoreService = req.scope.resolve("storeService")
|
const storeService: StoreService = req.scope.resolve("storeService")
|
||||||
|
|
||||||
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||||
|
const modulesHelper: ModulesHelper = req.scope.resolve("modulesHelper")
|
||||||
|
|
||||||
const paymentProviderService: PaymentProviderService = req.scope.resolve(
|
const paymentProviderService: PaymentProviderService = req.scope.resolve(
|
||||||
"paymentProviderService"
|
"paymentProviderService"
|
||||||
@@ -78,9 +81,11 @@ export default async (req, res) => {
|
|||||||
payment_providers: PaymentProvider[]
|
payment_providers: PaymentProvider[]
|
||||||
fulfillment_providers: FulfillmentProvider[]
|
fulfillment_providers: FulfillmentProvider[]
|
||||||
feature_flags: FeatureFlagsResponse
|
feature_flags: FeatureFlagsResponse
|
||||||
|
modules: ModulesResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
data.feature_flags = featureFlagRouter.listFlags()
|
data.feature_flags = featureFlagRouter.listFlags()
|
||||||
|
data.modules = modulesHelper.modules
|
||||||
|
|
||||||
const paymentProviders = await paymentProviderService.list()
|
const paymentProviders = await paymentProviderService.list()
|
||||||
const fulfillmentProviders = await fulfillmentProviderService.list()
|
const fulfillmentProviders = await fulfillmentProviderService.list()
|
||||||
|
|||||||
@@ -2,15 +2,15 @@ import { asValue, createContainer } from "awilix"
|
|||||||
import express from "express"
|
import express from "express"
|
||||||
import jwt from "jsonwebtoken"
|
import jwt from "jsonwebtoken"
|
||||||
import { MockManager } from "medusa-test-utils"
|
import { MockManager } from "medusa-test-utils"
|
||||||
|
import querystring from "querystring"
|
||||||
import "reflect-metadata"
|
import "reflect-metadata"
|
||||||
import supertest from "supertest"
|
import supertest from "supertest"
|
||||||
import querystring from "querystring"
|
|
||||||
import apiLoader from "../loaders/api"
|
import apiLoader from "../loaders/api"
|
||||||
import passportLoader from "../loaders/passport"
|
|
||||||
import featureFlagLoader, { featureFlagRouter } from "../loaders/feature-flags"
|
import featureFlagLoader, { featureFlagRouter } from "../loaders/feature-flags"
|
||||||
|
import { moduleHelper } from "../loaders/module"
|
||||||
|
import passportLoader from "../loaders/passport"
|
||||||
import servicesLoader from "../loaders/services"
|
import servicesLoader from "../loaders/services"
|
||||||
import strategiesLoader from "../loaders/strategies"
|
import strategiesLoader from "../loaders/strategies"
|
||||||
import logger from "../loaders/logger"
|
|
||||||
|
|
||||||
const adminSessionOpts = {
|
const adminSessionOpts = {
|
||||||
cookieName: "session",
|
cookieName: "session",
|
||||||
@@ -38,6 +38,7 @@ const testApp = express()
|
|||||||
const container = createContainer()
|
const container = createContainer()
|
||||||
|
|
||||||
container.register("featureFlagRouter", asValue(featureFlagRouter))
|
container.register("featureFlagRouter", asValue(featureFlagRouter))
|
||||||
|
container.register("modulesHelper", asValue(moduleHelper))
|
||||||
container.register("configModule", asValue(config))
|
container.register("configModule", asValue(config))
|
||||||
container.register({
|
container.register({
|
||||||
logger: asValue({
|
logger: asValue({
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
import { getConfigFile } from "medusa-core-utils"
|
||||||
import { ConfigModule } from "../types/global"
|
import { ConfigModule } from "../types/global"
|
||||||
import { getConfigFile } from "medusa-core-utils/dist"
|
|
||||||
import logger from "./logger"
|
import logger from "./logger"
|
||||||
|
import registerModuleDefinitions from "./module-definitions"
|
||||||
|
|
||||||
const isProduction = ["production", "prod"].includes(process.env.NODE_ENV || "")
|
const isProduction = ["production", "prod"].includes(process.env.NODE_ENV || "")
|
||||||
|
|
||||||
@@ -67,12 +68,16 @@ export default (rootDirectory: string): ConfigModule => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const moduleResolutions = registerModuleDefinitions(configModule)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
projectConfig: {
|
projectConfig: {
|
||||||
jwt_secret: jwt_secret ?? "supersecret",
|
jwt_secret: jwt_secret ?? "supersecret",
|
||||||
cookie_secret: cookie_secret ?? "supersecret",
|
cookie_secret: cookie_secret ?? "supersecret",
|
||||||
...configModule?.projectConfig,
|
...configModule?.projectConfig,
|
||||||
},
|
},
|
||||||
|
modules: configModule.modules ?? {},
|
||||||
|
moduleResolutions,
|
||||||
featureFlags: configModule?.featureFlags ?? {},
|
featureFlags: configModule?.featureFlags ?? {},
|
||||||
plugins: configModule?.plugins ?? [],
|
plugins: configModule?.plugins ?? [],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import path from "path"
|
|||||||
import { trackFeatureFlag } from "medusa-telemetry"
|
import { trackFeatureFlag } from "medusa-telemetry"
|
||||||
import { FlagSettings } from "../../types/feature-flags"
|
import { FlagSettings } from "../../types/feature-flags"
|
||||||
import { Logger } from "../../types/global"
|
import { Logger } from "../../types/global"
|
||||||
import { FlagRouter } from "../../utils/flag-router"
|
|
||||||
import { isDefined } from "../../utils"
|
import { isDefined } from "../../utils"
|
||||||
|
import { FlagRouter } from "../../utils/flag-router"
|
||||||
|
|
||||||
const isTruthy = (val: string | boolean | undefined): boolean => {
|
const isTruthy = (val: string | boolean | undefined): boolean => {
|
||||||
if (typeof val === "string") {
|
if (typeof val === "string") {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
import { ClassOrFunctionReturning } from "awilix/lib/container"
|
import { ClassOrFunctionReturning } from "awilix/lib/container"
|
||||||
import { Express, NextFunction, Request, Response } from "express"
|
import { Express, NextFunction, Request, Response } from "express"
|
||||||
import { track } from "medusa-telemetry"
|
import { track } from "medusa-telemetry"
|
||||||
|
import { EOL } from "os"
|
||||||
import "reflect-metadata"
|
import "reflect-metadata"
|
||||||
import requestIp from "request-ip"
|
import requestIp from "request-ip"
|
||||||
import { Connection, getManager } from "typeorm"
|
import { Connection, getManager } from "typeorm"
|
||||||
@@ -20,6 +21,7 @@ import expressLoader from "./express"
|
|||||||
import featureFlagsLoader from "./feature-flags"
|
import featureFlagsLoader from "./feature-flags"
|
||||||
import Logger from "./logger"
|
import Logger from "./logger"
|
||||||
import modelsLoader from "./models"
|
import modelsLoader from "./models"
|
||||||
|
import moduleLoader from "./module"
|
||||||
import passportLoader from "./passport"
|
import passportLoader from "./passport"
|
||||||
import pluginsLoader, { registerPluginModels } from "./plugins"
|
import pluginsLoader, { registerPluginModels } from "./plugins"
|
||||||
import redisLoader from "./redis"
|
import redisLoader from "./redis"
|
||||||
@@ -91,13 +93,13 @@ export default async ({
|
|||||||
|
|
||||||
await redisLoader({ container, configModule, logger: Logger })
|
await redisLoader({ container, configModule, logger: Logger })
|
||||||
|
|
||||||
const modelsActivity = Logger.activity("Initializing models")
|
const modelsActivity = Logger.activity(`Initializing models${EOL}`)
|
||||||
track("MODELS_INIT_STARTED")
|
track("MODELS_INIT_STARTED")
|
||||||
modelsLoader({ container })
|
modelsLoader({ container })
|
||||||
const mAct = Logger.success(modelsActivity, "Models initialized") || {}
|
const mAct = Logger.success(modelsActivity, "Models initialized") || {}
|
||||||
track("MODELS_INIT_COMPLETED", { duration: mAct.duration })
|
track("MODELS_INIT_COMPLETED", { duration: mAct.duration })
|
||||||
|
|
||||||
const pmActivity = Logger.activity("Initializing plugin models")
|
const pmActivity = Logger.activity(`Initializing plugin models${EOL}`)
|
||||||
track("PLUGIN_MODELS_INIT_STARTED")
|
track("PLUGIN_MODELS_INIT_STARTED")
|
||||||
await registerPluginModels({
|
await registerPluginModels({
|
||||||
rootDirectory,
|
rootDirectory,
|
||||||
@@ -107,13 +109,13 @@ export default async ({
|
|||||||
const pmAct = Logger.success(pmActivity, "Plugin models initialized") || {}
|
const pmAct = Logger.success(pmActivity, "Plugin models initialized") || {}
|
||||||
track("PLUGIN_MODELS_INIT_COMPLETED", { duration: pmAct.duration })
|
track("PLUGIN_MODELS_INIT_COMPLETED", { duration: pmAct.duration })
|
||||||
|
|
||||||
const repoActivity = Logger.activity("Initializing repositories")
|
const repoActivity = Logger.activity(`Initializing repositories${EOL}`)
|
||||||
track("REPOSITORIES_INIT_STARTED")
|
track("REPOSITORIES_INIT_STARTED")
|
||||||
repositoriesLoader({ container })
|
repositoriesLoader({ container })
|
||||||
const rAct = Logger.success(repoActivity, "Repositories initialized") || {}
|
const rAct = Logger.success(repoActivity, "Repositories initialized") || {}
|
||||||
track("REPOSITORIES_INIT_COMPLETED", { duration: rAct.duration })
|
track("REPOSITORIES_INIT_COMPLETED", { duration: rAct.duration })
|
||||||
|
|
||||||
const dbActivity = Logger.activity("Initializing database")
|
const dbActivity = Logger.activity(`Initializing database${EOL}`)
|
||||||
track("DATABASE_INIT_STARTED")
|
track("DATABASE_INIT_STARTED")
|
||||||
const dbConnection = await databaseLoader({
|
const dbConnection = await databaseLoader({
|
||||||
container,
|
container,
|
||||||
@@ -124,19 +126,19 @@ export default async ({
|
|||||||
|
|
||||||
container.register({ manager: asValue(dbConnection.manager) })
|
container.register({ manager: asValue(dbConnection.manager) })
|
||||||
|
|
||||||
const stratActivity = Logger.activity("Initializing strategies")
|
const stratActivity = Logger.activity(`Initializing strategies${EOL}`)
|
||||||
track("STRATEGIES_INIT_STARTED")
|
track("STRATEGIES_INIT_STARTED")
|
||||||
strategiesLoader({ container, configModule, isTest })
|
strategiesLoader({ container, configModule, isTest })
|
||||||
const stratAct = Logger.success(stratActivity, "Strategies initialized") || {}
|
const stratAct = Logger.success(stratActivity, "Strategies initialized") || {}
|
||||||
track("STRATEGIES_INIT_COMPLETED", { duration: stratAct.duration })
|
track("STRATEGIES_INIT_COMPLETED", { duration: stratAct.duration })
|
||||||
|
|
||||||
const servicesActivity = Logger.activity("Initializing services")
|
const servicesActivity = Logger.activity(`Initializing services${EOL}`)
|
||||||
track("SERVICES_INIT_STARTED")
|
track("SERVICES_INIT_STARTED")
|
||||||
servicesLoader({ container, configModule, isTest })
|
servicesLoader({ container, configModule, isTest })
|
||||||
const servAct = Logger.success(servicesActivity, "Services initialized") || {}
|
const servAct = Logger.success(servicesActivity, "Services initialized") || {}
|
||||||
track("SERVICES_INIT_COMPLETED", { duration: servAct.duration })
|
track("SERVICES_INIT_COMPLETED", { duration: servAct.duration })
|
||||||
|
|
||||||
const expActivity = Logger.activity("Initializing express")
|
const expActivity = Logger.activity(`Initializing express${EOL}`)
|
||||||
track("EXPRESS_INIT_STARTED")
|
track("EXPRESS_INIT_STARTED")
|
||||||
await expressLoader({ app: expressApp, configModule })
|
await expressLoader({ app: expressApp, configModule })
|
||||||
await passportLoader({ app: expressApp, container, configModule })
|
await passportLoader({ app: expressApp, container, configModule })
|
||||||
@@ -150,7 +152,7 @@ export default async ({
|
|||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
const pluginsActivity = Logger.activity("Initializing plugins")
|
const pluginsActivity = Logger.activity(`Initializing plugins${EOL}`)
|
||||||
track("PLUGINS_INIT_STARTED")
|
track("PLUGINS_INIT_STARTED")
|
||||||
await pluginsLoader({
|
await pluginsLoader({
|
||||||
container,
|
container,
|
||||||
@@ -162,31 +164,39 @@ export default async ({
|
|||||||
const pAct = Logger.success(pluginsActivity, "Plugins intialized") || {}
|
const pAct = Logger.success(pluginsActivity, "Plugins intialized") || {}
|
||||||
track("PLUGINS_INIT_COMPLETED", { duration: pAct.duration })
|
track("PLUGINS_INIT_COMPLETED", { duration: pAct.duration })
|
||||||
|
|
||||||
const subActivity = Logger.activity("Initializing subscribers")
|
const subActivity = Logger.activity(`Initializing subscribers${EOL}`)
|
||||||
track("SUBSCRIBERS_INIT_STARTED")
|
track("SUBSCRIBERS_INIT_STARTED")
|
||||||
subscribersLoader({ container })
|
subscribersLoader({ container })
|
||||||
const subAct = Logger.success(subActivity, "Subscribers initialized") || {}
|
const subAct = Logger.success(subActivity, "Subscribers initialized") || {}
|
||||||
track("SUBSCRIBERS_INIT_COMPLETED", { duration: subAct.duration })
|
track("SUBSCRIBERS_INIT_COMPLETED", { duration: subAct.duration })
|
||||||
|
|
||||||
const apiActivity = Logger.activity("Initializing API")
|
const apiActivity = Logger.activity(`Initializing API${EOL}`)
|
||||||
track("API_INIT_STARTED")
|
track("API_INIT_STARTED")
|
||||||
await apiLoader({ container, app: expressApp, configModule })
|
await apiLoader({ container, app: expressApp, configModule })
|
||||||
const apiAct = Logger.success(apiActivity, "API initialized") || {}
|
const apiAct = Logger.success(apiActivity, "API initialized") || {}
|
||||||
track("API_INIT_COMPLETED", { duration: apiAct.duration })
|
track("API_INIT_COMPLETED", { duration: apiAct.duration })
|
||||||
|
|
||||||
const defaultsActivity = Logger.activity("Initializing defaults")
|
const defaultsActivity = Logger.activity(`Initializing defaults${EOL}`)
|
||||||
track("DEFAULTS_INIT_STARTED")
|
track("DEFAULTS_INIT_STARTED")
|
||||||
await defaultsLoader({ container })
|
await defaultsLoader({ container })
|
||||||
const dAct = Logger.success(defaultsActivity, "Defaults initialized") || {}
|
const dAct = Logger.success(defaultsActivity, "Defaults initialized") || {}
|
||||||
track("DEFAULTS_INIT_COMPLETED", { duration: dAct.duration })
|
track("DEFAULTS_INIT_COMPLETED", { duration: dAct.duration })
|
||||||
|
|
||||||
const searchActivity = Logger.activity("Initializing search engine indexing")
|
const searchActivity = Logger.activity(
|
||||||
|
`Initializing search engine indexing${EOL}`
|
||||||
|
)
|
||||||
track("SEARCH_ENGINE_INDEXING_STARTED")
|
track("SEARCH_ENGINE_INDEXING_STARTED")
|
||||||
await searchIndexLoader({ container })
|
await searchIndexLoader({ container })
|
||||||
const searchAct =
|
const searchAct =
|
||||||
Logger.success(searchActivity, "Indexing event emitted") || {}
|
Logger.success(searchActivity, "Indexing event emitted") || {}
|
||||||
track("SEARCH_ENGINE_INDEXING_COMPLETED", { duration: searchAct.duration })
|
track("SEARCH_ENGINE_INDEXING_COMPLETED", { duration: searchAct.duration })
|
||||||
|
|
||||||
|
const modulesActivity = Logger.activity(`Initializing modules${EOL}`)
|
||||||
|
track("MODULES_INIT_STARTED")
|
||||||
|
await moduleLoader({ container, configModule, logger: Logger })
|
||||||
|
const modAct = Logger.success(modulesActivity, "Modules initialized") || {}
|
||||||
|
track("MODULES_INIT_COMPLETED", { duration: modAct.duration })
|
||||||
|
|
||||||
return { container, dbConnection, app: expressApp }
|
return { container, dbConnection, app: expressApp }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { ModuleDefinition } from "../../types/global"
|
||||||
|
|
||||||
|
export const MODULE_DEFINITIONS: ModuleDefinition[] = []
|
||||||
|
|
||||||
|
export default MODULE_DEFINITIONS
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import resolveCwd from "resolve-cwd"
|
||||||
|
|
||||||
|
import { ConfigModule, ModuleResolution } from "../../types/global"
|
||||||
|
import MODULE_DEFINITIONS from "./definitions"
|
||||||
|
|
||||||
|
export default ({ modules }: ConfigModule) => {
|
||||||
|
const moduleResolutions = {} as Record<string, ModuleResolution>
|
||||||
|
const projectModules = modules ?? {}
|
||||||
|
|
||||||
|
for (const definition of MODULE_DEFINITIONS) {
|
||||||
|
let resolutionPath = definition.defaultPackage
|
||||||
|
|
||||||
|
// If user added a module and it's overridable, we resolve that instead
|
||||||
|
if (definition.canOverride && definition.key in projectModules) {
|
||||||
|
const mod = projectModules[definition.key]
|
||||||
|
resolutionPath = resolveCwd(mod)
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleResolutions[definition.key] = {
|
||||||
|
resolutionPath,
|
||||||
|
definition,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return moduleResolutions
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { asFunction, asValue } from "awilix"
|
||||||
|
import { trackInstallation } from "medusa-telemetry"
|
||||||
|
import { ConfigModule, Logger, MedusaContainer } from "../types/global"
|
||||||
|
import { ModulesHelper } from "../utils/module-helper"
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
container: MedusaContainer
|
||||||
|
configModule: ConfigModule
|
||||||
|
logger: Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
export const moduleHelper = new ModulesHelper()
|
||||||
|
|
||||||
|
export default async ({
|
||||||
|
container,
|
||||||
|
configModule,
|
||||||
|
logger,
|
||||||
|
}: Options): Promise<void> => {
|
||||||
|
const moduleResolutions = configModule?.moduleResolutions ?? {}
|
||||||
|
|
||||||
|
for (const resolution of Object.values(moduleResolutions)) {
|
||||||
|
try {
|
||||||
|
const loadedModule = await import(resolution.resolutionPath!)
|
||||||
|
|
||||||
|
const moduleLoaders = loadedModule?.loaders || []
|
||||||
|
for (const loader of moduleLoaders) {
|
||||||
|
await loader({ container, configModule, logger })
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleServices = loadedModule?.services || []
|
||||||
|
|
||||||
|
for (const service of moduleServices) {
|
||||||
|
container.register({
|
||||||
|
[resolution.definition.registrationName]: asFunction(
|
||||||
|
(cradle) => new service(cradle, configModule)
|
||||||
|
).singleton(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const installation = {
|
||||||
|
module: resolution.definition.key,
|
||||||
|
resolution: resolution.resolutionPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
trackInstallation(installation, "module")
|
||||||
|
} catch (err) {
|
||||||
|
if (resolution.definition.isRequired) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not resolve required module: ${resolution.definition.label}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.warn(`Couldn not resolve module: ${resolution.definition.label}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleHelper.setModules(moduleResolutions)
|
||||||
|
|
||||||
|
container.register({
|
||||||
|
modulesHelper: asValue(moduleHelper),
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
FulfillmentService,
|
FulfillmentService,
|
||||||
OauthService,
|
OauthService,
|
||||||
} from "medusa-interfaces"
|
} from "medusa-interfaces"
|
||||||
|
import { trackInstallation } from "medusa-telemetry"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { EntitySchema } from "typeorm"
|
import { EntitySchema } from "typeorm"
|
||||||
import {
|
import {
|
||||||
@@ -77,6 +78,8 @@ export default async ({
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
resolved.map(async (pluginDetails) => runLoaders(pluginDetails, container))
|
resolved.map(async (pluginDetails) => runLoaders(pluginDetails, container))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
resolved.forEach((plugin) => trackInstallation(plugin.name, "plugin"))
|
||||||
}
|
}
|
||||||
|
|
||||||
function getResolvedPlugins(
|
function getResolvedPlugins(
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
import { asFunction } from "awilix"
|
||||||
import glob from "glob"
|
import glob from "glob"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { asFunction } from "awilix"
|
|
||||||
import formatRegistrationName from "../utils/format-registration-name"
|
|
||||||
import { ConfigModule, MedusaContainer } from "../types/global"
|
import { ConfigModule, MedusaContainer } from "../types/global"
|
||||||
import { isDefined } from "../utils"
|
import { isDefined } from "../utils"
|
||||||
|
import formatRegistrationName from "../utils/format-registration-name"
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
container: MedusaContainer
|
container: MedusaContainer
|
||||||
|
|||||||
@@ -37,6 +37,20 @@ export type Logger = _Logger & {
|
|||||||
warn: (msg: string) => void
|
warn: (msg: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ModuleResolution = {
|
||||||
|
resolutionPath: string
|
||||||
|
definition: ModuleDefinition
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModuleDefinition = {
|
||||||
|
key: string
|
||||||
|
registrationName: string
|
||||||
|
defaultPackage: string
|
||||||
|
label: string
|
||||||
|
canOverride?: boolean
|
||||||
|
isRequired?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export type ConfigModule = {
|
export type ConfigModule = {
|
||||||
projectConfig: {
|
projectConfig: {
|
||||||
redis_url?: string
|
redis_url?: string
|
||||||
@@ -56,6 +70,8 @@ export type ConfigModule = {
|
|||||||
admin_cors?: string
|
admin_cors?: string
|
||||||
}
|
}
|
||||||
featureFlags: Record<string, boolean | string>
|
featureFlags: Record<string, boolean | string>
|
||||||
|
modules?: Record<string, string>
|
||||||
|
moduleResolutions?: Record<string, ModuleResolution>
|
||||||
plugins: (
|
plugins: (
|
||||||
| {
|
| {
|
||||||
resolve: string
|
resolve: string
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export type ModulesResponse = {
|
||||||
|
module: string
|
||||||
|
resolution: string
|
||||||
|
}[]
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { ModuleResolution } from "../types/global"
|
||||||
|
import { ModulesResponse } from "../types/modules"
|
||||||
|
|
||||||
|
export class ModulesHelper {
|
||||||
|
private modules_: Record<string, ModuleResolution> = {}
|
||||||
|
|
||||||
|
setModules(modules: Record<string, ModuleResolution>) {
|
||||||
|
this.modules_ = modules
|
||||||
|
}
|
||||||
|
|
||||||
|
get modules(): ModulesResponse {
|
||||||
|
return Object.values(this.modules_ || {}).map((value) => ({
|
||||||
|
module: value.definition.key,
|
||||||
|
resolution: value.resolutionPath,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
{"id":"https://github.com/medusajs/medusa/releases/tag/v1.6.2","content":"v1.6.2 is out","isCloseable":true}
|
{"id":"https://github.com/medusajs/medusa/releases/tag/v1.6.3","content":"v1.6.3 is out","isCloseable":true}
|
||||||
Reference in New Issue
Block a user