/** * ESLint rule to enforce PageQueries use Builders * * PageQueries should not manually transform API DTOs. * They must use Builder classes to transform API DTOs to View Data. */ module.exports = { meta: { type: 'problem', docs: { description: 'Enforce PageQueries use Builders for data transformation', category: 'Page Query', recommended: true, }, fixable: null, schema: [], messages: { mustUseBuilder: 'PageQueries must use Builder classes to transform API DTOs. Found manual object literal transformation in execute() method.', multipleExports: 'PageQuery files should only export the PageQuery class, not DTOs.', }, }, create(context) { let inPageQueryExecute = false; let hasManualTransformation = false; let hasMultipleExports = false; return { // Track PageQuery class execute method MethodDefinition(node) { if (node.key.type === 'Identifier' && node.key.name === 'execute' && node.parent.type === 'ClassBody') { const classNode = node.parent.parent; if (classNode && classNode.id && classNode.id.name && classNode.id.name.endsWith('PageQuery')) { inPageQueryExecute = true; } } }, // Detect object literal assignments (manual transformation) VariableDeclarator(node) { if (inPageQueryExecute && node.init && node.init.type === 'ObjectExpression') { // This is: const dto = { ... } hasManualTransformation = true; } }, // Detect object literal in return statements ReturnStatement(node) { if (inPageQueryExecute && node.argument && node.argument.type === 'ObjectExpression') { // This is: return { ... } hasManualTransformation = true; } }, // Track exports ExportNamedDeclaration(node) { if (node.declaration) { if (node.declaration.type === 'ClassDeclaration') { const className = node.declaration.id?.name; if (className && !className.endsWith('PageQuery')) { hasMultipleExports = true; } } else if (node.declaration.type === 'InterfaceDeclaration' || node.declaration.type === 'TypeAlias') { hasMultipleExports = true; } } else if (node.specifiers && node.specifiers.length > 0) { hasMultipleExports = true; } }, 'MethodDefinition:exit'(node) { if (node.key.type === 'Identifier' && node.key.name === 'execute') { inPageQueryExecute = false; } }, 'Program:exit'() { if (hasManualTransformation) { context.report({ node: context.getSourceCode().ast, messageId: 'mustUseBuilder', }); } if (hasMultipleExports) { context.report({ node: context.getSourceCode().ast, messageId: 'multipleExports', }); } }, }; }, };