/** * `tailwind/arbitrary-color` — flags color-bearing Tailwind utilities with * arbitrary raw color values, e.g. `bg-[#abc123]` or * `text-[rgb(10,20,30)]`. */ import type { FactIndex, TailwindClassFact } from "../facts/index.js"; import { makeFinding } from "./finding.js"; import type { Finding } from "./types.js"; import { detectRawColor, isExemptColor, readUsageNode } from "./utils.js"; export const RULE_ID = "tailwind/arbitrary-color"; export const RULE_VERSION = "1"; const COLOR_UTILITIES = new Set([ "accent", "bg", "border", "border-b", "border-e", "border-l", "border-r", "border-s", "border-t", "border-x", "border-y", "caret", "decoration", "divide", "fill", "from", "outline", "placeholder", "ring", "ring-offset", "shadow", "stroke", "text", "to", "via", ]); export function ruleTailwindArbitraryColor(ix: FactIndex): Finding[] { const policy = ix.policy.rawColorPolicy(); if (!policy) return []; const findings: Finding[] = []; for (const klass of ix.byKind("tailwind_class")) { if (klass.value.kind !== "arbitrary") continue; if (!COLOR_UTILITIES.has(klass.utility)) continue; const color = detectRawColor(klass.value.raw); if (!color) continue; if (isExemptColor(color, policy.except)) continue; const node = readUsageNode(ix, klass.nodeId); const evidenceIds = node ? [node.id, klass.id, policy.id] : [klass.id, policy.id]; findings.push( makeFinding({ ruleId: RULE_ID, ruleVersion: RULE_VERSION, severity: policy.severity, message: `\`${klass.raw}\` uses arbitrary raw color ${color}. Use a ${policy.prefer === "token" ? "design token" : "CSS variable"} instead.`, location: klass.location, evidence: ix.evidence(evidenceIds), fingerprintIdentity: fingerprintIdentity(klass, color), attributes: { rawClass: klass.raw, rawValue: klass.value.raw, color, utility: klass.utility, source: "tailwind", }, }), ); } return findings; } function fingerprintIdentity(klass: TailwindClassFact, color: string): Record { return { source: "tailwind_class", file: klass.file, nodeId: klass.nodeId, originPath: klass.originPath, raw: klass.raw, utility: klass.utility, value: color, }; }