UNPKG

@addon24/eslint-config

Version:

ESLint configuration rules for WorldOfTextcraft projects - Centralized configuration for all project types

142 lines (124 loc) 4.53 kB
export default { meta: { type: "problem", docs: { description: "Enforce that Entity DTOs extend BaseEntityDto to prevent circular dependencies", category: "Best Practices", recommended: true, }, messages: { missingBaseClass: "Entity-DTO '{{className}}' muss BaseEntityDto erweitern, um zirkuläre Abhängigkeiten zu vermeiden", invalidBaseClass: "Entity-DTO '{{className}}' muss genau BaseEntityDto erweitern, nicht '{{extendedClass}}'", missingBaseImport: "Entity-DTO '{{className}}' muss BaseEntityDto importieren: import { BaseEntityDto } from '@/dto/BaseEntityDto'", }, fixable: "code", schema: [], }, create (context) { const filename = context.getFilename(); // Skip non-DTO files if (!filename.includes("/dto/Entity/")) { return {}; } // Skip BaseEntityDto itself if (filename.endsWith("/BaseEntityDto.ts")) { return {}; } // Skip test files if (filename.includes(".test.") || filename.includes(".spec.") || filename.includes("__tests__")) { return {}; } let hasBaseImport = false; let className = null; let hasFromEntity = false; return { ImportDeclaration (node) { // Check for BaseEntityDto import (absolute or relative) const isBaseImport = node.source.value === "@/dto/BaseEntityDto" || node.source.value.endsWith("/BaseEntityDto") || node.source.value === "./BaseEntityDto"; if (isBaseImport) { const hasBaseEntityDto = node.specifiers.some( (spec) => spec.type === "ImportSpecifier" && spec.imported.name === "BaseEntityDto", ); if (hasBaseEntityDto) { hasBaseImport = true; } } }, ClassDeclaration (node) { // Only check classes that are Entity DTOs if (!node.id || !node.id.name.endsWith("EntityDto")) { return; } className = node.id.name; // Check if class has fromEntity method hasFromEntity = node.body.body.some( (member) => member.type === "MethodDefinition" && member.static && member.key.name === "fromEntity", ); // Only enforce if class has fromEntity method if (!hasFromEntity) { return; } // Check if class extends BaseEntityDto if (!node.superClass) { context.report({ node, messageId: "missingBaseClass", data: { className }, fix (fixer) { const fixes = []; // Add import if missing if (!hasBaseImport) { const sourceCode = context.getSourceCode(); const firstImport = sourceCode.ast.body.find((n) => n.type === "ImportDeclaration"); if (firstImport) { fixes.push( fixer.insertTextBefore( firstImport, "import { BaseEntityDto } from \"@/dto/BaseEntityDto\";\n", ), ); } } // Add extends clause const classKeyword = context.getSourceCode().getFirstToken(node); const className = context.getSourceCode().getFirstToken(node, 1); fixes.push(fixer.insertTextAfter(className, " extends BaseEntityDto")); return fixes; }, }); } else if (node.superClass.name !== "BaseEntityDto") { context.report({ node: node.superClass, messageId: "invalidBaseClass", data: { className, extendedClass: node.superClass.name, }, }); } else if (!hasBaseImport) { // Class extends BaseEntityDto but import is missing context.report({ node, messageId: "missingBaseImport", data: { className }, fix (fixer) { const sourceCode = context.getSourceCode(); const firstImport = sourceCode.ast.body.find((n) => n.type === "ImportDeclaration"); if (firstImport) { return fixer.insertTextBefore( firstImport, "import { BaseEntityDto } from \"@/dto/BaseEntityDto\";\n", ); } return null; }, }); } }, }; }, };