Repository: nestjs/swagger
Pull request #3399 (2025-04-14 09:12:45)

fix: import handling for ESM compatibility
## 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/3362
Fixing errors in the environment that were not tested in the previous PR.

- ✅ commonjs
- ✅ esm + swc
- ❌ esm + tsc

## What is the new behavior?

- ✅ 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

The format of the output when building in `swc` and `tsc` is different.
`swc` converts `require` to `import` in the metadata, but `tsc` does not.

**tsc**
```js
__decorate([
    Get('memo/:id'),
    openapi.ApiResponse({ status: 200, type: require("../entities/test/memo.entity.js").Memo }),
    __param(0, Param('id', ParseIntPipe)),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Number]),
    __metadata("design:returntype", Promise)
], SampleController.prototype, "read", null);
```

**swc**
```js
_ts_decorate([
    Get('memo/:id'),
    _ts_param(0, Param('id', ParseIntPipe)),
    _ts_metadata("design:type", Function),
    _ts_metadata("design:paramtypes", [
        Number
    ]),
    _ts_metadata("design:returntype", Promise)
], SampleController.prototype, "read", null);
```


Patch:
diff --git a/lib/plugin/utils/plugin-utils.ts b/lib/plugin/utils/plugin-utils.ts
index 3e74c667..6c32a24d 100644
--- a/lib/plugin/utils/plugin-utils.ts
+++ b/lib/plugin/utils/plugin-utils.ts
@@ -177,8 +177,12 @@ export function replaceImportPath(
     if (isAbsolute(importPath)) {
       throw {};
     }
+
     require.resolve(importPath);
-    typeReference = typeReference.replace('import', 'require');
+    if (!options.esmCompatible) {
+      typeReference = typeReference.replace('import', 'require');
+    }
+
     return {
       typeReference,
       importPath: null
@@ -229,8 +233,12 @@ export function replaceImportPath(
       };
     }
 
+    if (!options.esmCompatible) {
+      typeReference = typeReference.replace('import', 'require');
+    }
+
     return {
-      typeReference: typeReference.replace('import', 'require'),
+      typeReference,
       importPath: relativePath
     };
   }
diff --git a/package-lock.json b/package-lock.json
index ba088280..861dc56e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,7 +14,7 @@
         "js-yaml": "4.1.0",
         "lodash": "4.17.21",
         "path-to-regexp": "8.2.0",
-        "swagger-ui-dist": "5.21.0"
+        "swagger-ui-dist": "5.20.8"
       },
       "devDependencies": {
         "@commitlint/cli": "19.8.0",
@@ -13245,9 +13245,9 @@
       }
     },
     "node_modules/swagger-ui-dist": {
-      "version": "5.21.0",
-      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.21.0.tgz",
-      "integrity": "sha512-E0K3AB6HvQd8yQNSMR7eE5bk+323AUxjtCz/4ZNKiahOlPhPJxqn3UPIGs00cyY/dhrTDJ61L7C/a8u6zhGrZg==",
+      "version": "5.20.8",
+      "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.20.8.tgz",
+      "integrity": "sha512-VXVhdRh5vuKVpkegw0n0wCXuFFG+pxNmXa0vDcf76r1yP2cYKqcpOE5g8l5crbtLGx+j2EiDnG4/EU5T0jyN1w==",
       "license": "Apache-2.0",
       "dependencies": {
         "@scarf/scarf": "=1.4.0"
diff --git a/package.json b/package.json
index 84468665..1296111a 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,7 @@
     "js-yaml": "4.1.0",
     "lodash": "4.17.21",
     "path-to-regexp": "8.2.0",
-    "swagger-ui-dist": "5.21.0"
+    "swagger-ui-dist": "5.20.8"
   },
   "devDependencies": {
     "@commitlint/cli": "19.8.0",


Test patch:
diff --git a/test/plugin/fixtures/parameter-property.dto.ts b/test/plugin/fixtures/parameter-property.dto.ts
index 02d1b53a..850790c0 100644
--- a/test/plugin/fixtures/parameter-property.dto.ts
+++ b/test/plugin/fixtures/parameter-property.dto.ts
@@ -23,10 +23,10 @@ export class ItemDto {
 `;
 
 export const parameterPropertyDtoTextTranspiled = (esmCompatible?: boolean) => {
-  let fileName = 'parameter-property.dto';
-  if (esmCompatible) {
-    fileName += getOutputExtension(fileName);
-  }
+  const fileName = 'parameter-property.dto';
+  const fileImport = esmCompatible
+    ? `import("./${fileName}${getOutputExtension(fileName)}")`
+    : `require("./${fileName}")`;
 
   return `import * as openapi from "@nestjs/swagger";
 export class ParameterPropertyDto {
@@ -37,7 +37,7 @@ export class ParameterPropertyDto {
         this.protectedValue = protectedValue;
     }
     static _OPENAPI_METADATA_FACTORY() {
-        return { readonlyValue: { required: false, type: () => String }, privateValue: { required: true, type: () => String, nullable: true }, publicValue: { required: true, type: () => [require("./${fileName}").ItemDto] }, protectedValue: { required: true, type: () => String, default: "1234" } };
+        return { readonlyValue: { required: false, type: () => String }, privateValue: { required: true, type: () => String, nullable: true }, publicValue: { required: true, type: () => [${fileImport}.ItemDto] }, protectedValue: { required: true, type: () => String, default: "1234" } };
     }
 }
 export var LettersEnum;
@@ -51,7 +51,7 @@ export class ItemDto {
         this.enumValue = enumValue;
     }
     static _OPENAPI_METADATA_FACTORY() {
-        return { enumValue: { required: true, enum: require("./${fileName}").LettersEnum } };
+        return { enumValue: { required: true, enum: ${fileImport}.LettersEnum } };
     }
 }
 `;