import { readFile } from 'node:fs/promises'; import path from 'node:path'; import { fail, pass, repositoryRoot, walk } from './lib.mjs'; const sourceCss = await readFile( path.join(repositoryRoot, 'contracts', 'phase9', 'tokens', 'semantic-tokens_v1.0.css'), 'utf8', ); const implementationCssPath = path.join(repositoryRoot, 'src', 'styles', 'tokens.css'); const implementationCss = await readFile(implementationCssPath, 'utf8'); const designTokens = JSON.parse( await readFile( path.join(repositoryRoot, 'src', 'contracts', 'phase9', 'design-tokens.json'), 'utf8', ), ); const errors = []; if (sourceCss !== implementationCss) errors.push('Implementation tokens.css differs from Phase 9 source.'); const rawColors = new Set(); for (const scale of Object.values(designTokens.raw.color)) { for (const value of Object.values(scale)) rawColors.add(String(value).toLowerCase()); } const applicationStyles = await walk( path.join(repositoryRoot, 'src'), (file) => file.endsWith('.css') && file !== implementationCssPath, ); for (const file of applicationStyles) { const text = (await readFile(file, 'utf8')).toLowerCase(); for (const color of rawColors) { if (text.includes(color)) errors.push(`${path.relative(repositoryRoot, file)} embeds raw brand color ${color}.`); } } for (const required of [ '--color-canvas', '--color-text', '--color-action-primary', '--color-focus', ]) { if (!implementationCss.includes(required)) errors.push(`Missing semantic token ${required}.`); } if (errors.length > 0) fail(errors); else pass( 'Phase 9 semantic tokens are unchanged and application styles contain no raw palette colors.', );