Files
gridpilot.gg/apps/website/eslint-rules/page-query-use-builder.js
2026-01-12 19:24:59 +01:00

90 lines
2.5 KiB
JavaScript

/**
* ESLint Rule: Page Query Must Use Builder
*
* Ensures page queries use builders to map their results
*/
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'Ensure page queries use builders to map their results',
category: 'Page Query',
recommended: true,
},
messages: {
mustUseBuilder: 'Page queries must use Builders to map Page DTO to View Data/View Model. See apps/website/docs/architecture/write/BUILDERS.md',
},
schema: [],
},
create(context) {
const filename = context.getFilename();
// Only apply to page query files
if (!filename.includes('/lib/page-queries/') || !filename.endsWith('.ts')) {
return {};
}
let hasBuilderImport = false;
let hasReturnStatement = false;
return {
// Check imports for builder
ImportDeclaration(node) {
const importPath = node.source.value;
if (importPath.includes('/lib/builders/')) {
hasBuilderImport = true;
}
},
// Check for return statements
ReturnStatement(node) {
hasReturnStatement = true;
},
'Program:exit'() {
// Skip if file doesn't look like a page query
const isPageQueryFile = filename.includes('/lib/page-queries/') &&
filename.endsWith('.ts') &&
!filename.endsWith('.test.ts') &&
!filename.includes('/result/');
if (!isPageQueryFile) return;
// Check if it's a class-based page query
const sourceCode = context.getSourceCode();
const classNode = sourceCode.ast.body.find(node =>
node.type === 'ClassDeclaration' &&
node.id &&
node.id.name.endsWith('PageQuery')
);
if (!classNode) return;
// Check if the class has an execute method
const executeMethod = classNode.body.body.find(member =>
member.type === 'MethodDefinition' &&
member.key.type === 'Identifier' &&
member.key.name === 'execute'
);
if (!executeMethod) return;
// Check if the execute method uses a builder
// Look for builder usage in the method body
const methodBody = executeMethod.value.body;
if (!methodBody) return;
// Check if there's a builder import
if (!hasBuilderImport) {
context.report({
node: classNode,
messageId: 'mustUseBuilder',
});
}
},
};
},
};