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 | 4x 4x 10x 10x 4x 6x 6x 6x 6x 7x 7x 7x 4x 4x 7x 6x 6x 4x 4x 6x 3x 6x 3x 3x 3x | /**
* Compliance row: no descendant-only color hooks.
*
* A color hook that only ever lives under a descendant selector (e.g.
* `.slds-button a { --slds-c-button-color-foreground: ... }`) should
* either disappear in favor of `currentColor` or be promoted to an
* element-segment hook on the base selector.
*/
import type { ComplianceCheck, ComplianceRow } from '../types.js';
import { classifyHook } from './helpers/classifyHook.js';
import { resolveHookPrefix } from './helpers/resolveHookPrefix.js';
import { selectorRole } from './helpers/selectorRole.js';
import {
hookDefsFromThemeData,
sampleHooks,
NOT_MIGRATED_DETAIL,
type HookDefRow,
} from './helpers/themeDataAccess.js';
const LABEL = 'No descendant-only color hooks';
export const noDescendantOwnedColorHooks: ComplianceCheck = (input): ComplianceRow => {
const { componentName: name, themeData } = input;
if (!themeData) {
return {
id: 'no-descendant-owned-color-hooks',
label: LABEL,
status: 'info',
count: 0,
detail: NOT_MIGRATED_DETAIL,
};
}
const defs = hookDefsFromThemeData(themeData);
const hookPrefix = `--slds-c-${resolveHookPrefix(name)}-`;
const defsByHook = new Map<string, HookDefRow[]>();
for (const d of defs) {
Iif (!d.prop?.startsWith(hookPrefix)) continue;
let bucket = defsByHook.get(d.prop);
if (!bucket) {
bucket = [];
defsByHook.set(d.prop, bucket);
}
bucket.push(d);
}
const descendantOwned: string[] = [];
for (const [hook, rows] of defsByHook) {
const cls = classifyHook(hook, name);
Iif (!cls.isColor) continue;
if (rows.every((d) => selectorRole(d.selector, name) === 'descendant')) {
descendantOwned.push(hook);
}
}
if (descendantOwned.length === 0) {
return {
id: 'no-descendant-owned-color-hooks',
label: LABEL,
status: 'pass',
count: 0,
detail:
'Every color hook is defined on the component root or an element segment — none exist only under descendant selectors.',
};
}
return {
id: 'no-descendant-owned-color-hooks',
label: LABEL,
status: 'fail',
count: descendantOwned.length,
detail: `${descendantOwned.length} color hook${descendantOwned.length === 1 ? '' : 's'} only ever defined under a descendant selector (e.g. \`.slds-button a { --slds-c-…-color-foreground: … }\`): ${sampleHooks(descendantOwned)}. Either drop the hook and inherit via \`currentColor\`, or promote it to an element-segment hook on the component root so customers can reach it.`,
offenders: descendantOwned.map((hook) => ({
hook,
fix: `Either drop \`${hook}\` and let the descendant inherit via \`currentColor\`, or define it on the component root selector so customers can override it.`,
})),
};
};
|