Repository: nestjs/swagger
Pull request #2067 (2022-09-01 09:15:52)

chore(): support typescript v4.8
## 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". -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [x] 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: N/A


## What is the new behavior?


## Does this PR introduce a breaking change?
- [ ] Yes
- [ ] No

<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->


## Other information



Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index bd067948..d7213ad3 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -16,6 +16,10 @@ aliases:
     run:
         name: Test
         command: npm run test -- --runInBand
+  - &run-unit-tests
+    run:
+        name: Test (TypeScript < v4.8)
+        command: npm i --no-save -D typescript@4.7.2 && npm run test -- --runInBand
   - &run-e2e-tests
     run:
         name: E2E test
diff --git a/lib/plugin/utils/plugin-utils.ts b/lib/plugin/utils/plugin-utils.ts
index ed224400..793855d4 100644
--- a/lib/plugin/utils/plugin-utils.ts
+++ b/lib/plugin/utils/plugin-utils.ts
@@ -6,20 +6,21 @@ import {
   getText,
   getTypeArguments,
   isArray,
+  isBigInt,
   isBoolean,
   isEnum,
   isInterface,
   isNumber,
-  isBigInt,
   isString,
   isStringLiteral
 } from './ast-utils';
 
 export function getDecoratorOrUndefinedByNames(
   names: string[],
-  decorators: ts.NodeArray<ts.Decorator>
+  decorators: ts.NodeArray<ts.Decorator>,
+  factory: ts.NodeFactory
 ): ts.Decorator | undefined {
-  return (decorators || ts.createNodeArray()).find((item) => {
+  return (decorators || factory.createNodeArray()).find((item) => {
     try {
       const decoratorName = getDecoratorName(item);
       return names.includes(decoratorName);
diff --git a/lib/plugin/visitors/abstract.visitor.ts b/lib/plugin/visitors/abstract.visitor.ts
index 68efbfb4..f5caf529 100644
--- a/lib/plugin/visitors/abstract.visitor.ts
+++ b/lib/plugin/visitors/abstract.visitor.ts
@@ -1,12 +1,13 @@
 import * as ts from 'typescript';
 import { OPENAPI_NAMESPACE, OPENAPI_PACKAGE_NAME } from '../plugin-constants';
 
+const [major, minor] = ts.versionMajorMinor?.split('.').map((x) => +x);
 export class AbstractFileVisitor {
   updateImports(
     sourceFile: ts.SourceFile,
-    factory: ts.NodeFactory | undefined
+    factory: ts.NodeFactory | undefined,
+    program: ts.Program
   ): ts.SourceFile {
-    const [major, minor] = ts.versionMajorMinor?.split('.').map((x) => +x);
     if (!factory) {
       // support TS v4.2+
       const importEqualsDeclaration =
@@ -34,17 +35,26 @@ export class AbstractFileVisitor {
       ]);
     }
     // support TS v4.2+
-    const importEqualsDeclaration =
-      major == 4 && minor >= 2
-        ? (factory.createImportEqualsDeclaration as any)(
-            undefined,
-            undefined,
-            false,
-            OPENAPI_NAMESPACE,
-            factory.createExternalModuleReference(
-              factory.createStringLiteral(OPENAPI_PACKAGE_NAME)
+    const importEqualsDeclaration: ts.ImportDeclaration =
+      major >= 4 && minor >= 2
+        ? minor >= 8
+          ? (factory.createImportEqualsDeclaration as any)(
+              undefined,
+              false,
+              factory.createIdentifier(OPENAPI_NAMESPACE),
+              factory.createExternalModuleReference(
+                factory.createStringLiteral(OPENAPI_PACKAGE_NAME)
+              )
+            )
+          : (factory.createImportEqualsDeclaration as any)(
+              undefined,
+              undefined,
+              false,
+              OPENAPI_NAMESPACE,
+              factory.createExternalModuleReference(
+                factory.createStringLiteral(OPENAPI_PACKAGE_NAME)
+              )
             )
-          )
         : (factory.createImportEqualsDeclaration as any)(
             undefined,
             undefined,
@@ -54,9 +64,48 @@ export class AbstractFileVisitor {
             )
           );
 
-    return factory.updateSourceFile(sourceFile, [
-      importEqualsDeclaration,
-      ...sourceFile.statements
-    ]);
+    const compilerOptions = program.getCompilerOptions();
+    // Support TS v4.8+
+    if (
+      compilerOptions.module >= ts.ModuleKind.ES2015 &&
+      compilerOptions.module <= ts.ModuleKind.ESNext
+    ) {
+      const importAsDeclaration =
+        (minor >= 8 && major >= 4) || major >= 5
+          ? (factory.createImportDeclaration as any)(
+              undefined,
+              factory.createImportClause(
+                false,
+                undefined,
+                factory.createNamespaceImport(
+                  factory.createIdentifier(OPENAPI_NAMESPACE)
+                )
+              ),
+              factory.createStringLiteral(OPENAPI_PACKAGE_NAME),
+              undefined
+            )
+          : (factory.createImportDeclaration as any)(
+              undefined,
+              undefined,
+              factory.createImportClause(
+                false,
+                undefined,
+                factory.createNamespaceImport(
+                  factory.createIdentifier(OPENAPI_NAMESPACE)
+                )
+              ),
+              factory.createStringLiteral(OPENAPI_PACKAGE_NAME),
+              undefined
+            );
+      return factory.updateSourceFile(sourceFile, [
+        importAsDeclaration,
+        ...sourceFile.statements
+      ]);
+    } else {
+      return factory.updateSourceFile(sourceFile, [
+        importEqualsDeclaration,
+        ...sourceFile.statements
+      ]);
+    }
   }
 }
diff --git a/lib/plugin/visitors/controller-class.visitor.ts b/lib/plugin/visitors/controller-class.visitor.ts
index a948ad15..f766e9f3 100644
--- a/lib/plugin/visitors/controller-class.visitor.ts
+++ b/lib/plugin/visitors/controller-class.visitor.ts
@@ -18,6 +18,11 @@ import {
 } from '../utils/plugin-utils';
 import { AbstractFileVisitor } from './abstract.visitor';
 
+const [tsVersionMajor, tsVersionMinor] = ts.versionMajorMinor
+  ?.split('.')
+  .map((x) => +x);
+const isInUpdatedAstContext = tsVersionMinor >= 8 || tsVersionMajor > 4;
+
 export class ControllerClassVisitor extends AbstractFileVisitor {
   visit(
     sourceFile: ts.SourceFile,
@@ -26,7 +31,7 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
     options: PluginOptions
   ) {
     const typeChecker = program.getTypeChecker();
-    sourceFile = this.updateImports(sourceFile, ctx.factory);
+    sourceFile = this.updateImports(sourceFile, ctx.factory, program);
 
     const visitNode = (node: ts.Node): ts.Node => {
       if (ts.isMethodDeclaration(node)) {
@@ -56,14 +61,18 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
     hostFilename: string,
     sourceFile: ts.SourceFile
   ): ts.MethodDeclaration {
-    if (!compilerNode.decorators) {
+    // Support both >= v4.8 and v4.7 and lower
+    const decorators = (ts as any).canHaveDecorators
+      ? (ts as any).getDecorators(compilerNode)
+      : compilerNode.decorators;
+    if (!decorators) {
       return compilerNode;
     }
 
     const apiOperationDecoratorsArray = this.createApiOperationDecorator(
       factory,
       compilerNode,
-      compilerNode.decorators,
+      decorators,
       options,
       sourceFile,
       typeChecker
@@ -72,43 +81,60 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
       apiOperationDecoratorsArray.length > 0;
 
     const existingDecorators = removeExistingApiOperationDecorator
-      ? compilerNode.decorators.filter(
+      ? decorators.filter(
           (item) => getDecoratorName(item) !== ApiOperation.name
         )
-      : compilerNode.decorators;
+      : decorators;
 
-    return factory.updateMethodDeclaration(
-      compilerNode,
-      [
-        ...apiOperationDecoratorsArray,
-        ...existingDecorators,
-        factory.createDecorator(
-          factory.createCallExpression(
-            factory.createIdentifier(
-              `${OPENAPI_NAMESPACE}.${ApiResponse.name}`
-            ),
-            undefined,
-            [
-              this.createDecoratorObjectLiteralExpr(
-                factory,
-                compilerNode,
-                typeChecker,
-                factory.createNodeArray(),
-                hostFilename
-              )
-            ]
-          )
+    // Support both >= v4.8 and v4.7 and lower
+    const modifiers = isInUpdatedAstContext
+      ? (ts as any).getModifiers(compilerNode)
+      : compilerNode.modifiers;
+
+    const updatedDecorators = [
+      ...apiOperationDecoratorsArray,
+      ...existingDecorators,
+      factory.createDecorator(
+        factory.createCallExpression(
+          factory.createIdentifier(`${OPENAPI_NAMESPACE}.${ApiResponse.name}`),
+          undefined,
+          [
+            this.createDecoratorObjectLiteralExpr(
+              factory,
+              compilerNode,
+              typeChecker,
+              factory.createNodeArray(),
+              hostFilename
+            )
+          ]
         )
-      ],
-      compilerNode.modifiers,
-      compilerNode.asteriskToken,
-      compilerNode.name,
-      compilerNode.questionToken,
-      compilerNode.typeParameters,
-      compilerNode.parameters,
-      compilerNode.type,
-      compilerNode.body
-    );
+      )
+    ];
+
+    return isInUpdatedAstContext
+      ? (factory as any).updateMethodDeclaration(
+          compilerNode,
+          [...updatedDecorators, ...modifiers],
+          compilerNode.asteriskToken,
+          compilerNode.name,
+          compilerNode.questionToken,
+          compilerNode.typeParameters,
+          compilerNode.parameters,
+          compilerNode.type,
+          compilerNode.body
+        )
+      : factory.updateMethodDeclaration(
+          compilerNode,
+          updatedDecorators,
+          modifiers,
+          compilerNode.asteriskToken,
+          compilerNode.name,
+          compilerNode.questionToken,
+          compilerNode.typeParameters,
+          compilerNode.parameters,
+          compilerNode.type,
+          compilerNode.body
+        );
   }
 
   createApiOperationDecorator(
@@ -125,7 +151,8 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
     const keyToGenerate = options.controllerKeyOfComment;
     const apiOperationDecorator = getDecoratorOrUndefinedByNames(
       [ApiOperation.name],
-      nodeArray
+      nodeArray,
+      factory
     );
     const apiOperationExpr: ts.ObjectLiteralExpression | undefined =
       apiOperationDecorator &&
@@ -248,10 +275,14 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
   }
 
   getStatusCodeIdentifier(factory: ts.NodeFactory, node: ts.MethodDeclaration) {
-    const decorators = node.decorators;
+    // Support both >= v4.8 and v4.7 and lower
+    const decorators = (ts as any).canHaveDecorators
+      ? (ts as any).getDecorators(node)
+      : node.decorators;
     const httpCodeDecorator = getDecoratorOrUndefinedByNames(
       ['HttpCode'],
-      decorators
+      decorators,
+      factory
     );
     if (httpCodeDecorator) {
       const argument = head(getDecoratorArguments(httpCodeDecorator));
@@ -259,7 +290,11 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
         return argument;
       }
     }
-    const postDecorator = getDecoratorOrUndefinedByNames(['Post'], decorators);
+    const postDecorator = getDecoratorOrUndefinedByNames(
+      ['Post'],
+      decorators,
+      factory
+    );
     if (postDecorator) {
       return factory.createIdentifier('201');
     }
diff --git a/lib/plugin/visitors/model-class.visitor.ts b/lib/plugin/visitors/model-class.visitor.ts
index 725e84be..c34fb6f7 100644
--- a/lib/plugin/visitors/model-class.visitor.ts
+++ b/lib/plugin/visitors/model-class.visitor.ts
@@ -1,5 +1,6 @@
 import { compact, flatten, head } from 'lodash';
 import * as ts from 'typescript';
+import { factory, PropertyAssignment } from 'typescript';
 import { ApiHideProperty } from '../../decorators';
 import { PluginOptions } from '../merge-options';
 import { METADATA_FACTORY_NAME } from '../plugin-constants';
@@ -23,10 +24,14 @@ import {
   replaceImportPath
 } from '../utils/plugin-utils';
 import { AbstractFileVisitor } from './abstract.visitor';
-import { PropertyAssignment } from 'typescript';
 
 type ClassMetadata = Record<string, ts.ObjectLiteralExpression>;
 
+const [tsVersionMajor, tsVersionMinor] = ts.versionMajorMinor
+  ?.split('.')
+  .map((x) => +x);
+const isInUpdatedAstContext = tsVersionMinor >= 8 || tsVersionMajor > 4;
+
 export class ModelClassVisitor extends AbstractFileVisitor {
   visit(
     sourceFile: ts.SourceFile,
@@ -35,16 +40,21 @@ export class ModelClassVisitor extends AbstractFileVisitor {
     options: PluginOptions
   ) {
     const typeChecker = program.getTypeChecker();
-    sourceFile = this.updateImports(sourceFile, ctx.factory);
+    sourceFile = this.updateImports(sourceFile, ctx.factory, program);
 
     const propertyNodeVisitorFactory =
       (metadata: ClassMetadata) =>
       (node: ts.Node): ts.Node => {
         if (ts.isPropertyDeclaration(node)) {
-          const decorators = node.decorators;
+          // Support both >= v4.8 and v4.7 and lower
+          const decorators = (ts as any).canHaveDecorators
+            ? (ts as any).getDecorators(node)
+            : node.decorators;
+
           const hidePropertyDecorator = getDecoratorOrUndefinedByNames(
             [ApiHideProperty.name],
-            decorators
+            decorators,
+            factory
           );
           if (hidePropertyDecorator) {
             return node;
@@ -106,27 +116,53 @@ export class ModelClassVisitor extends AbstractFileVisitor {
         )
       )
     );
-    const method = factory.createMethodDeclaration(
-      undefined,
-      [factory.createModifier(ts.SyntaxKind.StaticKeyword)],
-      undefined,
-      factory.createIdentifier(METADATA_FACTORY_NAME),
-      undefined,
-      undefined,
-      [],
-      undefined,
-      factory.createBlock([factory.createReturnStatement(returnValue)], true)
-    );
+    const method = isInUpdatedAstContext
+      ? (factory as any).createMethodDeclaration(
+          [factory.createModifier(ts.SyntaxKind.StaticKeyword)],
+          undefined,
+          factory.createIdentifier(METADATA_FACTORY_NAME),
+          undefined,
+          undefined,
+          [],
+          undefined,
+          factory.createBlock(
+            [factory.createReturnStatement(returnValue)],
+            true
+          )
+        )
+      : factory.createMethodDeclaration(
+          undefined,
+          [factory.createModifier(ts.SyntaxKind.StaticKeyword)],
+          undefined,
+          factory.createIdentifier(METADATA_FACTORY_NAME),
+          undefined,
+          undefined,
+          [],
+          undefined,
+          factory.createBlock(
+            [factory.createReturnStatement(returnValue)],
+            true
+          )
+        );
 
-    return factory.updateClassDeclaration(
-      node,
-      node.decorators,
-      node.modifiers,
-      node.name,
-      node.typeParameters,
-      node.heritageClauses,
-      [...node.members, method]
-    );
+    return isInUpdatedAstContext
+      ? (factory as any).updateClassDeclaration(
+          node,
+          node.modifiers,
+          node.name,
+          node.typeParameters,
+          node.heritageClauses,
+          [...node.members, method]
+        )
+      : factory.updateClassDeclaration(
+          node,
+          node.decorators,
+          node.modifiers as any,
+          node.name,
+          node.typeParameters,
+          node.heritageClauses,
+          [...node.members, method]
+        );
   }
 
   inspectPropertyDeclaration(
@@ -397,7 +433,10 @@ export class ModelClassVisitor extends AbstractFileVisitor {
     node: ts.PropertyDeclaration | ts.PropertySignature
   ): ts.PropertyAssignment[] {
     const assignments = [];
-    const decorators = node.decorators;
+    // Support both >= v4.8 and v4.7 and lower
+    const decorators = (ts as any).canHaveDecorators
+      ? (ts as any).getDecorators(node)
+      : node.decorators;
 
     this.addPropertyByValidationDecorator(
       factory,
@@ -539,7 +578,8 @@ export class ModelClassVisitor extends AbstractFileVisitor {
   ) {
     const decoratorRef: ts.Decorator = getDecoratorOrUndefinedByNames(
       [decoratorName],
-      decorators
+      decorators,
+      factory
     );
     if (!decoratorRef) {
       return;
diff --git a/lib/swagger-module.ts b/lib/swagger-module.ts
index 68e86376..5235d913 100644
--- a/lib/swagger-module.ts
+++ b/lib/swagger-module.ts
@@ -12,7 +12,7 @@ import { SwaggerScanner } from './swagger-scanner';
 import {
   buildSwaggerHTML,
   buildSwaggerInitJS,
-  getSwaggerAssetsAbsoluteFSPath
+  swaggerAssetsAbsoluteFSPath
 } from './swagger-ui';
 import { assignTwoLevelsDeep } from './utils/assign-two-levels-deep';
 import { getGlobalPrefix } from './utils/get-global-prefix';
@@ -44,7 +44,6 @@ export class SwaggerModule {
 
   private static serveStatic(finalPath: string, app: INestApplication) {
     const httpAdapter = app.getHttpAdapter();
-    const swaggerAssetsAbsoluteFSPath = getSwaggerAssetsAbsoluteFSPath();
 
     if (httpAdapter && httpAdapter.getType() === 'fastify') {
       (app as NestFastifyApplication).useStaticAssets({
diff --git a/lib/swagger-ui/swagger-ui.ts b/lib/swagger-ui/swagger-ui.ts
index 9a9b2435..f38a9bf7 100644
--- a/lib/swagger-ui/swagger-ui.ts
+++ b/lib/swagger-ui/swagger-ui.ts
@@ -1,3 +1,4 @@
+import * as swaggerUi from 'swagger-ui-dist';
 import { favIconHtml, htmlTemplateString, jsTemplateString } from './constants';
 import { OpenAPIObject, SwaggerCustomOptions } from '../interfaces';
 import { buildJSInitOptions } from './helpers';
@@ -20,21 +21,10 @@ export function buildSwaggerInitJS(
   return jsTemplateString.replace('<% swaggerOptions %>', jsInitOptions);
 }
 
-let swaggerAssetsAbsoluteFSPath: string | undefined;
-
 /**
- * Returns the absolute path to swagger-ui assets.
+ * Stores absolute path to swagger-ui assets
  */
-export function getSwaggerAssetsAbsoluteFSPath() {
-  if (!swaggerAssetsAbsoluteFSPath) {
-    // eslint-disable-next-line @typescript-eslint/no-var-requires
-    const swaggerUi = require('swagger-ui-dist');
-    swaggerAssetsAbsoluteFSPath = swaggerUi.getAbsoluteFSPath();
-  }
-
-  return swaggerAssetsAbsoluteFSPath;
-}
-
+export const swaggerAssetsAbsoluteFSPath = swaggerUi.getAbsoluteFSPath();
 /**
  * Used to build swagger-ui custom html
  */
diff --git a/package-lock.json b/package-lock.json
index a354aeaa..6aa8c5ea 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3544,7 +3544,7 @@
     "clone": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
-      "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+      "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
       "dev": true
     },
     "clone-response": {
@@ -3559,7 +3559,7 @@
     "co": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
-      "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
+      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
       "dev": true
     },
     "collect-v8-coverage": {
@@ -3893,13 +3893,13 @@
     "decamelize": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-      "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
       "dev": true
     },
     "decamelize-keys": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz",
-      "integrity": "sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==",
+      "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=",
       "dev": true,
       "requires": {
         "decamelize": "^1.1.0",
@@ -3909,7 +3909,7 @@
         "map-obj": {
           "version": "1.0.1",
           "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
-          "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==",
+          "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
           "dev": true
         }
       }
@@ -3934,7 +3934,7 @@
     "dedent": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
-      "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==",
+      "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
       "dev": true
     },
     "deep-extend": {
@@ -3958,7 +3958,7 @@
     "defaults": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
-      "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==",
+      "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
       "dev": true,
       "requires": {
         "clone": "^1.0.2"
@@ -4235,7 +4235,7 @@
         "levn": {
           "version": "0.3.0",
           "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
-          "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
+          "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
           "dev": true,
           "requires": {
             "prelude-ls": "~1.1.2",
@@ -4259,13 +4259,13 @@
         "prelude-ls": {
           "version": "1.1.2",
           "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
-          "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
+          "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
           "dev": true
         },
         "type-check": {
           "version": "0.3.2",
           "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
-          "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
+          "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
           "dev": true,
           "requires": {
             "prelude-ls": "~1.1.2"
@@ -4646,7 +4646,7 @@
     "exit": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
-      "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+      "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
       "dev": true
     },
     "expect": {
@@ -5159,7 +5159,7 @@
     "ftp": {
       "version": "0.3.10",
       "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz",
-      "integrity": "sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==",
+      "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=",
       "dev": true,
       "requires": {
         "readable-stream": "1.1.x",
@@ -5169,13 +5169,13 @@
         "isarray": {
           "version": "0.0.1",
           "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-          "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
           "dev": true
         },
         "readable-stream": {
           "version": "1.1.14",
           "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
-          "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
+          "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
           "dev": true,
           "requires": {
             "core-util-is": "~1.0.0",
@@ -5187,7 +5187,7 @@
         "string_decoder": {
           "version": "0.10.31",
           "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
-          "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
           "dev": true
         }
       }
@@ -5283,7 +5283,7 @@
         "jsonfile": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
-          "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+          "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
           "dev": true,
           "requires": {
             "graceful-fs": "^4.1.6"
@@ -5355,7 +5355,7 @@
     "global-dirs": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
-      "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==",
+      "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
       "dev": true,
       "requires": {
         "ini": "^1.3.4"
@@ -5979,7 +5979,7 @@
     "is-plain-obj": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
-      "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
+      "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
       "dev": true
     },
     "is-plain-object": {
@@ -6049,7 +6049,7 @@
     "is-text-path": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz",
-      "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==",
+      "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=",
       "dev": true,
       "requires": {
         "text-extensions": "^1.0.0"
@@ -6058,7 +6058,7 @@
     "is-typedarray": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-      "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
       "dev": true
     },
     "is-unicode-supported": {
@@ -7494,7 +7494,7 @@
     "json-stable-stringify-without-jsonify": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
-      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
       "dev": true
     },
     "json5": {
@@ -7516,7 +7516,7 @@
     "jsonparse": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
-      "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+      "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
       "dev": true
     },
     "keyv": {
@@ -7786,7 +7786,7 @@
     "lodash.memoize": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
-      "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
+      "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
       "dev": true
     },
     "lodash.merge": {
@@ -8185,7 +8185,7 @@
     "node-int64": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
-      "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
+      "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=",
       "dev": true
     },
     "node-releases": {
@@ -8416,7 +8416,7 @@
     "os-tmpdir": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-      "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
       "dev": true
     },
     "p-cancelable": {
@@ -8839,7 +8839,7 @@
     "q": {
       "version": "1.5.1",
       "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
-      "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==",
+      "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
       "dev": true
     },
     "qs": {
@@ -8902,7 +8902,7 @@
         "strip-json-comments": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
-          "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+          "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
           "dev": true
         }
       }
@@ -8996,7 +8996,7 @@
     "rechoir": {
       "version": "0.6.2",
       "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
-      "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
+      "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
       "dev": true,
       "requires": {
         "resolve": "^1.1.6"
@@ -9679,7 +9679,7 @@
     "sprintf-js": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
       "dev": true
     },
     "stack-utils": {
@@ -9926,7 +9926,7 @@
     "text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
-      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
       "dev": true
     },
     "thread-stream": {
@@ -9977,7 +9977,7 @@
     "to-fast-properties": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
-      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+      "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
       "dev": true
     },
     "to-regex-range": {
@@ -10145,9 +10145,9 @@
       }
     },
     "typescript": {
-      "version": "4.7.4",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
-      "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
+      "version": "4.8.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz",
+      "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==",
       "dev": true
     },
     "unbox-primitive": {
@@ -10323,7 +10323,7 @@
     "wcwidth": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
-      "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+      "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
       "dev": true,
       "requires": {
         "defaults": "^1.0.3"
@@ -10450,7 +10450,7 @@
     "xregexp": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz",
-      "integrity": "sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==",
+      "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=",
       "dev": true
     },
     "xtend": {
diff --git a/package.json b/package.json
index c4a5c735..1ff95428 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,7 @@
     "supertest": "6.2.4",
     "swagger-parser": "10.0.3",
     "ts-jest": "28.0.8",
-    "typescript": "4.7.4"
+    "typescript": "4.8.2"
   },
   "peerDependencies": {
     "@fastify/static": "^6.0.0",


Test patch:
diff --git a/test/plugin/fixtures/create-cat-alt.dto.ts b/test/plugin/fixtures/create-cat-alt.dto.ts
index 71c2b863..8161827f 100644
--- a/test/plugin/fixtures/create-cat-alt.dto.ts
+++ b/test/plugin/fixtures/create-cat-alt.dto.ts
@@ -45,9 +45,7 @@ export class CreateCatDto2 {
 }
 `;
 
-export const createCatDtoTextAltTranspiled = `import { createRequire as _createRequire } from "module";
-const __require = _createRequire(import.meta.url);
-const openapi = __require("@nestjs/swagger");
+export const createCatDtoTextAltTranspiled = `import * as openapi from "@nestjs/swagger";
 import * as package from 'class-validator';
 var Status;
 (function (Status) {
diff --git a/test/plugin/fixtures/create-cat-alt2.dto.ts b/test/plugin/fixtures/create-cat-alt2.dto.ts
index 8f502a4a..586085ed 100644
--- a/test/plugin/fixtures/create-cat-alt2.dto.ts
+++ b/test/plugin/fixtures/create-cat-alt2.dto.ts
@@ -84,9 +84,7 @@ export abstract class Audit {
 }
 `;
 
-export const createCatDtoTextAlt2Transpiled = `import { createRequire as _createRequire } from "module";
-const __require = _createRequire(import.meta.url);
-const openapi = __require("@nestjs/swagger");
+export const createCatDtoTextAlt2Transpiled = `import * as openapi from "@nestjs/swagger";
 import { CreateDateColumn, UpdateDateColumn, VersionColumn } from 'typeorm';
 export class Audit {
     static _OPENAPI_METADATA_FACTORY() {
diff --git a/test/plugin/fixtures/create-cat.dto.ts b/test/plugin/fixtures/create-cat.dto.ts
index 2dadd76e..60524665 100644
--- a/test/plugin/fixtures/create-cat.dto.ts
+++ b/test/plugin/fixtures/create-cat.dto.ts
@@ -61,9 +61,7 @@ export class CreateCatDto {
 }
 `;
 
-export const createCatDtoTextTranspiled = `import { createRequire as _createRequire } from "module";
-const __require = _createRequire(import.meta.url);
-const openapi = __require("@nestjs/swagger");
+export const createCatDtoTextTranspiled = `import * as openapi from "@nestjs/swagger";
 import { IsString, IsPositive, IsNegative, Length, Matches, IsIn } from 'class-validator';
 var Status;
 (function (Status) {
diff --git a/test/plugin/fixtures/nullable.dto.ts b/test/plugin/fixtures/nullable.dto.ts
index f570c6d2..c94ea481 100644
--- a/test/plugin/fixtures/nullable.dto.ts
+++ b/test/plugin/fixtures/nullable.dto.ts
@@ -11,9 +11,7 @@ export class NullableDto {
 }
 `;
 
-export const nullableDtoTextTranspiled = `import { createRequire as _createRequire } from "module";
-const __require = _createRequire(import.meta.url);
-const openapi = __require("@nestjs/swagger");
+export const nullableDtoTextTranspiled = `import * as openapi from "@nestjs/swagger";
 export class NullableDto {
     static _OPENAPI_METADATA_FACTORY() {
         return { stringValue: { required: true, type: () => String, nullable: true }, stringArr: { required: true, type: () => [String], nullable: true }, optionalString: { required: false, type: () => String }, undefinedString: { required: true, type: () => String } };
diff --git a/test/plugin/fixtures/string-literal.dto.ts b/test/plugin/fixtures/string-literal.dto.ts
index 4fc8ea08..9192e1a7 100644
--- a/test/plugin/fixtures/string-literal.dto.ts
+++ b/test/plugin/fixtures/string-literal.dto.ts
@@ -7,9 +7,7 @@ export class StringLiteralDto {
 }
 `;
 
-export const stringLiteralDtoTextTranspiled = `import { createRequire as _createRequire } from "module";
-const __require = _createRequire(import.meta.url);
-const openapi = __require("@nestjs/swagger");
+export const stringLiteralDtoTextTranspiled = `import * as openapi from "@nestjs/swagger";
 export class StringLiteralDto {
     static _OPENAPI_METADATA_FACTORY() {
         return { valueOne: { required: true, type: () => String }, valueTwo: { required: true, type: () => Object } };
diff --git a/test/plugin/model-class-visitor.spec.ts b/test/plugin/model-class-visitor.spec.ts
index 114d603c..3de7a218 100644
--- a/test/plugin/model-class-visitor.spec.ts
+++ b/test/plugin/model-class-visitor.spec.ts
@@ -33,7 +33,7 @@ import {
 describe('API model properties', () => {
   it('should add the metadata factory when no decorators exist, and generated propertyKey is title', () => {
     const options: ts.CompilerOptions = {
-      module: ts.ModuleKind.ES2020,
+      module: ts.ModuleKind.ES2022,
       target: ts.ScriptTarget.ES2020,
       newLine: ts.NewLineKind.LineFeed,
       noEmitHelpers: true,