Repository: nestjs/swagger
Pull request #3413 (2025-04-22 03:10:27)

fix: type import syntax for ApiResponse in esmCompatible
## PR Checklist
Please check if your PR fulfills the following requirements:

- [x] The commit message follows our guidelines: https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md
- [x] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)

## PR Type
What kind of change does this PR introduce?

<!-- Please check the one that applies to this PR using "x". -->
- [x] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Other... Please describe:

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->

Issue Number: https://github.com/nestjs/swagger/pull/3399

Fixes an issue where the `esmCompatible` option in the swagger plugin fails to load the `ApiResponse` type when using `tsc`.

<img width="894" alt="image" src="https://github.com/user-attachments/assets/999ac8ab-3615-411e-883a-de7324ef72fa" />

- ✅ esm + swc
- ❌ esm + tsc

## What is the new behavior?

<img width="969" alt="image" src="https://github.com/user-attachments/assets/1fbffc1e-9304-4f82-a67e-9b7b60a18d8f" />

- ✅ esm + tsc

## Does this PR introduce a breaking change?
- [ ] Yes
- [x] No

<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->

## Other information

<img width="432" alt="image" src="https://github.com/user-attachments/assets/33c17bd0-508c-4ac2-a81f-9f83e17583a8" />



Patch:
diff --git a/lib/plugin/utils/plugin-utils.ts b/lib/plugin/utils/plugin-utils.ts
index 6c32a24d..fec72948 100644
--- a/lib/plugin/utils/plugin-utils.ts
+++ b/lib/plugin/utils/plugin-utils.ts
@@ -233,12 +233,17 @@ export function replaceImportPath(
       };
     }
 
-    if (!options.esmCompatible) {
-      typeReference = typeReference.replace('import', 'require');
+    if (options.esmCompatible) {
+      const { typeName, typeImportStatement } =
+        convertToAsyncImport(typeReference);
+      return {
+        typeReference: `(${typeImportStatement}).${typeName}`,
+        importPath: relativePath
+      };
     }
 
     return {
-      typeReference,
+      typeReference: typeReference.replace('import', 'require'),
       importPath: relativePath
     };
   }
diff --git a/package-lock.json b/package-lock.json
index d1edc384..7e14574a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,7 +20,7 @@
         "@commitlint/cli": "19.8.0",
         "@commitlint/config-angular": "19.8.0",
         "@eslint/eslintrc": "3.3.1",
-        "@eslint/js": "9.25.1",
+        "@eslint/js": "9.25.0",
         "@fastify/static": "8.1.1",
         "@nestjs/common": "11.0.20",
         "@nestjs/core": "11.0.20",
@@ -32,7 +32,7 @@
         "@types/node": "22.14.1",
         "class-transformer": "0.5.1",
         "class-validator": "0.14.1",
-        "eslint": "9.25.1",
+        "eslint": "9.25.0",
         "eslint-config-prettier": "10.1.2",
         "eslint-plugin-prettier": "5.2.6",
         "express": "5.1.0",
@@ -51,7 +51,7 @@
         "swagger-parser": "10.0.3",
         "ts-jest": "29.3.2",
         "typescript": "5.8.3",
-        "typescript-eslint": "8.31.0"
+        "typescript-eslint": "8.30.1"
       },
       "peerDependencies": {
         "@fastify/static": "^8.0.0",
@@ -1059,9 +1059,9 @@
       "license": "MIT"
     },
     "node_modules/@eslint/js": {
-      "version": "9.25.1",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz",
-      "integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==",
+      "version": "9.25.0",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.0.tgz",
+      "integrity": "sha512-iWhsUS8Wgxz9AXNfvfOPFSW4VfMXdVhp1hjkZVhXCrpgh/aLcc45rX6MPu+tIVUWDw0HfNwth7O28M1xDxNf9w==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -3381,17 +3381,17 @@
       "license": "MIT"
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.31.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.0.tgz",
-      "integrity": "sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==",
+      "version": "8.30.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz",
+      "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/regexpp": "^4.10.0",
-        "@typescript-eslint/scope-manager": "8.31.0",
-        "@typescript-eslint/type-utils": "8.31.0",
-        "@typescript-eslint/utils": "8.31.0",
-        "@typescript-eslint/visitor-keys": "8.31.0",
+        "@typescript-eslint/scope-manager": "8.30.1",
+        "@typescript-eslint/type-utils": "8.30.1",
+        "@typescript-eslint/utils": "8.30.1",
+        "@typescript-eslint/visitor-keys": "8.30.1",
         "graphemer": "^1.4.0",
         "ignore": "^5.3.1",
         "natural-compare": "^1.4.0",
@@ -3411,16 +3411,16 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "8.31.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.0.tgz",
-      "integrity": "sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==",
+      "version": "8.30.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz",
+      "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/scope-manager": "8.31.0",
-        "@typescript-eslint/types": "8.31.0",
-        "@typescript-eslint/typescript-estree": "8.31.0",
-        "@typescript-eslint/visitor-keys": "8.31.0",
+        "@typescript-eslint/scope-manager": "8.30.1",
+        "@typescript-eslint/types": "8.30.1",
+        "@typescript-eslint/typescript-estree": "8.30.1",
+        "@typescript-eslint/visitor-keys": "8.30.1",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -3436,14 +3436,14 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.31.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.0.tgz",
-      "integrity": "sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==",
+      "version": "8.30.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz",
+      "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.31.0",
-        "@typescript-eslint/visitor-keys": "8.31.0"
+        "@typescript-eslint/types": "8.30.1",
+        "@typescript-eslint/visitor-keys": "8.30.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3454,14 +3454,14 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.31.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.0.tgz",
-      "integrity": "sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==",
+      "version": "8.30.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz",
+      "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "8.31.0",
-        "@typescript-eslint/utils": "8.31.0",
+        "@typescript-eslint/typescript-estree": "8.30.1",
+        "@typescript-eslint/utils": "8.30.1",
         "debug": "^4.3.4",
         "ts-api-utils": "^2.0.1"
       },
