feat(medusa): Prevent build command from throwing on missing config (#14540)
**What** Prevent the build command from failing on mising config
This commit is contained in:
1 parent
250b6fdf22
commit
8890f28470
6 files changed
+73
-7
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/framework": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(medusa): Prevent build command from throwing on missing config
|
||||||
@@ -38,4 +38,35 @@ describe("configLoader", () => {
|
|||||||
expect(configModule.projectConfig.databaseName).toBe("foo")
|
expect(configModule.projectConfig.databaseName).toBe("foo")
|
||||||
expect(configModule.projectConfig.workerMode).toBe("worker")
|
expect(configModule.projectConfig.workerMode).toBe("worker")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should load config without throwing errors when throwOnError is false", async () => {
|
||||||
|
await configLoader(entryDirectory, "medusa-config", {
|
||||||
|
throwOnError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const configModule = container.resolve(
|
||||||
|
ContainerRegistrationKeys.CONFIG_MODULE
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(configModule).toBeDefined()
|
||||||
|
expect(configModule.projectConfig).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should pass throwOnError option through to buildHttpConfig", async () => {
|
||||||
|
// When throwOnError is false, missing jwtSecret and cookieSecret should not cause errors
|
||||||
|
await configLoader(entryDirectory, "medusa-config-2", {
|
||||||
|
throwOnError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const configModule = container.resolve(
|
||||||
|
ContainerRegistrationKeys.CONFIG_MODULE
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(configModule).toBeDefined()
|
||||||
|
expect(configModule.projectConfig.databaseName).toBe("foo")
|
||||||
|
// http config should still be built with defaults even without throwing errors
|
||||||
|
expect(configModule.projectConfig.http).toBeDefined()
|
||||||
|
expect(configModule.projectConfig.http.jwtSecret).toBe("supersecret")
|
||||||
|
expect(configModule.projectConfig.http.cookieSecret).toBe("supersecret")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -70,8 +70,12 @@ export class ConfigManager {
|
|||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected buildHttpConfig(
|
protected buildHttpConfig(
|
||||||
projectConfig: Partial<ConfigModule["projectConfig"]>
|
projectConfig: Partial<ConfigModule["projectConfig"]>,
|
||||||
|
options?: {
|
||||||
|
throwOnError?: boolean
|
||||||
|
}
|
||||||
): ConfigModule["projectConfig"]["http"] {
|
): ConfigModule["projectConfig"]["http"] {
|
||||||
|
const { throwOnError = true } = options ?? {}
|
||||||
const http = (projectConfig.http ??
|
const http = (projectConfig.http ??
|
||||||
{}) as ConfigModule["projectConfig"]["http"]
|
{}) as ConfigModule["projectConfig"]["http"]
|
||||||
|
|
||||||
@@ -87,6 +91,7 @@ export class ConfigManager {
|
|||||||
http.jwtPublicKey = http?.jwtPublicKey ?? process.env.JWT_PUBLIC_KEY
|
http.jwtPublicKey = http?.jwtPublicKey ?? process.env.JWT_PUBLIC_KEY
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
throwOnError &&
|
||||||
http?.jwtPublicKey &&
|
http?.jwtPublicKey &&
|
||||||
((http.jwtVerifyOptions && !http.jwtVerifyOptions.algorithms?.length) ||
|
((http.jwtVerifyOptions && !http.jwtVerifyOptions.algorithms?.length) ||
|
||||||
(http.jwtOptions && !http.jwtOptions.algorithm))
|
(http.jwtOptions && !http.jwtOptions.algorithm))
|
||||||
@@ -97,11 +102,13 @@ export class ConfigManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!http.jwtSecret) {
|
if (!http.jwtSecret) {
|
||||||
|
if (throwOnError) {
|
||||||
this.rejectErrors(
|
this.rejectErrors(
|
||||||
`http.jwtSecret not found.${
|
`http.jwtSecret not found.${
|
||||||
this.#isProduction ? "" : "Using default 'supersecret'."
|
this.#isProduction ? "" : "Using default 'supersecret'."
|
||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
http.jwtSecret = "supersecret"
|
http.jwtSecret = "supersecret"
|
||||||
}
|
}
|
||||||
@@ -110,11 +117,13 @@ export class ConfigManager {
|
|||||||
process.env.COOKIE_SECRET)!
|
process.env.COOKIE_SECRET)!
|
||||||
|
|
||||||
if (!http.cookieSecret) {
|
if (!http.cookieSecret) {
|
||||||
|
if (throwOnError) {
|
||||||
this.rejectErrors(
|
this.rejectErrors(
|
||||||
`http.cookieSecret not found.${
|
`http.cookieSecret not found.${
|
||||||
this.#isProduction ? "" : " Using default 'supersecret'."
|
this.#isProduction ? "" : " Using default 'supersecret'."
|
||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
http.cookieSecret = "supersecret"
|
http.cookieSecret = "supersecret"
|
||||||
}
|
}
|
||||||
@@ -128,7 +137,10 @@ export class ConfigManager {
|
|||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected normalizeProjectConfig(
|
protected normalizeProjectConfig(
|
||||||
config: Partial<ConfigModule>
|
config: Partial<ConfigModule>,
|
||||||
|
options?: {
|
||||||
|
throwOnError?: boolean
|
||||||
|
}
|
||||||
): ConfigModule["projectConfig"] {
|
): ConfigModule["projectConfig"] {
|
||||||
const projConfig = config?.projectConfig ?? {}
|
const projConfig = config?.projectConfig ?? {}
|
||||||
const outputConfig = deepCopy(projConfig) as ConfigModule["projectConfig"]
|
const outputConfig = deepCopy(projConfig) as ConfigModule["projectConfig"]
|
||||||
@@ -140,7 +152,9 @@ export class ConfigManager {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
outputConfig.http = this.buildHttpConfig(projConfig)
|
outputConfig.http = this.buildHttpConfig(projConfig, {
|
||||||
|
throwOnError: options?.throwOnError,
|
||||||
|
})
|
||||||
|
|
||||||
let workerMode = outputConfig?.workerMode!
|
let workerMode = outputConfig?.workerMode!
|
||||||
|
|
||||||
@@ -168,13 +182,17 @@ export class ConfigManager {
|
|||||||
loadConfig({
|
loadConfig({
|
||||||
projectConfig = {},
|
projectConfig = {},
|
||||||
baseDir,
|
baseDir,
|
||||||
|
throwOnError = true,
|
||||||
}: {
|
}: {
|
||||||
projectConfig: Partial<ConfigModule>
|
projectConfig: Partial<ConfigModule>
|
||||||
baseDir: string
|
baseDir: string
|
||||||
|
throwOnError?: boolean
|
||||||
}): ConfigModule {
|
}): ConfigModule {
|
||||||
this.#baseDir = baseDir
|
this.#baseDir = baseDir
|
||||||
|
|
||||||
const normalizedProjectConfig = this.normalizeProjectConfig(projectConfig)
|
const normalizedProjectConfig = this.normalizeProjectConfig(projectConfig, {
|
||||||
|
throwOnError,
|
||||||
|
})
|
||||||
|
|
||||||
this.#config = {
|
this.#config = {
|
||||||
projectConfig: normalizedProjectConfig,
|
projectConfig: normalizedProjectConfig,
|
||||||
|
|||||||
@@ -25,22 +25,29 @@ container.register(
|
|||||||
*
|
*
|
||||||
* @param entryDirectory The directory to find the config file from
|
* @param entryDirectory The directory to find the config file from
|
||||||
* @param configFileName The name of the config file to search for in the entry directory
|
* @param configFileName The name of the config file to search for in the entry directory
|
||||||
|
* @param options.throwOnError When false, missing config files and validation errors won't throw.
|
||||||
|
* Useful for build/compile commands. Defaults to true.
|
||||||
*/
|
*/
|
||||||
export async function configLoader(
|
export async function configLoader(
|
||||||
entryDirectory: string,
|
entryDirectory: string,
|
||||||
configFileName: string = "medusa-config"
|
configFileName: string = "medusa-config",
|
||||||
|
options?: {
|
||||||
|
throwOnError?: boolean
|
||||||
|
}
|
||||||
): Promise<ConfigModule> {
|
): Promise<ConfigModule> {
|
||||||
|
const { throwOnError = true } = options ?? {}
|
||||||
const config = await getConfigFile<ConfigModule>(
|
const config = await getConfigFile<ConfigModule>(
|
||||||
entryDirectory,
|
entryDirectory,
|
||||||
configFileName
|
configFileName
|
||||||
)
|
)
|
||||||
|
|
||||||
if (config.error) {
|
if (config.error && throwOnError) {
|
||||||
handleConfigError(config.error)
|
handleConfigError(config.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return configManager.loadConfig({
|
return configManager.loadConfig({
|
||||||
projectConfig: config.configModule!,
|
projectConfig: config.configModule!,
|
||||||
baseDir: entryDirectory,
|
baseDir: entryDirectory,
|
||||||
|
throwOnError,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@ export default async function build({
|
|||||||
}) {
|
}) {
|
||||||
const container = await initializeContainer(directory, {
|
const container = await initializeContainer(directory, {
|
||||||
skipDbConnection: true,
|
skipDbConnection: true,
|
||||||
|
throwOnError: false,
|
||||||
})
|
})
|
||||||
const logger = container.resolve(ContainerRegistrationKeys.LOGGER)
|
const logger = container.resolve(ContainerRegistrationKeys.LOGGER)
|
||||||
|
|
||||||
|
|||||||
@@ -134,10 +134,13 @@ export async function initializeContainer(
|
|||||||
rootDirectory: string,
|
rootDirectory: string,
|
||||||
options?: {
|
options?: {
|
||||||
skipDbConnection?: boolean
|
skipDbConnection?: boolean
|
||||||
|
throwOnError?: boolean
|
||||||
}
|
}
|
||||||
): Promise<MedusaContainer> {
|
): Promise<MedusaContainer> {
|
||||||
await featureFlagsLoader(rootDirectory)
|
await featureFlagsLoader(rootDirectory)
|
||||||
const configDir = await configLoader(rootDirectory, "medusa-config")
|
const configDir = await configLoader(rootDirectory, "medusa-config", {
|
||||||
|
throwOnError: options?.throwOnError,
|
||||||
|
})
|
||||||
await featureFlagsLoader(join(__dirname, ".."))
|
await featureFlagsLoader(join(__dirname, ".."))
|
||||||
|
|
||||||
const customLogger = configDir.logger ?? defaultLogger
|
const customLogger = configDir.logger ?? defaultLogger
|
||||||
|
|||||||
Reference in new issue
Block a user