Files
gridpilot.gg/apps/website/eslint-rules/page-query-must-use-builders.js
2026-01-13 00:16:14 +01:00

101 lines
3.0 KiB
JavaScript

/**
* 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',
});
}
},
};
},
};