Files
Rental-operations-platform/scripts/validate-phase11-foundation.mjs
T
2026-06-25 19:06:59 -04:00

90 lines
2.8 KiB
JavaScript

import { readFile } from 'node:fs/promises';
import path from 'node:path';
import { fail, pass, repositoryRoot, walk } from './lib.mjs';
const errors = [];
const componentTokenPath = path.join(repositoryRoot, 'src', 'styles', 'component-tokens.css');
const componentTokens = await readFile(componentTokenPath, 'utf8');
const requiredTokens = [
'--surface-page',
'--surface-primary',
'--surface-elevated',
'--surface-muted',
'--text-primary',
'--text-secondary',
'--text-inverse',
'--border-standard',
'--border-strong',
'--interactive-primary',
'--interactive-conversion',
'--interactive-danger',
'--focus-ring',
'--status-success-surface',
'--status-warning-surface',
'--status-error-surface',
'--status-info-surface',
'--overlay-scrim',
'--control-disabled-surface',
'--selection-surface',
'--skeleton-base',
'--shadow-card',
'--container-wide',
'--layer-overlay',
'--type-display-size',
];
for (const token of requiredTokens) {
if (!componentTokens.includes(token)) errors.push(`Missing Phase 11 component token ${token}.`);
}
if (!componentTokens.includes("html[data-theme='dark']")) {
errors.push('Component token layer has no explicit dark-theme mapping.');
}
if (!componentTokens.includes("html[data-theme-preference='system']")) {
errors.push('Component token layer has no CSS-only system-theme fallback.');
}
const cssFiles = await walk(path.join(repositoryRoot, 'src'), (file) => file.endsWith('.css'));
const declarationFiles = new Set([
path.join(repositoryRoot, 'src', 'styles', 'tokens.css'),
componentTokenPath,
]);
const literalColor = /(?:#[0-9a-f]{3,8}\b|\brgba?\(|\bhsla?\()/gi;
const rawPaletteReference = /var\(--color-(?:blue|orange|navy|gray|green|amber|red)-/gi;
for (const file of cssFiles) {
if (declarationFiles.has(file)) continue;
const text = await readFile(file, 'utf8');
if (literalColor.test(text)) {
errors.push(
`${path.relative(repositoryRoot, file)} contains a literal color outside token declarations.`,
);
}
literalColor.lastIndex = 0;
if (rawPaletteReference.test(text)) {
errors.push(`${path.relative(repositoryRoot, file)} consumes a raw palette token.`);
}
rawPaletteReference.lastIndex = 0;
}
const shellFiles = [
'src/components/app-shell/SiteHeader.tsx',
'src/components/app-shell/MobileNavigation.tsx',
'src/components/app-shell/SiteFooter.tsx',
'src/app/[locale]/loading.tsx',
'src/app/[locale]/error.tsx',
'src/app/[locale]/not-found.tsx',
'src/app/[locale]/[slug]/page.tsx',
];
for (const relativePath of shellFiles) {
try {
await readFile(path.join(repositoryRoot, relativePath), 'utf8');
} catch {
errors.push(`Required Phase 11 shell file is missing: ${relativePath}`);
}
}
if (errors.length) fail(errors);
else {
pass('Phase 11 component tokens, theme fallback, and application-shell foundations are present.');
}