@echoes-of-order/eslint-config 1.121.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/CHANGELOG.md +1093 -0
  2. package/configs/.gitkeep +1 -0
  3. package/configs/admin.js +203 -0
  4. package/configs/api-client.js +46 -0
  5. package/configs/backend.js +895 -0
  6. package/configs/domains.js +123 -0
  7. package/configs/frontend.js +30 -0
  8. package/configs/image-server.js +26 -0
  9. package/configs/ionos-proxy.js +372 -0
  10. package/configs/nestjs.js +156 -0
  11. package/configs/node.js +92 -0
  12. package/configs/react.js +111 -0
  13. package/configs/wiki.js +42 -0
  14. package/index.js +39 -0
  15. package/package.json +85 -0
  16. package/rules/.gitkeep +1 -0
  17. package/rules/__tests__/analyze-relation-usage.test.js.disabled +300 -0
  18. package/rules/__tests__/complexity.test.js.disabled +300 -0
  19. package/rules/__tests__/enforce-dto-factory-in-services.integration.test.js +226 -0
  20. package/rules/__tests__/enforce-dto-factory-in-services.test.js +177 -0
  21. package/rules/__tests__/enforce-entity-dto-create-no-id.integration.test.js +18 -0
  22. package/rules/__tests__/enforce-function-argument-count.test.js.disabled +300 -0
  23. package/rules/__tests__/enforce-repository-token-handling.test.js +58 -0
  24. package/rules/__tests__/english-only-code-strings.test.js.disabled +300 -0
  25. package/rules/__tests__/eslint-rules.integration.test.ts +350 -0
  26. package/rules/__tests__/integration-test-controller-response-dto.js +261 -0
  27. package/rules/__tests__/integration-test-dto-factory-in-services.js +260 -0
  28. package/rules/__tests__/integration-test-no-entity-type-casting.js +161 -0
  29. package/rules/__tests__/integration-test-typeorm-naming-conventions.js +501 -0
  30. package/rules/__tests__/test-config.js +33 -0
  31. package/rules/admin-controller-security.js +180 -0
  32. package/rules/analyze-relation-usage.js +687 -0
  33. package/rules/api-response-dto.js +174 -0
  34. package/rules/auth-guard-required.js +142 -0
  35. package/rules/backend-specific.js +36 -0
  36. package/rules/best-practices.js +421 -0
  37. package/rules/complexity.js +20 -0
  38. package/rules/controller-architecture.js +340 -0
  39. package/rules/controller-naming-conventions.js +190 -0
  40. package/rules/controller-readonly-restriction.js +148 -0
  41. package/rules/controller-swagger-complete.js +312 -0
  42. package/rules/controller-swagger-docs.js +119 -0
  43. package/rules/controller-swagger-english.js +320 -0
  44. package/rules/coordinate-naming.js +132 -0
  45. package/rules/custom-mui-button.js +135 -0
  46. package/rules/dead-code-detection-backend.js +50 -0
  47. package/rules/dead-code-detection-frontend.js +48 -0
  48. package/rules/dead-code-detection.js +71 -0
  49. package/rules/debug-controller-response-dto.js +79 -0
  50. package/rules/deprecate.js +8 -0
  51. package/rules/dto-annotation-property-consistency.js +111 -0
  52. package/rules/dto-entity-mapping-completeness.js +688 -0
  53. package/rules/dto-entity-swagger-separation.js +265 -0
  54. package/rules/dto-entity-type-consistency.js +352 -0
  55. package/rules/dto-entity-type-matching.js +519 -0
  56. package/rules/dto-naming-convention.js +98 -0
  57. package/rules/dto-visibility-modifiers.js +159 -0
  58. package/rules/enforce-api-versioning.js +122 -0
  59. package/rules/enforce-app-module-registration.js +179 -0
  60. package/rules/enforce-basecontroller.js +152 -0
  61. package/rules/enforce-body-request-dto.js +141 -0
  62. package/rules/enforce-controller-response-dto.js +349 -0
  63. package/rules/enforce-custom-error-classes.js +242 -0
  64. package/rules/enforce-database-transaction-safety.js +179 -0
  65. package/rules/enforce-dto-constructor.js +95 -0
  66. package/rules/enforce-dto-create-parameter-types.js +170 -0
  67. package/rules/enforce-dto-create-pattern.js +274 -0
  68. package/rules/enforce-dto-entity-creation.js +164 -0
  69. package/rules/enforce-dto-factory-in-services.js +188 -0
  70. package/rules/enforce-dto-from-entity-method.js +47 -0
  71. package/rules/enforce-dto-from-entity.js +314 -0
  72. package/rules/enforce-dto-naming-conventions.js +212 -0
  73. package/rules/enforce-dto-naming.js +176 -0
  74. package/rules/enforce-dto-usage-simple.js +114 -0
  75. package/rules/enforce-dto-usage.js +407 -0
  76. package/rules/enforce-eager-translation-loading.js +178 -0
  77. package/rules/enforce-entity-creation-pattern.js +137 -0
  78. package/rules/enforce-entity-dto-convert-method.js +157 -0
  79. package/rules/enforce-entity-dto-create-no-id.js +117 -0
  80. package/rules/enforce-entity-dto-extends-base.js +141 -0
  81. package/rules/enforce-entity-dto-from-request-dto-structure.js +113 -0
  82. package/rules/enforce-entity-dto-fromentity-complex.js +69 -0
  83. package/rules/enforce-entity-dto-fromentity-simple.js +69 -0
  84. package/rules/enforce-entity-dto-fromrequestdto-structure.js +262 -0
  85. package/rules/enforce-entity-dto-methods-restriction.js +159 -0
  86. package/rules/enforce-entity-dto-no-request-dto.js +102 -0
  87. package/rules/enforce-entity-dto-optional-auto-fields.js +101 -0
  88. package/rules/enforce-entity-dto-required-methods.js +248 -0
  89. package/rules/enforce-entity-factory-pattern.js +180 -0
  90. package/rules/enforce-entity-instantiation-in-toentity.js +125 -0
  91. package/rules/enforce-enum-for-playable-entities.js +95 -0
  92. package/rules/enforce-error-handling.js +257 -0
  93. package/rules/enforce-explicit-dto-types.js +118 -0
  94. package/rules/enforce-from-request-dto-usage.js +62 -0
  95. package/rules/enforce-generic-entity-dto.js +71 -0
  96. package/rules/enforce-inject-decorator.js +133 -0
  97. package/rules/enforce-lazy-type-loading.js +170 -0
  98. package/rules/enforce-module-existence.js +157 -0
  99. package/rules/enforce-nonentity-dto-create.js +107 -0
  100. package/rules/enforce-playable-entity-naming.js +108 -0
  101. package/rules/enforce-repository-token-handling.js +92 -0
  102. package/rules/enforce-request-dto-no-entity-dto.js +201 -0
  103. package/rules/enforce-request-dto-required-fields.js +217 -0
  104. package/rules/enforce-result-pattern.js +45 -0
  105. package/rules/enforce-service-relation-loading.js +116 -0
  106. package/rules/enforce-test-coverage.js +96 -0
  107. package/rules/enforce-toentity-conditional-assignment.js +132 -0
  108. package/rules/enforce-translations-required.js +203 -0
  109. package/rules/enforce-typeorm-naming-conventions.js +366 -0
  110. package/rules/enforce-vite-health-metrics.js +240 -0
  111. package/rules/entity-required-properties.js +321 -0
  112. package/rules/entity-to-dto-test.js +73 -0
  113. package/rules/enum-database-validation.js +149 -0
  114. package/rules/errors.js +190 -0
  115. package/rules/es6.js +204 -0
  116. package/rules/eslint-plugin-no-comments.js +44 -0
  117. package/rules/filename-class-name-match.js +62 -0
  118. package/rules/forbid-fromentity-outside-entity-folder.js +237 -0
  119. package/rules/function-params-newline.js +111 -0
  120. package/rules/imports.js +264 -0
  121. package/rules/jest.js +13 -0
  122. package/rules/jsx.js +16 -0
  123. package/rules/max-classes-per-file.js +49 -0
  124. package/rules/multiline-formatting.js +146 -0
  125. package/rules/no-blank-lines-between-decorators-and-properties.js +95 -0
  126. package/rules/no-comments.js +62 -0
  127. package/rules/no-dto-constructors.js +126 -0
  128. package/rules/no-dto-default-values.js +220 -0
  129. package/rules/no-dto-duplicates.js +127 -0
  130. package/rules/no-dto-in-entity.js +99 -0
  131. package/rules/no-dynamic-import-in-types.js +71 -0
  132. package/rules/no-dynamic-imports-in-controllers.js +95 -0
  133. package/rules/no-entity-imports-in-controllers.js +101 -0
  134. package/rules/no-entity-in-swagger-docs.js +139 -0
  135. package/rules/no-entity-type-casting.js +104 -0
  136. package/rules/no-fetch.js +77 -0
  137. package/rules/no-import-meta-env.js +151 -0
  138. package/rules/no-inline-styles.js +5 -0
  139. package/rules/no-magic-values.js +85 -0
  140. package/rules/no-partial-type.js +168 -0
  141. package/rules/no-relative-imports.js +31 -0
  142. package/rules/no-tsyringe.js +181 -0
  143. package/rules/no-type-assertion.js +175 -0
  144. package/rules/no-undefined-entity-properties.js +121 -0
  145. package/rules/node.js +44 -0
  146. package/rules/perfectionist.js +50 -0
  147. package/rules/performance-minimal.js +155 -0
  148. package/rules/performance.js +44 -0
  149. package/rules/pino-logger-format.js +200 -0
  150. package/rules/prefer-dto-classes.js +112 -0
  151. package/rules/prefer-dto-create-method.js +225 -0
  152. package/rules/promises.js +17 -0
  153. package/rules/react-hooks.js +15 -0
  154. package/rules/react.js +28 -0
  155. package/rules/regexp.js +70 -0
  156. package/rules/require-dto-response.js +81 -0
  157. package/rules/require-valid-relations.js +388 -0
  158. package/rules/result-pattern.js +162 -0
  159. package/rules/security.js +37 -0
  160. package/rules/service-architecture.js +148 -0
  161. package/rules/sonarjs.js +26 -0
  162. package/rules/strict.js +7 -0
  163. package/rules/style.js +611 -0
  164. package/rules/stylistic.js +93 -0
  165. package/rules/typeorm-column-type-validation.js +224 -0
  166. package/rules/typescript-advanced.js +113 -0
  167. package/rules/typescript-core.js +111 -0
  168. package/rules/typescript.js +146 -0
  169. package/rules/unicorn.js +168 -0
  170. package/rules/variables.js +51 -0
  171. package/rules/websocket-architecture.js +115 -0
