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 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | 22x 331x 311x 22x 142x 130x 22x 106x 106x 106x | /**
* Resolve the `--slds-c-{prefix}-*` namespace a component uses for
* its hooks.
*
* Most components use their directory name lowercased (`alert` →
* `alert`, `card` → `card`), but some flatten camelCase
* (`avatarGroup` → `avatargroup`, `progressBar` → `progressbar`) and
* a few share another component's namespace entirely (`buttonIcon`
* → `button`).
*/
const HOOK_PREFIX_OVERRIDES = new Map<string, string>([
['buttonIcon', 'button'],
// buttonStateful and buttonDualStateful relay onto button's hook
// surface (same pattern as buttonIcon): both ship as button-shaped
// surfaces and write `--slds-c-button-*` hooks rather than minting
// their own namespace.
['buttonStateful', 'button'],
['buttonDualStateful', 'button'],
['combobox', 'input'],
['menu', 'dropdown'],
['progressIndicator', 'progress'],
['trees', 'tree'],
// dynamicIcons authors hooks under `--slds-c-dynamicicon-{ellie,eq,…}-*`.
// Singular prefix is intentional: each `.slds-icon-{name}` BEM root
// is one icon, so the namespace reads "this dynamic icon, ellie
// variant" rather than "the dynamic-icons collection".
['dynamicIcons', 'dynamicicon'],
]);
/**
* Default resolver: honor the override map, otherwise lowercase-and-strip-
* hyphens. The generator's validated variant consulted a "context" of
* already-seen hooks; that heuristic is not necessary at check time since
* every caller passes a themeData shape that is already scoped to one
* component.
*/
export function resolveHookPrefix(name: string): string {
if (HOOK_PREFIX_OVERRIDES.has(name)) return HOOK_PREFIX_OVERRIDES.get(name)!;
return name.toLowerCase().replaceAll('-', '').replaceAll('_', '');
}
/**
* Class-root override map: components whose authored CSS class root
* doesn't match the kebab-case form of their directory name. Most
* multi-word components map cleanly (`avatarGroup` → `avatar-group`,
* `buttonGroup` → `button-group`) — only list exceptions here.
*
* Examples that need overrides: `dataTable` ships as `slds-table`,
* `primitive-icon` ships as `slds-icon`. These match what the
* component's `<name>.system.slds.uif.json` declares at
* `structure.attributes.static.class`.
*/
const BEM_ROOT_OVERRIDES = new Map<string, string>([
// alert ships as `.slds-notify_alert` (a notify-family variant compound),
// with severity siblings (`alert_warning`, `alert_error`, `alert_offline`)
// registered below.
['alert', 'notify_alert'],
// breadcrumbs (plural directory) ships under `.slds-breadcrumb` (singular).
['breadcrumbs', 'breadcrumb'],
['buttonIcon', 'button'],
// buttonDualStateful and buttonStateful ship as button-shaped variant
// compounds: their primary class root uses an underscore-modifier form
// that the default kebab conversion would produce as a hyphenated form.
['buttonDualStateful', 'button_dual-stateful'],
['buttonStateful', 'button_stateful'],
['dataTable', 'table'],
['globalNavigation', 'context-bar'],
['menu', 'dropdown'],
['primitive-icon', 'icon'],
['progressIndicator', 'progress'],
// radioButtonGroup ships under `.slds-radio_button-group` (wrapper)
// and `.slds-radio_button` (per-item) — the class roots use an
// underscore where the default kebab conversion would produce a
// hyphen (`radio-button-group`). Override the primary root here and
// register `radio_button` as a sibling below.
['radioButtonGroup', 'radio_button-group'],
// radioGroup (a checkbox/radio form-element) ships under `.slds-radio`.
['radioGroup', 'radio'],
// regions (plural directory) ships under `.slds-region` (singular).
['regions', 'region'],
// scopedNotifications (plural directory) ships under
// `.slds-scoped-notification` (singular), with `_dark` / `_light`
// variant compounds registered as siblings below.
['scopedNotifications', 'scoped-notification'],
// toast ships as `.slds-notify_toast` (a notify-family variant compound),
// with `.slds-notify_container` registered as a sibling below.
['toast', 'notify_toast'],
// Tooltip ships as `.slds-popover_tooltip` — a popover variant compound,
// not its own root. Storing the full variant class as the BEM root lets
// ownership checks match the leftmost compound exactly while keeping the
// hook namespace (`--slds-c-tooltip-*`) intact.
['tooltip', 'popover_tooltip'],
// treeGrid ships as `.slds-table_tree` (a table variant compound).
['treeGrid', 'table_tree'],
['trees', 'tree'],
['trialBar', 'trial-header'],
// verticalNavigation ships under `.slds-nav-vertical` (the directory
// name and class root invert their word order).
['verticalNavigation', 'nav-vertical'],
// dynamicIcons ships under eight `.slds-icon-{name}` roots; the
// primary is ellie, the rest are siblings (see BEM_SIBLING_ROOTS).
['dynamicIcons', 'icon-ellie'],
]);
/**
* Resolve the `.slds-{root}` class a component uses for its BEM
* surface. Diverges from the hook prefix because hook names squash
* camelCase (`--slds-c-avatargroup-*`) while CSS class names are
* kebab-case (`.slds-avatar-group`). Both paths must agree to pass
* the same string into `leftmostAnchoredToOwnRoot`-style checks
* that match against the actual class on the leftmost compound.
*
* Default conversion:
* - directory names with `-` are kept as-is (`primitive-icon`
* overrides aside; `form-element` stays `form-element`).
* - camelCase directories convert to kebab-case
* (`avatarGroup` → `avatar-group`, `progressBar` → `progress-bar`).
* - all-lowercase directories pass through (`alert` → `alert`).
*/
export function resolveBemRoot(name: string): string {
if (BEM_ROOT_OVERRIDES.has(name)) return BEM_ROOT_OVERRIDES.get(name)!;
return name.replaceAll(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
}
/**
* Sibling-root map: components whose theme files own MORE than one
* top-level BEM root. Each entry lists the additional roots that
* share the directory's theme files alongside the primary root from
* {@link resolveBemRoot}. Each sibling is its own component-base for
* compliance purposes — its hooks are NOT descendants of the primary.
*
* Hook namespaces remain distinct (e.g. `--slds-c-card-wrapper-*`
* vs `--slds-c-card-*`); only the directory ownership is shared.
*
* Example: card ships `.slds-card` (primary) plus `.slds-card-wrapper`
* (sibling). Without this map, `selectorRole('.slds-card-wrapper')`
* walks the `-wrapper` tail and falsely classifies it as a descendant
* of `.slds-card`, which trips `no-descendant-only-color-hooks` on
* any wrapper-namespaced color hook.
*/
const BEM_SIBLING_ROOTS = new Map<string, ReadonlyArray<string>>([
// dataTable directory ships the primary `.slds-table` plus several
// composition-wrapper roots authored alongside it in the legacy
// CSS. All share the `--slds-c-datatable-*` hook namespace; without
// this entry the wrapper selectors classify as foreign.
['dataTable', ['table_header-fixed_container', 'table_edit_container', 'table_joined-wrapper']],
// buttonGroup directory ships THREE related layout roots:
// - `.slds-button-group` (primary; flex row of flush-bordered buttons)
// - `.slds-button-group-list` (alternate <ul>-based markup; same paint)
// - `.slds-button-group-row` (separated buttons with consistent gap;
// uses `.slds-button-group-item` as its row-item descendant)
// All three share the `--slds-c-buttongroup-*` hook namespace.
['buttonGroup', ['button-group-list', 'button-group-row']],
['card', ['card-wrapper']],
// checkboxButton directory ships TWO independent surfaces:
// - `.slds-checkbox-button` (icon-button shape, primary BEM root)
// - `.slds-checkbox_add-button` (checkbox-shaped add/remove button;
// spelled as a `slds-checkbox` modifier for HTML-wiring reasons,
// but its theme files own all paint and hooks for it)
// Without this entry the `_add-button` selectors classify as
// `slds-checkbox` modifiers and every hook write under them looks
// like a cross-component descendant write.
['checkboxButton', ['checkbox_add-button']],
// dynamicIcons ships eight decorative icon roots under one
// directory; each icon has its own paint surface and customization
// hooks. Primary root (`icon-ellie`) plus seven siblings.
[
'dynamicIcons',
['icon-eq', 'icon-score', 'icon-strength', 'icon-trend', 'icon-waffle', 'icon-help', 'icon-typing'],
],
// radioButtonGroup directory ships TWO surfaces:
// - `.slds-radio_button-group` (wrapper, primary BEM root)
// - `.slds-radio_button` (per-item; the button-shaped radio)
// The per-item is the dominant paint surface; the wrapper only
// anchors group-level radius. Both share the directory's theme
// files; without this entry every selector that paints
// `.slds-radio_button` classifies as foreign.
['radioButtonGroup', ['radio_button']],
// map directory ships THREE BEM roots:
// - `.slds-map` (primary) plus `.slds-map_container`: layout for
// the embedded map iframe (no themed paint).
// - `.slds-has-coordinates`: the parent grid wrapper that toggles
// the side coordinates panel; owns the panel-surface background.
// - `.slds-coordinates`: the side panel itself; owns
// header/title/item-action paint.
// All three roots share the `--slds-c-map-*` hook namespace; the
// directory owns every theme write under any of them.
['map', ['coordinates', 'has-coordinates']],
// combobox directory ships THREE BEM roots:
// - `.slds-combobox` (primary)
// - `.slds-combobox_container` (the LBC-stamped wrapper that
// carries the dropdown-toggle class and selection-ring shadow)
// - `.slds-listbox` (the dropdown listbox family — options, icon,
// meta, selection-group, object-switcher). Combobox is the
// de-facto theme owner of `slds-listbox*` paint: the LBC renders
// this markup and no other component's theme files paint hooks
// onto these classes.
['combobox', ['combobox_container', 'listbox']],
// alert directory ships the primary `.slds-notify_alert` plus three
// severity-variant compounds (`alert_warning`, `alert_error`,
// `alert_offline`). All four share the `--slds-c-alert-*` hook
// namespace.
['alert', ['alert_warning', 'alert_error', 'alert_offline']],
// toast directory ships the primary `.slds-notify_toast` (the toast
// body) plus `.slds-notify_container` (the positioning wrapper that
// groups multiple toasts). Both share the `--slds-c-toast-*`
// namespace; without this entry the container selectors classify as
// foreign.
['toast', ['notify_container']],
// scopedNotifications ships `.slds-scoped-notification` (primary)
// plus light/dark variant compounds. All share
// `--slds-c-scopednotifications-*`.
['scopedNotifications', ['scoped-notification_light', 'scoped-notification_dark']],
// trees directory ships `.slds-tree` (primary, registered via
// BEM_ROOT_OVERRIDES) plus `.slds-tree-container` for the outer
// scroll wrapper.
['trees', ['tree-container']],
]);
/**
* Resolve every `.slds-{root}` class the component's theme files own:
* the primary root from {@link resolveBemRoot} followed by any sibling
* roots from {@link BEM_SIBLING_ROOTS}. Order matters — the primary
* is always first, so callers that want "the canonical root" can keep
* using {@link resolveBemRoot}. Callers that need to recognize ANY
* owned root (most ownership / classification checks) iterate this
* list and accept a match against any entry.
*/
export function resolveBemRoots(name: string): ReadonlyArray<string> {
const primary = resolveBemRoot(name);
const siblings = BEM_SIBLING_ROOTS.get(name) ?? [];
return [primary, ...siblings];
}
|