@addon24/eslint-config
Version:
ESLint configuration rules for WorldOfTextcraft projects - Centralized configuration for all project types
127 lines (108 loc) • 4.24 kB
JavaScript
/**
* @fileoverview Verbietet Konstruktoren in DTO-Klassen
* DTOs sind reine Datencontainer und sollten keine Konstruktoren haben.
* Sie werden automatisch von class-transformer aus JSON erstellt.
*/
export default {
meta: {
type: "error",
docs: {
description: "Forbid constructors in DTO classes - DTOs should be pure data containers",
category: "Best Practices",
recommended: true,
},
fixable: "code",
schema: [],
messages: {
noDtoConstructor: "DTO class '{{className}}' must not have a constructor. DTOs are pure data containers that are automatically instantiated by class-transformer from JSON. Remove the constructor and let the framework handle object creation.",
constructorRemoved: "Constructor removed from DTO class '{{className}}'",
},
},
create(context) {
const filename = context.getFilename();
// Ignoriere Test-Dateien und Non-DTO-Dateien
if (!filename.includes("/dto/") ||
filename.includes("test") ||
filename.includes("spec") ||
filename.includes("__tests__") ||
filename.endsWith(".test.ts") ||
filename.endsWith(".spec.ts")) {
return {};
}
function isDtoClass(className) {
return (
className.endsWith("Dto") ||
className.endsWith("DTO") ||
className.includes("Request") ||
className.includes("Response") ||
className.includes("Create") ||
className.includes("Update") ||
className.includes("Delete")
);
}
function isInDtoClass(node) {
let parent = node.parent;
// Traverse up to find the class declaration
while (parent) {
if (parent.type === "ClassDeclaration" ||
(parent.type === "ExportDefaultDeclaration" && parent.declaration?.type === "ClassDeclaration")) {
const classNode = parent.type === "ClassDeclaration" ? parent : parent.declaration;
const className = classNode.id?.name;
if (className && isDtoClass(className)) {
return { isDto: true, className, classNode };
}
break;
}
parent = parent.parent;
}
return { isDto: false, className: null, classNode: null };
}
return {
MethodDefinition(node) {
// Prüfe nur Konstruktoren
if (node.kind !== "constructor") {
return;
}
const { isDto, className } = isInDtoClass(node);
if (isDto) {
context.report({
node,
messageId: "noDtoConstructor",
data: {
className: className || "unknown"
},
fix(fixer) {
const sourceCode = context.getSourceCode();
// Finde den Konstruktor-Block
const constructorStart = node.range[0];
const constructorEnd = node.range[1];
// Prüfe, ob es Leerzeilen vor/nach dem Konstruktor gibt
const beforeConstructor = sourceCode.getText().substring(0, constructorStart);
const afterConstructor = sourceCode.getText().substring(constructorEnd);
// Entferne auch überflüssige Leerzeilen
let startPos = constructorStart;
let endPos = constructorEnd;
// Prüfe auf Leerzeilen vor dem Konstruktor
const lines = beforeConstructor.split('\n');
if (lines.length > 1 && lines[lines.length - 1].trim() === '') {
// Finde die Position der letzten nicht-leeren Zeile vor dem Konstruktor
for (let i = lines.length - 2; i >= 0; i--) {
if (lines[i].trim() !== '') {
break;
}
startPos -= (lines[lines.length - 1 - (lines.length - 2 - i)].length + 1);
}
}
// Prüfe auf Leerzeilen nach dem Konstruktor
const afterLines = afterConstructor.split('\n');
if (afterLines.length > 1 && afterLines[0].trim() === '') {
endPos += afterLines[0].length + 1;
}
return fixer.removeRange([startPos, endPos]);
},
});
}
}
};
},
};