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

175 lines
4.9 KiB
JavaScript

/**
* ESLint rules for Page Query Guardrails
*
* Enforces page query contracts and boundaries
*/
module.exports = {
// Rule 1: No null returns in page queries
'no-null-returns-in-page-queries': {
meta: {
type: 'problem',
docs: {
description: 'Forbid null returns in page queries',
category: 'Page Query',
},
messages: {
message: 'PageQueries must return PageQueryResult union, not null - see apps/website/lib/contracts/page-queries/PageQuery.ts',
},
},
create(context) {
return {
ReturnStatement(node) {
if (node.argument &&
node.argument.type === 'Literal' &&
node.argument.value === null &&
!isInComment(node)) {
context.report({
node,
messageId: 'message',
});
}
},
};
},
},
// Rule 2: Invalid page query filename
'invalid-page-query-filename': {
meta: {
type: 'problem',
docs: {
description: 'Enforce correct page query filename',
category: 'Page Query',
},
messages: {
message: 'PageQuery files must end with PageQuery.ts - see apps/website/lib/contracts/page-queries/PageQuery.ts',
},
},
create(context) {
const filename = context.getFilename();
if (filename.includes('/page-queries/') && !filename.endsWith('PageQuery.ts')) {
context.report({
loc: { line: 1, column: 0 },
messageId: 'message',
});
}
return {};
},
},
// Rule 3: PageQuery must implement contract
'pagequery-must-implement-contract': {
meta: {
type: 'problem',
docs: {
description: 'Enforce PageQuery interface implementation',
category: 'Page Query',
},
messages: {
message: 'PageQuery class must implement PageQuery<TPageDto, TParams> interface - see apps/website/lib/contracts/page-queries/PageQuery.ts',
},
},
create(context) {
return {
ClassDeclaration(node) {
const className = node.id?.name;
if (className && className.endsWith('PageQuery')) {
const hasPageQueryImpl = node.implements && node.implements.some(impl => {
// Handle different AST node types for generic interfaces
if (impl.expression.type === 'TSExpressionWithTypeArguments') {
return impl.expression.expression.name === 'PageQuery';
}
if (impl.expression.type === 'Identifier') {
return impl.expression.name === 'PageQuery';
}
return false;
});
if (!hasPageQueryImpl) {
context.report({
node,
messageId: 'message',
});
}
}
},
};
},
},
// Rule 4: PageQuery must have execute method
'pagequery-must-have-execute': {
meta: {
type: 'problem',
docs: {
description: 'Enforce PageQuery execute method',
category: 'Page Query',
},
messages: {
message: 'PageQuery class must have execute(params) method - see apps/website/lib/contracts/page-queries/PageQuery.ts',
},
},
create(context) {
return {
ClassDeclaration(node) {
const className = node.id?.name;
if (className && className.endsWith('PageQuery')) {
const hasExecute = node.body.body.some(member =>
member.type === 'MethodDefinition' &&
member.key.type === 'Identifier' &&
member.key.name === 'execute'
);
if (!hasExecute) {
context.report({
node,
messageId: 'message',
});
}
}
},
};
},
},
// Rule 5: PageQuery execute return type
'pagequery-execute-return-type': {
meta: {
type: 'problem',
docs: {
description: 'Enforce PageQuery execute return type',
category: 'Page Query',
},
messages: {
message: 'PageQuery execute() must return Promise<PageQueryResult<TPageDto>> - see apps/website/lib/contracts/page-queries/PageQuery.ts',
},
},
create(context) {
return {
MethodDefinition(node) {
if (node.key.type === 'Identifier' &&
node.key.name === 'execute' &&
node.value.type === 'FunctionExpression') {
const returnType = node.value.returnType;
if (!returnType ||
!returnType.typeAnnotation ||
!returnType.typeAnnotation.typeName ||
returnType.typeAnnotation.typeName.name !== 'Promise') {
context.report({
node,
messageId: 'message',
});
}
}
},
};
},
},
};
// Helper functions
function isInComment(node) {
return false;
}