(input: ParsedDesignSystem)
| 43 | */ |
| 44 | export class ModelHandler implements ModelSpec { |
| 45 | execute(input: ParsedDesignSystem): ModelResult { |
| 46 | try { |
| 47 | const findings: Finding[] = []; |
| 48 | const symbolTable = new Map<string, ResolvedValue>(); |
| 49 | const colors = new Map<string, ResolvedColor>(); |
| 50 | const typography = new Map<string, ResolvedTypography>(); |
| 51 | const rounded = new Map<string, ResolvedDimension>(); |
| 52 | const spacing = new Map<string, ResolvedDimension>(); |
| 53 | |
| 54 | // ── Phase 1: Resolve primitive tokens ────────────────────────── |
| 55 | // Colors |
| 56 | if (input.colors) { |
| 57 | forEachLeaf(input.colors, (name, raw) => { |
| 58 | if (typeof raw === 'string' && isTokenReference(raw)) { |
| 59 | // Store raw reference for later resolution |
| 60 | symbolTable.set(`colors.${name}`, raw); |
| 61 | } else if (isValidColor(raw)) { |
| 62 | const resolved = parseColor(raw); |
| 63 | colors.set(name, resolved); |
| 64 | symbolTable.set(`colors.${name}`, resolved); |
| 65 | } else { |
| 66 | findings.push({ |
| 67 | severity: 'error', |
| 68 | path: `colors.${name}`, |
| 69 | message: `'${raw}' is not a valid color. Expected a CSS color value (e.g., #ffffff, rgb(0 0 0), oklch(0.5 0.2 240)).`, |
| 70 | }); |
| 71 | // Store as-is for fallback |
| 72 | symbolTable.set(`colors.${name}`, raw); |
| 73 | } |
| 74 | }, '', 0, findings, 'colors'); |
| 75 | } |
| 76 | |
| 77 | // Typography |
| 78 | if (input.typography) { |
| 79 | for (const [name, props] of Object.entries(input.typography)) { |
| 80 | const resolved = parseTypography(props, `typography.${name}`, findings); |
| 81 | typography.set(name, resolved); |
| 82 | symbolTable.set(`typography.${name}`, resolved); |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | // Rounded |
| 87 | if (input.rounded) { |
| 88 | forEachLeaf(input.rounded, (name, raw) => { |
| 89 | if (typeof raw === 'string') { |
| 90 | if (isParseableDimension(raw)) { |
| 91 | const resolved = parseDimension(raw); |
| 92 | if (resolved.unit !== 'px' && resolved.unit !== 'rem' && resolved.unit !== 'em') { |
| 93 | findings.push({ |
| 94 | severity: 'error', |
| 95 | path: `rounded.${name}`, |
| 96 | message: `'${raw}' has an invalid unit '${resolved.unit}'. Only px, rem, and em are allowed.`, |
| 97 | }); |
| 98 | } |
| 99 | rounded.set(name, resolved); |
| 100 | symbolTable.set(`rounded.${name}`, resolved); |
| 101 | } else if (!isTokenReference(raw)) { |
| 102 | findings.push({ |
no test coverage detected