website refactor

This commit is contained in:
2026-01-13 02:38:49 +01:00
parent e981ebd9e9
commit 38b25bafe1
20 changed files with 1138 additions and 80 deletions

View File

@@ -1,7 +1,7 @@
/**
* ESLint rule to enforce PageQueries use Builders
*
* PageQueries should not manually transform API DTOs.
* PageQueries should not manually transform API DTOs or return them directly.
* They must use Builder classes to transform API DTOs to View Data.
*/
@@ -16,7 +16,7 @@ module.exports = {
fixable: null,
schema: [],
messages: {
mustUseBuilder: 'PageQueries must use Builder classes to transform API DTOs. Found manual object literal transformation in execute() method.',
mustUseBuilder: 'PageQueries must use Builder classes to transform API DTOs to View Data. Found manual transformation or direct API DTO return.',
multipleExports: 'PageQuery files should only export the PageQuery class, not DTOs.',
},
},
@@ -25,8 +25,17 @@ module.exports = {
let inPageQueryExecute = false;
let hasManualTransformation = false;
let hasMultipleExports = false;
let hasBuilderCall = false;
let pageQueryClassName = null;
return {
// Track PageQuery class name
ClassDeclaration(node) {
if (node.id && node.id.name && node.id.name.endsWith('PageQuery')) {
pageQueryClassName = node.id.name;
}
},
// Track PageQuery class execute method
MethodDefinition(node) {
if (node.key.type === 'Identifier' &&
@@ -41,19 +50,75 @@ module.exports = {
}
},
// Detect Builder calls
CallExpression(node) {
if (inPageQueryExecute) {
// Check for Builder.build() or Builder.createViewData()
if (node.callee.type === 'MemberExpression' &&
node.callee.property.type === 'Identifier' &&
(node.callee.property.name === 'build' || node.callee.property.name === 'createViewData')) {
// Check if the object is a Builder
if (node.callee.object.type === 'Identifier' &&
(node.callee.object.name.includes('Builder') ||
node.callee.object.name.includes('builder'))) {
hasBuilderCall = 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;
if (inPageQueryExecute && node.argument) {
// Direct object literal return
if (node.argument.type === 'ObjectExpression') {
hasManualTransformation = true;
}
// Direct identifier return (likely API DTO)
else if (node.argument.type === 'Identifier' &&
!node.argument.name.includes('ViewData') &&
!node.argument.name.includes('viewData')) {
// This might be returning an API DTO directly
// We'll flag it as manual transformation since no builder was used
if (!hasBuilderCall) {
hasManualTransformation = true;
}
}
// CallExpression like Result.ok(apiDto) or Result.err()
else if (node.argument.type === 'CallExpression') {
// Check if it's a Result call with an identifier argument
const callExpr = node.argument;
if (callExpr.callee.type === 'MemberExpression' &&
callExpr.callee.object.type === 'Identifier' &&
callExpr.callee.object.name === 'Result' &&
callExpr.callee.property.type === 'Identifier' &&
(callExpr.callee.property.name === 'ok' || callExpr.callee.property.name === 'err')) {
// If it's Result.ok(someIdentifier), check if the identifier is likely an API DTO
if (callExpr.callee.property.name === 'ok' &&
callExpr.arguments.length > 0 &&
callExpr.arguments[0].type === 'Identifier') {
const argName = callExpr.arguments[0].name;
// Common API DTO naming patterns
const isApiDto = argName.includes('Dto') ||
argName.includes('api') ||
argName === 'result' ||
argName === 'data';
if (isApiDto && !hasBuilderCall) {
hasManualTransformation = true;
}
}
}
}
}
},
@@ -81,7 +146,8 @@ module.exports = {
},
'Program:exit'() {
if (hasManualTransformation) {
// Only report if no builder was used
if (hasManualTransformation && !hasBuilderCall) {
context.report({
node: context.getSourceCode().ast,
messageId: 'mustUseBuilder',