@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,314 @@
1
+ /**
2
+ * ESLint-Regel: enforce-dto-from-entity
3
+ * Stellt sicher, dass DTOs fromEntity-Methoden haben und Services diese verwenden
4
+ */
5
+
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+
9
+ /** @type {import('eslint').Rule.RuleModule} */
10
+ const enforceDtoFromEntityRule = {
11
+ meta: {
12
+ type: "problem",
13
+ docs: {
14
+ description: "DTOs müssen fromEntity-Methoden bereitstellen, Services dürfen keine eigene Entity-Konvertierung implementieren",
15
+ category: "Architecture",
16
+ recommended: true,
17
+ },
18
+ schema: [],
19
+ messages: {
20
+ missingFromEntityMethod: "DTO-Klasse '{{dtoName}}' muss eine statische fromEntity-Methode bereitstellen",
21
+ missingFromEntityArrayMethod: "DTO-Klasse '{{dtoName}}' muss eine statische fromEntityArray-Methode bereitstellen",
22
+ serviceEntityConversion: "Service '{{serviceName}}' darf keine eigene Entity-zu-DTO-Konvertierung implementieren. Verwende {{dtoName}}.fromEntity() stattdessen",
23
+ manualEntityMapping: "Manuelle Entity-zu-DTO-Konvertierung nicht erlaubt. Verwende {{dtoName}}.fromEntity() stattdessen",
24
+ unknownTypeForbidden: "fromEntity-Methode verwendet 'unknown' als Parameter-Typ. Verwende den korrekten Entity-Typ.",
25
+ anyTypeForbidden: "fromEntity-Methode verwendet 'any' als Parameter-Typ. Verwende den korrekten Entity-Typ.",
26
+ },
27
+ },
28
+ create(context) {
29
+ const filename = context.getFilename();
30
+ const isDtoFile = filename.includes("/dto/") && filename.endsWith(".ts");
31
+ const isServiceFile = filename.includes("/service/") && filename.endsWith(".ts") && !filename.includes("test");
32
+
33
+ let dtoClassName = "";
34
+ let hasFromEntityMethod = false;
35
+ let hasFromEntityArrayMethod = false;
36
+
37
+ // Hilfsfunktion um zu prüfen, ob eine entsprechende Entity existiert
38
+ function doesEntityExist(dtoName, context) {
39
+ try {
40
+ const filename = context.getFilename();
41
+ const projectRoot = path.resolve(path.dirname(filename), '../..');
42
+
43
+ // Entferne "Dto" Suffix und füge "Entity" hinzu
44
+ let entityName = dtoName;
45
+ if (entityName.endsWith('Dto')) {
46
+ entityName = entityName.slice(0, -3);
47
+ }
48
+ entityName += 'Entity';
49
+
50
+ // Rekursive Suche in allen Entity-Verzeichnissen
51
+ function findEntityRecursively(dir, entityFileName) {
52
+ if (!fs.existsSync(dir)) {
53
+ return false;
54
+ }
55
+
56
+ try {
57
+ const files = fs.readdirSync(dir, { withFileTypes: true });
58
+
59
+ for (const file of files) {
60
+ const fullPath = path.join(dir, file.name);
61
+
62
+ if (file.isFile() && file.name === entityFileName) {
63
+ return true;
64
+ } else if (file.isDirectory()) {
65
+ if (findEntityRecursively(fullPath, entityFileName)) {
66
+ return true;
67
+ }
68
+ }
69
+ }
70
+ } catch (error) {
71
+ // Ignore permission errors etc.
72
+ }
73
+
74
+ return false;
75
+ }
76
+
77
+ // Rekursive Suche im gesamten Entity-Verzeichnis
78
+ const entityDir = path.join(projectRoot, 'src/entity');
79
+ return findEntityRecursively(entityDir, `${entityName}.ts`);
80
+
81
+ } catch (error) {
82
+ // Bei Fehlern nehmen wir an, dass die Entity nicht existiert
83
+ return false;
84
+ }
85
+ }
86
+
87
+ // Hilfsfunktion um Service-Klassennamen zu extrahieren
88
+ function getServiceClassName(context) {
89
+ const sourceCode = context.getSourceCode();
90
+ const ast = sourceCode.ast;
91
+
92
+ for (const node of ast.body) {
93
+ if (node.type === "ClassDeclaration" && node.id?.name?.endsWith("Service")) {
94
+ return node.id.name;
95
+ }
96
+ }
97
+ return null;
98
+ }
99
+
100
+ return {
101
+ // Überwache DTO-Dateien
102
+ ClassDeclaration(node) {
103
+ if (isDtoFile) {
104
+ dtoClassName = node.id?.name || "";
105
+
106
+ // Prüfe auf fromEntity und fromEntityArray Methoden
107
+ const methods = node.body.body.filter(member =>
108
+ member.type === "MethodDefinition" &&
109
+ member.static === true
110
+ );
111
+
112
+ hasFromEntityMethod = methods.some(method =>
113
+ method.key?.name === "fromEntity"
114
+ );
115
+
116
+ hasFromEntityArrayMethod = methods.some(method =>
117
+ method.key?.name === "fromEntityArray"
118
+ );
119
+ }
120
+ },
121
+
122
+ // Überwache fromEntity-Methoden auf unknown/any Parameter
123
+ MethodDefinition(node) {
124
+ if (isDtoFile && node.key?.name === "fromEntity" && node.static) {
125
+ // Prüfe auf unknown oder any Parameter-Typen
126
+ if (node.value && node.value.params && node.value.params.length > 0) {
127
+ const firstParam = node.value.params[0];
128
+
129
+ if (firstParam.typeAnnotation && firstParam.typeAnnotation.typeAnnotation) {
130
+ const typeAnnotation = firstParam.typeAnnotation.typeAnnotation;
131
+
132
+ // Prüfe auf unknown
133
+ if (typeAnnotation.type === "TSUnknownKeyword") {
134
+ context.report({
135
+ node: firstParam,
136
+ messageId: "unknownTypeForbidden",
137
+ });
138
+ }
139
+
140
+ // Prüfe auf any
141
+ if (typeAnnotation.type === "TSAnyKeyword") {
142
+ context.report({
143
+ node: firstParam,
144
+ messageId: "anyTypeForbidden",
145
+ });
146
+ }
147
+ }
148
+ }
149
+ }
150
+ },
151
+
152
+ // Überwache Arrow Functions in statischen Methoden auf unknown/any Parameter
153
+ PropertyDefinition(node) {
154
+ if (isDtoFile && node.static && node.value && node.value.type === "ArrowFunctionExpression") {
155
+ const methodName = node.key?.name;
156
+
157
+ if (methodName === "fromEntity") {
158
+ // Prüfe auf unknown oder any Parameter-Typen
159
+ if (node.value.params && node.value.params.length > 0) {
160
+ const firstParam = node.value.params[0];
161
+
162
+ if (firstParam.typeAnnotation && firstParam.typeAnnotation.typeAnnotation) {
163
+ const typeAnnotation = firstParam.typeAnnotation.typeAnnotation;
164
+
165
+ // Prüfe auf unknown
166
+ if (typeAnnotation.type === "TSUnknownKeyword") {
167
+ context.report({
168
+ node: firstParam,
169
+ messageId: "unknownTypeForbidden",
170
+ });
171
+ }
172
+
173
+ // Prüfe auf any
174
+ if (typeAnnotation.type === "TSAnyKeyword") {
175
+ context.report({
176
+ node: firstParam,
177
+ messageId: "anyTypeForbidden",
178
+ });
179
+ }
180
+ }
181
+ }
182
+ }
183
+ }
184
+ },
185
+
186
+ // Überwache Service-Dateien auf Entity-Konvertierung
187
+ MethodDefinition(node) {
188
+ if (isServiceFile) {
189
+ const methodName = node.key?.name;
190
+
191
+ // Verbiete convertEntityToDto oder ähnliche Methoden
192
+ if (methodName &&
193
+ (methodName.includes("convertEntity") ||
194
+ methodName.includes("entityToDto") ||
195
+ methodName.includes("toDto"))) {
196
+
197
+ const serviceName = getServiceClassName(context);
198
+
199
+ context.report({
200
+ node,
201
+ messageId: "serviceEntityConversion",
202
+ data: {
203
+ serviceName: serviceName || "Service",
204
+ dtoName: "YourDto",
205
+ },
206
+ });
207
+ }
208
+ }
209
+ },
210
+
211
+ // Prüfe auf manuelle Entity-Property-Zuweisungen
212
+ AssignmentExpression(node) {
213
+ if (isServiceFile) {
214
+ // Erkenne Patterns wie: entityDto.property = entity.property
215
+ if (node.left.type === "MemberExpression" &&
216
+ node.right.type === "MemberExpression") {
217
+
218
+ const leftObject = node.left.object;
219
+ const rightObject = node.right.object;
220
+
221
+ // Prüfe ob links ein EntityDto und rechts eine Entity ist
222
+ if (leftObject?.name?.toLowerCase().includes("entitydto") &&
223
+ rightObject?.name?.toLowerCase().includes("entity")) {
224
+
225
+ context.report({
226
+ node,
227
+ messageId: "manualEntityMapping",
228
+ data: {
229
+ dtoName: "YourEntityDto",
230
+ },
231
+ });
232
+ }
233
+ }
234
+ }
235
+ },
236
+
237
+ // Prüfe auf new EntityDto() gefolgt von Property-Zuweisungen
238
+ VariableDeclaration(node) {
239
+ if (isServiceFile) {
240
+ for (const declarator of node.declarations) {
241
+ if (declarator.init?.type === "NewExpression" &&
242
+ declarator.init.callee?.name?.endsWith("EntityDto")) {
243
+
244
+ // Schaue nach nachfolgenden Property-Zuweisungen
245
+ const parent = node.parent;
246
+ if (parent?.type === "BlockStatement") {
247
+ const statements = parent.body;
248
+ const currentIndex = statements.indexOf(node);
249
+
250
+ // Prüfe die nächsten Statements auf Property-Zuweisungen
251
+ for (let i = currentIndex + 1; i < Math.min(currentIndex + 10, statements.length); i++) {
252
+ const stmt = statements[i];
253
+ if (stmt.type === "ExpressionStatement" &&
254
+ stmt.expression.type === "AssignmentExpression" &&
255
+ stmt.expression.left.type === "MemberExpression" &&
256
+ stmt.expression.left.object?.name === declarator.id?.name) {
257
+
258
+ context.report({
259
+ node: stmt,
260
+ messageId: "manualEntityMapping",
261
+ data: {
262
+ dtoName: declarator.init.callee.name,
263
+ },
264
+ });
265
+ break;
266
+ }
267
+ }
268
+ }
269
+ }
270
+ }
271
+ }
272
+ },
273
+
274
+ // Prüfe am Ende der DTO-Datei
275
+ "Program:exit"(node) {
276
+ if (isDtoFile && dtoClassName) {
277
+ // Request-DTOs sind von der fromEntity-Regel ausgenommen
278
+ const isRequestDto = dtoClassName.includes("Request") || dtoClassName.includes("Create") || dtoClassName.includes("Update");
279
+
280
+ // Prüfe nur, wenn es eine entsprechende Entity gibt
281
+ const entityExists = doesEntityExist(dtoClassName, context);
282
+
283
+ if (!isRequestDto && entityExists) {
284
+ if (!hasFromEntityMethod) {
285
+ context.report({
286
+ node,
287
+ messageId: "missingFromEntityMethod",
288
+ data: {
289
+ dtoName: dtoClassName,
290
+ },
291
+ });
292
+ }
293
+
294
+ if (!hasFromEntityArrayMethod) {
295
+ context.report({
296
+ node,
297
+ messageId: "missingFromEntityArrayMethod",
298
+ data: {
299
+ dtoName: dtoClassName,
300
+ },
301
+ });
302
+ }
303
+ }
304
+ }
305
+ },
306
+ };
307
+ },
308
+ };
309
+
310
+ export default {
311
+ rules: {
312
+ 'enforce-dto-from-entity': enforceDtoFromEntityRule,
313
+ },
314
+ };
@@ -0,0 +1,212 @@
1
+ import path from "path";
2
+
3
+ export default {
4
+ meta: {
5
+ type: "problem",
6
+ docs: {
7
+ description: "Enforces DTO naming and folder conventions",
8
+ category: "Architecture",
9
+ recommended: true,
10
+ },
11
+ schema: [],
12
+ messages: {
13
+ classNameMismatch: "Klassenname '{{className}}' muss mit dem Dateinamen '{{filename}}' übereinstimmen.",
14
+ entityDtoWrongSuffix: "Entity-DTO '{{className}}' muss den Suffix 'EntityDto' haben (nicht '{{currentSuffix}}')",
15
+ entityDtoDoubleSuffix: "Entity-DTO '{{className}}' hat doppelten Suffix. Verwende '{{correctName}}' statt '{{className}}'",
16
+ entityDtoMixedType: "Entity-DTO '{{className}}' darf keine anderen DTO-Typen im Namen haben (z.B. 'Response', 'Request'). Verwende '{{correctName}}' statt '{{className}}'",
17
+ responseDtoWrongSuffix: "Response-DTO '{{className}}' muss den Suffix 'ResponseDto' haben (nicht '{{currentSuffix}}')",
18
+ requestDtoWrongSuffix: "Request-DTO '{{className}}' muss den Suffix 'RequestDto' haben (nicht '{{currentSuffix}}')",
19
+ configDtoWrongSuffix: "Config-DTO '{{className}}' muss den Suffix 'ConfigDto' haben (nicht '{{currentSuffix}}')",
20
+ filterDtoWrongSuffix: "Filter-DTO '{{className}}' muss den Suffix 'FilterDto' haben (nicht '{{currentSuffix}}')",
21
+ unknownFolderSuffix: "DTO '{{className}}' im unbekannten Ordner '{{folder}}'. Definiere einen passenden Suffix.",
22
+ dtoMustBeInSubfolder: "DTO '{{className}}' muss in einem Unterordner von /dto/ liegen (z.B. /dto/Response/, /dto/Request/, etc.).",
23
+ },
24
+ },
25
+ create(context) {
26
+ return {
27
+ ClassDeclaration(node) {
28
+ const filename = context.getFilename();
29
+ const dtoClassName = node.id?.name;
30
+ if (!dtoClassName) return;
31
+
32
+ const baseFilename = path.basename(filename, ".ts");
33
+
34
+ // 1. Klassenname und Dateiname müssen übereinstimmen
35
+ if (dtoClassName !== baseFilename) {
36
+ context.report({
37
+ node: node,
38
+ messageId: "classNameMismatch",
39
+ data: { className: dtoClassName, filename: baseFilename },
40
+ });
41
+ }
42
+
43
+ // 2. Bestimme den Ordner und erwarteten Suffix
44
+ const pathParts = filename.split(path.sep);
45
+ const dtoIndex = pathParts.findIndex(part => part === "dto");
46
+
47
+ if (dtoIndex !== -1) {
48
+ // Ausnahme für BaseEntityDto (abstrakte Base-Klasse)
49
+ if (dtoClassName === "BaseEntityDto" && filename.endsWith("/dto/BaseEntityDto.ts")) {
50
+ return;
51
+ }
52
+
53
+ // Prüfe, ob es ein Unterordner gibt (nicht die Datei selbst)
54
+ if (dtoIndex + 1 < pathParts.length - 1) {
55
+ const folder = pathParts[dtoIndex + 1];
56
+ const currentSuffix = dtoClassName.match(/([A-Z][a-z]*Dto)$/)?.[1] || "unbekannt";
57
+
58
+ const expectedSuffixMap = {
59
+ "Entity": "EntityDto",
60
+ "Response": "ResponseDto",
61
+ "Request": "RequestDto",
62
+ "Config": "ConfigDto",
63
+ "Filter": "FilterDto",
64
+ // Add other common DTO subfolders here if they have a specific suffix
65
+ // For example, if you have /dto/Auth/AuthRequestDto, you'd add "Auth": "AuthDto"
66
+ // For now, general DTOs in other folders just need to end with "Dto"
67
+ "Eoo": "Dto",
68
+ "Game": "Dto",
69
+ "User": "Dto",
70
+ "Auth": "Dto",
71
+ "Chat": "Dto",
72
+ "World": "Dto",
73
+ "Asset": "Dto",
74
+ "Language": "Dto",
75
+ "Log": "Dto",
76
+ "Health": "Dto",
77
+ "Metrics": "Dto",
78
+ "Admin": "Dto",
79
+ "Utilities": "Dto",
80
+ "Common": "Dto",
81
+ "Shared": "Dto",
82
+ "Core": "Dto",
83
+ "System": "Dto",
84
+ "Event": "Dto",
85
+ "Notification": "Dto",
86
+ "Payment": "Dto",
87
+ "Inventory": "Dto",
88
+ "Quest": "Dto",
89
+ "Skill": "Dto",
90
+ "Spell": "Dto",
91
+ "Item": "Dto",
92
+ "Faction": "Dto",
93
+ "Race": "Dto",
94
+ "Class": "Dto",
95
+ "Ability": "Dto",
96
+ "Buff": "Dto",
97
+ "Debuff": "Dto",
98
+ "Effect": "Dto",
99
+ "Stat": "Dto",
100
+ "Map": "Dto",
101
+ "Zone": "Dto",
102
+ "Region": "Dto",
103
+ "Server": "Dto",
104
+ "Client": "Dto",
105
+ "Gateway": "Dto",
106
+ "Proxy": "Dto",
107
+ "Service": "Dto",
108
+ "Module": "Dto",
109
+ "Provider": "Dto",
110
+ "Repository": "Dto",
111
+ "Controller": "Dto",
112
+ "Middleware": "Dto",
113
+ "Guard": "Dto",
114
+ "Interceptor": "Dto",
115
+ "Pipe": "Dto",
116
+ "Decorator": "Dto",
117
+ "Enum": "Dto",
118
+ "Interface": "Dto",
119
+ "Type": "Dto",
120
+ "Util": "Dto",
121
+ "Helper": "Dto",
122
+ "Constant": "Dto",
123
+ "Factory": "Dto",
124
+ "Strategy": "Dto",
125
+ "Subscriber": "Dto",
126
+ "Seeder": "Dto",
127
+ "Migration": "Dto",
128
+ "Test": "Dto",
129
+ "Mock": "Dto",
130
+ "Stub": "Dto",
131
+ "Fixture": "Dto",
132
+ "Seed": "Dto",
133
+ "Script": "Dto",
134
+ "Tool": "Dto",
135
+ "Logger": "Dto", // Added Logger as a recognized folder, but no specific suffix beyond Dto
136
+ "Main": "Dto", // Added Main as a recognized folder, but no specific suffix beyond Dto
137
+ "OAuth": "Dto", // Added OAuth as a recognized folder, but no specific suffix beyond Dto
138
+ "Utility": "Dto", // Added Utility as a recognized folder, but no specific suffix beyond Dto
139
+ };
140
+
141
+ const expectedSuffix = expectedSuffixMap[folder];
142
+
143
+ if (folder === "Entity") {
144
+ if (!dtoClassName.endsWith("EntityDto")) {
145
+ context.report({
146
+ node: node,
147
+ messageId: "entityDtoWrongSuffix",
148
+ data: { className: dtoClassName, currentSuffix: currentSuffix },
149
+ });
150
+ } else if (dtoClassName.endsWith("DtoEntityDto")) {
151
+ const correctName = dtoClassName.replace("DtoEntityDto", "EntityDto");
152
+ context.report({
153
+ node: node,
154
+ messageId: "entityDtoDoubleSuffix",
155
+ data: { className: dtoClassName, correctName: correctName },
156
+ });
157
+ } else {
158
+ // Prüfe, ob Entity-DTO andere DTO-Typen im Namen hat
159
+ const forbiddenTypes = ["Response", "Request", "Config", "Filter"];
160
+ const hasForbiddenType = forbiddenTypes.some(type =>
161
+ dtoClassName.includes(type)
162
+ );
163
+
164
+ if (hasForbiddenType) {
165
+ // Entferne den verbotenen Typ und ersetze durch korrekten Namen
166
+ let correctName = dtoClassName;
167
+ forbiddenTypes.forEach(type => {
168
+ if (correctName.includes(type)) {
169
+ correctName = correctName.replace(type, "");
170
+ }
171
+ });
172
+ // Stelle sicher, dass es mit EntityDto endet
173
+ if (!correctName.endsWith("EntityDto")) {
174
+ correctName = correctName.replace(/Dto$/, "EntityDto");
175
+ }
176
+
177
+ context.report({
178
+ node: node,
179
+ messageId: "entityDtoMixedType",
180
+ data: { className: dtoClassName, correctName: correctName },
181
+ });
182
+ }
183
+ }
184
+ } else if (expectedSuffix && expectedSuffix !== "Dto") {
185
+ if (!dtoClassName.endsWith(expectedSuffix)) {
186
+ context.report({
187
+ node: node,
188
+ messageId: `${folder.toLowerCase()}DtoWrongSuffix`,
189
+ data: { className: dtoClassName, currentSuffix: currentSuffix },
190
+ });
191
+ }
192
+ } else if (!expectedSuffix) {
193
+ context.report({
194
+ node: node,
195
+ messageId: "unknownFolderSuffix",
196
+ data: { className: dtoClassName, folder: folder },
197
+ });
198
+ }
199
+ } else {
200
+ // DTO direkt im /dto/ Ordner - nicht erlaubt
201
+ context.report({
202
+ node: node,
203
+ messageId: "dtoMustBeInSubfolder",
204
+ data: { className: dtoClassName },
205
+ });
206
+ }
207
+ }
208
+ },
209
+ };
210
+ },
211
+ };
212
+