{"record":{"id":"cb49e594d9ebd45f","repo":"CherryHQ/cherry-studio","slug":"theme-contract-label-imports-must-be-exactly","errorCode":null,"errorMessage":"[theme-contract] ${label} imports must be exactly: ${expected.join(' -> ')}","messagePattern":"\\[theme-contract\\] (.+?) imports must be exactly: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ui/scripts/validate-theme-contract.ts","lineNumber":134,"sourceCode":"): void {\n  const surfaces = new Set<string>()\n\n  for (const [surface, foreground] of pairs) {\n    if (surface === foreground || surfaces.has(surface)) {\n      throw new Error(`[theme-contract] ${label} has an invalid or duplicate surface pair for ${surface}`)\n    }\n    if (!variableNames.has(surface) || !variableNames.has(foreground)) {\n      throw new Error(`[theme-contract] ${label} pair ${surface} / ${foreground} is outside its public contract`)\n    }\n    surfaces.add(surface)\n  }\n}\n\nfunction assertExactImports(label: string, source: string, expected: readonly string[]): void {\n  const actual = extractImports(source)\n\n  if (actual.length !== expected.length || actual.some((entry, index) => entry !== expected[index])) {\n    throw new Error(`[theme-contract] ${label} imports must be exactly: ${expected.join(' -> ')}`)\n  }\n}\n\nfunction buildDeclarationMap(entries: SourceEntry[], selector: ':root' | '.dark'): Map<string, Declaration> {\n  const declarations = new Map<string, Declaration>()\n\n  for (const [sourceName, source] of entries) {\n    for (const declaration of extractModeDeclarations(source, sourceName, selector)) {\n      const existing = declarations.get(declaration.name)\n      if (existing) {\n        throw new Error(\n          `[theme-contract] ${declaration.name} is defined twice in ${selector}: ${existing.source} and ${sourceName}`\n        )\n      }\n      declarations.set(declaration.name, declaration)\n    }\n  }\n","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/packages/ui/scripts/validate-theme-contract.ts#L116-L152","documentation":"Thrown by the theme contract validator when the @import statements in a specific CSS file (identified by label) do not exactly match the expected ordered list. The assertExactImports function extracts all @import URLs from the source and compares both count and order against the expected array. This enforces a strict, deterministic import chain for theme CSS entry points (tokens.css, tokens/index.css, contract.css) to guarantee correct CSS cascade ordering and layer dependencies.","triggerScenarios":"A CSS file (tokens.css, tokens/index.css, or contract.css) has @import statements that differ from the expected list — either extra imports, missing imports, imports in the wrong order, or wrong import paths. The validator extracts imports via extractImports and checks actual.length === expected.length and each entry matches positionally.","commonSituations":"A developer adds a new token file and updates tokens/index.css imports but forgets to update the expected list in the validator (or vice versa). A file is renamed and the import path changes but the validator's expected array isn't updated. Import statements are reordered by a formatter or by accident. An import is commented out or removed during debugging.","solutions":["Compare the actual @import statements in the file identified by the label against the expected list shown in the error message.","Update the CSS file's @import statements to exactly match the expected list in both content and order, OR update the expected array in validate-theme-contract.ts if the change is intentional.","If adding a new token file, update both the CSS @import in the entry file AND the expected array in assertExactImports call.","Run the validator to confirm: npx tsx packages/ui/scripts/validate-theme-contract.ts."],"exampleFix":"/* before — tokens/index.css has wrong import order or extra import */\n@import './colors/primitive.css';\n@import './colors/providers.css';\n@import './colors/status-legacy.css';\n@import './spacing.css';\n@import './radius.css';\n@import './typography.css';\n@import './colors/new-token.css';  /* ← not in expected list */\n\n/* after — match expected exactly, or update expected in validator */\n/* If the new import is intentional, update validate-theme-contract.ts: */\n/*   assertExactImports('tokens/index.css', sources.tokensIndex, [       */\n/*     './colors/primitive.css',                                         */\n/*     './colors/status-legacy.css',                                     */\n/*     './colors/providers.css',                                         */\n/*     './colors/new-token.css',                                         */\n/*     './spacing.css', './radius.css', './typography.css'               */\n/*   ])                                                                  */\n/* and ensure the CSS file matches this order.                           */","handlingStrategy":"validation","validationCode":"// Verify @import statements match expected list before committing CSS changes\nimport { readFileSync } from 'node:fs'\n\nfunction checkExactImports(cssPath: string, expected: readonly string[]): void {\n  const source = readFileSync(cssPath, 'utf8')\n  const actual = [...source.matchAll(/@import\\s+([^;]+);/g)].map(m => {\n    const v = m[1].trim()\n    const sm = v.match(/^(['\"])([^'\"]+)\\1$/)\n    return sm ? sm[2] : v\n  })\n  if (actual.length !== expected.length || actual.some((e, i) => e !== expected[i])) {\n    throw new Error(`${cssPath} imports mismatch. Expected: ${expected.join(' -> ')}, Got: ${actual.join(' -> ')}`)\n  }\n}\n\ncheckExactImports('packages/ui/src/styles/tokens.css', ['./tokens/index.css'])","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When modifying CSS @import statements in theme entry files (tokens.css, tokens/index.css, contract.css), update the corresponding assertExactImports expected array in validate-theme-contract.ts.","Keep the import order deterministic — avoid letting formatters reorder @import statements.","Run the theme contract validator after any change to theme CSS imports.","Document the required import chain in a README within packages/ui/src/styles/ for new developers."],"tags":["theme-contract","build-validation","css","import","cascade-order"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}