Spaces:
Sleeping
Sleeping
| // Guards the "Page = wiring container + data hook" convention (see | |
| // src/pages/PATTERN.md). A *Page.tsx default-export component should wire a | |
| // co-located use<Page>() hook into JSX β it must not own state/effects itself. | |
| // | |
| // We scan only the default-export component body (from `export default function` | |
| // up to the next top-level `function` declaration or EOF), so presentational | |
| // sub-components and helper hooks living in the same file are not flagged. | |
| // Context hooks like useTranslation/useParams are fine; the smell is stateful | |
| // logic β useState/useReducer/useEffect/useLayoutEffect/useMemo/useCallback/useRef. | |
| import { readdirSync, readFileSync } from 'node:fs' | |
| import { join, dirname } from 'node:path' | |
| import { fileURLToPath } from 'node:url' | |
| const pagesDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'src', 'pages') | |
| const BANNED = ['useState', 'useReducer', 'useEffect', 'useLayoutEffect', 'useMemo', 'useCallback', 'useRef'] | |
| const bannedRe = new RegExp(`\\b(${BANNED.join('|')})\\s*\\(`) | |
| const violations = [] | |
| for (const file of readdirSync(pagesDir)) { | |
| if (!file.endsWith('Page.tsx') || file.endsWith('.test.tsx')) continue | |
| const src = readFileSync(join(pagesDir, file), 'utf8') | |
| const lines = src.split('\n') | |
| const start = lines.findIndex(l => /export default function/.test(l)) | |
| if (start === -1) continue | |
| // The page body ends at the next top-level declaration (a `function` at | |
| // column 0) β everything after that is a sub-component or helper. | |
| let end = lines.length | |
| for (let i = start + 1; i < lines.length; i++) { | |
| if (/^(function |const [A-Z]\w* = )/.test(lines[i])) { end = i; break } | |
| } | |
| for (let i = start; i < end; i++) { | |
| if (bannedRe.test(lines[i])) { | |
| violations.push(`${file}:${i + 1} ${lines[i].trim()}`) | |
| } | |
| } | |
| } | |
| if (violations.length > 0) { | |
| console.error('Page-pattern violations β move this state/effect logic into the page\'s use<Page>() hook:\n') | |
| for (const v of violations) console.error(' ' + v) | |
| console.error(`\n${violations.length} violation(s). See src/pages/PATTERN.md.`) | |
| process.exit(1) | |
| } | |
| console.log('Page pattern OK β no state/effect logic in page containers.') | |