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 | 106x 106x 106x 1533x 1533x 1533x 1533x 2967x 2967x 1533x 1533x 106x 40x 40x 1620x 1620x 1620x 3092x 1099x 949x 949x 1620x 372x 40x 40x 40x 40x 80x 66x 80x 66x 66x 40x 42x 40x 40x 40x | /**
* Project an {@link EffectiveNewModel} into the themeData shape compliance
* checks consume.
*
* The mapping matches `design-system-2/scripts/buildCss/buildThemeData.js`
* byte-for-byte (minus the wrapper `{component, source}` metadata the checks
* don't read): same selector normalization, same first-appearance hook order
* within a selector, same category taxonomy via {@link groupHooksByCategory}.
*
* Source of each field:
*
* - `selectors` — walked from `effective.base.declsBySelector`. For each
* selector we collect every hook referenced by any declaration's value
* (deduplicated, preserving first-appearance order), then bucket them by
* category. Selectors whose base rules read no component hooks are omitted.
* - `baseAssignments` — walked from `effective.base.assignmentsBySelector`
* (already populated by the audit parse pass). Carried through verbatim.
* - `themes` — wraps each `effective.themes[name].assignmentsBySelector`
* under `{ selectors }` to match the ds2 on-disk layout. Themes that were
* missing or empty at load time are omitted.
*
* This function is pure and synchronous — the CSS parse has already happened
* during audit construction, so we're just reshaping in-memory data.
*
* @param effective The effective-new model returned by `buildEffectiveNewModel`.
* @returns A {@link ThemeData} object, or `null` when `effective` is falsy.
*/
import { groupHooksByCategory } from './categorize.js';
import type { EffectiveNewModel, HookAssignment } from './types.js';
import type { ThemeData, ThemeDataAssignmentBlock, ThemeDataSelectorReads } from '../types.js';
interface ProjectedAssignments {
values: Record<string, Record<string, string>>;
rows: ThemeDataAssignmentBlock;
}
function projectAssignmentBlock(block: Record<string, Record<string, HookAssignment>>): ProjectedAssignments {
const values: Record<string, Record<string, string>> = {};
const rows: ThemeDataAssignmentBlock = {};
for (const [selector, hooks] of Object.entries(block)) {
Iif (Object.keys(hooks).length === 0) continue;
const valueMap: Record<string, string> = {};
const rowMap: ThemeDataAssignmentBlock[string] = {};
for (const [hook, assignment] of Object.entries(hooks)) {
valueMap[hook] = assignment.value;
rowMap[hook] = { value: assignment.value, source: assignment.source, line: assignment.line };
}
values[selector] = valueMap;
rows[selector] = rowMap;
}
return { values, rows };
}
function readSelectorsFromBase(
base: NonNullable<EffectiveNewModel['base']>,
): Record<string, ThemeDataSelectorReads> {
const selectors: Record<string, ThemeDataSelectorReads> = {};
for (const [selector, decls] of base.declsBySelector) {
const seen = new Set<string>();
const hooks: string[] = [];
for (const decl of decls) {
for (const hook of decl.hooksRead) {
if (seen.has(hook)) continue;
seen.add(hook);
hooks.push(hook);
}
}
if (hooks.length === 0) continue;
selectors[selector] = groupHooksByCategory(hooks);
}
return selectors;
}
interface ProjectedThemes {
values: NonNullable<ThemeData['themes']>;
rows: NonNullable<ThemeData['themeAssignmentRows']>;
}
function projectThemes(themes: EffectiveNewModel['themes'] | undefined): ProjectedThemes {
const values: NonNullable<ThemeData['themes']> = {};
const rows: NonNullable<ThemeData['themeAssignmentRows']> = {};
for (const [name, block] of Object.entries(themes ?? {})) {
if (!block) continue;
const projected = projectAssignmentBlock(block.assignmentsBySelector ?? {});
Iif (Object.keys(projected.values).length === 0) continue;
values[name] = { selectors: projected.values };
rows[name] = { selectors: projected.rows };
}
return { values, rows };
}
export function projectThemeData(effective: EffectiveNewModel | null): ThemeData | null {
if (!effective?.base) return null;
const baseAssignments = projectAssignmentBlock(effective.base.assignmentsBySelector);
const themes = projectThemes(effective.themes);
return {
selectors: readSelectorsFromBase(effective.base),
baseAssignments: baseAssignments.values,
baseAssignmentRows: baseAssignments.rows,
themes: themes.values,
themeAssignmentRows: themes.rows,
};
}
|