Repository: nestjs/swagger
Pull request #866 (2020-07-24 14:38:21)

fix(plugin): update metadata correctly when using plugin in watch mode
## 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?
When using swagger plugin with tsc/ttsc in watch mode, removing property from DTO does not lead to its removal from metadata until tsc/ttsc is restarted.
This behavior can be reproduced in https://github.com/artemsmirnov/nestjs-swagger-bug-demo by starting it with
`node index.js` and then removing `foo` or `test` property from `src/example.dto.ts`.

Issue Number: N/A


## What is the new behavior?
Metadata is updated according to changes in source file.


## 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


Patch:
diff --git a/lib/plugin/visitors/model-class.visitor.ts b/lib/plugin/visitors/model-class.visitor.ts
index 8df70b92..dce7d548 100644
--- a/lib/plugin/visitors/model-class.visitor.ts
+++ b/lib/plugin/visitors/model-class.visitor.ts
@@ -15,7 +15,7 @@ import {
 } from '../utils/plugin-utils';
 import { AbstractFileVisitor } from './abstract.visitor';
 
-const metadataHostMap = new Map();
+type ClassMetadata = Record<string, ts.ObjectLiteralExpression>;
 
 export class ModelClassVisitor extends AbstractFileVisitor {
   visit(
@@ -27,11 +27,10 @@ export class ModelClassVisitor extends AbstractFileVisitor {
     const typeChecker = program.getTypeChecker();
     sourceFile = this.updateImports(sourceFile);
 
-    const visitNode = (node: ts.Node): ts.Node => {
-      if (ts.isClassDeclaration(node)) {
-        node = ts.visitEachChild(node, visitNode, ctx);
-        return this.addMetadataFactory(node as ts.ClassDeclaration);
-      } else if (ts.isPropertyDeclaration(node)) {
+    const propertyNodeVisitorFactory = (metadata: ClassMetadata) => (
+      node: ts.Node
+    ): ts.Node => {
+      if (ts.isPropertyDeclaration(node)) {
         const decorators = node.decorators;
         const hidePropertyDecorator = getDecoratorOrUndefinedByNames(
           [ApiHideProperty.name],
@@ -52,23 +51,32 @@ export class ModelClassVisitor extends AbstractFileVisitor {
             typeChecker,
             options,
             sourceFile.fileName,
-            sourceFile
+            sourceFile,
+            metadata
           );
         } catch (err) {
           return node;
         }
-        return node;
       }
-      return ts.visitEachChild(node, visitNode, ctx);
+      return node;
     };
-    return ts.visitNode(sourceFile, visitNode);
+
+    const visitClassNode = (node: ts.Node): ts.Node => {
+      if (ts.isClassDeclaration(node)) {
+        const metadata: ClassMetadata = {};
+        node = ts.visitEachChild(
+          node,
+          propertyNodeVisitorFactory(metadata),
+          ctx
+        );
+        return this.addMetadataFactory(node as ts.ClassDeclaration, metadata);
+      }
+      return ts.visitEachChild(node, visitClassNode, ctx);
+    };
+    return ts.visitNode(sourceFile, visitClassNode);
   }
 
-  addMetadataFactory(node: ts.ClassDeclaration) {
-    const classMetadata = this.getClassMetadata(node as ts.ClassDeclaration);
-    if (!classMetadata) {
-      return node;
-    }
+  addMetadataFactory(node: ts.ClassDeclaration, classMetadata: ClassMetadata) {
     const classMutableNode = ts.getMutableClone(node);
     const returnValue = ts.createObjectLiteral(
       Object.keys(classMetadata).map((key) =>
@@ -101,7 +109,8 @@ export class ModelClassVisitor extends AbstractFileVisitor {
     typeChecker: ts.TypeChecker,
     options: PluginOptions,
     hostFilename: string,
-    sourceFile: ts.SourceFile
+    sourceFile: ts.SourceFile,
+    metadata: ClassMetadata
   ) {
     const objectLiteralExpr = this.createDecoratorObjectLiteralExpr(
       compilerNode,
@@ -110,7 +119,12 @@ export class ModelClassVisitor extends AbstractFileVisitor {
       options,
       hostFilename
     );
-    this.addClassMetadata(compilerNode, objectLiteralExpr, sourceFile);
+    this.addClassMetadata(
+      compilerNode,
+      objectLiteralExpr,
+      sourceFile,
+      metadata
+    );
   }
 
   createDecoratorObjectLiteralExpr(
@@ -344,14 +358,14 @@ export class ModelClassVisitor extends AbstractFileVisitor {
   addClassMetadata(
     node: ts.PropertyDeclaration,
     objectLiteral: ts.ObjectLiteralExpression,
-    sourceFile: ts.SourceFile
+    sourceFile: ts.SourceFile,
+    metadata: ClassMetadata
   ) {
     const hostClass = node.parent;
     const className = hostClass.name && hostClass.name.getText();
     if (!className) {
       return;
     }
-    const existingMetadata = metadataHostMap.get(className) || {};
     const propertyName = node.name && node.name.getText(sourceFile);
     if (
       !propertyName ||
@@ -359,16 +373,6 @@ export class ModelClassVisitor extends AbstractFileVisitor {
     ) {
       return;
     }
-    metadataHostMap.set(className, {
-      ...existingMetadata,
-      [propertyName]: objectLiteral
-    });
-  }
-
-  getClassMetadata(node: ts.ClassDeclaration) {
-    if (!node.name) {
-      return;
-    }
-    return metadataHostMap.get(node.name.getText());
+    metadata[propertyName] = objectLiteral;
   }
 }
diff --git a/package-lock.json b/package-lock.json
index 500006d9..fdd88f61 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1808,9 +1808,9 @@
       }
     },
     "@octokit/core": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.1.1.tgz",
-      "integrity": "sha512-cQ2HGrtyNJ1IBxpTP1U5m/FkMAJvgw7d2j1q3c9P0XUuYilEgF6e4naTpsgm4iVcQeOnccZlw7XHRIUBy0ymcg==",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.1.0.tgz",
+      "integrity": "sha512-yPyQSmxIXLieEIRikk2w8AEtWkFdfG/LXcw1KvEtK3iP0ENZLW/WYQmdzOKqfSaLhooz4CJ9D+WY79C8ZliACw==",
       "dev": true,
       "requires": {
         "@octokit/auth-token": "^2.4.0",
@@ -1818,46 +1818,46 @@
         "@octokit/request": "^5.4.0",
         "@octokit/types": "^5.0.0",
         "before-after-hook": "^2.1.0",
-        "universal-user-agent": "^6.0.0"
+        "universal-user-agent": "^5.0.0"
       }
     },
     "@octokit/endpoint": {
-      "version": "6.0.5",
-      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.5.tgz",
-      "integrity": "sha512-70K5u6zd45ItOny6aHQAsea8HHQjlQq85yqOMe+Aj8dkhN2qSJ9T+Q3YjUjEYfPRBcuUWNgMn62DQnP/4LAIiQ==",
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.3.tgz",
+      "integrity": "sha512-Y900+r0gIz+cWp6ytnkibbD95ucEzDSKzlEnaWS52hbCDNcCJYO5mRmWW7HRAnDc7am+N/5Lnd8MppSaTYx1Yg==",
       "dev": true,
       "requires": {
         "@octokit/types": "^5.0.0",
-        "is-plain-object": "^4.0.0",
-        "universal-user-agent": "^6.0.0"
+        "is-plain-object": "^3.0.0",
+        "universal-user-agent": "^5.0.0"
       },
       "dependencies": {
         "is-plain-object": {
-          "version": "4.1.1",
-          "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-4.1.1.tgz",
-          "integrity": "sha512-5Aw8LLVsDlZsETVMhoMXzqsXwQqr/0vlnBYzIXJbYo2F4yYlhLHs+Ez7Bod7IIQKWkJbJfxrWD7pA1Dw1TKrwA==",
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz",
+          "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==",
           "dev": true
         }
       }
     },
     "@octokit/graphql": {
-      "version": "4.5.2",
-      "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.2.tgz",
-      "integrity": "sha512-SpB/JGdB7bxRj8qowwfAXjMpICUYSJqRDj26MKJAryRQBqp/ZzARsaO2LEFWzDaps0FLQoPYVGppS0HQXkBhdg==",
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.1.tgz",
+      "integrity": "sha512-qgMsROG9K2KxDs12CO3bySJaYoUu2aic90qpFrv7A8sEBzZ7UFGvdgPKiLw5gOPYEYbS0Xf8Tvf84tJutHPulQ==",
       "dev": true,
       "requires": {
         "@octokit/request": "^5.3.0",
         "@octokit/types": "^5.0.0",
-        "universal-user-agent": "^6.0.0"
+        "universal-user-agent": "^5.0.0"
       }
     },
     "@octokit/plugin-paginate-rest": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.3.0.tgz",
-      "integrity": "sha512-Ye2ZJreP0ZlqJQz8fz+hXvrEAEYK4ay7br1eDpWzr6j76VXs/gKqxFcH8qRzkB3fo/2xh4Vy9VtGii4ZDc9qlA==",
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.2.3.tgz",
+      "integrity": "sha512-eKTs91wXnJH8Yicwa30jz6DF50kAh7vkcqCQ9D7/tvBAP5KKkg6I2nNof8Mp/65G0Arjsb4QcOJcIEQY+rK1Rg==",
       "dev": true,
       "requires": {
-        "@octokit/types": "^5.2.0"
+        "@octokit/types": "^5.0.0"
       }
     },
     "@octokit/plugin-request-log": {
@@ -1867,35 +1867,35 @@
       "dev": true
     },
     "@octokit/plugin-rest-endpoint-methods": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.1.2.tgz",
-      "integrity": "sha512-PTI7wpbGEZ2IR87TVh+TNWaLcgX/RsZQalFbQCq8XxYUrQ36RHyERrHSNXFy5gkWpspUAOYRSV707JJv6BhqJA==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.0.0.tgz",
+      "integrity": "sha512-emS6gysz4E9BNi9IrCl7Pm4kR+Az3MmVB0/DoDCmF4U48NbYG3weKyDlgkrz6Jbl4Mu4nDx8YWZwC4HjoTdcCA==",
       "dev": true,
       "requires": {
-        "@octokit/types": "^5.1.1",
+        "@octokit/types": "^5.0.0",
         "deprecation": "^2.3.1"
       }
     },
     "@octokit/request": {
-      "version": "5.4.7",
-      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.7.tgz",
-      "integrity": "sha512-FN22xUDP0i0uF38YMbOfx6TotpcENP5W8yJM1e/LieGXn6IoRxDMnBf7tx5RKSW4xuUZ/1P04NFZy5iY3Rax1A==",
+      "version": "5.4.5",
+      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.5.tgz",
+      "integrity": "sha512-atAs5GAGbZedvJXXdjtKljin+e2SltEs48B3naJjqWupYl2IUBbB/CJisyjbNHcKpHzb3E+OYEZ46G8eakXgQg==",
       "dev": true,
       "requires": {
         "@octokit/endpoint": "^6.0.1",
         "@octokit/request-error": "^2.0.0",
         "@octokit/types": "^5.0.0",
         "deprecation": "^2.0.0",
-        "is-plain-object": "^4.0.0",
+        "is-plain-object": "^3.0.0",
         "node-fetch": "^2.3.0",
         "once": "^1.4.0",
-        "universal-user-agent": "^6.0.0"
+        "universal-user-agent": "^5.0.0"
       },
       "dependencies": {
         "is-plain-object": {
-          "version": "4.1.1",
-          "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-4.1.1.tgz",
-          "integrity": "sha512-5Aw8LLVsDlZsETVMhoMXzqsXwQqr/0vlnBYzIXJbYo2F4yYlhLHs+Ez7Bod7IIQKWkJbJfxrWD7pA1Dw1TKrwA==",
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz",
+          "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==",
           "dev": true
         }
       }
@@ -1912,30 +1912,30 @@
       }
     },
     "@octokit/rest": {
-      "version": "18.0.3",
-      "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.3.tgz",
-      "integrity": "sha512-GubgemnLvUJlkhouTM2BtX+g/voYT/Mqh0SASGwTnLvSkW1irjt14N911/ABb6m1Hru0TwScOgFgMFggp3igfQ==",
+      "version": "18.0.0",
+      "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.0.tgz",
+      "integrity": "sha512-4G/a42lry9NFGuuECnua1R1eoKkdBYJap97jYbWDNYBOUboWcM75GJ1VIcfvwDV/pW0lMPs7CEmhHoVrSV5shg==",
       "dev": true,
       "requires": {
         "@octokit/core": "^3.0.0",
         "@octokit/plugin-paginate-rest": "^2.2.0",
         "@octokit/plugin-request-log": "^1.0.0",
-        "@octokit/plugin-rest-endpoint-methods": "4.1.2"
+        "@octokit/plugin-rest-endpoint-methods": "4.0.0"
       }
     },
     "@octokit/types": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.2.0.tgz",
-      "integrity": "sha512-XjOk9y4m8xTLIKPe1NFxNWBdzA2/z3PFFA/bwf4EoH6oS8hM0Y46mEa4Cb+KCyj/tFDznJFahzQ0Aj3o1FYq4A==",
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.0.1.tgz",
+      "integrity": "sha512-GorvORVwp244fGKEt3cgt/P+M0MGy4xEDbckw+K5ojEezxyMDgCaYPKVct+/eWQfZXOT7uq0xRpmrl/+hliabA==",
       "dev": true,
       "requires": {
         "@types/node": ">= 8"
       }
     },
     "@sindresorhus/is": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.1.0.tgz",
-      "integrity": "sha512-n4J+zu52VdY43kdi/XdI9DzuMr1Mur8zFL5ZRG2opCans9aiFwkPxHYFEb5Xgy7n1Z4K6WfI4FpqUqsh3E8BPQ==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.0.0.tgz",
+      "integrity": "sha512-kqA5I6Yun7PBHk8WN9BBP1c7FfN2SrD05GuVSEYPqDb4nerv7HqYfgBfMIKmT/EuejURkJKLZuLyGKGs6WEG9w==",
       "dev": true
     },
     "@sinonjs/commons": {
@@ -3299,9 +3299,9 @@
       }
     },
     "cli-spinners": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.4.0.tgz",
-      "integrity": "sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA==",
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.3.0.tgz",
+      "integrity": "sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w==",
       "dev": true
     },
     "cli-truncate": {
@@ -5931,9 +5931,9 @@
       "dev": true
     },
     "got": {
-      "version": "11.5.1",
-      "resolved": "https://registry.npmjs.org/got/-/got-11.5.1.tgz",
-      "integrity": "sha512-reQEZcEBMTGnujmQ+Wm97mJs/OK6INtO6HmLI+xt3+9CvnRwWjXutUvb2mqr+Ao4Lu05Rx6+udx9sOQAmExMxA==",
+      "version": "11.5.0",
+      "resolved": "https://registry.npmjs.org/got/-/got-11.5.0.tgz",
+      "integrity": "sha512-vOZEcEaK0b6x11uniY0HcblZObKPRO75Jvz53VKuqGSaKCM/zEt0sj2LGYVdqDYJzO3wYdG+FPQQ1hsgoXy7vQ==",
       "dev": true,
       "requires": {
         "@sindresorhus/is": "^3.0.0",
@@ -5943,7 +5943,7 @@
         "cacheable-lookup": "^5.0.3",
         "cacheable-request": "^7.0.1",
         "decompress-response": "^6.0.0",
-        "http2-wrapper": "^1.0.0-beta.5.0",
+        "http2-wrapper": "^1.0.0-beta.4.8",
         "lowercase-keys": "^2.0.0",
         "p-cancelable": "^2.0.0",
         "responselike": "^2.0.0"
@@ -6121,9 +6121,9 @@
       }
     },
     "http2-wrapper": {
-      "version": "1.0.0-beta.5.2",
-      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz",
-      "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==",
+      "version": "1.0.0-beta.4.8",
+      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.4.8.tgz",
+      "integrity": "sha512-3XyRMia2QMy59nbxvXNX+57opqcy7/GlkJEaB7uRNw+TZw6mOiK3936IO83mVy7mzCttE66Cy9QEZ/xPXYmoCQ==",
       "dev": true,
       "requires": {
         "quick-lru": "^5.1.1",
@@ -6316,9 +6316,9 @@
       "dev": true
     },
     "inquirer": {
-      "version": "7.3.3",
-      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz",
-      "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==",
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.0.tgz",
+      "integrity": "sha512-K+LZp6L/6eE5swqIcVXrxl21aGDU4S50gKH0/d96OMQnSBCyGyZl/oZhbkVmdp5sBoINHd4xZvFSARh2dk6DWA==",
       "dev": true,
       "requires": {
         "ansi-escapes": "^4.2.1",
@@ -6327,7 +6327,7 @@
         "cli-width": "^3.0.0",
         "external-editor": "^3.0.3",
         "figures": "^3.0.0",
-        "lodash": "^4.17.19",
+        "lodash": "^4.17.15",
         "mute-stream": "0.0.8",
         "run-async": "^2.4.0",
         "rxjs": "^6.6.0",
@@ -9671,9 +9671,9 @@
       }
     },
     "macos-release": {
-      "version": "2.4.1",
-      "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.4.1.tgz",
-      "integrity": "sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg==",
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.4.0.tgz",
+      "integrity": "sha512-ko6deozZYiAkqa/0gmcsz+p4jSy3gY7/ZsCEokPaYd8k+6/aXGkiTgr61+Owup7Sf+xjqW8u2ElhoM9SEcEfuA==",
       "dev": true
     },
     "make-dir": {
@@ -10425,9 +10425,9 @@
       }
     },
     "ora": {
-      "version": "4.0.5",
-      "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.5.tgz",
-      "integrity": "sha512-jCDgm9DqvRcNIAEv2wZPrh7E5PcQiDUnbnWbAfu4NGAE2ZNqPFbDixmWldy1YG2QfLeQhuiu6/h5VRrk6cG50w==",
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.4.tgz",
+      "integrity": "sha512-77iGeVU1cIdRhgFzCK8aw1fbtT1B/iZAvWjS+l/o1x0RShMgxHUZaD2yDpWsNCPwXg9z1ZA78Kbdvr8kBmG/Ww==",
       "dev": true,
       "requires": {
         "chalk": "^3.0.0",
@@ -11186,9 +11186,9 @@
       "dev": true
     },
     "registry-auth-token": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.0.tgz",
-      "integrity": "sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w==",
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz",
+      "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==",
       "dev": true,
       "requires": {
         "rc": "^1.2.8"
@@ -11204,13 +11204,13 @@
       }
     },
     "release-it": {
-      "version": "13.6.6",
-      "resolved": "https://registry.npmjs.org/release-it/-/release-it-13.6.6.tgz",
-      "integrity": "sha512-tR0RkcMxc0lhtnPGWE2tBcjAG7Z+NXsQZTGiNr6M/XM5tOL4cSkWPrBo9+vEtVS2S7XlBiLtCZ85jCzcovBnBw==",
+      "version": "13.6.5",
+      "resolved": "https://registry.npmjs.org/release-it/-/release-it-13.6.5.tgz",
+      "integrity": "sha512-m4JcHmwcOTnOfBv2t1jgZ1ddqk9O71/9YZ7T5vClldez8EL+lCtMpBirNJZJwFj39xRAozLya2o0CrI0diyjEg==",
       "dev": true,
       "requires": {
         "@iarna/toml": "2.2.5",
-        "@octokit/rest": "18.0.3",
+        "@octokit/rest": "18.0.0",
         "async-retry": "1.3.1",
         "chalk": "4.1.0",
         "cosmiconfig": "6.0.0",
@@ -11222,15 +11222,15 @@
         "form-data": "3.0.0",
         "git-url-parse": "11.1.2",
         "globby": "11.0.1",
-        "got": "11.5.1",
+        "got": "11.5.0",
         "import-cwd": "3.0.0",
-        "inquirer": "7.3.3",
+        "inquirer": "7.3.0",
         "is-ci": "2.0.0",
         "lodash": "4.17.19",
         "mime-types": "2.1.27",
-        "ora": "4.0.5",
+        "ora": "4.0.4",
         "os-name": "3.1.0",
-        "parse-json": "5.0.1",
+        "parse-json": "5.0.0",
         "semver": "7.3.2",
         "shelljs": "0.8.4",
         "supports-color": "7.1.0",
@@ -11346,6 +11346,12 @@
           "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
           "dev": true
         },
+        "lodash": {
+          "version": "4.17.19",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
+          "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
+          "dev": true
+        },
         "mime-db": {
           "version": "1.44.0",
           "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
@@ -11376,18 +11382,6 @@
             "path-key": "^3.0.0"
           }
         },
-        "parse-json": {
-          "version": "5.0.1",
-          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.1.tgz",
-          "integrity": "sha512-ztoZ4/DYeXQq4E21v169sC8qWINGpcosGv9XhTDvg9/hWvx/zrFkc9BiWxR58OJLHGk28j5BL0SDLeV2WmFZlQ==",
-          "dev": true,
-          "requires": {
-            "@babel/code-frame": "^7.0.0",
-            "error-ex": "^1.3.1",
-            "json-parse-better-errors": "^1.0.1",
-            "lines-and-columns": "^1.1.6"
-          }
-        },
         "path-key": {
           "version": "3.1.1",
           "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -13113,10 +13107,13 @@
       }
     },
     "universal-user-agent": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
