@echoes-of-order/eslint-config 1.121.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/CHANGELOG.md +1093 -0
  2. package/configs/.gitkeep +1 -0
  3. package/configs/admin.js +203 -0
  4. package/configs/api-client.js +46 -0
  5. package/configs/backend.js +895 -0
  6. package/configs/domains.js +123 -0
  7. package/configs/frontend.js +30 -0
  8. package/configs/image-server.js +26 -0
  9. package/configs/ionos-proxy.js +372 -0
  10. package/configs/nestjs.js +156 -0
  11. package/configs/node.js +92 -0
  12. package/configs/react.js +111 -0
  13. package/configs/wiki.js +42 -0
  14. package/index.js +39 -0
  15. package/package.json +85 -0
  16. package/rules/.gitkeep +1 -0
  17. package/rules/__tests__/analyze-relation-usage.test.js.disabled +300 -0
  18. package/rules/__tests__/complexity.test.js.disabled +300 -0
  19. package/rules/__tests__/enforce-dto-factory-in-services.integration.test.js +226 -0
  20. package/rules/__tests__/enforce-dto-factory-in-services.test.js +177 -0
  21. package/rules/__tests__/enforce-entity-dto-create-no-id.integration.test.js +18 -0
  22. package/rules/__tests__/enforce-function-argument-count.test.js.disabled +300 -0
  23. package/rules/__tests__/enforce-repository-token-handling.test.js +58 -0
  24. package/rules/__tests__/english-only-code-strings.test.js.disabled +300 -0
  25. package/rules/__tests__/eslint-rules.integration.test.ts +350 -0
  26. package/rules/__tests__/integration-test-controller-response-dto.js +261 -0
  27. package/rules/__tests__/integration-test-dto-factory-in-services.js +260 -0
  28. package/rules/__tests__/integration-test-no-entity-type-casting.js +161 -0
  29. package/rules/__tests__/integration-test-typeorm-naming-conventions.js +501 -0
  30. package/rules/__tests__/test-config.js +33 -0
  31. package/rules/admin-controller-security.js +180 -0
  32. package/rules/analyze-relation-usage.js +687 -0
  33. package/rules/api-response-dto.js +174 -0
  34. package/rules/auth-guard-required.js +142 -0
  35. package/rules/backend-specific.js +36 -0
  36. package/rules/best-practices.js +421 -0
  37. package/rules/complexity.js +20 -0
  38. package/rules/controller-architecture.js +340 -0
  39. package/rules/controller-naming-conventions.js +190 -0
  40. package/rules/controller-readonly-restriction.js +148 -0
  41. package/rules/controller-swagger-complete.js +312 -0
  42. package/rules/controller-swagger-docs.js +119 -0
  43. package/rules/controller-swagger-english.js +320 -0
  44. package/rules/coordinate-naming.js +132 -0
  45. package/rules/custom-mui-button.js +135 -0
  46. package/rules/dead-code-detection-backend.js +50 -0
  47. package/rules/dead-code-detection-frontend.js +48 -0
  48. package/rules/dead-code-detection.js +71 -0
  49. package/rules/debug-controller-response-dto.js +79 -0
  50. package/rules/deprecate.js +8 -0
  51. package/rules/dto-annotation-property-consistency.js +111 -0
  52. package/rules/dto-entity-mapping-completeness.js +688 -0
  53. package/rules/dto-entity-swagger-separation.js +265 -0
  54. package/rules/dto-entity-type-consistency.js +352 -0
  55. package/rules/dto-entity-type-matching.js +519 -0
  56. package/rules/dto-naming-convention.js +98 -0
  57. package/rules/dto-visibility-modifiers.js +159 -0
  58. package/rules/enforce-api-versioning.js +122 -0
  59. package/rules/enforce-app-module-registration.js +179 -0
  60. package/rules/enforce-basecontroller.js +152 -0
  61. package/rules/enforce-body-request-dto.js +141 -0
  62. package/rules/enforce-controller-response-dto.js +349 -0
  63. package/rules/enforce-custom-error-classes.js +242 -0
  64. package/rules/enforce-database-transaction-safety.js +179 -0
  65. package/rules/enforce-dto-constructor.js +95 -0
  66. package/rules/enforce-dto-create-parameter-types.js +170 -0
  67. package/rules/enforce-dto-create-pattern.js +274 -0
  68. package/rules/enforce-dto-entity-creation.js +164 -0
  69. package/rules/enforce-dto-factory-in-services.js +188 -0
  70. package/rules/enforce-dto-from-entity-method.js +47 -0
  71. package/rules/enforce-dto-from-entity.js +314 -0
  72. package/rules/enforce-dto-naming-conventions.js +212 -0
  73. package/rules/enforce-dto-naming.js +176 -0
  74. package/rules/enforce-dto-usage-simple.js +114 -0
  75. package/rules/enforce-dto-usage.js +407 -0
  76. package/rules/enforce-eager-translation-loading.js +178 -0
  77. package/rules/enforce-entity-creation-pattern.js +137 -0
  78. package/rules/enforce-entity-dto-convert-method.js +157 -0
  79. package/rules/enforce-entity-dto-create-no-id.js +117 -0
  80. package/rules/enforce-entity-dto-extends-base.js +141 -0
  81. package/rules/enforce-entity-dto-from-request-dto-structure.js +113 -0
  82. package/rules/enforce-entity-dto-fromentity-complex.js +69 -0
  83. package/rules/enforce-entity-dto-fromentity-simple.js +69 -0
  84. package/rules/enforce-entity-dto-fromrequestdto-structure.js +262 -0
  85. package/rules/enforce-entity-dto-methods-restriction.js +159 -0
  86. package/rules/enforce-entity-dto-no-request-dto.js +102 -0
  87. package/rules/enforce-entity-dto-optional-auto-fields.js +101 -0
  88. package/rules/enforce-entity-dto-required-methods.js +248 -0
  89. package/rules/enforce-entity-factory-pattern.js +180 -0
  90. package/rules/enforce-entity-instantiation-in-toentity.js +125 -0
  91. package/rules/enforce-enum-for-playable-entities.js +95 -0
  92. package/rules/enforce-error-handling.js +257 -0
  93. package/rules/enforce-explicit-dto-types.js +118 -0
  94. package/rules/enforce-from-request-dto-usage.js +62 -0
  95. package/rules/enforce-generic-entity-dto.js +71 -0
  96. package/rules/enforce-inject-decorator.js +133 -0
  97. package/rules/enforce-lazy-type-loading.js +170 -0
  98. package/rules/enforce-module-existence.js +157 -0
  99. package/rules/enforce-nonentity-dto-create.js +107 -0
  100. package/rules/enforce-playable-entity-naming.js +108 -0
  101. package/rules/enforce-repository-token-handling.js +92 -0
  102. package/rules/enforce-request-dto-no-entity-dto.js +201 -0
  103. package/rules/enforce-request-dto-required-fields.js +217 -0
  104. package/rules/enforce-result-pattern.js +45 -0
  105. package/rules/enforce-service-relation-loading.js +116 -0
  106. package/rules/enforce-test-coverage.js +96 -0
  107. package/rules/enforce-toentity-conditional-assignment.js +132 -0
  108. package/rules/enforce-translations-required.js +203 -0
  109. package/rules/enforce-typeorm-naming-conventions.js +366 -0
  110. package/rules/enforce-vite-health-metrics.js +240 -0
  111. package/rules/entity-required-properties.js +321 -0
  112. package/rules/entity-to-dto-test.js +73 -0
  113. package/rules/enum-database-validation.js +149 -0
  114. package/rules/errors.js +190 -0
  115. package/rules/es6.js +204 -0
  116. package/rules/eslint-plugin-no-comments.js +44 -0
  117. package/rules/filename-class-name-match.js +62 -0
  118. package/rules/forbid-fromentity-outside-entity-folder.js +237 -0
  119. package/rules/function-params-newline.js +111 -0
  120. package/rules/imports.js +264 -0
  121. package/rules/jest.js +13 -0
  122. package/rules/jsx.js +16 -0
  123. package/rules/max-classes-per-file.js +49 -0
  124. package/rules/multiline-formatting.js +146 -0
  125. package/rules/no-blank-lines-between-decorators-and-properties.js +95 -0
  126. package/rules/no-comments.js +62 -0
  127. package/rules/no-dto-constructors.js +126 -0
  128. package/rules/no-dto-default-values.js +220 -0
  129. package/rules/no-dto-duplicates.js +127 -0
  130. package/rules/no-dto-in-entity.js +99 -0
  131. package/rules/no-dynamic-import-in-types.js +71 -0
  132. package/rules/no-dynamic-imports-in-controllers.js +95 -0
  133. package/rules/no-entity-imports-in-controllers.js +101 -0
  134. package/rules/no-entity-in-swagger-docs.js +139 -0
  135. package/rules/no-entity-type-casting.js +104 -0
  136. package/rules/no-fetch.js +77 -0
  137. package/rules/no-import-meta-env.js +151 -0
  138. package/rules/no-inline-styles.js +5 -0
  139. package/rules/no-magic-values.js +85 -0
  140. package/rules/no-partial-type.js +168 -0
  141. package/rules/no-relative-imports.js +31 -0
  142. package/rules/no-tsyringe.js +181 -0
  143. package/rules/no-type-assertion.js +175 -0
  144. package/rules/no-undefined-entity-properties.js +121 -0
  145. package/rules/node.js +44 -0
  146. package/rules/perfectionist.js +50 -0
  147. package/rules/performance-minimal.js +155 -0
  148. package/rules/performance.js +44 -0
  149. package/rules/pino-logger-format.js +200 -0
  150. package/rules/prefer-dto-classes.js +112 -0
  151. package/rules/prefer-dto-create-method.js +225 -0
  152. package/rules/promises.js +17 -0
  153. package/rules/react-hooks.js +15 -0
  154. package/rules/react.js +28 -0
  155. package/rules/regexp.js +70 -0
  156. package/rules/require-dto-response.js +81 -0
  157. package/rules/require-valid-relations.js +388 -0
  158. package/rules/result-pattern.js +162 -0
  159. package/rules/security.js +37 -0
  160. package/rules/service-architecture.js +148 -0
  161. package/rules/sonarjs.js +26 -0
  162. package/rules/strict.js +7 -0
  163. package/rules/style.js +611 -0
  164. package/rules/stylistic.js +93 -0
  165. package/rules/typeorm-column-type-validation.js +224 -0
  166. package/rules/typescript-advanced.js +113 -0
  167. package/rules/typescript-core.js +111 -0
  168. package/rules/typescript.js +146 -0
  169. package/rules/unicorn.js +168 -0
  170. package/rules/variables.js +51 -0
  171. package/rules/websocket-architecture.js +115 -0
@@ -0,0 +1,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
+