@addon24/eslint-config
Version:
ESLint configuration rules for WorldOfTextcraft projects - Centralized configuration for all project types
142 lines (124 loc) • 4.53 kB
JavaScript
export default {
meta: {
type: "problem",
docs: {
description: "Enforce that Entity DTOs extend BaseEntityDto to prevent circular dependencies",
category: "Best Practices",
recommended: true,
},
messages: {
missingBaseClass: "Entity-DTO '{{className}}' muss BaseEntityDto erweitern, um zirkuläre Abhängigkeiten zu vermeiden",
invalidBaseClass: "Entity-DTO '{{className}}' muss genau BaseEntityDto erweitern, nicht '{{extendedClass}}'",
missingBaseImport: "Entity-DTO '{{className}}' muss BaseEntityDto importieren: import { BaseEntityDto } from '@/dto/BaseEntityDto'",
},
fixable: "code",
schema: [],
},
create (context) {
const filename = context.getFilename();
// Skip non-DTO files
if (!filename.includes("/dto/Entity/")) {
return {};
}
// Skip BaseEntityDto itself
if (filename.endsWith("/BaseEntityDto.ts")) {
return {};
}
// Skip test files
if (filename.includes(".test.") || filename.includes(".spec.") || filename.includes("__tests__")) {
return {};
}
let hasBaseImport = false;
let className = null;
let hasFromEntity = false;
return {
ImportDeclaration (node) {
// Check for BaseEntityDto import (absolute or relative)
const isBaseImport = node.source.value === "@/dto/BaseEntityDto" ||
node.source.value.endsWith("/BaseEntityDto") ||
node.source.value === "./BaseEntityDto";
if (isBaseImport) {
const hasBaseEntityDto = node.specifiers.some(
(spec) => spec.type === "ImportSpecifier" && spec.imported.name === "BaseEntityDto",
);
if (hasBaseEntityDto) {
hasBaseImport = true;
}
}
},
ClassDeclaration (node) {
// Only check classes that are Entity DTOs
if (!node.id || !node.id.name.endsWith("EntityDto")) {
return;
}
className = node.id.name;
// Check if class has fromEntity method
hasFromEntity = node.body.body.some(
(member) => member.type === "MethodDefinition"
&& member.static
&& member.key.name === "fromEntity",
);
// Only enforce if class has fromEntity method
if (!hasFromEntity) {
return;
}
// Check if class extends BaseEntityDto
if (!node.superClass) {
context.report({
node,
messageId: "missingBaseClass",
data: { className },
fix (fixer) {
const fixes = [];
// Add import if missing
if (!hasBaseImport) {
const sourceCode = context.getSourceCode();
const firstImport = sourceCode.ast.body.find((n) => n.type === "ImportDeclaration");
if (firstImport) {
fixes.push(
fixer.insertTextBefore(
firstImport,
"import { BaseEntityDto } from \"@/dto/BaseEntityDto\";\n",
),
);
}
}
// Add extends clause
const classKeyword = context.getSourceCode().getFirstToken(node);
const className = context.getSourceCode().getFirstToken(node, 1);
fixes.push(fixer.insertTextAfter(className, " extends BaseEntityDto"));
return fixes;
},
});
} else if (node.superClass.name !== "BaseEntityDto") {
context.report({
node: node.superClass,
messageId: "invalidBaseClass",
data: {
className,
extendedClass: node.superClass.name,
},
});
} else if (!hasBaseImport) {
// Class extends BaseEntityDto but import is missing
context.report({
node,
messageId: "missingBaseImport",
data: { className },
fix (fixer) {
const sourceCode = context.getSourceCode();
const firstImport = sourceCode.ast.body.find((n) => n.type === "ImportDeclaration");
if (firstImport) {
return fixer.insertTextBefore(
firstImport,
"import { BaseEntityDto } from \"@/dto/BaseEntityDto\";\n",
);
}
return null;
},
});
}
},
};
},
};