@@ -0,0 +1,312 @@
1
+ /**
2
+ * @fileoverview Ensure all controller methods have complete Swagger documentation in English
3
+ */
4
+
5
+ "use strict";
6
+
7
+ export default {
8
+ meta: {
9
+ type: "problem",
10
+ docs: {
11
+ description: "enforce complete English Swagger documentation for all controller methods",
12
+ category: "Best Practices",
13
+ recommended: true,
14
+ },
15
+ fixable: null,
16
+ schema: [],
17
+ messages: {
18
+ missingApiOperation: "Controller method '{{methodName}}' is missing @ApiOperation decorator",
19
+ missingApiResponse: "Controller method '{{methodName}}' is missing @ApiResponse with status: {{status}}",
20
+ emptyApiOperation: "Controller method '{{methodName}}' has empty @ApiOperation summary or description",
21
+ germanDocumentation: "Swagger documentation must be in English. Found German text: '{{text}}'",
22
+ missingSwaggerTag: "Controller class is missing @ApiTags decorator",
23
+ missingApiBearerAuth: "Controller class is missing @ApiBearerAuth decorator",
24
+ },
25
+ },
26
+
27
+ create(context) {
28
+ // Common German words that shouldn't appear in API documentation
29
+ const germanWords = [
30
+ // Eindeutig deutsche Artikel & Pronomen
31
+ "der", "die", "das", "ein", "eine", "einen", "einem", "einer", "eines",
32
+ "alle", "aller", "dieser", "diese", "dieses", "jeder", "jede", "jedes",
33
+
34
+ // Eindeutig deutsche Verben
35
+ "ist", "sind", "wird", "werden", "hat", "haben", "kann", "können", "soll", "sollen",
36
+ "gibt", "geben", "ruft", "abrufen", "holt", "holen", "erstellt", "erstellen",
37
+ "aktualisiert", "aktualisieren", "löscht", "löschen", "zurück", "zurückgeben",
38
+ "erfolgreich", "fehlgeschlagen", "abgerufen", "gelöscht", "erstellt",
39
+
40
+ // Eindeutig deutsche Nomen (keine englischen Homonyme!)
41
+ "daten", "fehler", "serverfehler", "liste", "gesundheit", "koordinate", "koordinaten",
42
+ "karten", "karte", "benutzer", "nutzer", "antwort", "anfrage", "informationen",
43
+ "objekt", "objekte", "wert", "werte", "eigenschaft", "eigenschaften",
44
+
45
+ // Deutsche Adjektive/Adverbien
46
+ "neue", "neues", "neuer", "neuen", "bestimmten", "bestimmte", "bestimmter",
47
+ "ungültige", "ungültiger", "ungültiges", "gültige", "gültiger", "gültiges",
48
+ "vollständige", "vollständiger", "verfügbare", "verfügbarer",
49
+
50
+ // Deutsche Präpositionen & Konjunktionen
51
+ "mit", "von", "für", "beim", "nach", "oder", "und", "aber", "wenn", "dass",
52
+ "durch", "über", "unter", "zwischen", "ohne", "während", "seit",
53
+
54
+ // Deutsche Status-/Antwort-Wörter (eindeutig deutsch)
55
+ "nicht", "gefunden", "vorhanden", "verfügbar", "möglich", "unmöglich",
56
+ "korrekt", "inkorrekt",
57
+
58
+ // Deutsche Fragewörter
59
+ "wo", "wie", "was", "wann", "warum", "welche", "welcher", "welches",
60
+
61
+ // Deutsche Zahlwörter
62
+ "eins", "zwei", "drei", "vier", "fünf", "sechs", "sieben", "acht", "neun", "zehn"
63
+ ];
64
+
65
+ function isControllerClass(node) {
66
+ if (node.type !== "ClassDeclaration") return false;
67
+
68
+ return node.decorators && node.decorators.some(decorator => {
69
+ return decorator.expression &&
70
+ ((decorator.expression.type === "Identifier" && decorator.expression.name === "Controller") ||
71
+ (decorator.expression.type === "CallExpression" &&
72
+ decorator.expression.callee.name === "Controller"));
73
+ });
74
+ }
75
+
76
+ function containsGermanWords(text) {
77
+ if (!text || typeof text !== "string") return null;
78
+
79
+ const lowerText = text.toLowerCase();
80
+
81
+ for (const word of germanWords) {
82
+ // Use word boundaries to avoid false positives
83
+ const regex = new RegExp(`\\b${word}\\b`, "i");
84
+ if (regex.test(lowerText)) {
85
+ return word;
86
+ }
87
+ }
88
+
89
+ return null;
90
+ }
91
+
92
+ function checkStringForGerman(stringNode) {
93
+ if (stringNode && stringNode.type === "Literal" && typeof stringNode.value === "string") {
94
+ const germanWord = containsGermanWords(stringNode.value);
95
+ if (germanWord) {
96
+ context.report({
97
+ node: stringNode,
98
+ messageId: "germanDocumentation",
99
+ data: {
100
+ text: stringNode.value.substring(0, 80) + (stringNode.value.length > 80 ? "..." : "")
101
+ }
102
+ });
103
+ }
104
+ }
105
+ }
106
+
107
+ function hasDecorator(decorators, decoratorName) {
108
+ if (!decorators) return false;
109
+
110
+ return decorators.some(decorator => {
111
+ const name = decorator.expression?.name || decorator.expression?.callee?.name;
112
+ return name === decoratorName;
113
+ });
114
+ }
115
+
116
+ function hasApiResponseDecorator(decorators, statuses) {
117
+ if (!decorators) return false;
118
+
119
+ const statusArray = Array.isArray(statuses) ? statuses : [statuses];
120
+
121
+ return statusArray.some(status => {
122
+ return decorators.some(decorator => {
123
+ if (decorator.expression?.type === "CallExpression" &&
124
+ decorator.expression.callee.name === "ApiResponse") {
125
+
126
+ const args = decorator.expression.arguments;
127
+ if (args.length > 0 && args[0].type === "ObjectExpression") {
128
+ const statusProp = args[0].properties.find(prop =>
129
+ prop.key && prop.key.name === "status"
130
+ );
131
+
132
+ if (statusProp && statusProp.value) {
133
+ return statusProp.value.value === status || statusProp.value.raw === status.toString();
134
+ }
135
+ }
136
+ }
137
+ return false;
138
+ });
139
+ });
140
+ }
141
+
142
+ function getHttpMethodType(decorators) {
143
+ if (!decorators) return null;
144
+
145
+ const httpMethods = ["Get", "Post", "Put", "Delete", "Patch"];
146
+ for (const decorator of decorators) {
147
+ const decoratorName = decorator.expression?.name || decorator.expression?.callee?.name;
148
+ if (httpMethods.includes(decoratorName)) {
149
+ return decoratorName.toLowerCase();
150
+ }
151
+ }
152
+ return null;
153
+ }
154
+
155
+ function validateClassDecorators(node) {
156
+ if (!hasDecorator(node.decorators, "ApiTags")) {
157
+ context.report({
158
+ node: node,
159
+ messageId: "missingSwaggerTag"
160
+ });
161
+ }
162
+
163
+ const filename = context.getFilename();
164
+ const isPublicController = filename.includes("/Public/");
165
+
166
+ if (!hasDecorator(node.decorators, "ApiBearerAuth") && !isPublicController) {
167
+ context.report({
168
+ node: node,
169
+ messageId: "missingApiBearerAuth"
170
+ });
171
+ }
172
+ }
173
+
174
+ function validateApiOperation(member) {
175
+ const methodName = member.key.name;
176
+
177
+ if (!hasDecorator(member.decorators, "ApiOperation")) {
178
+ context.report({
179
+ node: member,
180
+ messageId: "missingApiOperation",
181
+ data: { methodName }
182
+ });
183
+ return;
184
+ }
185
+
186
+ // Check ApiOperation content for German text
187
+ member.decorators.forEach(decorator => {
188
+ if (decorator.expression?.type === "CallExpression" &&
189
+ decorator.expression.callee.name === "ApiOperation") {
190
+
191
+ const args = decorator.expression.arguments;
192
+ if (args.length > 0 && args[0].type === "ObjectExpression") {
193
+ let hasSummary = false;
194
+ let hasDescription = false;
195
+
196
+ args[0].properties.forEach(prop => {
197
+ if (prop.key && prop.key.name === "summary") {
198
+ hasSummary = true;
199
+ checkStringForGerman(prop.value);
200
+ } else if (prop.key && prop.key.name === "description") {
201
+ hasDescription = true;
202
+ checkStringForGerman(prop.value);
203
+ }
204
+ });
205
+
206
+ if (!hasSummary || !hasDescription) {
207
+ context.report({
208
+ node: member,
209
+ messageId: "emptyApiOperation",
210
+ data: { methodName }
211
+ });
212
+ }
213
+ }
214
+ }
215
+ });
216
+ }
217
+
218
+ function validateApiResponseDecorators(member) {
219
+ const httpMethod = getHttpMethodType(member.decorators);
220
+ if (!httpMethod) return;
221
+
222
+ const methodName = member.key.name;
223
+
224
+ // Check for required status codes
225
+ let successCodes = [200];
226
+ if (httpMethod === "post") {
227
+ successCodes = [200, 201];
228
+ }
229
+
230
+ const hasSuccessResponse = hasApiResponseDecorator(member.decorators, successCodes);
231
+ const has500Response = hasApiResponseDecorator(member.decorators, 500);
232
+
233
+ if (!hasSuccessResponse) {
234
+ const expectedCodes = successCodes.join(" or ");
235
+ context.report({
236
+ node: member,
237
+ messageId: "missingApiResponse",
238
+ data: { methodName, status: expectedCodes }
239
+ });
240
+ }
241
+
242
+ if (!has500Response) {
243
+ context.report({
244
+ node: member,
245
+ messageId: "missingApiResponse",
246
+ data: { methodName, status: "500" }
247
+ });
248
+ }
249
+
250
+ // Check ApiResponse content for German text
251
+ if (member.decorators) {
252
+ member.decorators.forEach(decorator => {
253
+ if (decorator.expression?.type === "CallExpression" &&
254
+ decorator.expression.callee.name === "ApiResponse") {
255
+
256
+ const args = decorator.expression.arguments;
257
+ if (args.length > 0 && args[0].type === "ObjectExpression") {
258
+ args[0].properties.forEach(prop => {
259
+ if (prop.key && prop.key.name === "description") {
260
+ checkStringForGerman(prop.value);
261
+ }
262
+ });
263
+ }
264
+ }
265
+ });
266
+ }
267
+ }
268
+
269
+ function validateApiParamDecorators(member) {
270
+ if (!member.decorators) return;
271
+
272
+ member.decorators.forEach(decorator => {
273
+ if (decorator.expression?.type === "CallExpression" &&
274
+ (decorator.expression.callee.name === "ApiParam" ||
275
+ decorator.expression.callee.name === "ApiQuery" ||
276
+ decorator.expression.callee.name === "ApiBody")) {
277
+
278
+ const args = decorator.expression.arguments;
279
+ if (args.length > 0 && args[0].type === "ObjectExpression") {
280
+ args[0].properties.forEach(prop => {
281
+ if (prop.key && prop.key.name === "description") {
282
+ checkStringForGerman(prop.value);
283
+ }
284
+ });
285
+ }
286
+ }
287
+ });
288
+ }
289
+
290
+ return {
291
+ ClassDeclaration(node) {
292
+ if (!isControllerClass(node)) return;
293
+
294
+ validateClassDecorators(node);
295
+
296
+ node.body.body.forEach(member => {
297
+ if (member.type === "MethodDefinition" &&
298
+ member.kind === "method" &&
299
+ member.accessibility === "public") {
300
+
301
+ const httpMethod = getHttpMethodType(member.decorators);
302
+ if (httpMethod) {
303
+ validateApiOperation(member);
304
+ validateApiResponseDecorators(member);
305
+ validateApiParamDecorators(member);
306
+ }
307
+ }
308
+ });
309
+ },
310
+ };
311
+ },
312
+ };
@@ -0,0 +1,119 @@
1
+ /**
2
+ * @fileoverview Ensure all controller methods have proper Swagger documentation including success and error status codes
3
+ */
4
+
5
+ "use strict";
6
+
7
+ export default {
8
+ meta: {
9
+ type: "problem",
10
+ docs: {
11
+ description: "enforce that all controller methods have proper @ApiResponse documentation",
12
+ category: "Best Practices",
13
+ recommended: true,
14
+ },
15
+ fixable: null,
16
+ schema: [],
17
+ messages: {
18
+ missingApiResponse: "Controller method '{{methodName}}' is missing @ApiResponse with status: {{status}}. All controller methods should document potential server errors (BaseController.sendError defaults to 500)",
19
+ },
20
+ },
21
+
22
+ create(context) {
23
+ function isControllerClass(node) {
24
+ if (node.type !== "ClassDeclaration") return false;
25
+
26
+ return node.decorators && node.decorators.some(decorator => {
27
+ return decorator.expression &&
28
+ ((decorator.expression.type === "Identifier" && decorator.expression.name === "Controller") ||
29
+ (decorator.expression.type === "CallExpression" &&
30
+ decorator.expression.callee.name === "Controller"));
31
+ });
32
+ }
33
+
34
+ function hasApiResponseDecorator(decorators, statuses) {
35
+ if (!decorators) return false;
36
+
37
+ const statusArray = Array.isArray(statuses) ? statuses : [statuses];
38
+
39
+ return statusArray.some(status => {
40
+ return decorators.some(decorator => {
41
+ if (decorator.expression.type === "CallExpression" &&
42
+ decorator.expression.callee.name === "ApiResponse") {
43
+
44
+ const args = decorator.expression.arguments;
45
+ if (args.length > 0 && args[0].type === "ObjectExpression") {
46
+ const statusProp = args[0].properties.find(prop =>
47
+ prop.key && prop.key.name === "status"
48
+ );
49
+
50
+ if (statusProp && statusProp.value) {
51
+ return statusProp.value.value === status || statusProp.value.raw === status.toString();
52
+ }
53
+ }
54
+ }
55
+ return false;
56
+ });
57
+ });
58
+ }
59
+
60
+ function getHttpMethodType(decorators) {
61
+ if (!decorators) return null;
62
+
63
+ const httpMethods = ["Get", "Post", "Put", "Delete", "Patch"];
64
+ for (const decorator of decorators) {
65
+ const decoratorName = decorator.expression?.name || decorator.expression?.callee?.name;
66
+ if (httpMethods.includes(decoratorName)) {
67
+ return decoratorName.toLowerCase();
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+
73
+ function validateApiResponseDecorators(member) {
74
+ const httpMethod = getHttpMethodType(member.decorators);
75
+ if (!httpMethod) return;
76
+
77
+ const methodName = member.key.name;
78
+
79
+ // Check for success status codes based on HTTP method
80
+ let successCodes = [200];
81
+ if (httpMethod === "post") {
82
+ successCodes = [200, 201]; // POST can return 200 or 201
83
+ }
84
+
85
+ const hasSuccessResponse = hasApiResponseDecorator(member.decorators, successCodes);
86
+ const has500Response = hasApiResponseDecorator(member.decorators, 500);
87
+
88
+ if (!hasSuccessResponse) {
89
+ const expectedCodes = successCodes.join(" or ");
90
+ context.report({
91
+ node: member,
92
+ message: `Controller method '${methodName}' is missing @ApiResponse with status: ${expectedCodes}. All controller methods should document successful responses.`,
93
+ });
94
+ }
95
+
96
+ if (!has500Response) {
97
+ context.report({
98
+ node: member,
99
+ message: `Controller method '${methodName}' is missing @ApiResponse with status: 500. All controller methods should document potential server errors (BaseController.sendError defaults to 500).`,
100
+ });
101
+ }
102
+ }
103
+
104
+ return {
105
+ ClassDeclaration(node) {
106
+ if (!isControllerClass(node)) return;
107
+
108
+ node.body.body.forEach(member => {
109
+ if (member.type === "MethodDefinition" &&
110
+ member.kind === "method" &&
111
+ member.accessibility === "public") {
112
+
113
+ validateApiResponseDecorators(member);
114
+ }
115
+ });
116
+ },
117
+ };
118
+ },
119
+ };