website refactor
This commit is contained in:
156
apps/website/eslint-rules/services-rules.js
Normal file
156
apps/website/eslint-rules/services-rules.js
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* ESLint rules for Services Guardrails
|
||||
*
|
||||
* Enforces service contracts and boundaries
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
// Rule 1: Services must be marked with @server-safe or @client-only
|
||||
'services-must-be-marked': {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Enforce service safety marking',
|
||||
category: 'Services',
|
||||
},
|
||||
messages: {
|
||||
message: 'Services must be explicitly marked with @server-safe or @client-only comment',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
return {
|
||||
Program(node) {
|
||||
const filename = context.getFilename();
|
||||
if (filename.includes('/lib/services/') && filename.endsWith('.ts')) {
|
||||
const sourceCode = context.getSourceCode();
|
||||
const text = sourceCode.getText();
|
||||
|
||||
const hasServerSafe = text.includes('@server-safe');
|
||||
const hasClientOnly = text.includes('@client-only');
|
||||
|
||||
if (!hasServerSafe && !hasClientOnly) {
|
||||
context.report({
|
||||
loc: { line: 1, column: 0 },
|
||||
messageId: 'message',
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
// Rule 2: No external API calls in services
|
||||
'no-external-api-in-services': {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Forbid external API calls in services',
|
||||
category: 'Services',
|
||||
},
|
||||
messages: {
|
||||
message: 'External API calls must be in adapters, not services',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
return {
|
||||
CallExpression(node) {
|
||||
const filename = context.getFilename();
|
||||
if (filename.includes('/lib/services/')) {
|
||||
// Check for fetch, axios, or other HTTP calls
|
||||
if (node.callee.type === 'Identifier' &&
|
||||
['fetch', 'axios'].includes(node.callee.name) &&
|
||||
!isInComment(node)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'message',
|
||||
});
|
||||
}
|
||||
|
||||
// Check for external API URLs
|
||||
if (node.arguments.length > 0) {
|
||||
const firstArg = node.arguments[0];
|
||||
if (firstArg.type === 'Literal' &&
|
||||
typeof firstArg.value === 'string' &&
|
||||
(firstArg.value.startsWith('http') ||
|
||||
firstArg.value.includes('api.') ||
|
||||
firstArg.value.includes('.com'))) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'message',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
// Rule 3: Services must be pure functions
|
||||
'services-must-be-pure': {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Enforce service purity',
|
||||
category: 'Services',
|
||||
},
|
||||
messages: {
|
||||
message: 'Services must be pure functions, no side effects allowed',
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
return {
|
||||
CallExpression(node) {
|
||||
const filename = context.getFilename();
|
||||
if (filename.includes('/lib/services/')) {
|
||||
// Check for common side effects
|
||||
if (node.callee.type === 'MemberExpression') {
|
||||
const object = node.callee.object;
|
||||
const property = node.callee.property;
|
||||
|
||||
// DOM manipulation
|
||||
if (object.type === 'Identifier' &&
|
||||
['document', 'window'].includes(object.name)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'message',
|
||||
});
|
||||
}
|
||||
|
||||
// State mutation
|
||||
if (property.type === 'Identifier' &&
|
||||
['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].includes(property.name)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'message',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Direct assignment to external state
|
||||
if (node.type === 'AssignmentExpression' &&
|
||||
node.left.type === 'MemberExpression' &&
|
||||
node.left.object.type === 'Identifier' &&
|
||||
!isInFunctionScope(node)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'message',
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Helper functions
|
||||
function isInComment(node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isInFunctionScope(node) {
|
||||
// Simplified check
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user