import { readFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import postcss, { type Declaration, type Root, type Rule } from "postcss"; import { describe, expect, it } from "vitest"; const contractsDir = dirname(fileURLToPath(import.meta.url)); const stylesheet = readFileSync(resolve(contractsDir, "../styles.css"), "utf8"); const sharedThemeSelector = "[data-toolcraft-theme][data-toolcraft-theme]"; const darkThemeSelector = "[data-toolcraft-theme='dark'][data-toolcraft-theme]"; const visualTokenDefinitions = [ ["--toolcraft-custom-viz-border", "color-mix(in oklab, var(--border) 12%, transparent)"], ["--toolcraft-custom-viz-border-hover", "color-mix(in oklab, var(--border) 20%, transparent)"], ["--toolcraft-custom-viz-border-focus", "color-mix(in oklab, var(--border) 30%, transparent)"], ["--toolcraft-custom-viz-surface", "color-mix(in oklab, var(--input) 5%, var(--popover))"], ["--toolcraft-custom-viz-grid", "color-mix(in oklab, var(--foreground) 7%, var(--toolcraft-custom-viz-surface))"], ["--toolcraft-custom-viz-fill-dim", "color-mix(in oklab, var(--foreground) 14%, var(--toolcraft-custom-viz-surface))"], ["--toolcraft-custom-viz-fill", "color-mix(in oklab, var(--foreground) 20%, var(--toolcraft-custom-viz-surface))"], ["--toolcraft-custom-viz-line", "color-mix(in oklab, var(--foreground) 30%, var(--toolcraft-custom-viz-surface))"], ["--toolcraft-custom-viz-line-strong", "color-mix(in oklab, var(--foreground) 45%, var(--toolcraft-custom-viz-surface))"], ["--toolcraft-custom-viz-data", "color-mix(in oklab, var(--foreground) 62%, var(--toolcraft-custom-viz-surface))"], ["--toolcraft-custom-viz-ink", "var(--foreground)"], ] as const; function requiredRule(root: Root, selector: string): Rule { const matches: Rule[] = []; root.walkRules((rule) => { if (rule.selector === selector) { matches.push(rule); } }); if (matches.length === 0) { throw new Error(`Missing stylesheet rule: ${selector}`); } if (matches.length > 1) { throw new Error(`Expected exactly one stylesheet rule: ${selector}`); } return matches[0]; } function customPropertyDeclarations(root: Root, token: string): Declaration[] { const declarations: Declaration[] = []; root.walkDecls(token, (declaration) => { declarations.push(declaration); }); return declarations; } function assertTokenOwnership( root: Root, sharedRule: Rule, darkRule: Rule, token: string, value: string, ) { const declarations = customPropertyDeclarations(root, token); const darkDeclarations: Declaration[] = []; darkRule.walkDecls(token, (declaration) => { darkDeclarations.push(declaration); }); if (declarations.length !== 1) { throw new Error(`Expected exactly one source declaration for ${token}`); } expect(declarations[0].parent).toBe(sharedRule); expect(declarations[0].value).toBe(value); expect(darkDeclarations).toHaveLength(0); } describe("custom control visual tokens", () => { it("fails explicitly when a required selector is missing", () => { expect(() => requiredRule(postcss.parse(`${darkThemeSelector} {}`), sharedThemeSelector), ).toThrow(`Missing stylesheet rule: ${sharedThemeSelector}`); }); it("fails explicitly when a required selector has duplicate ownership", () => { expect(() => requiredRule( postcss.parse(`${sharedThemeSelector} {} ${sharedThemeSelector} {}`), sharedThemeSelector, ), ).toThrow(`Expected exactly one stylesheet rule: ${sharedThemeSelector}`); }); it("accepts whitespace and comments through source declarations", () => { const [token, value] = visualTokenDefinitions[0]; const root = postcss.parse( `/* ${token}: ignored; */ ${sharedThemeSelector} { ${token} : ${value}; } ${darkThemeSelector} {}`, ); assertTokenOwnership( root, requiredRule(root, sharedThemeSelector), requiredRule(root, darkThemeSelector), token, value, ); }); it("rejects a duplicate declaration whose final value omits a semicolon", () => { const [token, value] = visualTokenDefinitions[0]; const root = postcss.parse( `${sharedThemeSelector} { ${token}: ${value}; } ${darkThemeSelector} { ${token}: ${value} }`, ); expect(() => assertTokenOwnership( root, requiredRule(root, sharedThemeSelector), requiredRule(root, darkThemeSelector), token, value, ), ).toThrow(`Expected exactly one source declaration for ${token}`); }); it("rejects duplicate declarations in the same rule", () => { const [token, value] = visualTokenDefinitions[0]; const root = postcss.parse( `${sharedThemeSelector} { ${token}: ${value}; ${token}: ${value}; } ${darkThemeSelector} {}`, ); expect(() => assertTokenOwnership( root, requiredRule(root, sharedThemeSelector), requiredRule(root, darkThemeSelector), token, value, ), ).toThrow(`Expected exactly one source declaration for ${token}`); }); it("keeps the shared theme-derived vocabulary defined once without dark overrides", () => { const root = postcss.parse(stylesheet); const sharedRule = requiredRule(root, sharedThemeSelector); const darkRule = requiredRule(root, darkThemeSelector); for (const [token, value] of visualTokenDefinitions) { assertTokenOwnership(root, sharedRule, darkRule, token, value); } }); });