/** * Ban template-literal classNames in TSX files. * * Tailwind v4's production content extractor does static analysis on source * files to discover utility classes. Template literals like * className={`foo ${bar}`} * can cause the extractor to miss classes adjacent to `${`, leading to * styles that work in dev (JIT) but break in production. * * Rule: always use cn() for dynamic class composition instead. */ import { execSync } from "node:child_process"; import { readFileSync } from "node:fs"; const PATTERN = /className\s*=\s*\{`/; function findFiles() { return execSync("git ls-files -- '*.tsx'", { encoding: "utf-8" }) .trim() .split("\n") .filter(Boolean); } let errors = 0; for (const file of findFiles()) { const lines = readFileSync(file, "utf-8").split("\n"); for (let i = 0; i < lines.length; i++) { if (PATTERN.test(lines[i])) { console.error( `${file}:${i + 1}: className uses template literal — use cn() instead`, ); errors++; } } } if (errors > 0) { console.error( `\n${errors} template-literal className(s) found. Use cn() for dynamic classes.`, ); process.exit(1); } else { console.log("No template-literal classNames found."); }