feat: map container types for core services to interface (#11295)

This commit is contained in:
Harminder Virk
2025-02-04 15:47:55 +05:30
committed by GitHub
parent 70b3e16683
commit 3c51709daf
5 changed files with 103 additions and 17 deletions

View File

@@ -0,0 +1,6 @@
---
"@medusajs/types": patch
"@medusajs/utils": patch
---
feat: map container types for core services to interface

View File

@@ -66,7 +66,7 @@ export interface FindConfig<Entity> {
/**
* An array of strings, each being attribute names of the entity to retrieve in the result.
*/
select?: (keyof Entity | string)[]
select?: (keyof Entity | (string & {}))[]
/**
* A number indicating the number of records to skip before retrieving the results.

View File

@@ -14,10 +14,10 @@ describe("generateContainerTypes", function () {
{
cache: {
__definition: {
key: "cache",
key: "foo-cache",
label: "Cache",
defaultPackage: "@medusajs/foo",
resolvePath: "@medusajs/foo",
defaultPackage: "@medusajs/foo-cache",
resolvePath: "@medusajs/foo-cache",
defaultModuleDeclaration: {
scope: "internal",
},
@@ -34,11 +34,46 @@ describe("generateContainerTypes", function () {
expect(await fileSystem.exists("modules-bindings.d.ts")).toBeTruthy()
expect(await fileSystem.contents("modules-bindings.d.ts"))
.toMatchInlineSnapshot(`
"import type Cache from '@medusajs/foo'
"import type FooCache from '@medusajs/foo-cache'
declare module '@medusajs/framework/types' {
interface ModulesImplementations {
cache: InstanceType<(typeof Cache)['service']>
'foo-cache': InstanceType<(typeof FooCache)['service']>
}
}"
`)
})
it("point inbuilt packages to their interfaces", async function () {
await generateContainerTypes(
{
cache: {
__definition: {
key: "cache",
label: "Cache",
defaultPackage: "@medusajs/foo-cache",
resolvePath: "@medusajs/foo-cache",
defaultModuleDeclaration: {
scope: "internal",
},
},
__joinerConfig: {},
},
},
{
outputDir: fileSystem.basePath,
interfaceName: "ModulesImplementations",
}
)
expect(await fileSystem.exists("modules-bindings.d.ts")).toBeTruthy()
expect(await fileSystem.contents("modules-bindings.d.ts"))
.toMatchInlineSnapshot(`
"import type { ICacheService } from '@medusajs/framework/types'
declare module '@medusajs/framework/types' {
interface ModulesImplementations {
'cache': ICacheService
}
}"
`)
@@ -47,10 +82,10 @@ describe("generateContainerTypes", function () {
it("should normalize module path pointing to a relative file", async function () {
await generateContainerTypes(
{
cache: {
bar: {
__definition: {
key: "cache",
label: "Cache",
key: "bar",
label: "Bar",
defaultPackage: "./foo/bar",
resolvePath: "./foo/bar",
defaultModuleDeclaration: {
@@ -69,11 +104,11 @@ describe("generateContainerTypes", function () {
expect(await fileSystem.exists("modules-bindings.d.ts")).toBeTruthy()
expect(await fileSystem.contents("modules-bindings.d.ts"))
.toMatchInlineSnapshot(`
"import type Cache from '../../foo/bar'
"import type Bar from '../../foo/bar'
declare module '@medusajs/framework/types' {
interface ModulesImplementations {
cache: InstanceType<(typeof Cache)['service']>
'bar': InstanceType<(typeof Bar)['service']>
}
}"
`)

View File

@@ -1,9 +1,43 @@
import { join } from "path"
import { Modules } from "./definition"
import type { LoadedModule } from "@medusajs/types"
import { FileSystem } from "../common/file-system"
import { toCamelCase } from "../common/to-camel-case"
import { upperCaseFirst } from "../common/upper-case-first"
/**
* For known services that has interfaces, we will set the container
* type to the interface than the actual service implementation.
*
* The idea is to provide more precise types.
*/
const SERVICES_INTERFACES = {
[Modules.AUTH]: "IAuthModuleService",
[Modules.CACHE]: "ICacheService",
[Modules.CART]: "ICartModuleService",
[Modules.CUSTOMER]: "ICustomerModuleService",
[Modules.EVENT_BUS]: "IEventBusModuleService",
[Modules.INVENTORY]: "IInventoryService",
[Modules.PAYMENT]: "IPaymentModuleService",
[Modules.PRICING]: "IPricingModuleService",
[Modules.PRODUCT]: "IProductModuleService",
[Modules.PROMOTION]: "IPromotionModuleService",
[Modules.SALES_CHANNEL]: "ISalesChannelModuleService",
[Modules.TAX]: "ITaxModuleService",
[Modules.FULFILLMENT]: "IFulfillmentModuleService",
[Modules.STOCK_LOCATION]: "IStockLocationService",
[Modules.USER]: "IUserModuleService",
[Modules.WORKFLOW_ENGINE]: "IWorkflowEngineService",
[Modules.REGION]: "IRegionModuleService",
[Modules.ORDER]: "IOrderModuleService",
[Modules.API_KEY]: "IApiKeyModuleService",
[Modules.STORE]: "IStoreModuleService",
[Modules.CURRENCY]: "ICurrencyModuleService",
[Modules.FILE]: "IFileModuleService",
[Modules.NOTIFICATION]: "INotificationModuleService",
[Modules.LOCKING]: "ILockingModule",
}
/**
* Modules registered inside the config file points to one
* of the following paths.
@@ -55,6 +89,15 @@ export async function generateContainerTypes(
* Key registered within the container
*/
const key = service.__definition.key
const interfaceKey = `'${key}'`
if (SERVICES_INTERFACES[key]) {
result.imports.push(
`import type { ${SERVICES_INTERFACES[key]} } from '@medusajs/framework/types'`
)
result.mappings.push(`${interfaceKey}: ${SERVICES_INTERFACES[key]}`)
return
}
/**
* @todo. The property should exist on "LoadedModule"
@@ -71,7 +114,7 @@ export async function generateContainerTypes(
result.imports.push(`import type ${serviceName} from '${servicePath}'`)
result.mappings.push(
`${key}: InstanceType<(typeof ${serviceName})['service']>`
`${interfaceKey}: InstanceType<(typeof ${serviceName})['service']>`
)
})
return result

View File

@@ -120,7 +120,7 @@ export type AbstractModuleService<
TModelName
>}`]: (
id: string,
config?: FindConfig<any>,
config?: FindConfig<TModelsDtoConfig[TModelName]["dto"]>,
sharedContext?: Context
) => Promise<TModelsDtoConfig[TModelName]["dto"]>
} & {
@@ -129,7 +129,7 @@ export type AbstractModuleService<
TModelName
>}`]: (
filters?: any,
config?: FindConfig<any>,
config?: FindConfig<TModelsDtoConfig[TModelName]["dto"]>,
sharedContext?: Context
) => Promise<TModelsDtoConfig[TModelName]["dto"][]>
} & {
@@ -137,9 +137,11 @@ export type AbstractModuleService<
TModelsDtoConfig,
TModelName
>}`]: {
(filters?: any, config?: FindConfig<any>, sharedContext?: Context): Promise<
[TModelsDtoConfig[TModelName]["dto"][], number]
>
(
filters?: any,
config?: FindConfig<TModelsDtoConfig[TModelName]["dto"]>,
sharedContext?: Context
): Promise<[TModelsDtoConfig[TModelName]["dto"][], number]>
}
} & {
[TModelName in keyof TModelsDtoConfig as `delete${ExtractPluralName<