website refactor

This commit is contained in:
2026-01-12 01:45:19 +01:00
parent fefd8d1cd6
commit 61db9116f6
11 changed files with 375 additions and 3 deletions

View File

@@ -161,6 +161,9 @@ export class ArchitectureGuardrails {
this.checkSortingFiltering(filePath, content);
this.checkNullReturns(filePath, content);
// NEW: Enforce PageQuery contract
this.checkPageQueryContract(filePath, content);
// Rule 8: Forbid 'as any' usage
this.checkAsAnyUsage(filePath, content);
@@ -226,6 +229,9 @@ export class ArchitectureGuardrails {
this.checkUseClientDirective(filePath, content);
this.checkViewModelPageQueryImports(filePath, content);
// NEW: Enforce ViewModel contract
this.checkViewModelContract(filePath, content);
// Rule 8: Forbid 'as any' usage
this.checkAsAnyUsage(filePath, content);
@@ -243,6 +249,9 @@ export class ArchitectureGuardrails {
// Rule 6: Client-only guardrails - presenters should not use HTTP
this.checkHttpCalls(filePath, content);
// NEW: Enforce Presenter contract
this.checkPresenterContract(filePath, content);
// Rule 8: Forbid 'as any' usage
this.checkAsAnyUsage(filePath, content);
@@ -1078,6 +1087,109 @@ export class ArchitectureGuardrails {
});
}
// ============================================================================
// NEW: VIOLATION CHECKS - CONTRACT ENFORCEMENT
// ============================================================================
private checkPageQueryContract(filePath: string, content: string): void {
// Check if file contains a class that should implement PageQuery
const classMatch = content.match(/class\s+(\w+PageQuery)\s+{/);
if (!classMatch) return;
// Check if it implements PageQuery interface
if (!content.includes('implements PageQuery<')) {
this.addViolation(
'pagequery-must-implement-contract',
filePath,
1,
'PageQuery class must implement PageQuery<TPageDto, TParams> interface'
);
}
// Check if it has execute method
if (!content.includes('execute(')) {
this.addViolation(
'pagequery-must-have-execute',
filePath,
1,
'PageQuery class must have execute(params) method'
);
}
// Check if execute returns Promise<PageQueryResult>
const executeMatch = content.match(/execute\([^)]*\):\s*Promise<PageQueryResult/);
if (!executeMatch) {
this.addViolation(
'pagequery-execute-return-type',
filePath,
1,
'PageQuery execute() must return Promise<PageQueryResult<TPageDto>>'
);
}
}
private checkPresenterContract(filePath: string, content: string): void {
// Check if file contains a class that should implement Presenter
const classMatch = content.match(/class\s+(\w+Presenter)\s+{/);
if (!classMatch) return;
// Check if it implements Presenter interface
if (!content.includes('implements Presenter<')) {
this.addViolation(
'presenter-must-implement-contract',
filePath,
1,
'Presenter class must implement Presenter<TInput, TOutput> interface'
);
}
// Check if it has present method
if (!content.includes('present(')) {
this.addViolation(
'presenter-must-have-present',
filePath,
1,
'Presenter class must have present(input) method'
);
}
// Check for 'use client' directive
if (!content.includes("'use client'") && !content.includes('"use client"')) {
this.addViolation(
'presenter-must-be-client',
filePath,
1,
'Presenter must have \'use client\' directive at top-level'
);
}
}
private checkViewModelContract(filePath: string, content: string): void {
// Check if file contains a class that should extend ViewModel
const classMatch = content.match(/class\s+(\w+ViewModel)\s+{/);
if (!classMatch) return;
// Check if it extends ViewModel
if (!content.includes('extends ViewModel')) {
this.addViolation(
'viewmodel-must-extend-contract',
filePath,
1,
'ViewModel class must extend ViewModel base class'
);
}
// Check for 'use client' directive
if (!content.includes("'use client'") && !content.includes('"use client"')) {
this.addViolation(
'viewmodel-must-be-client',
filePath,
1,
'ViewModel must have \'use client\' directive at top-level'
);
}
}
// ============================================================================
// HELPERS
// ============================================================================

View File

@@ -139,4 +139,42 @@ describe('Architecture Guardrails', () => {
expect(violations.length).toBe(0);
});
// NEW: Contract enforcement tests
it('should enforce: PageQuery classes must implement PageQuery contract', () => {
const violations = guardrails.getFilteredViolations().filter(
v => v.ruleName === 'pagequery-must-implement-contract' ||
v.ruleName === 'pagequery-must-have-execute' ||
v.ruleName === 'pagequery-execute-return-type'
);
expect(violations.length).toBe(0);
});
it('should enforce: Presenter classes must implement Presenter contract', () => {
const violations = guardrails.getFilteredViolations().filter(
v => v.ruleName === 'presenter-must-implement-contract' ||
v.ruleName === 'presenter-must-have-present' ||
v.ruleName === 'presenter-must-be-client'
);
expect(violations.length).toBe(0);
});
it('should enforce: ViewModel classes must extend ViewModel contract', () => {
const violations = guardrails.getFilteredViolations().filter(
v => v.ruleName === 'viewmodel-must-extend-contract' ||
v.ruleName === 'viewmodel-must-be-client'
);
expect(violations.length).toBe(0);
});
it('should enforce: DisplayObject files must export only classes', () => {
const violations = guardrails.getFilteredViolations().filter(
v => v.ruleName === 'no-non-class-display-exports'
);
expect(violations.length).toBe(0);
});
});