@addon24/eslint-config
Version:
ESLint configuration rules for WorldOfTextcraft projects - Centralized configuration for all project types
113 lines (98 loc) • 3.55 kB
JavaScript
/**
* @fileoverview Erzwingt die Verwendung von DTO-Klassen anstelle von Interfaces
*/
;
const preferDtoClassesRule = {
meta: {
type: "suggestion",
docs: {
description: "Erzwingt die Verwendung von DTO-Klassen anstelle von Interfaces für Datenübertragung",
category: "Best Practices",
recommended: true,
},
fixable: null,
schema: [],
messages: {
preferDtoClass: "Interface '{{interfaceName}}' sollte als DTO-Klasse implementiert werden. DTO-Klassen bieten Runtime-Validierung, bessere Type Safety und Konsistenz.",
useFactoryFunction: "Wenn ein Interface für '{{interfaceName}}' notwendig ist (z.B. interne Types), füge eine Factory-Funktion hinzu: 'export const create{{interfaceName}} = (...) => ({ ... });'",
},
},
create(context) {
const filename = context.getFilename();
// Nur DTO-Dateien prüfen
if (!filename.includes("/dto/") || filename.includes("test") || filename.includes("spec")) {
return {};
}
// Identifiziere DTO-Interfaces
function isDtoInterface(interfaceName) {
return (
interfaceName.endsWith("Dto") ||
interfaceName.endsWith("DTO") ||
interfaceName.includes("Request") ||
interfaceName.includes("Response") ||
interfaceName.includes("Create") ||
interfaceName.includes("Update") ||
interfaceName.includes("Delete") ||
interfaceName.includes("Model") ||
interfaceName.includes("Data")
);
}
// Ausnahmen für akzeptable Interface-Verwendungen
function isAcceptableInterface(interfaceName, context) {
const internalTypePatterns = [
"Type", "Config", "Options", "Settings", "Props", "State"
];
const componentPatterns = [
"Component", "Hook", "Context"
];
return (
internalTypePatterns.some(pattern => interfaceName.includes(pattern)) ||
componentPatterns.some(pattern => interfaceName.includes(pattern)) ||
filename.includes("/types/") || // Reine Type-Definitionen
filename.includes("/model/") // Business Models
);
}
return {
TSInterfaceDeclaration(node) {
const interfaceName = node.id.name;
if (isDtoInterface(interfaceName) && !isAcceptableInterface(interfaceName, context)) {
// Prüfe, ob eine Factory-Funktion existiert
const sourceCode = context.getSourceCode();
const text = sourceCode.getText();
const hasFactoryFunction = text.includes(`create${interfaceName}`) ||
text.includes(`new${interfaceName}`);
if (!hasFactoryFunction) {
context.report({
node,
messageId: "preferDtoClass",
data: { interfaceName }
});
} else {
// Warnung mit Hinweis auf Factory-Pattern
context.report({
node,
messageId: "useFactoryFunction",
data: { interfaceName }
});
}
}
},
// Prüfe auch Type-Aliase
TSTypeAliasDeclaration(node) {
const typeName = node.id.name;
if (isDtoInterface(typeName) && !isAcceptableInterface(typeName, context)) {
context.report({
node,
messageId: "preferDtoClass",
data: { interfaceName: typeName }
});
}
}
};
}
};
export default {
rules: {
"prefer-dto-classes": preferDtoClassesRule,
},
};