@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.
- package/CHANGELOG.md +1093 -0
- package/configs/.gitkeep +1 -0
- package/configs/admin.js +203 -0
- package/configs/api-client.js +46 -0
- package/configs/backend.js +895 -0
- package/configs/domains.js +123 -0
- package/configs/frontend.js +30 -0
- package/configs/image-server.js +26 -0
- package/configs/ionos-proxy.js +372 -0
- package/configs/nestjs.js +156 -0
- package/configs/node.js +92 -0
- package/configs/react.js +111 -0
- package/configs/wiki.js +42 -0
- package/index.js +39 -0
- package/package.json +85 -0
- package/rules/.gitkeep +1 -0
- package/rules/__tests__/analyze-relation-usage.test.js.disabled +300 -0
- package/rules/__tests__/complexity.test.js.disabled +300 -0
- package/rules/__tests__/enforce-dto-factory-in-services.integration.test.js +226 -0
- package/rules/__tests__/enforce-dto-factory-in-services.test.js +177 -0
- package/rules/__tests__/enforce-entity-dto-create-no-id.integration.test.js +18 -0
- package/rules/__tests__/enforce-function-argument-count.test.js.disabled +300 -0
- package/rules/__tests__/enforce-repository-token-handling.test.js +58 -0
- package/rules/__tests__/english-only-code-strings.test.js.disabled +300 -0
- package/rules/__tests__/eslint-rules.integration.test.ts +350 -0
- package/rules/__tests__/integration-test-controller-response-dto.js +261 -0
- package/rules/__tests__/integration-test-dto-factory-in-services.js +260 -0
- package/rules/__tests__/integration-test-no-entity-type-casting.js +161 -0
- package/rules/__tests__/integration-test-typeorm-naming-conventions.js +501 -0
- package/rules/__tests__/test-config.js +33 -0
- package/rules/admin-controller-security.js +180 -0
- package/rules/analyze-relation-usage.js +687 -0
- package/rules/api-response-dto.js +174 -0
- package/rules/auth-guard-required.js +142 -0
- package/rules/backend-specific.js +36 -0
- package/rules/best-practices.js +421 -0
- package/rules/complexity.js +20 -0
- package/rules/controller-architecture.js +340 -0
- package/rules/controller-naming-conventions.js +190 -0
- package/rules/controller-readonly-restriction.js +148 -0
- package/rules/controller-swagger-complete.js +312 -0
- package/rules/controller-swagger-docs.js +119 -0
- package/rules/controller-swagger-english.js +320 -0
- package/rules/coordinate-naming.js +132 -0
- package/rules/custom-mui-button.js +135 -0
- package/rules/dead-code-detection-backend.js +50 -0
- package/rules/dead-code-detection-frontend.js +48 -0
- package/rules/dead-code-detection.js +71 -0
- package/rules/debug-controller-response-dto.js +79 -0
- package/rules/deprecate.js +8 -0
- package/rules/dto-annotation-property-consistency.js +111 -0
- package/rules/dto-entity-mapping-completeness.js +688 -0
- package/rules/dto-entity-swagger-separation.js +265 -0
- package/rules/dto-entity-type-consistency.js +352 -0
- package/rules/dto-entity-type-matching.js +519 -0
- package/rules/dto-naming-convention.js +98 -0
- package/rules/dto-visibility-modifiers.js +159 -0
- package/rules/enforce-api-versioning.js +122 -0
- package/rules/enforce-app-module-registration.js +179 -0
- package/rules/enforce-basecontroller.js +152 -0
- package/rules/enforce-body-request-dto.js +141 -0
- package/rules/enforce-controller-response-dto.js +349 -0
- package/rules/enforce-custom-error-classes.js +242 -0
- package/rules/enforce-database-transaction-safety.js +179 -0
- package/rules/enforce-dto-constructor.js +95 -0
- package/rules/enforce-dto-create-parameter-types.js +170 -0
- package/rules/enforce-dto-create-pattern.js +274 -0
- package/rules/enforce-dto-entity-creation.js +164 -0
- package/rules/enforce-dto-factory-in-services.js +188 -0
- package/rules/enforce-dto-from-entity-method.js +47 -0
- package/rules/enforce-dto-from-entity.js +314 -0
- package/rules/enforce-dto-naming-conventions.js +212 -0
- package/rules/enforce-dto-naming.js +176 -0
- package/rules/enforce-dto-usage-simple.js +114 -0
- package/rules/enforce-dto-usage.js +407 -0
- package/rules/enforce-eager-translation-loading.js +178 -0
- package/rules/enforce-entity-creation-pattern.js +137 -0
- package/rules/enforce-entity-dto-convert-method.js +157 -0
- package/rules/enforce-entity-dto-create-no-id.js +117 -0
- package/rules/enforce-entity-dto-extends-base.js +141 -0
- package/rules/enforce-entity-dto-from-request-dto-structure.js +113 -0
- package/rules/enforce-entity-dto-fromentity-complex.js +69 -0
- package/rules/enforce-entity-dto-fromentity-simple.js +69 -0
- package/rules/enforce-entity-dto-fromrequestdto-structure.js +262 -0
- package/rules/enforce-entity-dto-methods-restriction.js +159 -0
- package/rules/enforce-entity-dto-no-request-dto.js +102 -0
- package/rules/enforce-entity-dto-optional-auto-fields.js +101 -0
- package/rules/enforce-entity-dto-required-methods.js +248 -0
- package/rules/enforce-entity-factory-pattern.js +180 -0
- package/rules/enforce-entity-instantiation-in-toentity.js +125 -0
- package/rules/enforce-enum-for-playable-entities.js +95 -0
- package/rules/enforce-error-handling.js +257 -0
- package/rules/enforce-explicit-dto-types.js +118 -0
- package/rules/enforce-from-request-dto-usage.js +62 -0
- package/rules/enforce-generic-entity-dto.js +71 -0
- package/rules/enforce-inject-decorator.js +133 -0
- package/rules/enforce-lazy-type-loading.js +170 -0
- package/rules/enforce-module-existence.js +157 -0
- package/rules/enforce-nonentity-dto-create.js +107 -0
- package/rules/enforce-playable-entity-naming.js +108 -0
- package/rules/enforce-repository-token-handling.js +92 -0
- package/rules/enforce-request-dto-no-entity-dto.js +201 -0
- package/rules/enforce-request-dto-required-fields.js +217 -0
- package/rules/enforce-result-pattern.js +45 -0
- package/rules/enforce-service-relation-loading.js +116 -0
- package/rules/enforce-test-coverage.js +96 -0
- package/rules/enforce-toentity-conditional-assignment.js +132 -0
- package/rules/enforce-translations-required.js +203 -0
- package/rules/enforce-typeorm-naming-conventions.js +366 -0
- package/rules/enforce-vite-health-metrics.js +240 -0
- package/rules/entity-required-properties.js +321 -0
- package/rules/entity-to-dto-test.js +73 -0
- package/rules/enum-database-validation.js +149 -0
- package/rules/errors.js +190 -0
- package/rules/es6.js +204 -0
- package/rules/eslint-plugin-no-comments.js +44 -0
- package/rules/filename-class-name-match.js +62 -0
- package/rules/forbid-fromentity-outside-entity-folder.js +237 -0
- package/rules/function-params-newline.js +111 -0
- package/rules/imports.js +264 -0
- package/rules/jest.js +13 -0
- package/rules/jsx.js +16 -0
- package/rules/max-classes-per-file.js +49 -0
- package/rules/multiline-formatting.js +146 -0
- package/rules/no-blank-lines-between-decorators-and-properties.js +95 -0
- package/rules/no-comments.js +62 -0
- package/rules/no-dto-constructors.js +126 -0
- package/rules/no-dto-default-values.js +220 -0
- package/rules/no-dto-duplicates.js +127 -0
- package/rules/no-dto-in-entity.js +99 -0
- package/rules/no-dynamic-import-in-types.js +71 -0
- package/rules/no-dynamic-imports-in-controllers.js +95 -0
- package/rules/no-entity-imports-in-controllers.js +101 -0
- package/rules/no-entity-in-swagger-docs.js +139 -0
- package/rules/no-entity-type-casting.js +104 -0
- package/rules/no-fetch.js +77 -0
- package/rules/no-import-meta-env.js +151 -0
- package/rules/no-inline-styles.js +5 -0
- package/rules/no-magic-values.js +85 -0
- package/rules/no-partial-type.js +168 -0
- package/rules/no-relative-imports.js +31 -0
- package/rules/no-tsyringe.js +181 -0
- package/rules/no-type-assertion.js +175 -0
- package/rules/no-undefined-entity-properties.js +121 -0
- package/rules/node.js +44 -0
- package/rules/perfectionist.js +50 -0
- package/rules/performance-minimal.js +155 -0
- package/rules/performance.js +44 -0
- package/rules/pino-logger-format.js +200 -0
- package/rules/prefer-dto-classes.js +112 -0
- package/rules/prefer-dto-create-method.js +225 -0
- package/rules/promises.js +17 -0
- package/rules/react-hooks.js +15 -0
- package/rules/react.js +28 -0
- package/rules/regexp.js +70 -0
- package/rules/require-dto-response.js +81 -0
- package/rules/require-valid-relations.js +388 -0
- package/rules/result-pattern.js +162 -0
- package/rules/security.js +37 -0
- package/rules/service-architecture.js +148 -0
- package/rules/sonarjs.js +26 -0
- package/rules/strict.js +7 -0
- package/rules/style.js +611 -0
- package/rules/stylistic.js +93 -0
- package/rules/typeorm-column-type-validation.js +224 -0
- package/rules/typescript-advanced.js +113 -0
- package/rules/typescript-core.js +111 -0
- package/rules/typescript.js +146 -0
- package/rules/unicorn.js +168 -0
- package/rules/variables.js +51 -0
- 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
|
+
|