From 181d5fa67122e4e5f38b91f6b716d35f2d08a204 Mon Sep 17 00:00:00 2001 From: Adrien de Peretti Date: Fri, 19 Dec 2025 10:13:17 +0100 Subject: [PATCH] feat(): Auto generated types generation upon build (#14337) * feat(): Auto generated types generation upon build * Create happy-steaks-cheer.md * test(): default types true * improvements * improvements * improvements * improvements * improvements --- .changeset/happy-steaks-cheer.md | 8 +++ .../core/framework/src/medusa-app-loader.ts | 10 ++- packages/core/modules-sdk/src/medusa-app.ts | 9 ++- packages/medusa/src/commands/build.ts | 11 ++- .../src/commands/utils/generate-types.ts | 72 +++++++++++++++++++ 5 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 .changeset/happy-steaks-cheer.md create mode 100644 packages/medusa/src/commands/utils/generate-types.ts diff --git a/.changeset/happy-steaks-cheer.md b/.changeset/happy-steaks-cheer.md new file mode 100644 index 0000000000..575492ccc6 --- /dev/null +++ b/.changeset/happy-steaks-cheer.md @@ -0,0 +1,8 @@ +--- +"@medusajs/medusa": patch +"@medusajs/framework": patch +"@medusajs/modules-sdk": patch +"@medusajs/cli": patch +--- + +feat(): Auto generated types generation upon build diff --git a/packages/core/framework/src/medusa-app-loader.ts b/packages/core/framework/src/medusa-app-loader.ts index 8b5788efbb..37fef781cc 100644 --- a/packages/core/framework/src/medusa-app-loader.ts +++ b/packages/core/framework/src/medusa-app-loader.ts @@ -310,13 +310,18 @@ export class MedusaAppLoader { * Load all modules and bootstrap all the modules and links to be ready to be consumed * @param config */ - async load(config = { registerInContainer: true }): Promise { + async load( + config: { registerInContainer?: boolean; migrationOnly?: boolean } = { + registerInContainer: true, + migrationOnly: false, + } + ): Promise { const configModule: ConfigModule = this.#container.resolve( ContainerRegistrationKeys.CONFIG_MODULE ) const { sharedResourcesConfig, injectedDependencies } = - this.prepareSharedResourcesAndDeps() + !config.migrationOnly ? this.prepareSharedResourcesAndDeps() : {} this.#container.register( ContainerRegistrationKeys.REMOTE_QUERY, @@ -343,6 +348,7 @@ export class MedusaAppLoader { injectedDependencies, medusaConfigPath: this.#medusaConfigPath, cwd: this.#cwd, + migrationOnly: config.migrationOnly, }) if (!config.registerInContainer) { diff --git a/packages/core/modules-sdk/src/medusa-app.ts b/packages/core/modules-sdk/src/medusa-app.ts index 1092a55925..5dc5a08199 100644 --- a/packages/core/modules-sdk/src/medusa-app.ts +++ b/packages/core/modules-sdk/src/medusa-app.ts @@ -308,6 +308,11 @@ export type MedusaAppOptions = { * Forces the modules bootstrapper to only run the modules loaders and return prematurely */ loaderOnly?: boolean + /** + * Only partially load modules to retrieve their joiner configs without running loaders. + * Useful for type generation and migrations. + */ + migrationOnly?: boolean cwd?: string } @@ -325,9 +330,7 @@ async function MedusaApp_({ loaderOnly = false, workerMode = "shared", cwd = process.cwd(), -}: MedusaAppOptions & { - migrationOnly?: boolean -} = {}): Promise { +}: MedusaAppOptions = {}): Promise { const sharedContainer_ = createMedusaContainer({}, sharedContainer) const config = sharedContainer_.resolve( diff --git a/packages/medusa/src/commands/build.ts b/packages/medusa/src/commands/build.ts index 2b84fee600..e2cd6a29ec 100644 --- a/packages/medusa/src/commands/build.ts +++ b/packages/medusa/src/commands/build.ts @@ -1,6 +1,7 @@ import { Compiler } from "@medusajs/framework/build-tools" import { ContainerRegistrationKeys } from "@medusajs/framework/utils" import { initializeContainer } from "../loaders" +import { generateTypes } from "./utils/generate-types" export default async function build({ directory, @@ -14,6 +15,12 @@ export default async function build({ }) const logger = container.resolve(ContainerRegistrationKeys.LOGGER) + await generateTypes({ + directory, + container, + logger, + }) + logger.info("Starting build...") const compiler = new Compiler(directory, logger) @@ -32,7 +39,9 @@ export default async function build({ promises.push(compiler.buildAppFrontend(adminOnly, tsConfig, bundler)) const responses = await Promise.all(promises) - if (responses.every((response) => response === true)) { + const buildSucceeded = responses.every((response) => response === true) + + if (buildSucceeded) { process.exit(0) } else { process.exit(1) diff --git a/packages/medusa/src/commands/utils/generate-types.ts b/packages/medusa/src/commands/utils/generate-types.ts new file mode 100644 index 0000000000..0de904c380 --- /dev/null +++ b/packages/medusa/src/commands/utils/generate-types.ts @@ -0,0 +1,72 @@ +import { LinkLoader, MedusaAppLoader } from "@medusajs/framework" +import { MedusaModule } from "@medusajs/framework/modules-sdk" +import { + ContainerRegistrationKeys, + FileSystem, + generateContainerTypes, + getResolvedPlugins, + gqlSchemaToTypes, + mergePluginModules, + validateModuleName, +} from "@medusajs/framework/utils" +import { Logger, MedusaContainer } from "@medusajs/types" +import path, { join } from "path" + +export async function generateTypes({ + directory, + container, + logger, +}: { + directory: string + container: MedusaContainer + logger: Logger +}) { + logger.info("Generating types...") + + const configModule = container.resolve( + ContainerRegistrationKeys.CONFIG_MODULE + ) + + const plugins = await getResolvedPlugins(directory, configModule, true) + mergePluginModules(configModule, plugins) + + Object.keys(configModule.modules ?? {}).forEach((key) => { + validateModuleName(key) + }) + + const linksSourcePaths = plugins.map((plugin) => + join(plugin.resolve, "links") + ) + await new LinkLoader(linksSourcePaths, logger).load() + + const { gqlSchema, modules } = await new MedusaAppLoader().load({ + registerInContainer: false, + migrationOnly: true, + }) + + const typesDirectory = path.join(directory, ".medusa/types") + + /** + * Cleanup existing types directory before creating new artifacts + */ + await new FileSystem(typesDirectory).cleanup({ recursive: true }) + + await generateContainerTypes(modules, { + outputDir: typesDirectory, + interfaceName: "ModuleImplementations", + }) + logger.debug("Generated container types") + + if (gqlSchema) { + await gqlSchemaToTypes({ + outputDir: typesDirectory, + filename: "query-entry-points", + interfaceName: "RemoteQueryEntryPoints", + schema: gqlSchema, + joinerConfigs: MedusaModule.getAllJoinerConfigs(), + }) + logger.debug("Generated modules types") + } + + logger.info("Types generated successfully") +}