/** * ESLint rule to enforce ViewData contract implementation * * ViewData files in lib/view-data/ must: * 1. Be interfaces or types named *ViewData * 2. Extend the ViewData interface from contracts */ module.exports = { meta: { type: 'problem', docs: { description: 'Enforce ViewData contract implementation', category: 'Contracts', recommended: true, }, fixable: null, schema: [], messages: { notAnInterface: 'ViewData files must be interfaces or types named *ViewData', missingExtends: 'ViewData must extend the ViewData interface from lib/contracts/view-data/ViewData.ts', }, }, create(context) { const filename = context.getFilename(); const isInViewData = filename.includes('/lib/view-data/'); if (!isInViewData) return {}; let hasViewDataExtends = false; let hasCorrectName = false; return { // Check interface declarations TSInterfaceDeclaration(node) { const interfaceName = node.id?.name; if (interfaceName && interfaceName.endsWith('ViewData')) { hasCorrectName = true; // Check if it extends ViewData if (node.extends && node.extends.length > 0) { for (const ext of node.extends) { if (ext.type === 'TSExpressionWithTypeArguments' && ext.expression.type === 'Identifier' && ext.expression.name === 'ViewData') { hasViewDataExtends = true; } } } } }, // Check type alias declarations TSTypeAliasDeclaration(node) { const typeName = node.id?.name; if (typeName && typeName.endsWith('ViewData')) { hasCorrectName = true; // For type aliases, check if it's an intersection with ViewData if (node.typeAnnotation && node.typeAnnotation.type === 'TSIntersectionType') { for (const type of node.typeAnnotation.types) { if (type.type === 'TSTypeReference' && type.typeName && type.typeName.type === 'Identifier' && type.typeName.name === 'ViewData') { hasViewDataExtends = true; } } } } }, 'Program:exit'() { if (!hasCorrectName) { context.report({ node: context.getSourceCode().ast, messageId: 'notAnInterface', }); } else if (!hasViewDataExtends) { context.report({ node: context.getSourceCode().ast, messageId: 'missingExtends', }); } }, }; }, };