@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,159 @@
1
+ /**
2
+ * @fileoverview Enforce that Entity-specific methods (toEntity, fromEntity, fromEntityArray) are only allowed in EntityDto classes
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: "Entity-specific methods (toEntity, fromEntity, fromEntityArray) are only allowed in EntityDto classes",
16
+ category: "Architecture",
17
+ recommended: true,
18
+ },
19
+ fixable: null,
20
+ schema: [],
21
+ messages: {
22
+ entityMethodInNonEntityDto: "Method '{{methodName}}' is only allowed in Entity DTOs (classes ending with 'EntityDto' in /dto/Entity/). Found in '{{className}}' which is a {{dtoType}}.",
23
+ entityMethodMustBeStatic: "Method '{{methodName}}' in Entity DTO '{{className}}' must be static.",
24
+ },
25
+ },
26
+
27
+ create(context) {
28
+ //--------------------------------------------------------------------------
29
+ // Helpers
30
+ //--------------------------------------------------------------------------
31
+
32
+ const ENTITY_SPECIFIC_METHODS = [
33
+ "toEntity",
34
+ "fromEntity",
35
+ "fromEntityArray",
36
+ ];
37
+
38
+ /**
39
+ * Check if a file is an Entity DTO
40
+ * @param {string} filename - The filename
41
+ * @returns {boolean} True if it's an Entity DTO
42
+ */
43
+ function isEntityDtoFile(filename) {
44
+ return (
45
+ (filename.includes("/dto/Entity/") || filename.includes("/test-fixtures/")) &&
46
+ (filename.endsWith("EntityDto.ts") || filename.includes("EntityDto"))
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Check if a class name indicates it's an Entity DTO
52
+ * @param {string} className - The class name
53
+ * @returns {boolean} True if it's an Entity DTO
54
+ */
55
+ function isEntityDtoClass(className) {
56
+ return className.endsWith("EntityDto");
57
+ }
58
+
59
+ /**
60
+ * Get the DTO type from class name
61
+ * @param {string} className - The class name
62
+ * @returns {string} The DTO type
63
+ */
64
+ function getDtoType(className) {
65
+ if (className.endsWith("RequestDto")) {
66
+ return "Request DTO";
67
+ }
68
+
69
+ if (className.endsWith("ResponseDto")) {
70
+ return "Response DTO";
71
+ }
72
+
73
+ if (className.endsWith("FilterDto")) {
74
+ return "Filter DTO";
75
+ }
76
+
77
+ if (className.endsWith("ConfigDto")) {
78
+ return "Config DTO";
79
+ }
80
+
81
+ if (className.endsWith("Dto")) {
82
+ return "DTO";
83
+ }
84
+
85
+ return "non-Entity DTO";
86
+ }
87
+
88
+ /**
89
+ * Check if a method is an entity-specific method
90
+ * @param {string} methodName - The method name
91
+ * @returns {boolean} True if it's an entity-specific method
92
+ */
93
+ function isEntitySpecificMethod(methodName) {
94
+ return ENTITY_SPECIFIC_METHODS.includes(methodName);
95
+ }
96
+
97
+ //--------------------------------------------------------------------------
98
+ // Public
99
+ //--------------------------------------------------------------------------
100
+
101
+ return {
102
+ MethodDefinition(node) {
103
+ const filename = context.getFilename();
104
+ const methodName = node.key.name;
105
+
106
+ // Only check entity-specific methods
107
+ if (!isEntitySpecificMethod(methodName)) {
108
+ return;
109
+ }
110
+
111
+ // Find the ClassDeclaration by walking up the AST
112
+ let parent = node.parent;
113
+ while (parent && parent.type !== "ClassDeclaration") {
114
+ parent = parent.parent;
115
+ }
116
+
117
+ if (!parent || !parent.id) {
118
+ return;
119
+ }
120
+
121
+ const className = parent.id.name;
122
+ const isEntityDto = isEntityDtoFile(filename) && isEntityDtoClass(className);
123
+
124
+ // If it's NOT an Entity DTO, report error
125
+ if (!isEntityDto) {
126
+ const dtoType = getDtoType(className);
127
+
128
+ context.report({
129
+ node,
130
+ messageId: "entityMethodInNonEntityDto",
131
+ data: {
132
+ methodName,
133
+ className,
134
+ dtoType,
135
+ },
136
+ });
137
+
138
+ return;
139
+ }
140
+
141
+ // If it IS an Entity DTO, ensure the method is static
142
+ if (isEntityDto && !node.static) {
143
+ context.report({
144
+ node,
145
+ messageId: "entityMethodMustBeStatic",
146
+ data: {
147
+ methodName,
148
+ className,
149
+ },
150
+ });
151
+ }
152
+ },
153
+ };
154
+ },
155
+ };
156
+
157
+
158
+
159
+
@@ -0,0 +1,102 @@
1
+ export default {
2
+ meta: {
3
+ type: "problem",
4
+ docs: {
5
+ description: "Entity DTOs dürfen keine Request DTOs als Properties haben",
6
+ category: "Best Practices",
7
+ recommended: true,
8
+ },
9
+ fixable: null,
10
+ schema: [],
11
+ messages: {
12
+ requestDtoInEntityDto: "Entity-DTO '{{dtoName}}' darf keine Request-DTO '{{requestDtoName}}' als Property haben. Verwende stattdessen normale DTOs oder Entity-DTOs.",
13
+ },
14
+ },
15
+ create(context) {
16
+ const filename = context.getFilename();
17
+
18
+ // Nur in Entity-DTO-Dateien anwenden
19
+ if (!filename.includes("/dto/Entity/") && !filename.includes("test-fixtures")) {
20
+ return {};
21
+ }
22
+
23
+ // Prüfe ob es eine Entity-DTO-Datei ist
24
+ if (!filename.endsWith("EntityDto.ts") && !filename.includes("EntityDto")) {
25
+ return {};
26
+ }
27
+
28
+ return {
29
+ ClassDeclaration(node) {
30
+ const className = node.id.name;
31
+
32
+ // Prüfe alle Properties der Klasse
33
+ node.body.body.forEach(member => {
34
+ if (member.type === "PropertyDefinition" ||
35
+ (member.type === "TSParameterProperty" && member.parameter.type === "TSTypeAnnotation")) {
36
+
37
+ let typeAnnotation = null;
38
+ if (member.type === "PropertyDefinition" && member.typeAnnotation) {
39
+ typeAnnotation = member.typeAnnotation.typeAnnotation;
40
+ } else if (member.type === "TSParameterProperty" && member.parameter.typeAnnotation) {
41
+ typeAnnotation = member.parameter.typeAnnotation.typeAnnotation;
42
+ }
43
+
44
+ if (typeAnnotation) {
45
+ // Prüfe auf Request-DTO-Typen
46
+ if (typeAnnotation.type === "TSTypeReference") {
47
+ const typeName = typeAnnotation.typeName.name;
48
+ if (typeName && typeName.endsWith("RequestDto")) {
49
+ context.report({
50
+ node: member,
51
+ messageId: "requestDtoInEntityDto",
52
+ data: {
53
+ dtoName: className,
54
+ requestDtoName: typeName,
55
+ },
56
+ });
57
+ }
58
+ }
59
+
60
+ // Prüfe auf Array-Typen mit Request-DTOs
61
+ if (typeAnnotation.type === "TSArrayType") {
62
+ const elementType = typeAnnotation.elementType;
63
+ if (elementType.type === "TSTypeReference") {
64
+ const typeName = elementType.typeName.name;
65
+ if (typeName && typeName.endsWith("RequestDto")) {
66
+ context.report({
67
+ node: member,
68
+ messageId: "requestDtoInEntityDto",
69
+ data: {
70
+ dtoName: className,
71
+ requestDtoName: typeName,
72
+ },
73
+ });
74
+ }
75
+ }
76
+ }
77
+
78
+ // Prüfe auf Union-Typen
79
+ if (typeAnnotation.type === "TSUnionType") {
80
+ typeAnnotation.types.forEach(unionType => {
81
+ if (unionType.type === "TSTypeReference") {
82
+ const typeName = unionType.typeName.name;
83
+ if (typeName && typeName.endsWith("RequestDto")) {
84
+ context.report({
85
+ node: member,
86
+ messageId: "requestDtoInEntityDto",
87
+ data: {
88
+ dtoName: className,
89
+ requestDtoName: typeName,
90
+ },
91
+ });
92
+ }
93
+ }
94
+ });
95
+ }
96
+ }
97
+ }
98
+ });
99
+ },
100
+ };
101
+ },
102
+ };
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @fileoverview Enforce that id, createdAt, and updatedAt are optional in Entity DTOs
3
+ * @author Echoes of Order Team
4
+ */
5
+
6
+ //------------------------------------------------------------------------------
7
+ // Rule Definition
8
+ //------------------------------------------------------------------------------
9
+
10
+ /** @type {import('eslint').Rule.RuleModule} */
11
+ const enforceEntityDtoOptionalAutoFieldsRule = {
12
+ meta: {
13
+ type: "problem",
14
+ docs: {
15
+ description: "id, createdAt, and updatedAt must be optional in Entity DTOs (generated by database/TypeORM)",
16
+ category: "Architecture",
17
+ recommended: true,
18
+ },
19
+ fixable: "code",
20
+ schema: [],
21
+ messages: {
22
+ idMustBeOptional: "Property 'id' in Entity DTO '{{className}}' must be optional (id?: string). The ID is generated by the database.",
23
+ createdAtMustBeOptional: "Property 'createdAt' in Entity DTO '{{className}}' must be optional (createdAt?: Date). The field is set by TypeORM automatically.",
24
+ updatedAtMustBeOptional: "Property 'updatedAt' in Entity DTO '{{className}}' must be optional (updatedAt?: Date). The field is set by TypeORM automatically.",
25
+ },
26
+ },
27
+
28
+ create(context) {
29
+ const filename = context.getFilename();
30
+
31
+ // Only check Entity DTO files
32
+ const isEntityDtoFile = filename.includes("/dto/Entity/") && filename.endsWith("EntityDto.ts");
33
+
34
+ if (!isEntityDtoFile) {
35
+ return {};
36
+ }
37
+
38
+ // Skip BaseEntityDto
39
+ if (filename.endsWith("/dto/BaseEntityDto.ts")) {
40
+ return {};
41
+ }
42
+
43
+ const AUTO_GENERATED_FIELDS = {
44
+ "id": "idMustBeOptional",
45
+ "createdAt": "createdAtMustBeOptional",
46
+ "updatedAt": "updatedAtMustBeOptional",
47
+ };
48
+
49
+ return {
50
+ PropertyDefinition(node) {
51
+ // Find the containing class
52
+ let parent = node.parent;
53
+ while (parent && parent.type !== "ClassDeclaration") {
54
+ parent = parent.parent;
55
+ }
56
+
57
+ if (!parent || !parent.id) {
58
+ return;
59
+ }
60
+
61
+ const className = parent.id.name;
62
+
63
+ // Only check Entity DTOs
64
+ if (!className.endsWith("EntityDto")) {
65
+ return;
66
+ }
67
+
68
+ const propertyName = node.key?.name;
69
+
70
+ // Check if this is one of the auto-generated fields
71
+ if (propertyName && AUTO_GENERATED_FIELDS[propertyName]) {
72
+ // Check if property is optional
73
+ const isOptional = node.optional === true;
74
+
75
+ if (!isOptional) {
76
+ context.report({
77
+ node,
78
+ messageId: AUTO_GENERATED_FIELDS[propertyName],
79
+ data: { className },
80
+ fix(fixer) {
81
+ // Find the property name in the source
82
+ const sourceCode = context.getSourceCode();
83
+ const propertyText = sourceCode.getText(node.key);
84
+
85
+ // Add ? after property name
86
+ return fixer.replaceText(node.key, `${propertyText}?`);
87
+ },
88
+ });
89
+ }
90
+ }
91
+ },
92
+ };
93
+ },
94
+ };
95
+
96
+ export default {
97
+ rules: {
98
+ "enforce-entity-dto-optional-auto-fields": enforceEntityDtoOptionalAutoFieldsRule,
99
+ },
100
+ };
101
+
@@ -0,0 +1,248 @@
1
+ /**
2
+ * @fileoverview Enforce required methods in Entity DTOs and validate fromRequestDto parameter types
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: "Entity DTOs must have fromEntity and toEntity methods. fromEntityArray and fromRequestDto are optional but must use correct types.",
16
+ category: "Architecture",
17
+ recommended: true,
18
+ },
19
+ fixable: null,
20
+ schema: [],
21
+ messages: {
22
+ missingFromEntity: "Entity DTO '{{className}}' must have a static 'fromEntity' method.",
23
+ missingToEntity: "Entity DTO '{{className}}' must have a static 'toEntity' method.",
24
+ invalidFromRequestDtoParameter: "Entity DTO '{{className}}' has 'fromRequestDto' method but parameter type '{{paramType}}' does not match expected '{{expectedType}}'.",
25
+ fromRequestDtoMustBeStatic: "Method 'fromRequestDto' in Entity DTO '{{className}}' must be static.",
26
+ optionalMethodMustBeStatic: "Method '{{methodName}}' in Entity DTO '{{className}}' must be static.",
27
+ requiredMethodMustBeStatic: "Method '{{methodName}}' in Entity DTO '{{className}}' must be static.",
28
+ forbiddenMethod: "Entity DTO '{{className}}' must not have method '{{methodName}}'. Only 'fromEntity', 'toEntity', and optionally 'fromEntityArray', 'fromRequestDto' are allowed.",
29
+ },
30
+ },
31
+
32
+ create(context) {
33
+ //--------------------------------------------------------------------------
34
+ // Helpers
35
+ //--------------------------------------------------------------------------
36
+
37
+ const REQUIRED_METHODS = ["fromEntity", "toEntity"];
38
+ const ALLOWED_OPTIONAL_METHODS = ["fromEntityArray", "fromRequestDto"];
39
+ const ALL_ALLOWED_METHODS = [...REQUIRED_METHODS, ...ALLOWED_OPTIONAL_METHODS];
40
+
41
+ /**
42
+ * Check if a file is an Entity DTO
43
+ * @param {string} filename - The filename
44
+ * @returns {boolean} True if it's an Entity DTO
45
+ */
46
+ function isEntityDtoFile(filename) {
47
+ return (
48
+ filename.includes("/dto/Entity/") &&
49
+ filename.endsWith("EntityDto.ts")
50
+ );
51
+ }
52
+
53
+ /**
54
+ * Check if a class name indicates it's an Entity DTO
55
+ * @param {string} className - The class name
56
+ * @returns {boolean} True if it's an Entity DTO
57
+ */
58
+ function isEntityDtoClass(className) {
59
+ return className && className.endsWith("EntityDto");
60
+ }
61
+
62
+ /**
63
+ * Get expected RequestDto name from EntityDto name
64
+ * @param {string} entityDtoName - The Entity DTO name (e.g., "ItemEntityDto")
65
+ * @returns {string} The expected RequestDto name (e.g., "ItemRequestDto")
66
+ */
67
+ function getExpectedRequestDtoName(entityDtoName) {
68
+ // Remove "EntityDto" suffix and add "RequestDto"
69
+ return entityDtoName.replace(/EntityDto$/, "RequestDto");
70
+ }
71
+
72
+ /**
73
+ * Get parameter type name from TypeAnnotation
74
+ * @param {object} param - The parameter node
75
+ * @returns {string|null} The type name or null
76
+ */
77
+ function getParameterTypeName(param) {
78
+ if (!param || !param.typeAnnotation) {
79
+ return null;
80
+ }
81
+
82
+ const typeAnnotation = param.typeAnnotation.typeAnnotation;
83
+
84
+ if (typeAnnotation.type === "TSTypeReference") {
85
+ if (typeAnnotation.typeName.type === "Identifier") {
86
+ return typeAnnotation.typeName.name;
87
+ }
88
+ }
89
+
90
+ return null;
91
+ }
92
+
93
+ /**
94
+ * Check if a method name is allowed in Entity DTOs
95
+ * @param {string} methodName - The method name
96
+ * @returns {boolean} True if allowed
97
+ */
98
+ function isAllowedMethod(methodName) {
99
+ return ALL_ALLOWED_METHODS.includes(methodName);
100
+ }
101
+
102
+ //--------------------------------------------------------------------------
103
+ // Public
104
+ //--------------------------------------------------------------------------
105
+
106
+ let classNode = null;
107
+ let className = null;
108
+ let foundMethods = {};
109
+ let methodNodes = {};
110
+
111
+ return {
112
+ ClassDeclaration(node) {
113
+ const filename = context.getFilename();
114
+
115
+ // Only check Entity DTOs
116
+ if (!isEntityDtoFile(filename)) {
117
+ return;
118
+ }
119
+
120
+ classNode = node;
121
+ className = node.id?.name;
122
+
123
+ // Only check classes that are Entity DTOs
124
+ if (!isEntityDtoClass(className)) {
125
+ return;
126
+ }
127
+
128
+ // Reset tracking
129
+ foundMethods = {};
130
+ methodNodes = {};
131
+
132
+ // Special case: Skip BaseEntityDto
133
+ if (className === "BaseEntityDto" && filename.endsWith("/dto/BaseEntityDto.ts")) {
134
+ return;
135
+ }
136
+
137
+ // Collect all static methods
138
+ node.body.body.forEach((member) => {
139
+ if (member.type === "MethodDefinition" && member.key && member.key.name) {
140
+ const methodName = member.key.name;
141
+
142
+ // Track found methods
143
+ foundMethods[methodName] = {
144
+ isStatic: member.static === true,
145
+ node: member,
146
+ };
147
+ methodNodes[methodName] = member;
148
+
149
+ // Check if method is allowed
150
+ if (!isAllowedMethod(methodName) && member.kind !== "constructor") {
151
+ context.report({
152
+ node: member,
153
+ messageId: "forbiddenMethod",
154
+ data: {
155
+ className,
156
+ methodName,
157
+ },
158
+ });
159
+ }
160
+
161
+ // Check if required methods are static
162
+ if (REQUIRED_METHODS.includes(methodName) && !member.static) {
163
+ context.report({
164
+ node: member,
165
+ messageId: "requiredMethodMustBeStatic",
166
+ data: {
167
+ className,
168
+ methodName,
169
+ },
170
+ });
171
+ }
172
+
173
+ // Check if optional methods are static (fromEntityArray, fromRequestDto)
174
+ if (ALLOWED_OPTIONAL_METHODS.includes(methodName) && !member.static) {
175
+ context.report({
176
+ node: member,
177
+ messageId: "optionalMethodMustBeStatic",
178
+ data: {
179
+ className,
180
+ methodName,
181
+ },
182
+ });
183
+ }
184
+
185
+ // Check fromRequestDto parameter type
186
+ if (methodName === "fromRequestDto") {
187
+
188
+ // Check parameter type
189
+ if (member.value && member.value.params && member.value.params.length > 0) {
190
+ const firstParam = member.value.params[0];
191
+ const paramTypeName = getParameterTypeName(firstParam);
192
+ const expectedTypeName = getExpectedRequestDtoName(className);
193
+
194
+ if (paramTypeName && paramTypeName !== expectedTypeName) {
195
+ context.report({
196
+ node: firstParam,
197
+ messageId: "invalidFromRequestDtoParameter",
198
+ data: {
199
+ className,
200
+ paramType: paramTypeName,
201
+ expectedType: expectedTypeName,
202
+ },
203
+ });
204
+ }
205
+ }
206
+ }
207
+ }
208
+ });
209
+ },
210
+
211
+ "Program:exit"(node) {
212
+ if (!classNode || !className) {
213
+ return;
214
+ }
215
+
216
+ const filename = context.getFilename();
217
+
218
+ // Only check Entity DTOs
219
+ if (!isEntityDtoFile(filename) || !isEntityDtoClass(className)) {
220
+ return;
221
+ }
222
+
223
+ // Special case: Skip BaseEntityDto
224
+ if (className === "BaseEntityDto" && filename.endsWith("/dto/BaseEntityDto.ts")) {
225
+ return;
226
+ }
227
+
228
+ // Check for missing required methods
229
+ REQUIRED_METHODS.forEach((methodName) => {
230
+ if (!foundMethods[methodName]) {
231
+ context.report({
232
+ node: classNode,
233
+ messageId: `missing${methodName.charAt(0).toUpperCase()}${methodName.slice(1)}`,
234
+ data: { className },
235
+ });
236
+ }
237
+ });
238
+
239
+ // Reset for next file
240
+ classNode = null;
241
+ className = null;
242
+ foundMethods = {};
243
+ methodNodes = {};
244
+ },
245
+ };
246
+ },
247
+ };
248
+