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 | 4x 1x 1x 4x 10x 10x 4x 6x 7x 6x 6x 6x 6x 2x 4x 3x 1x 1x 1x 1x | /**
* Compliance row: color hook names use the
* `color-{background|foreground|border}` pattern.
*
* Flags property-first names (`--slds-c-{C}-text-color`,
* `-background-color`, `-border-color`) that invert category and property;
* canonical names put the category segment last.
*/
import type { ComplianceCheck, ComplianceRow } from '../types.js';
import { classifyHook } from './helpers/classifyHook.js';
import { hookDefsFromThemeData, sampleHooks, NOT_MIGRATED_DETAIL } from './helpers/themeDataAccess.js';
const LABEL = 'Color hook names use `color-{background|foreground|border}` pattern';
/**
* Compute the canonical-shape rename for a property-first color hook.
* `--slds-c-btn-text-color` → `--slds-c-btn-color-foreground`
* `--slds-c-btn-background-color` → `--slds-c-btn-color-background`
* `--slds-c-btn-icon-text-color` → `--slds-c-btn-icon-color-foreground`
* Falls back to a generic message if the property segment can't be located.
*/
function canonicalRename(hook: string): string | null {
const replacement = hook
.replace(/-text-color(?=-|$)/, '-color-foreground')
.replace(/-background-color(?=-|$)/, '-color-background')
.replace(/-border-color(?=-|$)/, '-color-border');
return replacement === hook ? null : replacement;
}
export const hookNamesSpecCompliant: ComplianceCheck = (input): ComplianceRow => {
const { componentName: name, themeData } = input;
if (!themeData) {
return {
id: 'hook-names-spec-compliant',
label: LABEL,
status: 'info',
count: 0,
detail: NOT_MIGRATED_DETAIL,
};
}
const defs = hookDefsFromThemeData(themeData);
const uniqueHooksDefined = [...new Set(defs.map((d) => d.prop))];
const classified = uniqueHooksDefined.map((h) => ({ hook: h, ...classifyHook(h, name) }));
const colorHooks = classified.filter((c): c is typeof c & { isColor: true } => c.isColor);
const propertyFirst = colorHooks.filter((c) => c.classification === 'property-first');
// Zero color hooks defined means there are no names for this rule to
// evaluate — the naming constraint is trivially satisfied.
if (colorHooks.length === 0) {
return {
id: 'hook-names-spec-compliant',
label: LABEL,
status: 'pass',
count: 0,
detail: 'No color hooks defined on this component — the naming convention is trivially satisfied.',
};
}
if (propertyFirst.length === 0) {
return {
id: 'hook-names-spec-compliant',
label: LABEL,
status: 'pass',
count: 0,
detail: `All ${colorHooks.length} color hook name${colorHooks.length === 1 ? '' : 's'} follow the \`color-{background|foreground|border}\` pattern.`,
};
}
return {
id: 'hook-names-spec-compliant',
label: LABEL,
status: 'fail',
count: propertyFirst.length,
detail: `${propertyFirst.length} of ${colorHooks.length} color hook name${colorHooks.length === 1 ? '' : 's'} put the property before the category (e.g. \`-text-color\`, \`-background-color\`). Rename them so the category comes last: \`-color-foreground\`, \`-color-background\`, or \`-color-border\`. Offending hooks: ${sampleHooks(propertyFirst.map((c) => c.hook))}.`,
offenders: propertyFirst.map((c) => {
const renamed = canonicalRename(c.hook);
return {
hook: c.hook,
fix: renamed
? `Rename to \`${renamed}\` and update every read/write site so the category segment comes last.`
: `Rename so the category segment (\`-color-foreground\`, \`-color-background\`, or \`-color-border\`) comes last.`,
};
}),
};
};
|