@addon24/eslint-config
Version:
ESLint configuration rules for WorldOfTextcraft projects - Centralized configuration for all project types
115 lines (97 loc) • 3.56 kB
JavaScript
export default {
meta: {
type: "problem",
docs: {
description: "Enforce usage of DTOs instead of anonymous objects",
category: "Best Practices",
recommended: true,
},
fixable: null,
schema: [],
messages: {
anonymousObjectForbidden: "Anonymous objects are not allowed. Use a DTO class instead.",
anonymousObjectInReturn: "Returning anonymous objects is not allowed. Create a DTO class for the return type.",
anonymousObjectInAssignment: "Assigning anonymous objects is not allowed. Use a DTO class instead.",
anonymousObjectInFunctionCall: "Passing anonymous objects to functions is not allowed. Use a DTO class instead.",
},
},
create(context) {
function isTestFile() {
const filename = context.getFilename();
return filename.includes('.test.') || filename.includes('.spec.') || filename.includes('__tests__');
}
function isExemptFunction(node) {
if (!node || !node.callee) return false;
// Check for method calls like logger.error
if (node.callee.type === "MemberExpression") {
const objectName = node.callee.object.name;
const propertyName = node.callee.property.name;
const fullName = `${objectName}.${propertyName}`;
// Allow logger methods
if (fullName.startsWith('logger.')) {
return true;
}
// Allow console methods
if (fullName.startsWith('console.')) {
return true;
}
// Allow DTO factory methods
if (propertyName === "create" && objectName && objectName.endsWith("Dto")) {
return true;
}
}
// Check for direct function calls
if (node.callee.type === "Identifier") {
const exemptFunctions = [
"Object.assign", "JSON.stringify", "JSON.parse", "collectDefaultMetrics"
];
return exemptFunctions.includes(node.callee.name);
}
return false;
}
function isInsideExemptFunction(node) {
let parent = node.parent;
while (parent) {
if (parent.type === "CallExpression" && isExemptFunction(parent)) {
return true;
}
parent = parent.parent;
}
return false;
}
function checkObjectExpression(node) {
// Skip if it's a test file
if (isTestFile()) return;
// Skip if it's inside an exempt function
if (isInsideExemptFunction(node)) return;
// Skip if it's a simple object with only basic properties
if (node.properties && node.properties.length <= 2) {
const propertyNames = node.properties
.filter(prop => prop.type === "Property" && prop.key.type === "Identifier")
.map(prop => prop.key.name);
// Allow simple objects with basic properties
if (propertyNames.every(name => ["id", "isActive", "name", "type", "value"].includes(name))) {
return;
}
}
// Report the anonymous object
let messageId = "anonymousObjectForbidden";
if (node.parent) {
if (node.parent.type === "ReturnStatement") {
messageId = "anonymousObjectInReturn";
} else if (node.parent.type === "VariableDeclarator") {
messageId = "anonymousObjectInAssignment";
} else if (node.parent.type === "CallExpression") {
messageId = "anonymousObjectInFunctionCall";
}
}
context.report({
node,
messageId,
});
}
return {
ObjectExpression: checkObjectExpression,
};
},
};