import { readFile, readdir, stat } from 'node:fs/promises'; import path from 'node:path'; import process from 'node:process'; import { fileURLToPath } from 'node:url'; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const errors = []; async function exists(target) { try { await stat(target); return true; } catch { return false; } } async function walk(directory) { const files = []; for (const entry of await readdir(directory, { withFileTypes: true })) { if (['node_modules', '.next', '.turbo', 'dist', 'coverage'].includes(entry.name)) continue; const target = path.join(directory, entry.name); if (entry.isDirectory()) files.push(...(await walk(target))); else files.push(target); } return files; } function requireText(contents, required, label) { for (const marker of required) { if (!contents.includes(marker)) errors.push(`${label}: missing ${marker}`); } } for (const app of ['admin', 'api', 'dashboard', 'homepage', 'storefront']) { if (!(await exists(path.join(root, app)))) errors.push(`missing application directory: ${app}`); if (!(await exists(path.join(root, app, 'package.json')))) errors.push(`${app}: missing package.json`); } for (const app of ['admin', 'dashboard']) { const tokenPath = path.join(root, app, 'src/styles/phase16-tokens.css'); const globalsPath = path.join(root, app, 'src/app/globals.css'); if (!(await exists(tokenPath))) errors.push(`${app}: missing Phase 16 token file`); if (!(await exists(globalsPath))) errors.push(`${app}: missing globals.css`); const tokens = await readFile(tokenPath, 'utf8'); requireText(tokens, [ '--rdg-blue-700', '--rdg-orange-600', '--rdg-sidebar-size', '--rdg-header-size', '--rdg-radius-lg', '--rdg-shadow-card', ], `${app} tokens`); const globals = await readFile(globalsPath, 'utf8'); requireText(globals, [ 'phase16-tokens.css', '.rdg-app-shell', '.rdg-sidebar', '.rdg-topbar', '.rdg-page-hero', '.rdg-page-heading', '.btn-primary', '.btn-conversion', '.panel', '.field', '@media (forced-colors: active)', ], `${app} globals`); const withoutComments = globals.replace(/\/\*[\s\S]*?\*\//g, ''); const balance = [...withoutComments].reduce((value, character) => character === '{' ? value + 1 : character === '}' ? value - 1 : value, 0); if (balance !== 0) errors.push(`${app}: unbalanced CSS braces (${balance})`); } const dashboardLayout = await readFile(path.join(root, 'dashboard/src/app/(dashboard)/layout.tsx'), 'utf8'); requireText(dashboardLayout, ['rdg-app-shell', 'rdg-workspace', 'rdg-app-main', 'rdg-app-content'], 'dashboard shell'); const dashboardSidebar = await readFile(path.join(root, 'dashboard/src/components/layout/Sidebar.tsx'), 'utf8'); requireText(dashboardSidebar, ['rdg-sidebar', 'rdg-nav-item', 'aria-current', 'RentalDriveGo'], 'dashboard sidebar'); const dashboardTopbar = await readFile(path.join(root, 'dashboard/src/components/layout/TopBar.tsx'), 'utf8'); requireText(dashboardTopbar, ['rdg-topbar', 'rdg-system-status', 'rdg-search-field'], 'dashboard topbar'); const adminLayout = await readFile(path.join(root, 'admin/src/app/dashboard/layout.tsx'), 'utf8'); requireText(adminLayout, [ 'rdg-app-shell', 'rdg-sidebar', 'rdg-nav-item', 'rdg-topbar', 'rdg-system-status', 'aria-current', 'RentalDriveGo', ], 'admin shell'); const routeRoots = [ path.join(root, 'admin/src/app/dashboard'), path.join(root, 'dashboard/src/app/(dashboard)'), ]; for (const routeRoot of routeRoots) { const pages = (await walk(routeRoot)).filter((file) => file.endsWith('page.tsx')); for (const page of pages) { const contents = await readFile(page, 'utf8'); if (!['rdg-page-heading', 'rdg-page-hero'].some((marker) => contents.includes(marker))) { errors.push(`${path.relative(root, page)}: missing Phase 16 page heading or hero`); } } } for (const app of ['admin', 'dashboard']) { for (const file of await walk(path.join(root, app, 'src'))) { if (!/\.(ts|tsx|css|json)$/.test(file)) continue; const contents = await readFile(file, 'utf8'); if (/RentalDriveGo/i.test(contents)) errors.push(`${path.relative(root, file)}: retired RentalDriveGo brand remains`); } } const authChecks = [ 'dashboard/src/app/sign-in/[[...sign-in]]/SignInPageClient.tsx', 'dashboard/src/app/sign-up/[[...sign-up]]/SignUpForm.tsx', ]; for (const relative of authChecks) { const contents = await readFile(path.join(root, relative), 'utf8'); if (!contents.includes('PublicPageLayout')) errors.push(`${relative}: shared public layout not applied`); } for (const relative of [ 'storefront/src/components/public/SiteNavbar.tsx', 'storefront/src/components/public/SiteFooter.tsx', 'storefront/src/components/public/SitePageLayout.tsx', ]) { if (!(await exists(path.join(root, relative)))) errors.push(`missing shared storefront component: ${relative}`); } if (errors.length) { console.error(errors.join('\n')); process.exit(1); } console.log('Phase 16 dashboard/admin design checks passed.');