Files
TrueGrowth/scripts/check-truegrowth-ui-system.mjs

167 lines
4.0 KiB
JavaScript

#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
const root = process.cwd();
const appSourceRoots = ['apps/web/src'];
const strictVisualRoots = ['apps/web/src/ui', 'docs/page-templates'];
const allowedLegacyFiles = new Set([
'apps/web/src/workbench/WorkbenchShell.tsx',
]);
const bannedImports = [
{
pattern: /from\s+['"]tdesign-react['"]/,
message: 'New app UI must use @/ui instead of tdesign-react.',
},
{
pattern: /from\s+['"]tdesign-icons-react['"]/,
message: 'New icons must use lucide-react instead of tdesign-icons-react.',
},
];
const bannedBluePrimary = [
/#1677ff/i,
/#1890ff/i,
/#2563eb/i,
/#3b82f6/i,
/rgb\(\s*37\s*,\s*99\s*,\s*235\s*\)/i,
];
const uiRequiredDirs = [
'apps/web/src/ui',
'docs/page-templates',
];
const requiredFiles = [
{
path: 'docs/TRUEGROWTH_GOLDEN_IMAGE_PAGE_UI.md',
includes: [
'/image',
'golden page',
'Browser QA Requirement',
'light and dark',
],
},
{
path: 'docs/TRUEGROWTH_UI_SYSTEM.md',
includes: ['TRUEGROWTH_GOLDEN_IMAGE_PAGE_UI.md', '/image'],
},
{
path: 'docs/TRUEGROWTH_GOLDEN_PAGE_MIGRATION_AUDIT.md',
includes: ['/video', '/avatar', '/assets', 'Browser Evidence'],
},
{
path: 'AGENTS.md',
includes: ['TRUEGROWTH_GOLDEN_IMAGE_PAGE_UI.md', '/image'],
},
{
path: 'docs/page-templates/generator-page.tsx',
includes: ['tg-golden-generator'],
},
{
path: 'docs/page-templates/media-library-page.tsx',
includes: ['tg-golden-library'],
},
{
path: 'docs/page-templates/status-page.tsx',
includes: ['tg-golden-status'],
},
];
const errors = [];
for (const dir of appSourceRoots) {
walk(path.join(root, dir), (file) => {
const relative = path.relative(root, file).replace(/\\/g, '/');
if (!/\.(tsx?)$/.test(relative)) {
return;
}
const content = fs.readFileSync(file, 'utf8');
if (!allowedLegacyFiles.has(relative)) {
for (const rule of bannedImports) {
if (rule.pattern.test(content)) {
errors.push(`${relative}: ${rule.message}`);
}
}
}
});
}
for (const dir of strictVisualRoots) {
walk(path.join(root, dir), (file) => {
const relative = path.relative(root, file).replace(/\\/g, '/');
if (!/\.(tsx?|scss|css)$/.test(relative)) {
return;
}
const content = fs.readFileSync(file, 'utf8');
for (const rule of bannedImports) {
if (rule.pattern.test(content)) {
errors.push(`${relative}: ${rule.message}`);
}
}
for (const pattern of bannedBluePrimary) {
if (pattern.test(content)) {
errors.push(
`${relative}: Blue primary/focus colors are not allowed in the UI system. Use TrueGrowth orange tokens.`
);
}
}
});
}
for (const dir of uiRequiredDirs) {
if (!fs.existsSync(path.join(root, dir))) {
errors.push(`${dir}: required UI system directory is missing.`);
}
}
for (const file of requiredFiles) {
const absolute = path.join(root, file.path);
if (!fs.existsSync(absolute)) {
errors.push(`${file.path}: required golden UI standard file is missing.`);
continue;
}
const content = fs.readFileSync(absolute, 'utf8');
for (const text of file.includes) {
if (!content.includes(text)) {
errors.push(`${file.path}: missing required golden UI reference "${text}".`);
}
}
}
if (errors.length > 0) {
console.error('TrueGrowth UI system check failed:');
for (const error of errors) {
console.error(`- ${error}`);
}
process.exit(1);
}
console.log('TrueGrowth UI system check passed.');
function walk(dir, visit) {
if (!fs.existsSync(dir)) {
return;
}
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
if (
entry.name === 'node_modules' ||
entry.name === 'dist' ||
entry.name === '.nx'
) {
continue;
}
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
walk(fullPath, visit);
} else if (entry.isFile()) {
visit(fullPath);
}
}
}