/** * ESLint rule to suggest proper component classification * * Architecture: * - app/ - Pages and layouts only (no business logic) * - components/ - App-level components (can be stateful, can use hooks) * - ui/ - Pure, reusable UI elements (stateless, no hooks) * - hooks/ - Shared stateful logic * * This rule provides SUGGESTIONS, not errors, for component placement. */ module.exports = { meta: { type: 'suggestion', docs: { description: 'Suggest proper component classification', category: 'Architecture', recommended: false, }, fixable: 'code', schema: [], messages: { uiShouldBePure: 'This appears to be a pure UI element. Consider moving to ui/ for maximum reusability.', componentShouldBeInComponents: 'This component uses state/hooks. Consider moving to components/.', pureComponentInComponents: 'Pure component in components/. Consider moving to ui/ for better reusability.', }, }, create(context) { const filename = context.getFilename(); const isInUi = filename.includes('/ui/'); const isInComponents = filename.includes('/components/'); const isInApp = filename.includes('/app/'); if (!isInUi && !isInComponents) return {}; return { Program(node) { const sourceCode = context.getSourceCode(); const text = sourceCode.getText(); // Detect stateful patterns const hasState = /useState|useReducer|this\.state/.test(text); const hasEffects = /useEffect|useLayoutEffect/.test(text); const hasContext = /useContext/.test(text); const hasComplexLogic = /if\s*\(|switch\s*\(|for\s*\(|while\s*\(/.test(text); // Detect pure UI patterns (just JSX, props, simple functions) const hasOnlyJsx = /^\s*import.*from.*;\s*export\s+function\s+\w+\s*\([^)]*\)\s*{?\s*return\s*\(?.*\)?;?\s*}?\s*$/m.test(text); const hasNoLogic = !hasState && !hasEffects && !hasContext && !hasComplexLogic; if (isInUi && hasState) { context.report({ loc: { line: 1, column: 0 }, messageId: 'componentShouldBeInComponents', }); } if (isInComponents && hasNoLogic && hasOnlyJsx) { context.report({ loc: { line: 1, column: 0 }, messageId: 'pureComponentInComponents', }); } if (isInComponents && !hasState && !hasEffects && !hasContext) { // Check if it's mostly just rendering props const hasManyProps = /\{\s*\.\.\.props\s*\}/.test(text) || /\{\s*props\./.test(text); if (hasManyProps && hasNoLogic) { context.report({ loc: { line: 1, column: 0 }, messageId: 'uiShouldBePure', }); } } }, }; }, };