UNPKG

@addon24/eslint-config

Version:

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

158 lines (134 loc) 4.92 kB
export default { meta: { type: "problem", docs: { description: "Enforce that Entity DTOs use convertEntityToDto in fromEntity method", category: "Best Practices", recommended: true, }, messages: { missingConvertImport: "Entity-DTO '{{className}}' muss convertEntityToDto importieren: import { convertEntityToDto } from '@/dto/BaseEntityDto'", fromEntityMustUseConvert: "fromEntity-Methode in '{{className}}' muss 'return convertEntityToDto(...)' verwenden, um zirkuläre Abhängigkeiten zu vermeiden", invalidConvertStructure: "convertEntityToDto in '{{className}}' muss 3 Argumente haben: (entity, factory, populate)", factoryMustBeArrowFunction: "Zweites Argument von convertEntityToDto muss eine Arrow-Function sein: () => new {{className}}()", populateMustBeArrowFunction: "Drittes Argument von convertEntityToDto muss eine Arrow-Function sein: (dto, entity) => {{ ... }}", }, fixable: null, 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 hasConvertImport = false; let className = null; return { ImportDeclaration (node) { // Check for convertEntityToDto import (absolute or relative) const isBaseImport = node.source.value === "@/dto/BaseEntityDto" || node.source.value.endsWith("/BaseEntityDto") || node.source.value === "./BaseEntityDto"; if (isBaseImport) { const hasConvert = node.specifiers.some( (spec) => spec.type === "ImportSpecifier" && spec.imported.name === "convertEntityToDto", ); if (hasConvert) { hasConvertImport = true; } } }, ClassDeclaration (node) { // Only check Entity DTOs if (!node.id || !node.id.name.endsWith("EntityDto")) { return; } className = node.id.name; }, MethodDefinition (node) { // Only check static fromEntity methods if (!node.static || !node.key || node.key.name !== "fromEntity") { return; } // Check if import exists if (!hasConvertImport) { context.report({ node, messageId: "missingConvertImport", data: { className }, }); return; } // Check if method body uses convertEntityToDto const methodBody = node.value.body; if (!methodBody || methodBody.type !== "BlockStatement") { return; } // Find return statement const returnStatement = methodBody.body.find((stmt) => stmt.type === "ReturnStatement"); if (!returnStatement || !returnStatement.argument) { return; } // Check if return statement calls convertEntityToDto const returnArg = returnStatement.argument; if ( returnArg.type !== "CallExpression" || !returnArg.callee || returnArg.callee.name !== "convertEntityToDto" ) { context.report({ node: returnStatement, messageId: "fromEntityMustUseConvert", data: { className }, }); return; } // Validate convertEntityToDto arguments const args = returnArg.arguments; if (args.length !== 3) { context.report({ node: returnArg, messageId: "invalidConvertStructure", data: { className }, }); return; } // Validate factory function (second argument) const factoryArg = args[1]; if (factoryArg.type !== "ArrowFunctionExpression") { context.report({ node: factoryArg, messageId: "factoryMustBeArrowFunction", data: { className }, }); } // Validate populate function (third argument) const populateArg = args[2]; if (populateArg.type !== "ArrowFunctionExpression") { context.report({ node: populateArg, messageId: "populateMustBeArrowFunction", data: { className }, }); } // Validate populate function has 2 parameters (dto, entity) if (populateArg.type === "ArrowFunctionExpression" && populateArg.params.length !== 2) { context.report({ node: populateArg, messageId: "populateMustBeArrowFunction", data: { className }, }); } }, }; }, };