UNPKG

@addon24/eslint-config

Version:

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

165 lines (149 loc) 5.64 kB
/** * ESLint-Regel: no-dto-default-values * Verbietet Default Values in DTO-Klassen * Verbesserte Version für TypeScript */ /** @type {import('eslint').Rule.RuleModule} */ const noDtoDefaultValuesRule = { meta: { type: "problem", docs: { description: "DTO-Klassen dürfen keine Default Values haben", category: "Best Practices", recommended: true, }, fixable: "code", schema: [], messages: { noDefaultValue: "DTO property '{{propertyName}}' darf keinen Default Value haben. Verwende stattdessen explizite Typ-Annotation ohne Initialisierung.", noDefaultValueInConstructor: "DTO-Properties dürfen nicht im Constructor initialisiert werden. Verwende stattdessen die create()-Methode.", }, }, create(context) { function isDtoClass(node) { return node.id && node.id.name && node.id.name.endsWith('Dto'); } function hasDefaultValue(member) { return member.value !== null && member.value !== undefined; } function isPrivateProperty(propertyName) { return propertyName.startsWith('_'); } function inferTypeFromValue(value) { if (!value) return "unknown"; switch (value.type) { case "Literal": if (typeof value.value === "string") return "string"; if (typeof value.value === "number") return "number"; if (typeof value.value === "boolean") return "boolean"; return "unknown"; case "ArrayExpression": return "any[]"; case "ObjectExpression": return "object"; case "NewExpression": return "object"; case "Identifier": if (value.name === "true" || value.name === "false") return "boolean"; if (value.name === "null") return "null"; if (value.name === "undefined") return "undefined"; return "unknown"; default: return "unknown"; } } function checkPropertyForDefaultValue(member) { if (!member.key?.name) return; const propertyName = member.key.name; // Überspringe private Properties und statische Properties if (isPrivateProperty(propertyName) || member.static) { return; } // Prüfe ob Default Value vorhanden ist if (hasDefaultValue(member)) { context.report({ node: member, messageId: "noDefaultValue", data: { propertyName }, fix(fixer) { if (member.typeAnnotation) { // Hat bereits Type Annotation, entferne nur den Default Value const valueStart = member.value.range[0]; // Finde das "=" Zeichen vor dem Wert let equalPos = valueStart - 1; const sourceCode = context.getSourceCode(); const text = sourceCode.getText(); while (equalPos > 0 && text[equalPos] !== '=') { equalPos--; } // Entferne alles von "=" bis zum Ende des Wertes (inklusive Whitespace) return fixer.removeRange([equalPos - 1, member.value.range[1]]); } else { // Keine Type Annotation, infere Typ und ersetze const inferredType = inferTypeFromValue(member.value); if (inferredType !== "unknown") { return fixer.replaceText(member, `${propertyName}: ${inferredType};`); } } return null; }, }); } } return { ClassDeclaration(node) { if (!isDtoClass(node)) return; node.body.body.forEach(member => { // Unterstütze verschiedene Property-Typen für TypeScript if ((member.type === "PropertyDefinition" || member.type === "ClassProperty" || member.type === "TSPropertySignature" || member.type === "MethodDefinition") && member.key?.name && member.kind !== "method" && member.kind !== "constructor") { checkPropertyForDefaultValue(member); } }); }, ClassExpression(node) { if (!isDtoClass(node)) return; node.body.body.forEach(member => { if ((member.type === "PropertyDefinition" || member.type === "ClassProperty" || member.type === "TSPropertySignature" || member.type === "MethodDefinition") && member.key?.name && member.kind !== "method" && member.kind !== "constructor") { checkPropertyForDefaultValue(member); } }); }, MethodDefinition(node) { if (node.kind === "constructor") { const classNode = node.parent.parent; if (!isDtoClass(classNode)) return; if (node.value.body) { node.value.body.body.forEach(stmt => { if (stmt.type === "ExpressionStatement" && stmt.expression.type === "AssignmentExpression" && stmt.expression.left.type === "MemberExpression" && stmt.expression.left.object.type === "ThisExpression") { const propertyName = stmt.expression.left.property.name; if (!isPrivateProperty(propertyName)) { context.report({ node: stmt, messageId: "noDefaultValueInConstructor", data: { propertyName }, }); } } }); } } }, }; }, }; export default { rules: { "no-dto-default-values": noDtoDefaultValuesRule, }, };