#!/usr/bin/env node import { readFile } from "node:fs/promises"; import path from "node:path"; import process from "node:process"; import { fileURLToPath } from "node:url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const projectRoot = path.resolve(__dirname, ".."); const cssFiles = ["app/globals.css", "styles/globals.css"]; const hardcodedColorPattern = /#[\da-fA-F]{3,8}\b|rgba?\(|hsla?\(/; const varFallbackPattern = /var\([^),]+,\s*[^)]+\)/; function removeInlineComment(line) { const start = line.indexOf("/*"); if (start < 0) { return line; } return line.slice(0, start); } async function checkCssFile(relativePath) { const absolutePath = path.join(projectRoot, relativePath); const file = await readFile(absolutePath, "utf8"); const lines = file.split(/\r?\n/); const issues = []; for (let index = 0; index < lines.length; index += 1) { const lineNumber = index + 1; const line = removeInlineComment(lines[index]).trim(); if (!line || line.startsWith("@")) { continue; } if (!line.includes(":")) { continue; } const [property, ...valueParts] = line.split(":"); const propertyName = property.trim(); const value = valueParts.join(":").trim(); if (!propertyName || propertyName.startsWith("--")) { continue; } if (varFallbackPattern.test(value)) { issues.push(`${relativePath}:${lineNumber} forbidden CSS fallback: ${value}`); continue; } if (hardcodedColorPattern.test(value)) { issues.push(`${relativePath}:${lineNumber} hardcoded color literal/function: ${value}`); } } return issues; } async function main() { const issues = []; for (const cssFile of cssFiles) { issues.push(...(await checkCssFile(cssFile))); } if (issues.length > 0) { console.error("[check-global-theme-tokens] failed:"); for (const issue of issues) { console.error(`- ${issue}`); } process.exit(1); } console.log("[check-global-theme-tokens] OK"); } main().catch((error) => { console.error(`[check-global-theme-tokens] ${String(error)}`); process.exit(1); });