{"record":{"id":"20e3e1c7ea93a78c","repo":"CherryHQ/cherry-studio","slug":"theme-contract-sourcename-references-invalid","errorCode":null,"errorMessage":"[theme-contract] ${sourceName} references invalid custom property ${name}","messagePattern":"\\[theme-contract\\] (.+?) references invalid custom property (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ui/scripts/validate-theme-contract.ts","lineNumber":87,"sourceCode":"  })\n}\n\nfunction extractModeDeclarations(source: string, sourceName: string, selector: ':root' | '.dark'): Declaration[] {\n  const declarations: Declaration[] = []\n  const blockPattern = new RegExp(`${escapeRegExp(selector)}\\\\s*\\\\{([\\\\s\\\\S]*?)\\\\}`, 'g')\n\n  for (const match of stripComments(source).matchAll(blockPattern)) {\n    declarations.push(...extractDeclarations(match[1], sourceName))\n  }\n\n  return declarations\n}\n\nfunction extractReferences(value: string, sourceName: string): string[] {\n  return [...value.matchAll(/var\\(\\s*(--[^\\s,)]+)/g)].map((match) => {\n    const name = match[1]\n    if (!CUSTOM_PROPERTY_NAME_PATTERN.test(name)) {\n      throw new Error(`[theme-contract] ${sourceName} references invalid custom property ${name}`)\n    }\n    return name\n  })\n}\n\nfunction extractImports(source: string): string[] {\n  return [...stripComments(source).matchAll(/@import\\s+([^;]+);/g)].map((match) => {\n    const importValue = match[1].trim()\n    const stringMatch = importValue.match(/^(['\"])([^'\"]+)\\1$/)\n    if (stringMatch) return stringMatch[2]\n\n    const urlMatch = importValue.match(/^url\\(\\s*(?:(['\"])([^'\"]+)\\1|([^'\")\\s][^)]*?))\\s*\\)$/)\n    if (urlMatch) return (urlMatch[2] ?? urlMatch[3]).trim()\n\n    throw new Error(`[theme-contract] unsupported @import syntax: ${importValue}`)\n  })\n}\n","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/packages/ui/scripts/validate-theme-contract.ts#L69-L105","documentation":"Thrown by the theme contract validator when a CSS var() reference points to a custom property whose name does not match ^--[a-z0-9-]+$. The extractReferences function scans declaration values for var(--name) patterns and validates each referenced name against the same naming pattern used for declarations. This ensures both definitions and references follow the kebab-case convention.","triggerScenarios":"A CSS declaration's value contains a var() reference like var(--my_Var) or var(--MyColor) where the referenced name has uppercase, underscores, or invalid characters. The validator catches this when parsing references within any declaration value processed by extractReferences.","commonSituations":"A developer writes var(--camelCaseName) or var(--snake_case_name) in a CSS value. A typo in a var() reference. Copy-pasting a reference from external CSS that uses non-conforming names. Renaming a property but forgetting to update its references.","solutions":["Rename the var() reference target to kebab-case: var(--my-var-name) instead of var(--myVarName).","If the reference target is defined elsewhere, update both the definition and all references to use the kebab-case name consistently.","Run the validator to confirm: npx tsx packages/ui/scripts/validate-theme-contract.ts."],"exampleFix":"/* before */\n--accent: var(--brand_Color);\n--text: var(--primaryText);\n\n/* after — kebab-case references */\n--accent: var(--brand-color);\n--text: var(--primary-text);","handlingStrategy":"validation","validationCode":"// Validate var() references in CSS before committing\nimport { readFileSync } from 'node:fs'\n\nconst VALID_NAME = /^--[a-z0-9-]+$/\n\nfunction checkReferences(cssPath: string): void {\n  const source = readFileSync(cssPath, 'utf8').replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n  const refs = [...source.matchAll(/var\\(\\s*(--[^\\s,)]+)/g)]\n  for (const match of refs) {\n    if (!VALID_NAME.test(match[1])) {\n      throw new Error(`${cssPath}: invalid reference ${match[1]} — use kebab-case`)\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure all var() references use kebab-case custom property names matching the declaration convention.","When renaming a property, update all its var() references simultaneously.","Run the theme contract validator in CI to catch invalid references.","Use find-and-replace with care when renaming tokens to keep declarations and references in sync."],"tags":["theme-contract","build-validation","css","naming-convention","custom-properties"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}