51 lines
1.6 KiB
JavaScript
51 lines
1.6 KiB
JavaScript
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.',
|
|
);
|