@addon24/eslint-config
Version:
ESLint configuration rules for WorldOfTextcraft projects - Centralized configuration for all project types
153 lines (139 loc) • 5.32 kB
JavaScript
/**
* ESLint-Regel, die sicherstellt, dass Controller den BaseController erweitern
* und dessen Methoden für Response-Strukturen verwenden
*/
/** @type {import('eslint').Rule.RuleModule} */
const enforceBaseControllerRule = {
meta: {
type: "problem",
docs: {
description: "Stellt sicher, dass Controller den BaseController erweitern und dessen Methoden verwenden",
category: "Best Practices",
recommended: true,
},
fixable: null,
schema: [
{
type: "object",
properties: {
controllerPattern: {
type: "string",
description: "Regex pattern for identifying controller files",
default: "Controller\\.ts$"
},
baseControllerName: {
type: "string",
description: "Name of the base controller class that all controllers should extend",
default: "BaseController"
},
allowDirectResponse: {
type: "boolean",
description: "Whether to allow direct response usage (res.json, res.status)",
default: false
},
excludePatterns: {
type: "array",
items: { type: "string" },
description: "File patterns to exclude from the rule",
default: ["BaseController.ts"]
}
},
additionalProperties: false
}
],
messages: {
notExtendingBaseController: "Controller muss BaseController erweitern: '{{controllerName}}'",
directResponseUsage: "Verwende die BaseController-Methoden (sendSuccess, sendError, etc.) anstatt direkt res.json/status aufzurufen",
},
},
create(context) {
const options = context.options[0] || {};
const controllerPattern = new RegExp(options.controllerPattern || "Controller\\.ts$");
const baseControllerName = options.baseControllerName || "BaseController";
const allowDirectResponse = options.allowDirectResponse || false;
const excludePatterns = options.excludePatterns || ["BaseController.ts"];
const filename = context.getFilename();
const isExcluded = excludePatterns.some(pattern => filename.includes(pattern));
const isControllerFile = (controllerPattern.test(filename) || filename.includes('/fixtures/')) && !isExcluded;
// Speichert, ob die aktuelle Datei ein Controller ist, der BaseController erweitert
let extendsBaseController = false;
// Speichert den Namen des Controllers für Fehlermeldungen
let controllerName = "";
return {
// Prüfen, ob ein Controller den BaseController erweitert
ClassDeclaration(node) {
if (!isControllerFile) return;
// Extrahiere den Controller-Namen
controllerName = node.id.name;
// Ignoriere BaseController selbst
if (controllerName === baseControllerName) return;
// Prüfe, ob der Controller von BaseController erbt
if (node.superClass &&
node.superClass.type === "Identifier" &&
node.superClass.name === baseControllerName) {
extendsBaseController = true;
} else {
// Melde einen Fehler, wenn der Controller nicht von BaseController erbt
context.report({
node,
messageId: "notExtendingBaseController",
data: {
controllerName: controllerName,
},
});
}
},
// Prüfen, ob direkte Response-Methoden verwendet werden
CallExpression(node) {
if (!isControllerFile || !extendsBaseController) return;
// Prüfe auf direkte res.json() Aufrufe
if (
!allowDirectResponse &&
node.callee.type === "MemberExpression" &&
node.callee.object.type === "Identifier" &&
node.callee.object.name === "res" &&
node.callee.property.type === "Identifier" &&
node.callee.property.name === "json"
) {
context.report({
node,
messageId: "directResponseUsage",
});
}
// Prüfe auf res.status().json() Aufrufe
if (
node.callee.type === "MemberExpression" &&
node.callee.property.type === "Identifier" &&
node.callee.property.name === "json" &&
node.callee.object.type === "CallExpression" &&
node.callee.object.callee.type === "MemberExpression" &&
node.callee.object.callee.object.type === "Identifier" &&
node.callee.object.callee.object.name === "res" &&
node.callee.object.callee.property.type === "Identifier" &&
node.callee.object.callee.property.name === "status"
) {
context.report({
node,
messageId: "directResponseUsage",
});
}
},
// Prüfen auf Import des BaseController
ImportDeclaration(node) {
if (!isControllerFile) return;
// Prüfe, ob BaseController importiert wird
const specifiers = node.specifiers;
if (specifiers.some(specifier =>
specifier.type === "ImportDefaultSpecifier" &&
specifier.local.name === baseControllerName)) {
return;
}
},
};
},
};
export default {
rules: {
"enforce-basecontroller": enforceBaseControllerRule,
},
};