Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 62 additions & 54 deletions docs/architecture/theme-token-optimization.md

Large diffs are not rendered by default.

158 changes: 158 additions & 0 deletions scripts/audit-theme-colors.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const VAR_FALLBACK_PATTERN = /var\(\s*(--[a-zA-Z0-9_-]+)\s*,/g;
const CSS_VAR_SET_PROPERTY_PATTERN = /\.setProperty\(\s*['"`](--[a-zA-Z0-9_-]+)/g;
const CSS_VAR_INLINE_STYLE_PATTERN = /['"`](--[a-zA-Z0-9_-]+)['"`]\s*:/g;
const CSS_VAR_DYNAMIC_SET_PATTERN = /\.setProperty\(\s*`(--[a-zA-Z0-9_-]*)\$\{/g;
const CSS_VAR_LITERAL_PATTERN = /['"`](--[a-zA-Z0-9_-]+)['"`]/g;
const GENERATED_WIDGET_THEME_PAYLOAD_PATH = 'tools/generative-widget/themePayload.ts';
const REPORT_ROW_LIMIT = 100;
const COLOR_DOMAIN_CONTRACT_BY_KEY = new Map(COLOR_DOMAIN_CONTRACTS.map(contract => [contract.key, contract]));
const FALLBACK_VAR_CONTRACT_BY_KEY = new Map(FALLBACK_VAR_CONTRACTS.map(contract => [contract.key, contract]));
Expand Down Expand Up @@ -194,6 +196,10 @@ function isExceptionFile(relativePath) {
return EXCEPTION_PATH_PARTS.some(part => relativePath.toLowerCase().includes(part.toLowerCase()));
}

function isGeneratedWidgetThemePayloadFile(relativePath) {
return relativePath.endsWith(GENERATED_WIDGET_THEME_PAYLOAD_PATH);
}

function pathMatchesPart(relativePath, pathPart) {
const normalizedPath = relativePath.toLowerCase();
const normalizedPart = pathPart.toLowerCase();
Expand Down Expand Up @@ -742,6 +748,8 @@ function audit(options) {
const tokenAliasLiteralCounts = new Map();
const tokenAliasLiteralFiles = new Map();
const tokenAliasLiteralExamples = new Map();
const generatedWidgetPayloadVarCounts = new Map();
const generatedWidgetPayloadVarFiles = new Map();

let colorOccurrences = 0;
let componentColorOccurrences = 0;
Expand Down Expand Up @@ -828,6 +836,13 @@ function audit(options) {
addToSetMap(dynamicDefinitionFiles, match[1], relativePath);
}

if (isGeneratedWidgetThemePayloadFile(relativePath)) {
for (const match of collectMatches(content, CSS_VAR_LITERAL_PATTERN)) {
incrementMap(generatedWidgetPayloadVarCounts, match[1]);
addToSetMap(generatedWidgetPayloadVarFiles, match[1], relativePath);
}
}

for (const match of collectMatches(content, VAR_FALLBACK_PATTERN)) {
fallbackOccurrences += 1;
incrementMap(fallbackTokenCounts, match[1]);
Expand All @@ -837,6 +852,9 @@ function audit(options) {

const definedVars = new Set(varDefinitionCounts.keys());
const getDefinitionKinds = name => Array.from(varDefinitionKinds.get(name) ?? ['unknown']).sort();
const getExplicitDefinitionKind = name => (
definedVars.has(name) ? getDefinitionKinds(name).join('+') : null
);
const getDefinitionKind = name => {
if (definedVars.has(name)) {
return getDefinitionKinds(name).join('+');
Expand Down Expand Up @@ -996,6 +1014,62 @@ function audit(options) {
files: entry.files,
}))
.sort((a, b) => a.key.localeCompare(b.key));
const generatedWidgetPayloadVars = Array.from(generatedWidgetPayloadVarCounts.entries())
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
.map(([key, count]) => ({
key,
count,
definitionKind: getExplicitDefinitionKind(key),
files: Array.from(generatedWidgetPayloadVarFiles.get(key) ?? []).sort().slice(0, 5),
}));
const generatedWidgetPayloadCompatibilityAliases = generatedWidgetPayloadVars
.map(entry => {
const contract = resolveCompatibilityAliasContract(entry.key);
if (!contract || contract.familyPrefix) {
return null;
}
return {
...entry,
canonical: contract.canonical,
canonicalDefinitionKind: getExplicitDefinitionKind(contract.canonical),
};
})
.filter(Boolean)
.sort((a, b) => b.count - a.count || a.key.localeCompare(b.key));
const generatedWidgetPayloadCompatibilityFamilies = generatedWidgetPayloadVars
.map(entry => {
const contract = resolveCompatibilityAliasContract(entry.key);
if (!contract?.familyPrefix) {
return null;
}
return {
...entry,
canonical: contract.canonical,
familyPrefix: contract.familyPrefix,
canonicalPrefix: contract.canonicalPrefix,
canonicalDefinitionKind: getExplicitDefinitionKind(contract.canonical),
};
})
.filter(Boolean)
.sort((a, b) => b.count - a.count || a.key.localeCompare(b.key));
const generatedWidgetPayloadUndefinedVars = generatedWidgetPayloadVars
.filter(entry => !entry.definitionKind)
.sort((a, b) => a.key.localeCompare(b.key));
const generatedWidgetPayloadVarNames = new Set(generatedWidgetPayloadVars.map(entry => entry.key));
const generatedWidgetPayloadMissingCompatibilityCanonicals = [
...generatedWidgetPayloadCompatibilityAliases,
...generatedWidgetPayloadCompatibilityFamilies,
]
.filter(entry => !entry.canonicalDefinitionKind)
.map(({ key, canonical, count, files }) => ({ key, canonical, count, files }))
.sort((a, b) => a.key.localeCompare(b.key));
const generatedWidgetPayloadUnexportedCompatibilityCanonicals = [
...generatedWidgetPayloadCompatibilityAliases,
...generatedWidgetPayloadCompatibilityFamilies,
]
.filter(entry => entry.canonicalDefinitionKind && !generatedWidgetPayloadVarNames.has(entry.canonical))
.map(({ key, canonical, count, files }) => ({ key, canonical, count, files }))
.sort((a, b) => a.key.localeCompare(b.key));
const staleCompatibilityAliasEntries = checksFullThemeSourceRoot
? TOKEN_COMPATIBILITY_ALIAS_CONTRACTS
.map(contract => ({
Expand Down Expand Up @@ -1102,6 +1176,31 @@ function audit(options) {
top: compatibilityAliasEntries.slice(0, options.top),
families: compatibilityAliasFamilyEntries,
},
generatedWidgetPayload: {
varUnique: generatedWidgetPayloadVars.length,
occurrences: generatedWidgetPayloadVars.reduce((total, entry) => total + entry.count, 0),
undefinedUnique: generatedWidgetPayloadUndefinedVars.length,
compatibilityAliasUnique: generatedWidgetPayloadCompatibilityAliases.length,
compatibilityAliasOccurrences: generatedWidgetPayloadCompatibilityAliases.reduce(
(total, entry) => total + entry.count,
0,
),
compatibilityAliasFamilyUnique: generatedWidgetPayloadCompatibilityFamilies.length,
compatibilityAliasFamilyOccurrences: generatedWidgetPayloadCompatibilityFamilies.reduce(
(total, entry) => total + entry.count,
0,
),
missingCompatibilityCanonicalUnique: generatedWidgetPayloadMissingCompatibilityCanonicals.length,
unexportedCompatibilityCanonicalUnique: generatedWidgetPayloadUnexportedCompatibilityCanonicals.length,
topCompatibilityAliases: generatedWidgetPayloadCompatibilityAliases.slice(0, options.top),
topCompatibilityFamilies: generatedWidgetPayloadCompatibilityFamilies.slice(0, options.top),
undefinedVars: generatedWidgetPayloadUndefinedVars.slice(0, REPORT_ROW_LIMIT),
missingCompatibilityCanonicals: generatedWidgetPayloadMissingCompatibilityCanonicals.slice(0, REPORT_ROW_LIMIT),
unexportedCompatibilityCanonicals: generatedWidgetPayloadUnexportedCompatibilityCanonicals.slice(
0,
REPORT_ROW_LIMIT,
),
},
staleCompatibilityAliases: staleCompatibilityAliasEntries,
staleCompatibilityAliasFamilies: staleCompatibilityAliasFamilyEntries,
missingCompatibilityAliasCanonicals: missingCompatibilityAliasCanonicalEntries,
Expand Down Expand Up @@ -1183,6 +1282,14 @@ function printText(report) {
`staleFamilies=${report.compatibilityAliases.staleRegisteredFamilyUnique}, ` +
`missingCanonicals=${report.compatibilityAliases.missingCanonicalUnique}`
);
console.log(
`Generated widget payload: vars=${report.generatedWidgetPayload.varUnique}, ` +
`undefined=${report.generatedWidgetPayload.undefinedUnique}, ` +
`compatAliases=${report.generatedWidgetPayload.compatibilityAliasUnique}, ` +
`compatAliasFamilies=${report.generatedWidgetPayload.compatibilityAliasFamilyUnique}, ` +
`missingCompatCanonicals=${report.generatedWidgetPayload.missingCompatibilityCanonicalUnique}, ` +
`unexportedCompatCanonicals=${report.generatedWidgetPayload.unexportedCompatibilityCanonicalUnique}`
);
console.log(
`Fallback contracts: registered=${report.fallbackContracts.registeredUnique}, ` +
`uncontracted=${report.fallbackContracts.uncontractedUnique}, ` +
Expand Down Expand Up @@ -1280,6 +1387,57 @@ function printText(report) {
}
}

console.log('\nGenerated widget payload compatibility aliases:');
if (report.generatedWidgetPayload.topCompatibilityAliases.length === 0) {
console.log(' none');
} else {
for (const row of report.generatedWidgetPayload.topCompatibilityAliases.slice(0, 10)) {
console.log(
` ${row.count.toString().padStart(5)} ${row.key} -> ${row.canonical} ` +
`canonicalDefined=${Boolean(row.canonicalDefinitionKind)}`
);
}
}

console.log('\nGenerated widget payload compatibility families:');
if (report.generatedWidgetPayload.topCompatibilityFamilies.length === 0) {
console.log(' none');
} else {
for (const row of report.generatedWidgetPayload.topCompatibilityFamilies.slice(0, 10)) {
console.log(
` ${row.count.toString().padStart(5)} ${row.key} -> ${row.canonical} ` +
`canonicalDefined=${Boolean(row.canonicalDefinitionKind)}`
);
}
}

console.log('\nGenerated widget payload undefined vars:');
console.log(printRows(report.generatedWidgetPayload.undefinedVars.slice(0, 10)));

console.log('\nGenerated widget payload missing compatibility canonicals:');
if (report.generatedWidgetPayload.missingCompatibilityCanonicals.length === 0) {
console.log(' none');
} else {
for (const row of report.generatedWidgetPayload.missingCompatibilityCanonicals.slice(0, 10)) {
console.log(
` ${row.key} -> ${row.canonical} ` +
`count=${row.count} files=${row.files.join(', ')}`
);
}
}

console.log('\nGenerated widget payload unexported compatibility canonicals:');
if (report.generatedWidgetPayload.unexportedCompatibilityCanonicals.length === 0) {
console.log(' none');
} else {
for (const row of report.generatedWidgetPayload.unexportedCompatibilityCanonicals.slice(0, 10)) {
console.log(
` ${row.key} -> ${row.canonical} ` +
`count=${row.count} files=${row.files.join(', ')}`
);
}
}

console.log('\nColor domain contract gaps:');
const colorDomainGapRows = [
...report.missingColorDomainContracts.map(row => ({ ...row, count: 1 })),
Expand Down
101 changes: 101 additions & 0 deletions scripts/audit-theme-colors.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ test('repository dynamic CSS var families match the registered contract', () =>
assert.equal(report.compatibilityAliases.staleRegisteredUnique, 0);
assert.equal(report.compatibilityAliases.staleRegisteredFamilyUnique, 0);
assert.equal(report.compatibilityAliases.missingCanonicalUnique, 0);
assert.equal(report.generatedWidgetPayload.undefinedUnique, 0);
assert.equal(report.generatedWidgetPayload.missingCompatibilityCanonicalUnique, 0);
assert.equal(report.generatedWidgetPayload.unexportedCompatibilityCanonicalUnique, 0);
assert.equal(report.fallbackContracts.uncontractedUnique, 0);
assert.equal(report.fallbackContracts.staleRegisteredUnique, 0);
assert.equal(report.colorDomainContracts.missingRegisteredUnique, 0);
Expand Down Expand Up @@ -256,6 +259,104 @@ test('theme color audit reports compatibility alias usage without treating it as
assert.equal(report.colorScopes.appUi.occurrences, 0);
});

test('theme color audit budgets generated widget payload compatibility aliases separately', (t) => {
const { dir, sourceRoot } = createFixture({
'component-library/styles/tokens.scss': [
':root {',
' --color-accent-500: #60a5fa;',
' --color-primary: var(--color-accent-500);',
' --size-radius-sm: 6px;',
' --radius-sm: var(--size-radius-sm);',
'}',
'',
].join('\n'),
'tools/generative-widget/themePayload.ts': [
"export const payloadVars = ['--color-accent-500', '--color-primary', '--size-radius-sm', '--radius-sm'];",
'',
].join('\n'),
});
t.after(() => fs.rmSync(dir, { recursive: true, force: true }));

const result = runAudit(['--root', sourceRoot, '--json', '--no-baseline']);
assert.equal(result.status, 0, result.stderr || result.stdout);

const report = JSON.parse(result.stdout);
assert.equal(report.compatibilityAliases.usedUnique, 0);
assert.equal(report.compatibilityAliases.familyUsedUnique, 0);
assert.equal(report.generatedWidgetPayload.varUnique, 4);
assert.equal(report.generatedWidgetPayload.compatibilityAliasUnique, 1);
assert.equal(report.generatedWidgetPayload.compatibilityAliasFamilyUnique, 1);
assert.equal(report.generatedWidgetPayload.undefinedUnique, 0);
assert.equal(report.generatedWidgetPayload.missingCompatibilityCanonicalUnique, 0);
assert.equal(report.generatedWidgetPayload.unexportedCompatibilityCanonicalUnique, 0);
assert.deepEqual(
report.generatedWidgetPayload.topCompatibilityAliases.map(row => [row.key, row.canonical]),
[['--color-primary', '--color-accent-500']],
);
assert.deepEqual(
report.generatedWidgetPayload.topCompatibilityFamilies.map(row => [row.key, row.canonical]),
[['--radius-sm', '--size-radius-sm']],
);
});

test('theme color audit reports generated widget payload compatibility aliases without canonicals', (t) => {
const { dir, sourceRoot } = createFixture({
'component-library/styles/tokens.scss': [
':root {',
' --radius-ghost: 10px;',
'}',
'',
].join('\n'),
'tools/generative-widget/themePayload.ts': [
"export const payloadVars = ['--radius-ghost'];",
'',
].join('\n'),
});
t.after(() => fs.rmSync(dir, { recursive: true, force: true }));

const result = runAudit(['--root', sourceRoot, '--json', '--no-baseline']);
assert.equal(result.status, 0, result.stderr || result.stdout);

const report = JSON.parse(result.stdout);
assert.equal(report.generatedWidgetPayload.undefinedUnique, 0);
assert.equal(report.generatedWidgetPayload.compatibilityAliasFamilyUnique, 1);
assert.equal(report.generatedWidgetPayload.missingCompatibilityCanonicalUnique, 1);
assert.equal(report.generatedWidgetPayload.unexportedCompatibilityCanonicalUnique, 0);
assert.deepEqual(
report.generatedWidgetPayload.missingCompatibilityCanonicals.map(row => [row.key, row.canonical]),
[['--radius-ghost', '--size-radius-ghost']],
);
});

test('theme color audit reports generated widget payload aliases whose canonicals are not exported', (t) => {
const { dir, sourceRoot } = createFixture({
'component-library/styles/tokens.scss': [
':root {',
' --color-accent-500: #60a5fa;',
' --color-primary: var(--color-accent-500);',
'}',
'',
].join('\n'),
'tools/generative-widget/themePayload.ts': [
"export const payloadVars = ['--color-primary'];",
'',
].join('\n'),
});
t.after(() => fs.rmSync(dir, { recursive: true, force: true }));

const result = runAudit(['--root', sourceRoot, '--json', '--no-baseline']);
assert.equal(result.status, 0, result.stderr || result.stdout);

const report = JSON.parse(result.stdout);
assert.equal(report.generatedWidgetPayload.undefinedUnique, 0);
assert.equal(report.generatedWidgetPayload.missingCompatibilityCanonicalUnique, 0);
assert.equal(report.generatedWidgetPayload.unexportedCompatibilityCanonicalUnique, 1);
assert.deepEqual(
report.generatedWidgetPayload.unexportedCompatibilityCanonicals.map(row => [row.key, row.canonical]),
[['--color-primary', '--color-accent-500']],
);
});

test('theme color audit reports fallback tokens that lack a boundary contract', (t) => {
const { dir, sourceRoot } = createFixture({
'app/App.scss': [
Expand Down
33 changes: 30 additions & 3 deletions scripts/theme-color-governance-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
"description": "Baseline for Web UI theme color governance. Lower values when debt is removed; do not raise without a documented review reason.",
"budgets": {
"fallbackOccurrences": {
"max": 25
"max": 0
},
"fallbackUniqueTokens": {
"max": 7
"max": 0
},
"fallbackContracts.uncontractedUnique": {
"max": 0
Expand Down Expand Up @@ -41,6 +41,33 @@
"compatibilityAliases.missingCanonicalUnique": {
"max": 0
},
"generatedWidgetPayload.varUnique": {
"max": 326
},
"generatedWidgetPayload.occurrences": {
"max": 326
},
"generatedWidgetPayload.undefinedUnique": {
"max": 0
},
"generatedWidgetPayload.compatibilityAliasUnique": {
"max": 64
},
"generatedWidgetPayload.compatibilityAliasOccurrences": {
"max": 64
},
"generatedWidgetPayload.compatibilityAliasFamilyUnique": {
"max": 17
},
"generatedWidgetPayload.compatibilityAliasFamilyOccurrences": {
"max": 17
},
"generatedWidgetPayload.missingCompatibilityCanonicalUnique": {
"max": 0
},
"generatedWidgetPayload.unexportedCompatibilityCanonicalUnique": {
"max": 0
},
"colorDomainContracts.registeredUnique": {
"max": 13
},
Expand Down Expand Up @@ -111,7 +138,7 @@
"max": 54
},
"colorDomainScopes.tokenContract.occurrences": {
"max": 268
"max": 267
},
"colorDomainScopes.generatedWidget.occurrences": {
"max": 0
Expand Down
Loading
Loading