-      "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==",
-      "dev": true
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-5.0.0.tgz",
+      "integrity": "sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q==",
+      "dev": true,
+      "requires": {
+        "os-name": "^3.1.0"
+      }
     },
     "universalify": {
       "version": "0.1.2",
diff --git a/package.json b/package.json
index 96213768..75519248 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,7 @@
     "lint-staged": "10.2.11",
     "prettier": "2.0.5",
     "reflect-metadata": "0.1.13",
-    "release-it": "13.6.6",
+    "release-it": "13.6.5",
     "swagger-parser": "10.0.1",
     "swagger-ui-express": "4.1.4",
     "ts-jest": "26.1.3",


Test patch:
diff --git a/test/plugin/fixtures/changed-class.dto.ts b/test/plugin/fixtures/changed-class.dto.ts
new file mode 100644
index 00000000..ea1f3865
--- /dev/null
+++ b/test/plugin/fixtures/changed-class.dto.ts
@@ -0,0 +1,27 @@
+export const originalCatDtoText = `
+export class ChangedCatDto {
+  name: string;
+  status: string;
+}
+`;
+
+export const changedCatDtoText = `
+export class ChangedCatDto {
+  name: string;
+}
+`;
+
+export const changedCatDtoTextTranspiled = `\"use strict\";
+Object.defineProperty(exports, \"__esModule\", { value: true });
+exports.ChangedCatDto = void 0;
+var openapi = require(\"@nestjs/swagger\");
+var ChangedCatDto = /** @class */ (function () {
+    function ChangedCatDto() {
+    }
+    ChangedCatDto._OPENAPI_METADATA_FACTORY = function () {
+        return { name: { required: true, type: function () { return String; } } };
+    };
+    return ChangedCatDto;
+}());
+exports.ChangedCatDto = ChangedCatDto;
+`;
diff --git a/test/plugin/model-class-visitor.spec.ts b/test/plugin/model-class-visitor.spec.ts
index 08ad2994..8545ea29 100644
--- a/test/plugin/model-class-visitor.spec.ts
+++ b/test/plugin/model-class-visitor.spec.ts
@@ -16,6 +16,11 @@ import {
   es5CreateCatDtoText,
   es5CreateCatDtoTextTranspiled
 } from './fixtures/es5-class.dto';
+import {
+  changedCatDtoText,
+  changedCatDtoTextTranspiled,
+  originalCatDtoText
+} from "./fixtures/changed-class.dto";
 
 describe('API model properties', () => {
   it('should add the metadata factory when no decorators exist', () => {
@@ -101,4 +106,34 @@ describe('API model properties', () => {
     });
     expect(result.outputText).toEqual(es5CreateCatDtoTextTranspiled);
   });
+
+  it('should remove properties from metadata when properties removed from dto', () => {
+    const options: ts.CompilerOptions = {
+      module: ts.ModuleKind.CommonJS,
+      target: ts.ScriptTarget.ES5,
+      newLine: ts.NewLineKind.LineFeed,
+      noEmitHelpers: true,
+      strict: true
+    };
+    const filename = 'changed-class.dto.ts';
+    const fakeProgram = ts.createProgram([filename], options);
+
+    ts.transpileModule(originalCatDtoText, {
+      compilerOptions: options,
+      fileName: filename,
+      transformers: {
+        before: [before({ classValidatorShim: true }, fakeProgram)]
+      }
+    });
+
+    const changedResult = ts.transpileModule(changedCatDtoText, {
+      compilerOptions: options,
+      fileName: filename,
+      transformers: {
+        before: [before({ classValidatorShim: true }, fakeProgram)]
+      }
+    });
+
+    expect(changedResult.outputText).toEqual(changedCatDtoTextTranspiled);
+  });
 });