@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* components/workspace 配下の .tsx ファイルを走査し、
|
||||
* 同一クラス文字列内で `bg-card` と `rounded-md` が共起する箇所を検出して warn 表示する。
|
||||
*
|
||||
* 使い方:
|
||||
* node scripts/check-radius-drift.mjs
|
||||
* npm run check:radius
|
||||
*
|
||||
* 参照: mockup-guide.md §9「角丸の役割マッピング」
|
||||
* bg-card で囲った「島」は原則 rounded-lg を使う(SKILL.md 階層ルール 2)。
|
||||
* rounded-md は「メニューアイテム / リスト行 / サイドバー行」向け。
|
||||
*/
|
||||
|
||||
import { readFileSync, readdirSync, statSync } from "node:fs";
|
||||
import { join, relative } from "node:path";
|
||||
|
||||
const TARGET_DIR = join(import.meta.dirname, "..", "components", "workspace");
|
||||
const PATTERN = /bg-card.*rounded-md|rounded-md.*bg-card/;
|
||||
|
||||
function walk(dir) {
|
||||
const entries = [];
|
||||
for (const name of readdirSync(dir)) {
|
||||
const full = join(dir, name);
|
||||
if (statSync(full).isDirectory()) {
|
||||
entries.push(...walk(full));
|
||||
} else if (name.endsWith(".tsx") || name.endsWith(".ts")) {
|
||||
entries.push(full);
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
let warnings = 0;
|
||||
|
||||
for (const file of walk(TARGET_DIR)) {
|
||||
const lines = readFileSync(file, "utf-8").split("\n");
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (PATTERN.test(lines[i])) {
|
||||
const rel = relative(join(import.meta.dirname, ".."), file);
|
||||
console.warn(
|
||||
`⚠ ${rel}:${i + 1} bg-card + rounded-md の共起(rounded-lg を検討)`,
|
||||
);
|
||||
console.warn(` ${lines[i].trim()}`);
|
||||
console.warn();
|
||||
warnings++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (warnings > 0) {
|
||||
console.warn(`${warnings} 件の角丸ドリフト候補が見つかりました。`);
|
||||
console.warn("参照: mockup-guide.md §9 / SKILL.md「角丸の階層ルール」");
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log("✓ 角丸ドリフト候補なし");
|
||||
}
|
||||
Reference in New Issue
Block a user