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 | 9x 9x 9x 949x 949x 949x 1852x 1852x 76x 76x 76x 54x 22x 18x 372x 372x 949x 949x 949x 372x 4464x 372x 372x | /**
* Hook category taxonomy.
*
* Theme-layer hooks follow the
* `--<ns>-<scope>-<component>-<element>-<category>-<property>` naming convention
* from the Theme Layer property-rules RFC. The *category* segment (color,
* spacing, sizing, …) determines how a hook participates in the theme-data
* projection: within a selector, reads are grouped into buckets so downstream
* consumers (compliance checks, the Theme Layer panel, docs) can show them
* in a stable, category-first order instead of raw source order.
*
* This module is pure and has no Node dependencies — it's safe to import
* from both the CLI and the browser audit path.
*/
export const CATEGORIES = [
'color',
'spacing',
'margin',
'sizing',
'radius',
'font',
'shadow',
'image',
'position',
'gap',
'opacity',
'display',
] as const;
export type HookCategory = (typeof CATEGORIES)[number] | 'other';
const CATEGORY_SET = new Set<string>(CATEGORIES);
/**
* Sizing-shorthand suffixes that resolve to the `sizing` category even
* though the hook name doesn't include the literal `sizing` token.
* Mirrors `packages/design-system-2/scripts/buildCss/buildThemeData.js`
* so the projection and the on-disk JSON agree.
*/
const SIZING_SHORTHAND_SUFFIXES = new Set(['size', 'height', 'width']);
/**
* Classify a hook name into one of the canonical categories. First scans
* for an explicit category token (`color`, `spacing`, `sizing`, etc.); if
* none is found, recognizes a few sizing shorthands (`-size`, `-height`,
* `-width`) so element-sizing hooks like `--slds-c-button-icon-size` land
* in `sizing` rather than `other`. The `line-height` compound is a font
* property, not sizing, so the shorthand match skips a `-height` whose
* preceding token is `line`.
*
* Note: the loop intentionally starts at index 1 — `parts[0]` is the
* component name (e.g. `alert` in `--slds-c-alert-color-background`) and
* shouldn't be treated as a category even if a component is ever named
* `color`.
*/
export function categorizeHook(hook: string): HookCategory {
const body = hook.startsWith('--slds-c-') ? hook.slice('--slds-c-'.length) : hook;
const parts = body.split('-');
for (let i = 1; i < parts.length; i++) {
const seg = parts[i];
if (CATEGORY_SET.has(seg)) return seg as HookCategory;
}
const tail = parts.at(-1);
const prev = parts.at(-2);
if (tail && SIZING_SHORTHAND_SUFFIXES.has(tail) && !(tail === 'height' && prev === 'line')) {
return 'sizing';
}
if (tail === 'height' && prev === 'line') return 'font';
return 'other';
}
/**
* Group a list of hook names into `{ [category]: hook[] }`, preserving source
* order within each bucket and emitting categories in {@link CATEGORIES}
* order (with `other` last when present).
*/
export function groupHooksByCategory(hooks: readonly string[]): Record<string, string[]> {
const buckets: Record<string, string[]> = {};
for (const hook of hooks) {
const cat = categorizeHook(hook);
if (!buckets[cat]) buckets[cat] = [];
buckets[cat].push(hook);
}
const ordered: Record<string, string[]> = {};
for (const cat of CATEGORIES) if (buckets[cat]) ordered[cat] = buckets[cat];
if (buckets.other) ordered.other = buckets.other;
return ordered;
}
|