import fs from 'node:fs' import path from 'node:path' const args = process.argv.slice(2) const rootArg = args[args.indexOf('--root') + 1] ?? 'frontend' const root = path.resolve(rootArg) const featureRoot = path.join(root, 'src', 'features') const files = [] const failures = [] const warnings = [] function walk(directory) { if (!fs.existsSync(directory)) return for (const entry of fs.readdirSync(directory, { withFileTypes: true })) { const file = path.join(directory, entry.name) if (entry.isDirectory()) walk(file) else if (/\.(ts|tsx|vue|css|scss)$/.test(entry.name)) files.push(file) } } function relative(file) { return path.relative(process.cwd(), file).replaceAll('\\', '/') } walk(featureRoot) const vendorImport = /(?:from|import\s*\(|import\s+)['"](?:primevue(?:\/|$)|ag-grid(?:-vue3)?(?:\/|$))/ const cssLeakage = /(?:\.p-[a-z0-9_-]+|\.ag-[a-z0-9_-]+|:deep\s*\([^)]*(?:\.p-|\.ag-)|!important)/i const rawColor = /(?:#[0-9a-f]{3,8}\b|\brgba?\s*\(|\bhsl\s*\()/i for (const file of files) { const source = fs.readFileSync(file, 'utf8') const name = relative(file) if (vendorImport.test(source)) failures.push(`${name}: direct PrimeVue/AG Grid import`) if (cssLeakage.test(source)) failures.push(`${name}: supplier CSS leakage or !important`) if (rawColor.test(source)) warnings.push(`${name}: raw color requires token-debt classification`) } if (!fs.existsSync(featureRoot)) failures.push(`missing feature root: ${relative(featureRoot)}`) console.log(`UI_BOUNDARY files=${files.length} failures=${failures.length} warnings=${warnings.length}`) for (const warning of warnings) console.log(`WARN ${warning}`) for (const failure of failures) console.log(`FAIL ${failure}`) process.exitCode = failures.length ? 1 : 0