/** * ESLint rule to enforce View Data Builder contract * * View Data Builders must: * 1. Be classes named *ViewDataBuilder * 2. Have a static build() method * 3. Accept API DTO as parameter (named 'apiDto', NOT 'pageDto') * 4. Return View Data */ module.exports = { meta: { type: 'problem', docs: { description: 'Enforce View Data Builder contract', category: 'Builders', recommended: true, }, fixable: null, schema: [], messages: { notAClass: 'View Data Builders must be classes named *ViewDataBuilder', missingBuildMethod: 'View Data Builders must have a static build() method', invalidBuildSignature: 'build() method must accept API DTO and return View Data', wrongParameterName: 'Parameter must be named "apiDto", not "pageDto" or other names', }, }, create(context) { const filename = context.getFilename(); const isInViewDataBuilders = filename.includes('/lib/builders/view-data/'); if (!isInViewDataBuilders) return {}; let hasBuildMethod = false; let hasCorrectSignature = false; let hasCorrectParameterName = false; return { // Check class declaration ClassDeclaration(node) { const className = node.id?.name; if (!className || !className.endsWith('ViewDataBuilder')) { context.report({ node, messageId: 'notAClass', }); } // Check for static build method const buildMethod = node.body.body.find(member => member.type === 'MethodDefinition' && member.key.type === 'Identifier' && member.key.name === 'build' && member.static === true ); if (buildMethod) { hasBuildMethod = true; // Check signature - should have at least one parameter if (buildMethod.value && buildMethod.value.params && buildMethod.value.params.length > 0) { hasCorrectSignature = true; // Check parameter name const firstParam = buildMethod.value.params[0]; if (firstParam.type === 'Identifier' && firstParam.name === 'apiDto') { hasCorrectParameterName = true; } else if (firstParam.type === 'Identifier' && firstParam.name === 'pageDto') { // Report specific error for pageDto context.report({ node: firstParam, messageId: 'wrongParameterName', }); } } } }, 'Program:exit'() { if (!hasBuildMethod) { context.report({ node: context.getSourceCode().ast, messageId: 'missingBuildMethod', }); } else if (!hasCorrectSignature) { context.report({ node: context.getSourceCode().ast, messageId: 'invalidBuildSignature', }); } else if (!hasCorrectParameterName) { // Only report if not already reported for pageDto context.report({ node: context.getSourceCode().ast, messageId: 'wrongParameterName', }); } }, }; }, };