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 | 214x 214x 214x 1806x 1806x 1806x 1806x 5448x 5448x 1806x 1806x 214x 76x 76x 2889x 2889x 2889x 6490x 3172x 2720x 2720x 2889x 1093x 76x 76x 76x 76x 152x 138x 152x 138x 138x 76x 78x 76x 76x 76x | /**
* 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,
};
}
|