All files / packages/sds-customization-compliance/src/checks hook-names-spec-compliant.ts

100% Statements 24/24
71.42% Branches 10/14
100% Functions 8/8
100% Lines 20/20

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.`,
      };
    }),
  };
};