/** * ESLint rule: Presenters should only do pure mapping, not business logic * * Presenters should NOT use: * - .filter() - filtering should happen in PageQuery * - .sort() - sorting should happen in PageQuery * - .reduce() - aggregation should happen in PageQuery * - .find() - lookup should happen in PageQuery * - .some() / .every() - checks should happen in PageQuery */ module.exports = { meta: { type: 'problem', docs: { description: 'Forbid business logic operations in presenters', category: 'Presenter Purity', }, messages: { message: 'Presenter should only do pure mapping. Move {{operation}}() to PageQuery - see apps/website/lib/contracts/presenters/Presenter.ts', }, }, create(context) { return { CallExpression(node) { const filename = context.getFilename(); // Check if this is a presenter file if (filename.includes('/lib/presenters/') && filename.endsWith('.ts')) { // Check for business logic operations if (node.callee.type === 'MemberExpression') { const property = node.callee.property; if (property.type === 'Identifier') { const forbiddenOperations = ['filter', 'sort', 'reduce', 'find', 'some', 'every', 'map']; const operation = property.name; // Allow .map() - that's the core presenter operation if (forbiddenOperations.includes(operation) && operation !== 'map') { context.report({ node, messageId: 'message', data: { operation }, }); } } } } }, }; }, };