Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | 4x 2x 1x 1x 4x 10x 10x 4x 6x 6x 6x 5x 5x 5x 6x 10x 10x 10x 5x 5x 5x 5x 3x 3x 5x 6x 6x 5x 8x 5x 1x 6x 1x 1x | /**
* Compliance row: component hooks with no theme assignment (informational).
*
* Enumerates hooks that the base file reads via `var()` that no tracked
* theme ever assigns. Informational — the missing hook might be a future-
* facing customer knob we haven't populated yet — but useful to see at a
* glance during rollout.
*/
import type { ComplianceCheck, ComplianceRow } from '../types.js';
import { resolveHookPrefix } from './helpers/resolveHookPrefix.js';
import { hookDefsFromThemeData, NOT_MIGRATED_DETAIL } from './helpers/themeDataAccess.js';
const LABEL = 'Component hooks with no theme assignment (informational)';
function fixForUnassignedHook(hook: string, missingFromThemes: readonly string[]): string {
const themeFiles = missingFromThemes.map((t) => `themes/${t}.css`);
const target = themeFiles.length === 1 ? themeFiles[0] : `each of: ${themeFiles.join(', ')}`;
return `Either assign \`${hook}\` a value in ${target}, or remove the public read in base if the hook is not yet a customer-facing API.`;
}
export const hooksMissingFromAllThemes: ComplianceCheck = (input): ComplianceRow => {
const { componentName: name, themeData } = input;
if (!themeData) {
return {
id: 'hooks-missing-from-all-themes',
label: LABEL,
status: 'info',
count: 0,
detail: NOT_MIGRATED_DETAIL,
};
}
const hookPrefix = `--slds-c-${resolveHookPrefix(name)}-`;
const hooksReadInBase = new Set<string>();
for (const cats of Object.values(themeData.selectors ?? {})) {
for (const list of Object.values(cats)) {
for (const h of list ?? []) {
Eif (h.startsWith(hookPrefix)) hooksReadInBase.add(h);
}
}
}
const trackedThemes = Object.keys(themeData.themes ?? {}).sort();
const assignedBy = new Map<string, Set<string>>();
const defs = hookDefsFromThemeData(themeData);
for (const d of defs) {
Iif (!d.prop?.startsWith(hookPrefix)) continue;
Iif (String(d.rawValue ?? '').trim() === 'revert') continue;
let bucket = assignedBy.get(d.prop);
if (!bucket) {
bucket = new Set();
assignedBy.set(d.prop, bucket);
}
bucket.add(d.theme);
}
const unassigned: Array<{ hook: string; missingFromThemes: string[] }> = [];
for (const hook of [...hooksReadInBase].sort()) {
const assigned = assignedBy.get(hook) ?? new Set();
const missing = trackedThemes.filter((t) => !assigned.has(t));
if (trackedThemes.length > 0 && missing.length === trackedThemes.length) {
unassigned.push({ hook, missingFromThemes: missing });
}
}
return {
id: 'hooks-missing-from-all-themes',
label: LABEL,
status: 'info',
count: unassigned.length,
detail:
unassigned.length === 0
? 'Every hook that the component base reads is assigned by at least one tracked theme — no future-facing hooks are sitting unwired.'
: `${unassigned.length} component hook${unassigned.length === 1 ? ' is' : 's are'} read by base but not assigned by any tracked theme (likely future-facing customer knobs). Examples: ` +
unassigned
.slice(0, 3)
.map((u) => `\`${u.hook}\` (missing from ${u.missingFromThemes.join(', ')})`)
.join('; ') +
(unassigned.length > 3 ? `; +${unassigned.length - 3} more` : '') +
'.',
offenders:
unassigned.length > 0
? unassigned.map((u) => ({
hook: u.hook,
note: `missing from ${u.missingFromThemes.join(', ')}`,
fix: fixForUnassignedHook(u.hook, u.missingFromThemes),
}))
: undefined,
};
};
|