@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,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint-Regel: enforce-module-existence
|
|
3
|
+
*
|
|
4
|
+
* Überprüft, ob importierte Module tatsächlich existieren.
|
|
5
|
+
* Verhindert TS2307-Fehler zur Build-Zeit.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import path from "path";
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
rules: {
|
|
13
|
+
"enforce-module-existence": {
|
|
14
|
+
meta: {
|
|
15
|
+
type: "problem",
|
|
16
|
+
docs: {
|
|
17
|
+
description: "Enforce that imported modules actually exist",
|
|
18
|
+
category: "Type Safety",
|
|
19
|
+
recommended: true,
|
|
20
|
+
},
|
|
21
|
+
messages: {
|
|
22
|
+
moduleNotFound: "Module '{{moduleName}}' not found at path '{{resolvedPath}}'",
|
|
23
|
+
cannotResolveModule: "Cannot resolve module '{{moduleName}}'",
|
|
24
|
+
},
|
|
25
|
+
schema: [
|
|
26
|
+
{
|
|
27
|
+
type: "object",
|
|
28
|
+
properties: {
|
|
29
|
+
ignorePatterns: {
|
|
30
|
+
type: "array",
|
|
31
|
+
items: { type: "string" },
|
|
32
|
+
description: "Patterns to ignore",
|
|
33
|
+
default: []
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
additionalProperties: false
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
create(context) {
|
|
42
|
+
const options = context.options[0] || {};
|
|
43
|
+
const ignorePatterns = options.ignorePatterns || [];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolves a module path relative to the current file
|
|
47
|
+
*/
|
|
48
|
+
function resolveModulePath(moduleName, currentFilePath) {
|
|
49
|
+
const projectRoot = context.getCwd();
|
|
50
|
+
|
|
51
|
+
// Handle @/ imports
|
|
52
|
+
if (moduleName.startsWith("@/")) {
|
|
53
|
+
const relativePath = moduleName.substring(2);
|
|
54
|
+
return path.join(projectRoot, "src", relativePath);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Handle relative imports
|
|
58
|
+
if (moduleName.startsWith("./") || moduleName.startsWith("../")) {
|
|
59
|
+
const currentDir = path.dirname(currentFilePath);
|
|
60
|
+
return path.resolve(currentDir, moduleName);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Handle absolute imports (node_modules)
|
|
64
|
+
if (!moduleName.startsWith(".") && !moduleName.startsWith("/")) {
|
|
65
|
+
return path.join(projectRoot, "node_modules", moduleName);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Checks if a module exists
|
|
73
|
+
*/
|
|
74
|
+
function checkModuleExists(moduleName, currentFilePath) {
|
|
75
|
+
// Ignore certain patterns
|
|
76
|
+
for (const pattern of ignorePatterns) {
|
|
77
|
+
if (moduleName.match(new RegExp(pattern))) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Ignore npm packages (don't start with @/ or ./ or ../)
|
|
83
|
+
if (!moduleName.startsWith("@/") && !moduleName.startsWith("./") && !moduleName.startsWith("../")) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const resolvedPath = resolveModulePath(moduleName, currentFilePath);
|
|
88
|
+
if (!resolvedPath) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Check for .ts, .tsx, .js, .jsx files
|
|
93
|
+
const extensions = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js", "/index.jsx"];
|
|
94
|
+
|
|
95
|
+
for (const ext of extensions) {
|
|
96
|
+
const fullPath = resolvedPath + ext;
|
|
97
|
+
if (fs.existsSync(fullPath)) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check if it's a directory with index file
|
|
103
|
+
if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isDirectory()) {
|
|
104
|
+
const indexPath = path.join(resolvedPath, "index.ts");
|
|
105
|
+
if (fs.existsSync(indexPath)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return resolvedPath;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
ImportDeclaration(node) {
|
|
115
|
+
const moduleName = node.source.value;
|
|
116
|
+
const currentFilePath = context.getFilename();
|
|
117
|
+
|
|
118
|
+
const missingPath = checkModuleExists(moduleName, currentFilePath);
|
|
119
|
+
if (missingPath) {
|
|
120
|
+
context.report({
|
|
121
|
+
node: node.source,
|
|
122
|
+
messageId: "moduleNotFound",
|
|
123
|
+
data: {
|
|
124
|
+
moduleName,
|
|
125
|
+
resolvedPath: missingPath
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
CallExpression(node) {
|
|
132
|
+
// Check dynamic imports
|
|
133
|
+
if (node.callee.type === "Import" && node.arguments.length > 0) {
|
|
134
|
+
const arg = node.arguments[0];
|
|
135
|
+
if (arg.type === "Literal" && typeof arg.value === "string") {
|
|
136
|
+
const moduleName = arg.value;
|
|
137
|
+
const currentFilePath = context.getFilename();
|
|
138
|
+
|
|
139
|
+
const missingPath = checkModuleExists(moduleName, currentFilePath);
|
|
140
|
+
if (missingPath) {
|
|
141
|
+
context.report({
|
|
142
|
+
node: arg,
|
|
143
|
+
messageId: "moduleNotFound",
|
|
144
|
+
data: {
|
|
145
|
+
moduleName,
|
|
146
|
+
resolvedPath: missingPath
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const enforceNonEntityDtoCreateRule = {
|
|
2
|
+
meta: {
|
|
3
|
+
type: "problem",
|
|
4
|
+
docs: {
|
|
5
|
+
description: "Non-Entity-DTOs müssen create-Methoden bereitstellen und dürfen keine fromEntity-Methoden haben",
|
|
6
|
+
category: "Architecture",
|
|
7
|
+
recommended: true,
|
|
8
|
+
},
|
|
9
|
+
schema: [],
|
|
10
|
+
messages: {
|
|
11
|
+
missingCreateMethod: "Non-Entity-DTO-Klasse '{{dtoName}}' muss eine statische create-Methode bereitstellen",
|
|
12
|
+
forbiddenStaticMethods: "Non-Entity-DTO-Klasse '{{dtoName}}' darf nur eine statische 'create'-Methode haben. Die Methode '{{methodName}}' ist nicht erlaubt.",
|
|
13
|
+
forbiddenInstanceMethods: "Non-Entity-DTO-Klasse '{{dtoName}}' darf keine Instance-Methoden haben. Die Methode '{{methodName}}' ist nicht erlaubt.",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
create(context) {
|
|
17
|
+
const filename = context.getFilename();
|
|
18
|
+
|
|
19
|
+
// Prüfe, ob es sich um eine Non-Entity-DTO-Datei handelt
|
|
20
|
+
const isNonEntityDtoFile = filename.includes("/dto/") && !filename.includes("/dto/Entity/");
|
|
21
|
+
|
|
22
|
+
if (!isNonEntityDtoFile) {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let classNode = null;
|
|
27
|
+
let dtoClassName = "";
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
ClassDeclaration(node) {
|
|
31
|
+
classNode = node;
|
|
32
|
+
dtoClassName = node.id?.name;
|
|
33
|
+
},
|
|
34
|
+
ExportDefaultDeclaration(node) {
|
|
35
|
+
if (node.declaration.type === "ClassDeclaration") {
|
|
36
|
+
classNode = node.declaration;
|
|
37
|
+
dtoClassName = node.declaration.id?.name;
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"Program:exit"(node) {
|
|
41
|
+
if (!classNode || !dtoClassName) return;
|
|
42
|
+
|
|
43
|
+
// Ausnahme für BaseEntityDto (abstrakte Base-Klasse)
|
|
44
|
+
if (dtoClassName === "BaseEntityDto" && filename.endsWith("/dto/BaseEntityDto.ts")) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Hole alle Methoden (static und non-static)
|
|
49
|
+
const allMethods = classNode.body.body.filter(member =>
|
|
50
|
+
member.type === "MethodDefinition" &&
|
|
51
|
+
member.kind !== "constructor"
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const staticMethods = allMethods.filter(method => method.static === true);
|
|
55
|
+
const instanceMethods = allMethods.filter(method => method.static !== true);
|
|
56
|
+
|
|
57
|
+
const hasCreateMethod = staticMethods.some(method =>
|
|
58
|
+
method.key?.name === "create"
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Prüfe, ob create-Methode vorhanden ist
|
|
62
|
+
if (!hasCreateMethod) {
|
|
63
|
+
context.report({
|
|
64
|
+
node: classNode,
|
|
65
|
+
messageId: "missingCreateMethod",
|
|
66
|
+
data: { dtoName: dtoClassName }
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Prüfe statische Methoden: Nur 'create' ist erlaubt
|
|
71
|
+
const forbiddenStaticMethods = staticMethods.filter(method =>
|
|
72
|
+
method.key?.name && method.key.name !== "create"
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
forbiddenStaticMethods.forEach(method => {
|
|
76
|
+
context.report({
|
|
77
|
+
node: method,
|
|
78
|
+
messageId: "forbiddenStaticMethods",
|
|
79
|
+
data: {
|
|
80
|
+
dtoName: dtoClassName,
|
|
81
|
+
methodName: method.key.name
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Prüfe Instance-Methoden: KEINE sind erlaubt
|
|
87
|
+
instanceMethods.forEach(method => {
|
|
88
|
+
context.report({
|
|
89
|
+
node: method,
|
|
90
|
+
messageId: "forbiddenInstanceMethods",
|
|
91
|
+
data: {
|
|
92
|
+
dtoName: dtoClassName,
|
|
93
|
+
methodName: method.key?.name || "unknown"
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export default {
|
|
103
|
+
rules: {
|
|
104
|
+
"enforce-nonentity-dto-create": enforceNonEntityDtoCreateRule,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
meta: {
|
|
3
|
+
type: "problem",
|
|
4
|
+
docs: {
|
|
5
|
+
description: "Enforce consistent naming for playable entity references (playableRace, playableClass, playableFaction instead of race, class, faction)",
|
|
6
|
+
category: "Best Practices",
|
|
7
|
+
recommended: true,
|
|
8
|
+
},
|
|
9
|
+
messages: {
|
|
10
|
+
usePlayablePrefix: "Use '{{correctName}}' instead of '{{incorrectName}}' for playable entity references. Properties and parameters should use 'playable' prefix for consistency.",
|
|
11
|
+
},
|
|
12
|
+
fixable: "code",
|
|
13
|
+
schema: [],
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
create (context) {
|
|
17
|
+
const entityMapping = {
|
|
18
|
+
class: "playableClass",
|
|
19
|
+
faction: "playableFaction",
|
|
20
|
+
race: "playableRace",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function isAlreadyCorrect (name) {
|
|
24
|
+
return name && name.startsWith("playable");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function shouldSkipFile (filename) {
|
|
28
|
+
return filename.includes("Enum.ts") || filename.includes("enum/");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isRelevantIdentifier (node) {
|
|
32
|
+
const parent = node.parent;
|
|
33
|
+
if (!parent) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (parent.type === "Property" && parent.key === node) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (parent.type === "PropertyDefinition" && parent.key === node) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (parent.type === "VariableDeclarator" && parent.id === node) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (parent.type === "TSPropertySignature" && parent.key === node) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const grandparent = parent.parent;
|
|
54
|
+
if (grandparent) {
|
|
55
|
+
if (grandparent.type === "FunctionDeclaration" && grandparent.params && grandparent.params.includes(node)) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (grandparent.type === "MethodDefinition" && grandparent.value && grandparent.value.params && grandparent.value.params.includes(node)) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (grandparent.type === "ArrowFunctionExpression" && grandparent.params && grandparent.params.includes(node)) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
Identifier (node) {
|
|
73
|
+
const name = node.name;
|
|
74
|
+
|
|
75
|
+
if (!name || !(name in entityMapping)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (isAlreadyCorrect(name)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const filename = context.getFilename();
|
|
84
|
+
if (shouldSkipFile(filename)) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!isRelevantIdentifier(node)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const correctName = entityMapping[name];
|
|
93
|
+
|
|
94
|
+
context.report({
|
|
95
|
+
messageId: "usePlayablePrefix",
|
|
96
|
+
node,
|
|
97
|
+
data: {
|
|
98
|
+
correctName,
|
|
99
|
+
incorrectName: name,
|
|
100
|
+
},
|
|
101
|
+
fix (fixer) {
|
|
102
|
+
return fixer.replaceText(node, correctName);
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Enforce centralized token handling in repositories
|
|
3
|
+
* @author Echoes of Order Team
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
meta: {
|
|
10
|
+
type: "problem",
|
|
11
|
+
docs: {
|
|
12
|
+
description: "Enforce centralized token handling in repositories",
|
|
13
|
+
category: "Best Practices",
|
|
14
|
+
recommended: true,
|
|
15
|
+
},
|
|
16
|
+
messages: {
|
|
17
|
+
mustExtendAbstractRepo: "Repository '{{className}}' must extend AbstractRepo for centralized token handling",
|
|
18
|
+
noManualTokenCalls: "Manual token calls are forbidden. Use AbstractRepo for automatic token handling",
|
|
19
|
+
noDirectApiClient: "Direct ApiClient instantiation is forbidden. Use AbstractRepo instead",
|
|
20
|
+
noManualSetToken: "Manual setToken() calls are forbidden. Use AbstractRepo for automatic token handling",
|
|
21
|
+
noManualEnsureToken: "Manual ensureToken() calls are forbidden. Use AbstractRepo for automatic token handling",
|
|
22
|
+
},
|
|
23
|
+
fixable: null,
|
|
24
|
+
schema: [],
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
create(context) {
|
|
28
|
+
const filename = context.getFilename();
|
|
29
|
+
|
|
30
|
+
// Nur in Frontend-Repository-Dateien anwenden
|
|
31
|
+
if (!filename.includes("/infrastructure/") || !filename.includes("Repo")) {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let className = null;
|
|
36
|
+
let extendsAbstractRepo = false;
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
ClassDeclaration(node) {
|
|
40
|
+
className = node.id.name;
|
|
41
|
+
extendsAbstractRepo = node.superClass &&
|
|
42
|
+
node.superClass.type === "Identifier" &&
|
|
43
|
+
node.superClass.name === "AbstractRepo";
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
NewExpression(node) {
|
|
47
|
+
if (node.callee.type === "Identifier" && node.callee.name === "ApiClient") {
|
|
48
|
+
context.report({
|
|
49
|
+
node,
|
|
50
|
+
messageId: "noDirectApiClient",
|
|
51
|
+
data: { className },
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
CallExpression(node) {
|
|
57
|
+
const callee = node.callee;
|
|
58
|
+
|
|
59
|
+
// Prüfe auf manuelle Token-Aufrufe
|
|
60
|
+
if (callee.type === "MemberExpression" && callee.property.type === "Identifier") {
|
|
61
|
+
const methodName = callee.property.name;
|
|
62
|
+
|
|
63
|
+
if (methodName === "ensureToken" || methodName === "ensureAuthenticated") {
|
|
64
|
+
context.report({
|
|
65
|
+
node,
|
|
66
|
+
messageId: "noManualTokenCalls",
|
|
67
|
+
data: { className },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (methodName === "setToken") {
|
|
72
|
+
context.report({
|
|
73
|
+
node,
|
|
74
|
+
messageId: "noManualSetToken",
|
|
75
|
+
data: { className },
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
"ClassDeclaration:exit"(node) {
|
|
82
|
+
if (className && !extendsAbstractRepo) {
|
|
83
|
+
context.report({
|
|
84
|
+
node: node.id,
|
|
85
|
+
messageId: "mustExtendAbstractRepo",
|
|
86
|
+
data: { className },
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
};
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Enforce that Request DTOs cannot have EntityDto properties
|
|
3
|
+
* @author Echoes of Order Team
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
//------------------------------------------------------------------------------
|
|
7
|
+
// Rule Definition
|
|
8
|
+
//------------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
11
|
+
export default {
|
|
12
|
+
meta: {
|
|
13
|
+
type: "problem",
|
|
14
|
+
docs: {
|
|
15
|
+
description: "Request DTOs cannot have EntityDto properties",
|
|
16
|
+
category: "Architecture",
|
|
17
|
+
recommended: true,
|
|
18
|
+
},
|
|
19
|
+
fixable: null,
|
|
20
|
+
schema: [],
|
|
21
|
+
messages: {
|
|
22
|
+
requestDtoEntityDtoForbidden: "Request DTO '{{className}}' cannot have EntityDto property '{{propertyName}}'. Use a regular DTO instead.",
|
|
23
|
+
requestDtoEntityDtoImportForbidden: "Request DTO '{{className}}' cannot import EntityDto '{{importName}}'. Use a regular DTO instead.",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
create(context) {
|
|
28
|
+
//--------------------------------------------------------------------------
|
|
29
|
+
// Helpers
|
|
30
|
+
//--------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Check if a file is a Request DTO
|
|
34
|
+
* @param {string} filename - The filename
|
|
35
|
+
* @returns {boolean} True if it's a Request DTO
|
|
36
|
+
*/
|
|
37
|
+
function isRequestDto(filename) {
|
|
38
|
+
// Allow specific test fixtures for testing
|
|
39
|
+
if (filename.includes("test-fixtures")) {
|
|
40
|
+
return filename.includes("InvalidRequestDto.ts") ||
|
|
41
|
+
filename.includes("ValidRequestDtoNoEntityDto.ts");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return filename.includes("/dto/Request/") ||
|
|
45
|
+
filename.includes("RequestDto.ts") ||
|
|
46
|
+
filename.includes("RequestDto.js");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Check if a class name indicates it's a Request DTO
|
|
51
|
+
* @param {string} className - The class name
|
|
52
|
+
* @returns {boolean} True if it's a Request DTO
|
|
53
|
+
*/
|
|
54
|
+
function isRequestDtoClass(className) {
|
|
55
|
+
return className.endsWith("RequestDto") ||
|
|
56
|
+
className.endsWith("Request");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Check if an import is an EntityDto
|
|
61
|
+
* @param {string} importName - The import name
|
|
62
|
+
* @returns {boolean} True if it's an EntityDto
|
|
63
|
+
*/
|
|
64
|
+
function isEntityDtoImport(importName) {
|
|
65
|
+
return importName.endsWith("EntityDto") ||
|
|
66
|
+
importName.includes("EntityDto");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Check if a property type is an EntityDto
|
|
71
|
+
* @param {ASTNode} property - The property node
|
|
72
|
+
* @returns {boolean} True if it's an EntityDto
|
|
73
|
+
*/
|
|
74
|
+
function isEntityDtoProperty(property) {
|
|
75
|
+
if (!property.typeAnnotation || !property.typeAnnotation.typeAnnotation) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const typeAnnotation = property.typeAnnotation.typeAnnotation;
|
|
80
|
+
|
|
81
|
+
// Check for direct EntityDto type
|
|
82
|
+
if (typeAnnotation.type === "TSTypeReference" &&
|
|
83
|
+
typeAnnotation.typeName &&
|
|
84
|
+
typeAnnotation.typeName.type === "Identifier") {
|
|
85
|
+
return isEntityDtoImport(typeAnnotation.typeName.name);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check for array of EntityDto
|
|
89
|
+
if (typeAnnotation.type === "TSTypeReference" &&
|
|
90
|
+
typeAnnotation.typeName &&
|
|
91
|
+
typeAnnotation.typeName.type === "TSQualifiedName") {
|
|
92
|
+
const qualifiedName = typeAnnotation.typeName;
|
|
93
|
+
if (qualifiedName.right && qualifiedName.right.type === "Identifier") {
|
|
94
|
+
return isEntityDtoImport(qualifiedName.right.name);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
//--------------------------------------------------------------------------
|
|
102
|
+
// Public
|
|
103
|
+
//--------------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
// Check imports in Request DTO files
|
|
107
|
+
ImportDeclaration(node) {
|
|
108
|
+
const filename = context.getFilename();
|
|
109
|
+
|
|
110
|
+
if (!isRequestDto(filename)) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Check if importing an EntityDto
|
|
115
|
+
if (node.specifiers) {
|
|
116
|
+
for (const specifier of node.specifiers) {
|
|
117
|
+
if (specifier.type === "ImportDefaultSpecifier" ||
|
|
118
|
+
specifier.type === "ImportSpecifier") {
|
|
119
|
+
const importName = specifier.imported ?
|
|
120
|
+
specifier.imported.name :
|
|
121
|
+
specifier.local.name;
|
|
122
|
+
|
|
123
|
+
if (isEntityDtoImport(importName)) {
|
|
124
|
+
context.report({
|
|
125
|
+
node: specifier,
|
|
126
|
+
messageId: "requestDtoEntityDtoImportForbidden",
|
|
127
|
+
data: {
|
|
128
|
+
className: "Unknown", // Will be updated when we find the class
|
|
129
|
+
importName: importName,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
// Check class properties in Request DTO files
|
|
139
|
+
ClassDeclaration(node) {
|
|
140
|
+
const filename = context.getFilename();
|
|
141
|
+
|
|
142
|
+
if (!isRequestDto(filename) && !isRequestDtoClass(node.id.name)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check all class properties
|
|
147
|
+
for (const member of node.body.body) {
|
|
148
|
+
if (member.type === "TSParameterProperty" ||
|
|
149
|
+
member.type === "ClassProperty" ||
|
|
150
|
+
member.type === "PropertyDefinition") {
|
|
151
|
+
|
|
152
|
+
if (isEntityDtoProperty(member)) {
|
|
153
|
+
const propertyName = member.key.name || member.key.value;
|
|
154
|
+
|
|
155
|
+
context.report({
|
|
156
|
+
node: member,
|
|
157
|
+
messageId: "requestDtoEntityDtoForbidden",
|
|
158
|
+
data: {
|
|
159
|
+
className: node.id.name,
|
|
160
|
+
propertyName: propertyName,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
// Check method parameters in Request DTO files
|
|
169
|
+
MethodDefinition(node) {
|
|
170
|
+
const filename = context.getFilename();
|
|
171
|
+
|
|
172
|
+
if (!isRequestDto(filename)) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Check method parameters
|
|
177
|
+
if (node.value && node.value.params) {
|
|
178
|
+
for (const param of node.value.params) {
|
|
179
|
+
if (param.typeAnnotation &&
|
|
180
|
+
param.typeAnnotation.typeAnnotation &&
|
|
181
|
+
isEntityDtoProperty(param)) {
|
|
182
|
+
|
|
183
|
+
const methodName = node.key.name || node.key.value;
|
|
184
|
+
|
|
185
|
+
context.report({
|
|
186
|
+
node: param,
|
|
187
|
+
messageId: "requestDtoEntityDtoForbidden",
|
|
188
|
+
data: {
|
|
189
|
+
className: "Unknown", // Will be updated when we find the class
|
|
190
|
+
propertyName: `${methodName} parameter`,
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
|