UNPKG

@addon24/eslint-config

Version:

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

148 lines (129 loc) 5.16 kB
/** * @fileoverview Enforce 1:1 type matching for DTO create method parameters. * @author Addon24 */ import { AST_NODE_TYPES } from '@typescript-eslint/utils'; export default { meta: { type: "problem", docs: { description: "Enforce that DTO create method parameters match the DTO's properties 1:1 regarding optionality.", category: "Possible Errors", recommended: true, }, messages: { parameterOptionalityMismatch: "Parameter '{{paramName}}' in create method has optionality mismatch with DTO property '{{propertyName}}'. DTO property is '{{dtoOptionality}}', but parameter is '{{paramOptionality}}'.", propertyNotFound: "Parameter '{{paramName}}' in create method does not correspond to a property in the DTO.", }, schema: [], }, create(context) { /** * Extrahiert Properties aus einer Klasse */ function getClassProperties(classNode) { const properties = new Map(); for (const member of classNode.body.body) { if (member.type === AST_NODE_TYPES.PropertyDefinition) { const propertyName = member.key.name; const isOptional = member.optional || false; properties.set(propertyName, { isOptional }); } } return properties; } /** * Extrahiert Properties aus einem Type Literal */ function getTypeLiteralProperties(typeLiteral) { const properties = new Map(); for (const member of typeLiteral.members) { if (member.type === AST_NODE_TYPES.TSPropertySignature) { const propertyName = member.key.name; const isOptional = member.optional || false; properties.set(propertyName, { isOptional }); } } return properties; } return { MethodDefinition(node) { if ( node.kind === 'method' && node.static && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === 'create' && node.parent.type === AST_NODE_TYPES.ClassBody && node.parent.parent.type === AST_NODE_TYPES.ClassDeclaration ) { const classNode = node.parent.parent; const classProperties = getClassProperties(classNode); // Find the 'data' parameter (with or without default value) const dataParam = node.value.params.find( (param) => { if (param.type === AST_NODE_TYPES.Identifier && param.name === 'data') { return true; } // Handle default parameters: AssignmentPattern with Identifier if (param.type === AST_NODE_TYPES.AssignmentPattern && param.left.type === AST_NODE_TYPES.Identifier && param.left.name === 'data') { return true; } return false; } ); if (!dataParam) { return; // 'data' parameter not found } // Get type annotation from the parameter (handle both regular and default parameters) let typeAnnotation; if (dataParam.type === AST_NODE_TYPES.Identifier) { typeAnnotation = dataParam.typeAnnotation; } else if (dataParam.type === AST_NODE_TYPES.AssignmentPattern) { typeAnnotation = dataParam.left.typeAnnotation; } if (!typeAnnotation?.typeAnnotation || typeAnnotation.typeAnnotation.type !== AST_NODE_TYPES.TSTypeLiteral) { return; // type annotation not found or not an object literal } const dataTypeLiteral = typeAnnotation.typeAnnotation; const dataProperties = getTypeLiteralProperties(dataTypeLiteral); // Check each parameter property against class properties for (const [paramName, paramInfo] of dataProperties) { const classProperty = classProperties.get(paramName); if (!classProperty) { context.report({ node: dataTypeLiteral, messageId: "propertyNotFound", data: { paramName }, }); continue; } if (paramInfo.isOptional !== classProperty.isOptional) { context.report({ node: dataTypeLiteral, messageId: "parameterOptionalityMismatch", data: { paramName, propertyName: paramName, dtoOptionality: classProperty.isOptional ? 'optional' : 'required', paramOptionality: paramInfo.isOptional ? 'optional' : 'required', }, }); } } // Check if all required class properties are present in parameters for (const [className, classProperty] of classProperties) { if (!classProperty.isOptional && !dataProperties.has(className)) { context.report({ node: dataTypeLiteral, messageId: "propertyNotFound", data: { paramName: className }, }); } } } }, }; }, };