UNPKG

@addon24/eslint-config

Version:

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

142 lines (128 loc) 4.87 kB
const enforceBodyRequestDtoRule = { meta: { type: "problem", docs: { description: "Enforces that DTOs used in @Body() decorators must be RequestDto", category: "Architecture", recommended: true, }, schema: [], messages: { bodyMustBeRequestDto: "DTO '{{dtoName}}' in @Body() must be a RequestDto. Rename to '{{suggestedName}}' or move to /dto/Request/ folder.", bodyMustEndWithRequestDto: "DTO '{{dtoName}}' in @Body() must end with 'RequestDto' suffix.", }, }, create(context) { const checkBodyParameter = (param, node) => { if (!param.typeAnnotation) return; // Extrahiere den DTO-Namen aus dem Type const typeAnnotation = param.typeAnnotation.typeAnnotation; let dtoName = null; if (typeAnnotation.type === "TSTypeReference") { // Prüfe, ob es ein Partial<T> Type ist if (typeAnnotation.typeName.type === "TSQualifiedName" && typeAnnotation.typeName.left.name === "Partial" && typeAnnotation.typeParameters) { const innerType = typeAnnotation.typeParameters.params[0]; if (innerType.type === "TSTypeReference") { dtoName = innerType.typeName.name; } } else if (typeAnnotation.typeName.name === "Partial" && typeAnnotation.typeParameters) { // Fallback für einfache Partial<T> Syntax const innerType = typeAnnotation.typeParameters.params[0]; if (innerType.type === "TSTypeReference") { dtoName = innerType.typeName.name; } } else { dtoName = typeAnnotation.typeName.name; } } else if (typeAnnotation.type === "TSUnionType") { // Für Union Types wie "Partial<CreateUserRequestDto>" const unionTypes = typeAnnotation.types; const partialType = unionTypes.find(t => t.type === "TSTypeReference" && t.typeName.type === "TSQualifiedName" && t.typeName.left.name === "Partial" ); if (partialType && partialType.typeParameters) { const innerType = partialType.typeParameters.params[0]; if (innerType.type === "TSTypeReference") { dtoName = innerType.typeName.name; } } } if (!dtoName) return; // Skip built-in types if (["Partial", "Record", "Array", "Promise", "Response", "Request"].includes(dtoName)) { return; } // Prüfe, ob es ein RequestDto ist const isRequestDto = dtoName.endsWith("RequestDto"); const isInRequestFolder = context.getFilename().includes("/dto/Request/"); if (!isRequestDto) { if (isInRequestFolder) { // DTO ist im Request-Ordner aber hat falschen Namen context.report({ node: node, messageId: "bodyMustEndWithRequestDto", data: { dtoName: dtoName }, }); } else { // DTO ist nicht im Request-Ordner und hat falschen Namen let suggestedName = dtoName; if (dtoName.endsWith("ResponseDto")) { suggestedName = dtoName.replace("ResponseDto", "RequestDto"); } else if (dtoName.endsWith("Dto")) { suggestedName = dtoName.replace("Dto", "RequestDto"); } else { suggestedName = dtoName + "RequestDto"; } context.report({ node: node, messageId: "bodyMustBeRequestDto", data: { dtoName: dtoName, suggestedName: suggestedName }, }); } } }; return { MethodDefinition(node) { // Finde Parameter mit @Body() Decorators if (!node.value || !node.value.params) return; node.value.params.forEach(param => { const paramDecorators = param.decorators || []; const bodyDecorator = paramDecorators.find(decorator => { if (decorator.expression.type === "CallExpression") { return decorator.expression.callee.name === "Body"; } return decorator.expression.name === "Body"; }); if (bodyDecorator) { checkBodyParameter(param, param); } }); }, TSParameterProperty(node) { // Prüfe Parameter mit Decorators const decorators = node.decorators || []; const bodyDecorator = decorators.find(decorator => { if (decorator.expression.type === "CallExpression") { return decorator.expression.callee.name === "Body"; } return decorator.expression.name === "Body"; }); if (bodyDecorator) { checkBodyParameter(node.parameter, node.parameter); } }, }; }, }; export default { rules: { 'enforce-body-request-dto': enforceBodyRequestDtoRule, }, };