@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,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Ensure all controller Swagger documentation is written in English
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
"use strict";
|
|
6
|
+
|
|
7
|
+
import { franc } from "franc";
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
meta: {
|
|
11
|
+
type: "problem",
|
|
12
|
+
docs: {
|
|
13
|
+
description: "enforce that all Swagger documentation in controllers is written in English",
|
|
14
|
+
category: "Best Practices",
|
|
15
|
+
recommended: true,
|
|
16
|
+
},
|
|
17
|
+
fixable: null,
|
|
18
|
+
schema: [],
|
|
19
|
+
messages: {
|
|
20
|
+
germanDocumentation: "Swagger documentation should be in English. Found German text: '{{text}}'",
|
|
21
|
+
nonEnglishDocumentation: "Swagger documentation should be in English. Detected language: '{{language}}' in text: '{{text}}'",
|
|
22
|
+
missingApiOperation: "Controller method '{{methodName}}' is missing @ApiOperation decorator",
|
|
23
|
+
emptyApiOperation: "Controller method '{{methodName}}' has empty @ApiOperation summary or description",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
create(context) {
|
|
28
|
+
// Common German words that shouldn't appear in API documentation (fallback)
|
|
29
|
+
const germanWords = [
|
|
30
|
+
// Articles
|
|
31
|
+
"der", "die", "das", "ein", "eine", "einen", "einem", "einer", "eines",
|
|
32
|
+
// Common verbs
|
|
33
|
+
"ist", "sind", "wird", "werden", "hat", "haben", "kann", "können",
|
|
34
|
+
"gibt", "geben", "ruft", "abrufen", "erstellt", "erstellen", "aktualisiert",
|
|
35
|
+
"aktualisieren", "löscht", "löschen", "zurück", "erfolgreich",
|
|
36
|
+
// Common nouns
|
|
37
|
+
"daten", "fehler", "serverfehler", "liste", "details",
|
|
38
|
+
"gesundheit", "position", "koordinate", "karten",
|
|
39
|
+
// Adjectives/Adverbs
|
|
40
|
+
"alle", "aller", "neues", "neue", "bestimmten", "ungültige", "ungültiger",
|
|
41
|
+
// Prepositions
|
|
42
|
+
"mit", "von", "für", "beim", "nach", "oder", "und",
|
|
43
|
+
// Other common words
|
|
44
|
+
"nicht", "gefunden", "wo", "wenn", "dass", "welche", "welcher",
|
|
45
|
+
// Status messages
|
|
46
|
+
"erfolgreich", "fehlgeschlagen", "abgerufen", "gelöscht"
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
// Language code mappings for better error messages
|
|
50
|
+
const languageNames = {
|
|
51
|
+
"deu": "German",
|
|
52
|
+
"fra": "French",
|
|
53
|
+
"spa": "Spanish",
|
|
54
|
+
"ita": "Italian",
|
|
55
|
+
"por": "Portuguese",
|
|
56
|
+
"rus": "Russian",
|
|
57
|
+
"jpn": "Japanese",
|
|
58
|
+
"kor": "Korean",
|
|
59
|
+
"chi": "Chinese",
|
|
60
|
+
"ara": "Arabic",
|
|
61
|
+
"hin": "Hindi",
|
|
62
|
+
"ben": "Bengali",
|
|
63
|
+
"urd": "Urdu",
|
|
64
|
+
"tur": "Turkish",
|
|
65
|
+
"pol": "Polish",
|
|
66
|
+
"nld": "Dutch",
|
|
67
|
+
"swe": "Swedish",
|
|
68
|
+
"dan": "Danish",
|
|
69
|
+
"nor": "Norwegian",
|
|
70
|
+
"fin": "Finnish",
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
function isControllerClass(node) {
|
|
74
|
+
if (node.type !== "ClassDeclaration") return false;
|
|
75
|
+
|
|
76
|
+
return node.decorators && node.decorators.some(decorator => {
|
|
77
|
+
return decorator.expression &&
|
|
78
|
+
((decorator.expression.type === "Identifier" && decorator.expression.name === "Controller") ||
|
|
79
|
+
(decorator.expression.type === "CallExpression" &&
|
|
80
|
+
decorator.expression.callee.name === "Controller"));
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function containsGermanWords(text) {
|
|
85
|
+
if (!text || typeof text !== "string") return null;
|
|
86
|
+
|
|
87
|
+
const lowerText = text.toLowerCase();
|
|
88
|
+
|
|
89
|
+
for (const word of germanWords) {
|
|
90
|
+
// Use word boundaries to avoid false positives
|
|
91
|
+
const regex = new RegExp(`\\b${word}\\b`, "i");
|
|
92
|
+
if (regex.test(lowerText)) {
|
|
93
|
+
return word;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function detectLanguageWithFranc(text) {
|
|
101
|
+
if (!text || typeof text !== "string" || text.trim().length < 20) {
|
|
102
|
+
// For very short texts, franc is unreliable, skip detection
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const detectedLanguage = franc(text);
|
|
108
|
+
|
|
109
|
+
// franc returns 'und' for undetermined language
|
|
110
|
+
if (detectedLanguage === "und" || detectedLanguage === "eng") {
|
|
111
|
+
return null; // Assume English or undetermined is OK
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Only report if confidence is high and text is long enough
|
|
115
|
+
if (text.length < 30) {
|
|
116
|
+
return null; // Skip detection for short technical texts
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return detectedLanguage;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
// If franc fails, fall back to word detection
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function checkStringForLanguage(stringNode, context) {
|
|
127
|
+
if (stringNode && stringNode.type === "Literal" && typeof stringNode.value === "string") {
|
|
128
|
+
const text = stringNode.value.trim();
|
|
129
|
+
|
|
130
|
+
// Skip very short texts (likely single words or technical terms)
|
|
131
|
+
if (text.length < 10) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Skip common API terms that might be misdetected
|
|
136
|
+
const commonApiTerms = [
|
|
137
|
+
"retrieved", "retrieves", "monster", "monsters", "error", "server",
|
|
138
|
+
"fetching", "filter", "list", "found", "not found", "successfully",
|
|
139
|
+
"user", "details", "available", "aura", "definitions", "specific",
|
|
140
|
+
"statistic", "formula", "returns", "get", "cat"
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
const lowerText = text.toLowerCase();
|
|
144
|
+
const hasCommonApiTerms = commonApiTerms.some(term => lowerText.includes(term));
|
|
145
|
+
|
|
146
|
+
if (hasCommonApiTerms && text.length < 100) {
|
|
147
|
+
// Skip short texts with common API terms
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Primary check: Use franc for language detection
|
|
152
|
+
const detectedLanguage = detectLanguageWithFranc(text);
|
|
153
|
+
if (detectedLanguage) {
|
|
154
|
+
const languageName = languageNames[detectedLanguage] || detectedLanguage;
|
|
155
|
+
context.report({
|
|
156
|
+
node: stringNode,
|
|
157
|
+
messageId: "nonEnglishDocumentation",
|
|
158
|
+
data: {
|
|
159
|
+
language: languageName,
|
|
160
|
+
text: text.substring(0, 50) + (text.length > 50 ? "..." : "")
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Fallback check: Use German word detection for more specific cases
|
|
167
|
+
const germanWord = containsGermanWords(text);
|
|
168
|
+
if (germanWord) {
|
|
169
|
+
context.report({
|
|
170
|
+
node: stringNode,
|
|
171
|
+
messageId: "germanDocumentation",
|
|
172
|
+
data: {
|
|
173
|
+
text: text.substring(0, 50) + (text.length > 50 ? "..." : "")
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function hasApiOperationDecorator(decorators) {
|
|
181
|
+
if (!decorators) return false;
|
|
182
|
+
|
|
183
|
+
return decorators.some(decorator => {
|
|
184
|
+
return decorator.expression &&
|
|
185
|
+
decorator.expression.type === "CallExpression" &&
|
|
186
|
+
decorator.expression.callee.name === "ApiOperation";
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function getApiOperationContent(decorators) {
|
|
191
|
+
if (!decorators) return null;
|
|
192
|
+
|
|
193
|
+
for (const decorator of decorators) {
|
|
194
|
+
if (decorator.expression &&
|
|
195
|
+
decorator.expression.type === "CallExpression" &&
|
|
196
|
+
decorator.expression.callee.name === "ApiOperation") {
|
|
197
|
+
|
|
198
|
+
const args = decorator.expression.arguments;
|
|
199
|
+
if (args.length > 0 && args[0].type === "ObjectExpression") {
|
|
200
|
+
return args[0];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function validateApiOperation(member) {
|
|
208
|
+
const methodName = member.key.name;
|
|
209
|
+
|
|
210
|
+
if (!hasApiOperationDecorator(member.decorators)) {
|
|
211
|
+
context.report({
|
|
212
|
+
node: member,
|
|
213
|
+
messageId: "missingApiOperation",
|
|
214
|
+
data: { methodName }
|
|
215
|
+
});
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const apiOpContent = getApiOperationContent(member.decorators);
|
|
220
|
+
if (!apiOpContent) return;
|
|
221
|
+
|
|
222
|
+
let hasSummary = false;
|
|
223
|
+
let hasDescription = false;
|
|
224
|
+
|
|
225
|
+
// Check each property in ApiOperation
|
|
226
|
+
apiOpContent.properties.forEach(prop => {
|
|
227
|
+
if (prop.key && prop.key.name === "summary") {
|
|
228
|
+
hasSummary = true;
|
|
229
|
+
checkStringForLanguage(prop.value, context);
|
|
230
|
+
} else if (prop.key && prop.key.name === "description") {
|
|
231
|
+
hasDescription = true;
|
|
232
|
+
checkStringForLanguage(prop.value, context);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
if (!hasSummary || !hasDescription) {
|
|
237
|
+
context.report({
|
|
238
|
+
node: member,
|
|
239
|
+
messageId: "emptyApiOperation",
|
|
240
|
+
data: { methodName }
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function validateApiResponseDecorators(member) {
|
|
246
|
+
if (!member.decorators) return;
|
|
247
|
+
|
|
248
|
+
member.decorators.forEach(decorator => {
|
|
249
|
+
if (decorator.expression &&
|
|
250
|
+
decorator.expression.type === "CallExpression" &&
|
|
251
|
+
decorator.expression.callee.name === "ApiResponseDecorator") {
|
|
252
|
+
|
|
253
|
+
const args = decorator.expression.arguments;
|
|
254
|
+
if (args.length > 0 && args[0].type === "ObjectExpression") {
|
|
255
|
+
args[0].properties.forEach(prop => {
|
|
256
|
+
if (prop.key && prop.key.name === "description") {
|
|
257
|
+
checkStringForLanguage(prop.value, context);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function validateApiParamDecorators(member) {
|
|
266
|
+
if (!member.decorators) return;
|
|
267
|
+
|
|
268
|
+
member.decorators.forEach(decorator => {
|
|
269
|
+
if (decorator.expression &&
|
|
270
|
+
decorator.expression.type === "CallExpression" &&
|
|
271
|
+
(decorator.expression.callee.name === "ApiParam" ||
|
|
272
|
+
decorator.expression.callee.name === "ApiQuery" ||
|
|
273
|
+
decorator.expression.callee.name === "ApiBody")) {
|
|
274
|
+
|
|
275
|
+
const args = decorator.expression.arguments;
|
|
276
|
+
if (args.length > 0 && args[0].type === "ObjectExpression") {
|
|
277
|
+
args[0].properties.forEach(prop => {
|
|
278
|
+
if (prop.key && prop.key.name === "description") {
|
|
279
|
+
checkStringForLanguage(prop.value, context);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function getHttpMethodType(decorators) {
|
|
288
|
+
if (!decorators) return null;
|
|
289
|
+
|
|
290
|
+
const httpMethods = ["Get", "Post", "Put", "Delete", "Patch"];
|
|
291
|
+
for (const decorator of decorators) {
|
|
292
|
+
const decoratorName = decorator.expression?.name || decorator.expression?.callee?.name;
|
|
293
|
+
if (httpMethods.includes(decoratorName)) {
|
|
294
|
+
return decoratorName.toLowerCase();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
ClassDeclaration(node) {
|
|
302
|
+
if (!isControllerClass(node)) return;
|
|
303
|
+
|
|
304
|
+
node.body.body.forEach(member => {
|
|
305
|
+
if (member.type === "MethodDefinition" &&
|
|
306
|
+
member.kind === "method" &&
|
|
307
|
+
member.accessibility === "public") {
|
|
308
|
+
|
|
309
|
+
const httpMethod = getHttpMethodType(member.decorators);
|
|
310
|
+
if (httpMethod) {
|
|
311
|
+
validateApiOperation(member);
|
|
312
|
+
validateApiResponseDecorators(member);
|
|
313
|
+
validateApiParamDecorators(member);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
},
|
|
320
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint-Regel: coordinate-naming
|
|
3
|
+
*
|
|
4
|
+
* Erweitert die standard id-length Regel um spezifische Vorschläge
|
|
5
|
+
* für Koordinaten-Variablen x und y.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
meta: {
|
|
10
|
+
type: "suggestion",
|
|
11
|
+
docs: {
|
|
12
|
+
description: "Enforce minimum identifier length with coordinate naming suggestions",
|
|
13
|
+
category: "Stylistic Issues",
|
|
14
|
+
recommended: false,
|
|
15
|
+
},
|
|
16
|
+
fixable: "code",
|
|
17
|
+
schema: [
|
|
18
|
+
{
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
min: {
|
|
22
|
+
type: "integer",
|
|
23
|
+
minimum: 0,
|
|
24
|
+
default: 2,
|
|
25
|
+
},
|
|
26
|
+
max: {
|
|
27
|
+
type: "integer",
|
|
28
|
+
},
|
|
29
|
+
exceptions: {
|
|
30
|
+
type: "array",
|
|
31
|
+
uniqueItems: true,
|
|
32
|
+
items: {
|
|
33
|
+
type: "string",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
additionalProperties: false,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
create (context) {
|
|
43
|
+
const options = context.options[0] || {};
|
|
44
|
+
const min = typeof options.min === "number" ? options.min : 2;
|
|
45
|
+
const max = typeof options.max === "number" ? options.max : Infinity;
|
|
46
|
+
const exceptions = new Set(options.exceptions || []);
|
|
47
|
+
|
|
48
|
+
const coordinateMapping = {
|
|
49
|
+
x: "positionX",
|
|
50
|
+
y: "positionY",
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Checks if an identifier is a coordinate variable that needs better naming
|
|
55
|
+
*/
|
|
56
|
+
function checkIdentifier (node, name) {
|
|
57
|
+
if (exceptions.has(name)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Only check for coordinate variables x and y
|
|
62
|
+
if (coordinateMapping[name]) {
|
|
63
|
+
context.report({
|
|
64
|
+
node,
|
|
65
|
+
message: `Identifier name '${name}' is too short (< ${min}). For coordinates, consider using '${coordinateMapping[name]}' instead.`,
|
|
66
|
+
fix (fixer) {
|
|
67
|
+
// Only fix if it's a variable declaration or parameter
|
|
68
|
+
const parent = node.parent;
|
|
69
|
+
|
|
70
|
+
// Check if this is a safe context to rename
|
|
71
|
+
if (
|
|
72
|
+
parent.type === "VariableDeclarator" ||
|
|
73
|
+
parent.type === "Property" ||
|
|
74
|
+
parent.type === "FunctionExpression" ||
|
|
75
|
+
parent.type === "ArrowFunctionExpression" ||
|
|
76
|
+
parent.type === "AssignmentPattern"
|
|
77
|
+
) {
|
|
78
|
+
return fixer.replaceText(node, coordinateMapping[name]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Don't autofix in complex contexts to avoid breaking code
|
|
82
|
+
return null;
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
Identifier (node) {
|
|
90
|
+
const name = node.name;
|
|
91
|
+
const parent = node.parent;
|
|
92
|
+
|
|
93
|
+
/*
|
|
94
|
+
* Don't check properties that don't have their name computed:
|
|
95
|
+
* - foo.bar
|
|
96
|
+
* - foo["bar"] (computed property names are checked)
|
|
97
|
+
* Don't check destructured identifiers (parameters):
|
|
98
|
+
* - function foo({ prop }) {}
|
|
99
|
+
* - function foo([item1, item2]) {}
|
|
100
|
+
* Don't check property shorthand (ES6 features):
|
|
101
|
+
* - const { x } = {}; (destructuring shorthand)
|
|
102
|
+
* - const { x: x } = {}; (destructuring alias)
|
|
103
|
+
*/
|
|
104
|
+
if (parent.type === "MemberExpression") {
|
|
105
|
+
if (parent.property === node && !parent.computed) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
} else if (parent.type === "Property") {
|
|
109
|
+
if (parent.method || parent.shorthand || parent.key === node) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
} else if (parent.type === "ImportDefaultSpecifier") {
|
|
113
|
+
return;
|
|
114
|
+
} else if (parent.type === "RestElement") {
|
|
115
|
+
return;
|
|
116
|
+
} else if (parent.type === "ExportSpecifier") {
|
|
117
|
+
return;
|
|
118
|
+
} else if (parent.type === "FunctionExpression" || parent.type === "ArrowFunctionExpression") {
|
|
119
|
+
// Don't check function names
|
|
120
|
+
if (parent.id === node) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Only check coordinate variables (x, y)
|
|
126
|
+
if (coordinateMapping[name]) {
|
|
127
|
+
checkIdentifier(node, name);
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint-Regel, die die Verwendung von MUI-Button statt unserer eigenen Button-Komponente prüft
|
|
3
|
+
*/
|
|
4
|
+
const noMuiButtonRule = {
|
|
5
|
+
meta: {
|
|
6
|
+
type: "suggestion",
|
|
7
|
+
docs: {
|
|
8
|
+
description: "Verbietet die direkte Verwendung von MUI-Button",
|
|
9
|
+
category: "Best Practices",
|
|
10
|
+
recommended: true,
|
|
11
|
+
},
|
|
12
|
+
fixable: "code",
|
|
13
|
+
messages: {
|
|
14
|
+
noMuiButton: "Bitte verwende die eigene Button-Komponente aus '@/components/UI/Button' statt der MUI-Button-Komponente.",
|
|
15
|
+
noMuiButtonImport: "Bitte verwende die eigene Button-Komponente aus '@/components/UI/Button' statt der MUI-Button-Komponente.",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
create(context) {
|
|
19
|
+
// Speichert Material-UI Button Importe
|
|
20
|
+
const muiButtonImports = new Set();
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
// Prüft alle Import-Deklarationen
|
|
24
|
+
ImportDeclaration(node) {
|
|
25
|
+
// Überprüft, ob aus @mui/material importiert wird
|
|
26
|
+
if (node.source.value === "@mui/material" || node.source.value === "@material-ui/core") {
|
|
27
|
+
// Überprüft alle Importe aus diesem Modul
|
|
28
|
+
node.specifiers.forEach((specifier) => {
|
|
29
|
+
// Wenn Button importiert wird
|
|
30
|
+
if (
|
|
31
|
+
(specifier.type === "ImportSpecifier" && specifier.imported && specifier.imported.name === "Button") ||
|
|
32
|
+
(specifier.type === "ImportDefaultSpecifier" && specifier.local.name === "Button")
|
|
33
|
+
) {
|
|
34
|
+
// Speichert den lokalen Namen des importierten Buttons
|
|
35
|
+
muiButtonImports.add(specifier.local.name);
|
|
36
|
+
|
|
37
|
+
context.report({
|
|
38
|
+
node,
|
|
39
|
+
messageId: "noMuiButtonImport",
|
|
40
|
+
fix(fixer) {
|
|
41
|
+
// Entfernt Button aus dem Import
|
|
42
|
+
// Behandelt verschiedene Fälle, je nachdem wie der Import aussieht
|
|
43
|
+
|
|
44
|
+
// Fall 1: Einziger Import aus @mui/material
|
|
45
|
+
if (node.specifiers.length === 1) {
|
|
46
|
+
return fixer.remove(node);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Fall 2: Benannter Import in geschweiften Klammern
|
|
50
|
+
if (specifier.type === "ImportSpecifier") {
|
|
51
|
+
const importText = context.getSourceCode().getText(node);
|
|
52
|
+
const buttonPattern = specifier.local.name === "Button"
|
|
53
|
+
? "Button" // Direkter Import: import { Button } from "@mui/material"
|
|
54
|
+
: `Button as ${specifier.local.name}`; // Alias Import: import { Button as MuiButton } from "@mui/material"
|
|
55
|
+
|
|
56
|
+
// Entfernt nur Button aus der Import-Liste
|
|
57
|
+
if (importText.includes(`, ${buttonPattern}`)) {
|
|
58
|
+
return fixer.replaceText(node, importText.replace(`, ${buttonPattern}`, ""));
|
|
59
|
+
} else if (importText.includes(`${buttonPattern}, `)) {
|
|
60
|
+
return fixer.replaceText(node, importText.replace(`${buttonPattern}, `, ""));
|
|
61
|
+
} else if (importText.includes(`{ ${buttonPattern} }`)) {
|
|
62
|
+
return fixer.remove(node);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Prüft alle JSX-Elemente
|
|
75
|
+
JSXOpeningElement(node) {
|
|
76
|
+
// Wenn ein Element mit dem Namen "Button" gefunden wird
|
|
77
|
+
if (node.name.type === "JSXIdentifier" && node.name.name === "Button") {
|
|
78
|
+
// Prüfe anhand des Scopes, ob dieser Button aus @mui/material stammt
|
|
79
|
+
const scope = context.getScope();
|
|
80
|
+
let currentScope = scope;
|
|
81
|
+
|
|
82
|
+
// Durchsuche alle Scopes nach dem Button-Variablennamen
|
|
83
|
+
while (currentScope) {
|
|
84
|
+
const buttonVar = currentScope.variables.find(v => v.name === "Button");
|
|
85
|
+
|
|
86
|
+
if (buttonVar) {
|
|
87
|
+
// Prüfe jede Definition der Button-Variable
|
|
88
|
+
for (const def of buttonVar.defs) {
|
|
89
|
+
// Wenn es sich um einen Import handelt
|
|
90
|
+
if (def.type === "ImportBinding" && def.parent && def.parent.source) {
|
|
91
|
+
const importSource = def.parent.source.value;
|
|
92
|
+
|
|
93
|
+
// Wenn der Import von @mui/material oder @material-ui/core stammt
|
|
94
|
+
if (importSource === "@mui/material" || importSource === "@material-ui/core") {
|
|
95
|
+
context.report({
|
|
96
|
+
node,
|
|
97
|
+
messageId: "noMuiButton",
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Wenn wir eine Definition gefunden haben, aber sie ist nicht von MUI,
|
|
105
|
+
// dann brechen wir die Suche ab (überschreibende Variable gefunden)
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Zum übergeordneten Scope wechseln
|
|
110
|
+
currentScope = currentScope.upper;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Außerdem prüfen wir anhand unserer Liste von MUI-Button-Importen
|
|
114
|
+
if (muiButtonImports.has("Button")) {
|
|
115
|
+
context.report({
|
|
116
|
+
node,
|
|
117
|
+
messageId: "noMuiButton",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
// Am Ende der Datei werden die gesammelten Informationen zurückgesetzt
|
|
124
|
+
"Program:exit": function() {
|
|
125
|
+
muiButtonImports.clear();
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export default {
|
|
132
|
+
rules: {
|
|
133
|
+
"no-mui-button": noMuiButtonRule
|
|
134
|
+
}
|
|
135
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dead Code Detection Rules für Backend
|
|
3
|
+
*
|
|
4
|
+
* Diese Regeln erkennen und verhindern toten Code in Backend-Anwendungen
|
|
5
|
+
* Ohne Frontend-spezifische Regeln (React, JSX)
|
|
6
|
+
*/
|
|
7
|
+
export default {
|
|
8
|
+
rules: {
|
|
9
|
+
// Ungenutzte Variablen und Funktionen
|
|
10
|
+
"@typescript-eslint/no-unused-vars": ["error", {
|
|
11
|
+
"vars": "all",
|
|
12
|
+
"args": "after-used",
|
|
13
|
+
"ignoreRestSiblings": true,
|
|
14
|
+
"argsIgnorePattern": "^_",
|
|
15
|
+
"varsIgnorePattern": "^_"
|
|
16
|
+
}],
|
|
17
|
+
|
|
18
|
+
// Ungenutzte private Klassen-Member
|
|
19
|
+
"no-unused-private-class-members": "error",
|
|
20
|
+
|
|
21
|
+
// Unreachable Code Detection
|
|
22
|
+
"no-unreachable": "error",
|
|
23
|
+
"no-unreachable-loop": "error",
|
|
24
|
+
|
|
25
|
+
// Unused Expressions
|
|
26
|
+
"@typescript-eslint/no-unused-expressions": ["error", {
|
|
27
|
+
"allowShortCircuit": true,
|
|
28
|
+
"allowTernary": true,
|
|
29
|
+
"allowTaggedTemplates": true
|
|
30
|
+
}],
|
|
31
|
+
|
|
32
|
+
// Dead Code durch logische Operatoren
|
|
33
|
+
"no-constant-condition": ["error", {
|
|
34
|
+
"checkLoops": false
|
|
35
|
+
}],
|
|
36
|
+
|
|
37
|
+
// Ungenutzte Labels
|
|
38
|
+
"no-unused-labels": "error",
|
|
39
|
+
|
|
40
|
+
// Ungenutzte Escape-Sequenzen in RegExp
|
|
41
|
+
"no-useless-escape": "error",
|
|
42
|
+
|
|
43
|
+
// Ungenutzte Return-Werte
|
|
44
|
+
"no-useless-return": "error",
|
|
45
|
+
|
|
46
|
+
// Redundante Konstruktor-Calls
|
|
47
|
+
"no-useless-constructor": "off", // Deaktiviert wegen TypeScript
|
|
48
|
+
"@typescript-eslint/no-useless-constructor": "error",
|
|
49
|
+
}
|
|
50
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dead Code Detection Rules für Frontend/Admin
|
|
3
|
+
*
|
|
4
|
+
* Diese Regeln erkennen und verhindern toten Code in Frontend-Anwendungen
|
|
5
|
+
*/
|
|
6
|
+
export default {
|
|
7
|
+
rules: {
|
|
8
|
+
// Import/Export Dead Code Detection - deaktiviert wegen Flat Config Kompatibilität
|
|
9
|
+
// "import/no-unused-modules": ["error", {
|
|
10
|
+
// "missingExports": true,
|
|
11
|
+
// "unusedExports": true,
|
|
12
|
+
// "ignoreExports": [
|
|
13
|
+
// "src/main.tsx",
|
|
14
|
+
// "src/App.tsx",
|
|
15
|
+
// "src/AppRouter.tsx",
|
|
16
|
+
// "src/vite-env.d.ts",
|
|
17
|
+
// "**/*.d.ts",
|
|
18
|
+
// "**/test/**",
|
|
19
|
+
// "**/tests/**",
|
|
20
|
+
// "**/*.test.*",
|
|
21
|
+
// "**/*.spec.*"
|
|
22
|
+
// ]
|
|
23
|
+
// }],
|
|
24
|
+
|
|
25
|
+
// Ungenutzte Variablen und Funktionen
|
|
26
|
+
"@typescript-eslint/no-unused-vars": ["error", {
|
|
27
|
+
"vars": "all",
|
|
28
|
+
"args": "after-used",
|
|
29
|
+
"ignoreRestSiblings": true,
|
|
30
|
+
"argsIgnorePattern": "^_",
|
|
31
|
+
"varsIgnorePattern": "^_"
|
|
32
|
+
}],
|
|
33
|
+
|
|
34
|
+
// Ungenutzte private Klassen-Member
|
|
35
|
+
"no-unused-private-class-members": "error",
|
|
36
|
+
|
|
37
|
+
// Unreachable Code Detection
|
|
38
|
+
"no-unreachable": "error",
|
|
39
|
+
"no-unreachable-loop": "error",
|
|
40
|
+
|
|
41
|
+
// Unused Expressions
|
|
42
|
+
"@typescript-eslint/no-unused-expressions": ["error", {
|
|
43
|
+
"allowShortCircuit": true,
|
|
44
|
+
"allowTernary": true,
|
|
45
|
+
"allowTaggedTemplates": true
|
|
46
|
+
}],
|
|
47
|
+
}
|
|
48
|
+
};
|