@@ -3478,9 +3478,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "8.31.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz",
-      "integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==",
+      "version": "8.30.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz",
+      "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==",
       "dev": true,
       "license": "MIT",
       "engines": {
@@ -3492,14 +3492,14 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.31.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.0.tgz",
-      "integrity": "sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==",
+      "version": "8.30.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz",
+      "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.31.0",
-        "@typescript-eslint/visitor-keys": "8.31.0",
+        "@typescript-eslint/types": "8.30.1",
+        "@typescript-eslint/visitor-keys": "8.30.1",
         "debug": "^4.3.4",
         "fast-glob": "^3.3.2",
         "is-glob": "^4.0.3",
@@ -3545,16 +3545,16 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "8.31.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.0.tgz",
-      "integrity": "sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==",
+      "version": "8.30.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz",
+      "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@typescript-eslint/scope-manager": "8.31.0",
-        "@typescript-eslint/types": "8.31.0",
-        "@typescript-eslint/typescript-estree": "8.31.0"
+        "@typescript-eslint/scope-manager": "8.30.1",
+        "@typescript-eslint/types": "8.30.1",
+        "@typescript-eslint/typescript-estree": "8.30.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3569,13 +3569,13 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.31.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.0.tgz",
-      "integrity": "sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==",
+      "version": "8.30.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz",
+      "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/types": "8.31.0",
+        "@typescript-eslint/types": "8.30.1",
         "eslint-visitor-keys": "^4.2.0"
       },
       "engines": {
@@ -5693,9 +5693,9 @@
       }
     },
     "node_modules/eslint": {
-      "version": "9.25.1",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz",
-      "integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==",
+      "version": "9.25.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.0.tgz",
+      "integrity": "sha512-MsBdObhM4cEwkzCiraDv7A6txFXEqtNXOb877TsSp2FCkBNl8JfVQrmiuDqC1IkejT6JLPzYBXx/xAiYhyzgGA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -5705,7 +5705,7 @@
         "@eslint/config-helpers": "^0.2.1",
         "@eslint/core": "^0.13.0",
         "@eslint/eslintrc": "^3.3.1",
-        "@eslint/js": "9.25.1",
+        "@eslint/js": "9.25.0",
         "@eslint/plugin-kit": "^0.2.8",
         "@humanfs/node": "^0.16.6",
         "@humanwhocodes/module-importer": "^1.0.1",
@@ -13224,15 +13224,15 @@
       }
     },
     "node_modules/typescript-eslint": {
-      "version": "8.31.0",
-      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.31.0.tgz",
-      "integrity": "sha512-u+93F0sB0An8WEAPtwxVhFby573E8ckdjwUUQUj9QA4v8JAvgtoDdIyYR3XFwFHq2W1KJ1AurwJCO+w+Y1ixyQ==",
+      "version": "8.30.1",
+      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz",
+      "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@typescript-eslint/eslint-plugin": "8.31.0",
-        "@typescript-eslint/parser": "8.31.0",
-        "@typescript-eslint/utils": "8.31.0"
+        "@typescript-eslint/eslint-plugin": "8.30.1",
+        "@typescript-eslint/parser": "8.30.1",
+        "@typescript-eslint/utils": "8.30.1"
       },
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
diff --git a/package.json b/package.json
index b612f2a1..7a01db96 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
     "@commitlint/cli": "19.8.0",
     "@commitlint/config-angular": "19.8.0",
     "@eslint/eslintrc": "3.3.1",
-    "@eslint/js": "9.25.1",
+    "@eslint/js": "9.25.0",
     "@fastify/static": "8.1.1",
     "@nestjs/common": "11.0.20",
     "@nestjs/core": "11.0.20",
@@ -50,7 +50,7 @@
     "@types/node": "22.14.1",
     "class-transformer": "0.5.1",
     "class-validator": "0.14.1",
-    "eslint": "9.25.1",
+    "eslint": "9.25.0",
     "eslint-config-prettier": "10.1.2",
     "eslint-plugin-prettier": "5.2.6",
     "express": "5.1.0",
@@ -69,7 +69,7 @@
     "swagger-parser": "10.0.3",
     "ts-jest": "29.3.2",
     "typescript": "5.8.3",
-    "typescript-eslint": "8.31.0"
+    "typescript-eslint": "8.30.1"
   },
   "peerDependencies": {
     "@fastify/static": "^8.0.0",


Test patch:
diff --git a/test/plugin/fixtures/parameter-property.dto.ts b/test/plugin/fixtures/parameter-property.dto.ts
index 850790c0..bd7d1db5 100644
--- a/test/plugin/fixtures/parameter-property.dto.ts
+++ b/test/plugin/fixtures/parameter-property.dto.ts
@@ -25,7 +25,7 @@ export class ItemDto {
 export const parameterPropertyDtoTextTranspiled = (esmCompatible?: boolean) => {
   const fileName = 'parameter-property.dto';
   const fileImport = esmCompatible
-    ? `import("./${fileName}${getOutputExtension(fileName)}")`
+    ? `(await import("./${fileName}${getOutputExtension(fileName)}"))`
     : `require("./${fileName}")`;
 
   return `import * as openapi from "@nestjs/swagger";