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 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 | 1x 9x 22x 9x 5x 4x 1x | /**
* Shared types for the SDS Customization Engine compliance package.
*
* A "compliance check" inspects a component (CSS diff result + themeData +
* color-audit entry) and emits a single {@link ComplianceRow}. The package
* ships 11 checks today; the registry is in `./checks/index.ts`.
*/
import type { Root } from 'postcss';
import type { Flag, FlagSummary } from './audit/types.js';
/**
* Severity for a single compliance row. The host UI maps these onto tones.
*
* - `pass` — the component satisfies the check
* - `fail` — the component violates the check; action required
* - `review` — the check surfaced items that need a human look but are not
* a hard regression. Sits between `fail` and `pass` in the UI. Use for
* signals that may be acceptable (e.g. legacy-only selectors cleaned up
* on purpose) but still warrant a glance before shipping.
* - `info` — informational only; neither pass nor fail (e.g. "not verified
* from this data source" or "shared-hook routing counts")
*/
export type ComplianceStatus = 'pass' | 'fail' | 'review' | 'info';
/**
* Sort priority used by the UI layer. `fail` floats to the top, then
* `review` (needs-attention, non-blocking), then `pass`, then `info`.
* Hosts rendering their own row list should use this to match the
* package's visual order.
*/
export const COMPLIANCE_STATUS_PRIORITY: Record<ComplianceStatus, number> = {
fail: 0,
review: 1,
pass: 2,
info: 3,
};
/**
* Aggregate verdict for a component, derived from its rows.
*
* - `conformant` — every check that ran returned `pass`. Component is
* theme-layer ready, no human action required.
* - `conformant-pending-review` — no failures, but at least one row
* came back as `review`. The component is mechanically clean; an
* intentional migration delta or other "needs eyes" signal is
* waiting on a human sign-off.
* - `not-conformant` — at least one check returned `fail`. The
* component will not work with the customization engine until those
* issues are resolved.
* - `not-evaluated` — every row is `info` (or there are no rows).
* None of the checks had their data source available; the verdict
* is undetermined until a fuller run produces real signal.
*/
export type ComplianceVerdict =
| 'conformant'
| 'conformant-pending-review'
| 'not-conformant'
| 'not-evaluated';
/** Counts of rows by status, plus the derived verdict. */
export interface ComplianceVerdictSummary {
verdict: ComplianceVerdict;
counts: Record<ComplianceStatus, number>;
}
/**
* Reduce a row list to a single verdict. Stable for any row order;
* pure (no side effects). Hosts that don't render the panel can call
* this directly off `ComponentReportFile.summary` to drive their own
* dashboards.
*/
export function getVerdict(rows: readonly ComplianceRow[]): ComplianceVerdictSummary {
const counts: Record<ComplianceStatus, number> = { pass: 0, fail: 0, review: 0, info: 0 };
for (const row of rows) counts[row.status] += 1;
if (counts.fail > 0) return { verdict: 'not-conformant', counts };
if (counts.review > 0) return { verdict: 'conformant-pending-review', counts };
if (counts.pass > 0) return { verdict: 'conformant', counts };
return { verdict: 'not-evaluated', counts };
}
/**
* An "offender" is a single piece of structured evidence a check can attach
* to its row so the UI doesn't have to parse the detail string. Additive —
* every check today also ships a prose `detail`.
*/
export interface Offender {
/** Selector the offender is attached to, when applicable. */
selector?: string;
/** Hook name (`--slds-c-*`) the offender is about, when applicable. */
hook?: string;
/** CSS property the offender is about, when applicable. */
prop?: string;
/**
* Source citation as `path/to/file.css:42` (or just the path when no
* line is available). When set, renderers display it as a distinct
* monospace chip so authors can jump straight to the offending CSS.
* Stored separately from `note` so the UI can format it without
* regex-parsing prose.
*/
location?: string;
/** Free-form note — e.g. a gap description for `base-hook-shape`. */
note?: string;
/**
* Optional, single-sentence remediation hint phrased so the author can
* apply it without re-reading the row's prose `detail`. Renderers show
* this as a third line under the offender row when present.
*/
fix?: string;
}
/**
* Structured breakdown for the row body. When set, renderers prefer this
* over the flat `detail` string so the body lays out as:
*
* 1. a tonal `callout` badge ("24 warning-level regressions detected"),
* 2. one `categories` block per flag id / sub-class with its own
* headline + description, and
* 3. optional `guidance` paragraphs explaining the remediation path.
*
* Hosts that don't recognize `summary` fall back to `detail` (which the
* check can keep emitting as a flat newline-joined version).
*/
export interface ComplianceRowSummary {
/** Tonal one-line headline rendered above the body. */
callout?: string;
/** Per-category breakdown blocks. */
categories?: ReadonlyArray<ComplianceRowCategory>;
/** Standalone paragraphs rendered after the categories. */
guidance?: ReadonlyArray<string>;
}
export interface ComplianceRowCategory {
/** Stable id for `data-` attribution, e.g. the audit flag id. */
id?: string;
/** Human-readable category heading (title case). */
heading: string;
/** Optional count for the badge attached to the heading. */
count?: number;
/** Description paragraph for the category. */
description: string;
}
/**
* Sub-group within the structural-source check tier. Distinguishes
* checks that block customer overrides ('customer-reach') from checks
* that protect the cascade against misuse without blocking customers
* ('cascade-hygiene'). Hook-surface and theme-regression checks use
* 'hook-surface' and 'regression' respectively, mirroring their tier
* in the registry.
*
* The category drives severity and reporting:
*
* - `customer-reach` violations are `fail` — the customer's override
* cannot land. Action required.
* - `cascade-hygiene` violations are `review` — the system surface
* can leak via inheritance or misuse, but correctly-rendered
* components are unaffected. Needs eyes, not blocking.
* - `hook-surface` and `regression` keep their existing severity
* model.
* - `lbc-alignment` violations are `review` — record observed
* drift between the design-system-2 hook surface and a `lightning-*`
* LBC element whose embedded CSS doesn't read the public hooks.
* LBC-specific by design: the integration risk surface is
* well-defined (one consumer category, one drift contract) and
* keeping the category name narrow makes the check's scope
* unambiguous in the UI. The component itself is fine; the drift
* is captured statically in
* `extensions['com.salesforce-ux.lbc-contract']` and surfaces
* until the LBC source aligns.
*/
export type ComplianceCategory =
| 'customer-reach'
| 'cascade-hygiene'
| 'hook-surface'
| 'regression'
| 'lbc-alignment';
/** A single compliance result. One row per check per component. */
export interface ComplianceRow {
id: string;
label: string;
status: ComplianceStatus;
/**
* Sub-group the check belongs to. Drives severity (failures in
* 'customer-reach' rows block; failures in 'cascade-hygiene' rows
* surface as `review`) and grouping in the report UI.
*/
category?: ComplianceCategory;
/** Count of items the check tallied (0 for pass/info). */
count: number;
detail: string;
/**
* Structured alternative to `detail`. When present, aware renderers
* paint the row body as callout + categories + guidance blocks instead
* of the flat prose. `detail` is kept in sync as a fallback.
*/
summary?: ComplianceRowSummary;
offenders?: Offender[];
}
/**
* Per-theme hook-read index for a single selector: `{ category -> hook[] }`
* — mirrors the shape produced by design-system-2's themeData build.
*/
export type ThemeDataSelectorReads = Record<string, string[]>;
/**
* One hook-prop assignment as projected from the audit's in-memory model.
* Carries the raw assigned value plus the source `path` and `line` where
* the declaration was authored (both `null` when source-position info
* was unavailable, e.g. browser path or synthesized fixtures).
*/
export interface ThemeDataAssignmentRow {
value: string;
source: string | null;
line: number | null;
}
/** `{ selector -> { hookProp -> ThemeDataAssignmentRow } }`. */
export type ThemeDataAssignmentBlock = Record<string, Record<string, ThemeDataAssignmentRow>>;
/**
* Partial ambient shape for the themeData JSON a single component produces
* at build time. We only type the subsets the checks actually consume.
*
* The string-keyed `baseAssignments` / `themes[*].selectors` fields match
* the on-disk JSON contract that design-system-2's `buildThemeData.js`
* ships (bare `{ hookProp: rawValue }` maps). The CLI also threads
* source-position info through the parallel `*Rows` fields below — these
* carry `{ value, source, line }` per assignment so checks can cite
* file:line in their offender reports. Browser / themeData-only paths can
* omit the `*Rows` fields, in which case checks degrade to "no line
* cited" without erroring.
*/
export interface ThemeData {
/** `{ selector -> { category -> hookName[] } }` — base-file reads. */
selectors?: Record<string, ThemeDataSelectorReads>;
/**
* `{ selector -> { hookProp -> rawValue } }` — hook assignments found
* *inside* `themes/base.css` (typically state/composition scopes like
* `.slds-current-color .slds-icon { --slds-c-icon-color-foreground: currentColor; }`).
* Consumed by the `base-hook-assignments-reference-opened-hooks` check
* to flag assignments that target a hook base never reads.
*/
baseAssignments?: Record<string, Record<string, string>>;
/** `{ themeName -> { selectors: { selector -> { hookProp -> rawValue } } } }`. */
themes?: Record<string, { selectors?: Record<string, Record<string, string>> }>;
/**
* Source-position-enriched mirror of `baseAssignments`. Populated by the
* CLI build path; absent on browser / pre-existing on-disk JSON.
*/
baseAssignmentRows?: ThemeDataAssignmentBlock;
/** Source-position-enriched mirror of `themes[*].selectors`. Same caveats as `baseAssignmentRows`. */
themeAssignmentRows?: Record<string, { selectors?: ThemeDataAssignmentBlock }>;
/**
* Rule-5-canonical projection of the public hook API: one entry per
* authoring surface (component-base, modifier compound, descendant)
* with its hooks bucketed by category. Populated by the build
* pipeline; the property-risk / property-demand compliance checks
* walk this to evaluate every public hook.
*/
editableSurfaces?: Array<{
surface?: string;
targetSelector?: string;
role?: string;
hooks?: Record<string, Array<string | { name: string; source?: string }>>;
}>;
}
/**
* Output of the theme-regression audit for a single component — matches
* what the CLI writes to `build/themeRegressions/<name>.json`. Consumed by
* the regression check (`no-unintended-visual-changes`).
*/
export interface ThemeRegressionReport {
name: string;
flags: Flag[];
summary: FlagSummary;
}
/**
* Plain-object slice of a single `var()`-aware declaration from
* `themes/base.css`. Mirrors the subset of `audit/types.DeclModel` that
* compliance checks actually need, without dragging the postcss-flavored
* runtime type into the check signature.
*/
export interface BaseDecl {
/** CSS property — e.g. `border-color`, `color`, `background-color`. */
prop: string;
/** Raw declaration value, as authored (trimmed). */
rawValue: string;
/**
* Literal terminal fallback of the outermost `var()` chain, or `null`
* when the chain bottoms out without a literal (e.g. `var(--x)` with
* no comma, or `var(--x, var(--y))`). `transparent`, `currentColor`,
* `#fff`, `0`, etc. all come through verbatim.
*/
terminalFallback: string | null;
/**
* Path to the source file that contains the declaration, relative to
* the design-system-2 package root (e.g. `src/slds2/button/themes/base.css`).
* Threaded through so reports can cite the exact file.
*/
source?: string;
/** 1-based line number for the declaration, or `null` when unavailable. */
line?: number | null;
}
/**
* Plain `{ selector -> decls[] }` projection of
* `EffectiveNewModel.base.declsBySelector`. Compliance checks that need
* to reason about the raw paint values in `themes/base.css` (e.g. the
* `border-color` terminal-fallback rule) consume this instead of the
* hook-only `ThemeData`, which doesn't carry property/rawValue info.
*/
export type BaseDeclsBySelector = Record<string, BaseDecl[]>;
/**
* Role a CSS file plays inside a single SLDS2 component directory.
* Drives the structural rules: e.g. Rule 2 (layer placement) expects
* `theme` files to sit inside `@layer theme`, while `base` files must
* stay unlayered.
*
* - `base` — the top-level `<component>.css` (the painting/IR layer)
* - `theme-base` — `themes/base.css` (the wiring layer for hooks)
* - `theme` — any other file under `themes/` (per-theme assignments)
* - `aux` — any other component-local CSS (variants, type tracks, etc.)
*/
export type ComponentSourceFileRole = 'base' | 'theme-base' | 'theme' | 'aux';
/**
* One CSS source file inside a component directory, in the shape the
* structural rules consume. The PostCSS root is already flattened
* (nesting + `@import` resolved) so checks can walk the AST directly
* without re-parsing.
*/
export interface ComponentSourceFile {
/** Path relative to the design-system-2 package root. */
path: string;
/** Inferred role; see {@link ComponentSourceFileRole}. */
role: ComponentSourceFileRole;
/** Flattened PostCSS root. Carries source-position info for line citations. */
root: Root;
}
/**
* Input to a compliance check. Every field is optional because checks
* degrade gracefully when a data source is missing: a check whose data
* source isn't loaded reports `info` ("not run yet") instead of erroring.
*/
export interface ComponentCheckInput {
componentName: string;
themeData?: ThemeData | null;
regression?: ThemeRegressionReport | null;
/**
* Raw base-layer declarations (`themes/base.css`), projected into a
* plain object. Supplied by the CLI; omitted on the browser /
* themeData-only paths. Checks that need it fall back to `info` when
* absent.
*/
baseDeclsBySelector?: BaseDeclsBySelector | null;
/**
* Flattened PostCSS roots for every CSS file inside the component
* directory. Required by the structural rules (no `:root` writes,
* layer placement, no pseudo-state hook writes, customer-reachable
* hook writes). Supplied by the CLI; omitted on browser / themeData-
* only paths, in which case structural checks report `info`.
*/
componentSourceFiles?: ComponentSourceFile[] | null;
/**
* Parsed `<component>.system.slds.uif.json` for the component, when
* available. Carries metadata extensions like
* `extensions['com.salesforce-ux.lbc-contract']` that the
* `lbc-design-system-alignment` lbc-alignment check reads to
* surface recorded drift between the design-system-2 hook surface
* and a downstream LBC consumer. Optional; checks that need it
* report `info` when absent.
*/
componentSystemUif?: Record<string, unknown> | null;
}
/** Pure check function signature. */
export type ComplianceCheck = (input: ComponentCheckInput) => ComplianceRow;
/** Per-component report written by the CLI to `build/<component>.json`. */
export interface ComponentComplianceReport {
componentName: string;
generatedAt: string;
rows: ComplianceRow[];
}
/** Top-level index written to `build/index.json` by the CLI. */
export interface ComplianceIndex {
generatedAt: string;
components: Array<{
componentName: string;
passCount: number;
failCount: number;
reviewCount: number;
infoCount: number;
}>;
}
/**
* Raw CSS input for a component, suitable for running the audit step
* directly (via the `audit/` library) ahead of the checks. Kept for
* tests and potential in-browser audit paths.
*/
export interface ComponentCssInput {
componentName: string;
legacyCss?: string | null;
baseCss?: string | null;
themeCss?: Partial<Record<string, string | null>>;
}
|