/** * ESLint rule to enforce ViewModel contract implementation * * ViewModel files in lib/view-models/ must: * 1. Be classes named *ViewModel * 2. Extend the ViewModel class from contracts */ module.exports = { meta: { type: 'problem', docs: { description: 'Enforce ViewModel contract implementation', category: 'Contracts', recommended: true, }, fixable: null, schema: [], messages: { notAClass: 'ViewModel files must be classes named *ViewModel', missingExtends: 'ViewModel must extend the ViewModel class from lib/contracts/view-models/ViewModel.ts', }, }, create(context) { const filename = context.getFilename(); const isInViewModels = filename.includes('/lib/view-models/'); if (!isInViewModels) return {}; let hasViewModelExtends = false; let hasCorrectName = false; return { // Check class declarations ClassDeclaration(node) { const className = node.id?.name; if (className && className.endsWith('ViewModel')) { hasCorrectName = true; // Check if it extends ViewModel if (node.superClass && node.superClass.type === 'Identifier' && node.superClass.name === 'ViewModel') { hasViewModelExtends = true; } } }, 'Program:exit'() { if (!hasCorrectName) { context.report({ node: context.getSourceCode().ast, messageId: 'notAClass', }); } else if (!hasViewModelExtends) { context.report({ node: context.getSourceCode().ast, messageId: 'missingExtends', }); } }, }; }, };