UNPKG

@addon24/eslint-config

Version:

ESLint configuration rules for WorldOfTextcraft projects - Centralized configuration for all project types

180 lines (159 loc) 5.4 kB
/** * ESLint-Regel: enforce-app-module-registration * * Überprüft, ob alle Controller und Services korrekt im AppModule registriert sind. * Verhindert Dependency Injection Fehler durch fehlende Registrierungen. */ export default { meta: { type: "problem", docs: { description: "Enforce proper registration of controllers and services in AppModule", category: "Architecture", recommended: true, }, messages: { missingController: "Controller '{{controllerName}}' is not registered in AppModule controllers array", missingService: "Service '{{serviceName}}' is not registered in AppModule providers array", invalidControllerImport: "Controller '{{controllerName}}' is imported but not used in controllers array", invalidServiceImport: "Service '{{serviceName}}' is imported but not used in providers array", }, fixable: "code", }, create(context) { const filename = context.getFilename(); // Nur für AppModule.ts ausführen if (!filename.includes('AppModule.ts')) { return {}; } return { Program(node) { try { // Analysiere AppModule AST const appModuleAnalysis = analyzeAppModule(node); // Prüfe fehlende Controller appModuleAnalysis.missingControllers.forEach(controller => { context.report({ node, messageId: "missingController", data: { controllerName: controller.name } }); }); // Prüfe fehlende Services appModuleAnalysis.missingServices.forEach(service => { context.report({ node, messageId: "missingService", data: { serviceName: service.name } }); }); // Prüfe ungenutzte Imports appModuleAnalysis.unusedImports.forEach(importItem => { context.report({ node: importItem.node, messageId: importItem.type === 'controller' ? "invalidControllerImport" : "invalidServiceImport", data: { controllerName: importItem.name, serviceName: importItem.name } }); }); } catch (error) { // Fehler beim Analysieren ignorieren console.warn('AppModule registration check failed:', error.message); } } }; } }; /** * Analysiert das AppModule und findet fehlende Registrierungen */ function analyzeAppModule(node) { const missingControllers = []; const missingServices = []; const unusedImports = []; let controllersArray = []; let providersArray = []; let imports = []; // Durchlaufe alle Import-Statements node.body.forEach(statement => { if (statement.type === 'ImportDeclaration') { const importPath = statement.source.value; statement.specifiers.forEach(spec => { if (spec.type === 'ImportDefaultSpecifier') { imports.push({ name: spec.local.name, path: importPath, node: statement }); } }); } }); // Finde @Module Decorator - suche nach der korrekten NestJS-Struktur const moduleDecorator = node.body.find(statement => { if (statement.type === 'ExportDefaultDeclaration' && statement.declaration.type === 'ClassDeclaration') { // Prüfe ob es Decorators gibt if (statement.declaration.decorators && statement.declaration.decorators.length > 0) { return statement.declaration.decorators.some(dec => { if (dec.expression && dec.expression.type === 'CallExpression') { return dec.expression.callee && dec.expression.callee.name === 'Module'; } return false; }); } } return false; }); if (moduleDecorator) { const classBody = moduleDecorator.declaration.body; // Finde controllers und providers Arrays classBody.body.forEach(member => { if (member.type === 'ClassProperty' && member.key && member.key.name === 'controllers') { controllersArray = extractArrayElements(member.value); } if (member.type === 'ClassProperty' && member.key && member.key.name === 'providers') { providersArray = extractArrayElements(member.value); } }); } // Prüfe Controller-Imports imports.forEach(importItem => { if (importItem.path.includes('/controller/') || importItem.path.includes('Controller')) { const isRegistered = controllersArray.includes(importItem.name); if (!isRegistered) { missingControllers.push(importItem); } } }); // Prüfe Service-Imports imports.forEach(importItem => { if (importItem.path.includes('/service/') || importItem.path.includes('Service')) { const isRegistered = providersArray.includes(importItem.name); if (!isRegistered) { missingServices.push(importItem); } } }); return { missingControllers, missingServices, unusedImports }; } /** * Extrahiert Elemente aus einem Array-Literal */ function extractArrayElements(arrayNode) { if (!arrayNode || arrayNode.type !== 'ArrayExpression') { return []; } return arrayNode.elements.map(element => { if (element && element.type === 'Identifier') { return element.name; } return null; }).filter(Boolean); }