chore(web): add check:colors guard banning raw color utilities outside ui/ (#49)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-07 14:22:15 +02:00
parent cde7be9f2a
commit 93234aae29
3 changed files with 44 additions and 0 deletions
+1
View File
@@ -14,6 +14,7 @@
"lint": "eslint .",
"gen:api": "openapi-typescript http://localhost:8080/api-docs/openapi.json -o src/api/schema.d.ts",
"check:size": "node scripts/check-bundle-size.mjs",
"check:colors": "node scripts/check-no-raw-colors.mjs",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
+42
View File
@@ -0,0 +1,42 @@
// Fails if any raw Tailwind color utility appears outside src/components/ui/.
import { readdirSync, readFileSync } from "node:fs";
import { join, relative } from "node:path";
const root = "src";
const excludeDir = join("src", "components", "ui");
const RAW_COLOR =
/(?:text|bg|border|ring|fill|stroke|from|to|via|decoration|outline|divide|placeholder)-(?:neutral|gray|slate|zinc|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950)\b/;
function walk(dir) {
const files = [];
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const path = join(dir, entry.name);
if (entry.isDirectory()) {
if (path === excludeDir) continue;
files.push(...walk(path));
} else if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
files.push(path);
}
}
return files;
}
const files = walk(root);
const offenses = [];
for (const file of files) {
const lines = readFileSync(file, "utf8").split("\n");
for (let i = 0; i < lines.length; i++) {
const match = RAW_COLOR.exec(lines[i]);
if (match) offenses.push(`${relative(".", file)}:${i + 1}: ${match[0]}`);
}
}
if (offenses.length > 0) {
console.error(
`raw color utilities found outside components/ui/ (${offenses.length}):`,
);
for (const offense of offenses) console.error(` ${offense}`);
process.exit(1);
}
console.log(`no raw color utilities outside components/ui/ (${files.length} files scanned)`);