mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-11 14:52:55 +01:00
238 lines
7.6 KiB
JavaScript
238 lines
7.6 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Phase 0 Foundation Health Check
|
|
*
|
|
* Validates that Phase 0 fixes are in place and working:
|
|
* - Cache configuration
|
|
* - useEventListener hook availability
|
|
* - Build canary presence
|
|
* - Anti-pattern detection
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const rootDir = path.join(__dirname, '..');
|
|
let exitCode = 0;
|
|
let checks = { passed: 0, warnings: 0, failed: 0 };
|
|
|
|
function pass(message) {
|
|
console.log(`✅ ${message}`);
|
|
checks.passed++;
|
|
}
|
|
|
|
function warn(message) {
|
|
console.log(`⚠️ ${message}`);
|
|
checks.warnings++;
|
|
}
|
|
|
|
function fail(message) {
|
|
console.log(`❌ ${message}`);
|
|
checks.failed++;
|
|
exitCode = 1;
|
|
}
|
|
|
|
function section(title) {
|
|
console.log(`\n${'='.repeat(60)}`);
|
|
console.log(` ${title}`);
|
|
console.log(`${'='.repeat(60)}\n`);
|
|
}
|
|
|
|
// ==============================================================================
|
|
// CHECK 1: Webpack Cache Configuration
|
|
// ==============================================================================
|
|
section('1. Webpack Cache Configuration');
|
|
|
|
const webpackCorePath = path.join(rootDir, 'packages/noodl-editor/webpackconfigs/shared/webpack.renderer.core.js');
|
|
|
|
if (!fs.existsSync(webpackCorePath)) {
|
|
fail('webpack.renderer.core.js not found');
|
|
} else {
|
|
const webpackCore = fs.readFileSync(webpackCorePath, 'utf8');
|
|
|
|
if (webpackCore.includes('cacheDirectory: false')) {
|
|
pass('Babel cache disabled in webpack config');
|
|
} else if (webpackCore.includes('cacheDirectory: true')) {
|
|
fail('Babel cache ENABLED in webpack (should be false in dev)');
|
|
} else {
|
|
warn('Babel cacheDirectory setting not found (might be using defaults)');
|
|
}
|
|
}
|
|
|
|
// ==============================================================================
|
|
// CHECK 2: Clean Script Exists
|
|
// ==============================================================================
|
|
section('2. Clean Script');
|
|
|
|
const packageJsonPath = path.join(rootDir, 'package.json');
|
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
|
|
if (packageJson.scripts && packageJson.scripts['clean:all']) {
|
|
pass('clean:all script exists in package.json');
|
|
} else {
|
|
fail('clean:all script missing from package.json');
|
|
}
|
|
|
|
// ==============================================================================
|
|
// CHECK 3: Build Canary
|
|
// ==============================================================================
|
|
section('3. Build Canary');
|
|
|
|
const editorEntryPath = path.join(rootDir, 'packages/noodl-editor/src/editor/index.ts');
|
|
|
|
if (!fs.existsSync(editorEntryPath)) {
|
|
fail('Editor entry point not found');
|
|
} else {
|
|
const editorEntry = fs.readFileSync(editorEntryPath, 'utf8');
|
|
|
|
if (editorEntry.includes('BUILD TIMESTAMP')) {
|
|
pass('Build canary present in editor entry point');
|
|
} else {
|
|
fail('Build canary missing from editor entry point');
|
|
}
|
|
}
|
|
|
|
// ==============================================================================
|
|
// CHECK 4: useEventListener Hook
|
|
// ==============================================================================
|
|
section('4. useEventListener Hook');
|
|
|
|
const hookPath = path.join(rootDir, 'packages/noodl-editor/src/editor/src/hooks/useEventListener.ts');
|
|
|
|
if (!fs.existsSync(hookPath)) {
|
|
fail('useEventListener.ts not found');
|
|
} else {
|
|
const hookContent = fs.readFileSync(hookPath, 'utf8');
|
|
|
|
if (hookContent.includes('export function useEventListener')) {
|
|
pass('useEventListener hook exists and is exported');
|
|
} else {
|
|
fail('useEventListener hook not properly exported');
|
|
}
|
|
|
|
if (hookContent.includes('useRef') && hookContent.includes('useEffect')) {
|
|
pass('useEventListener uses correct React hooks');
|
|
} else {
|
|
warn('useEventListener might not be using proper hook pattern');
|
|
}
|
|
}
|
|
|
|
// ==============================================================================
|
|
// CHECK 5: Anti-Pattern Detection (Optional Warning)
|
|
// ==============================================================================
|
|
section('5. Anti-Pattern Detection (Scanning for Direct .on() Usage)');
|
|
|
|
const scanDirs = ['packages/noodl-editor/src/editor/src/views', 'packages/noodl-editor/src/editor/src/hooks'];
|
|
|
|
let foundAntiPatterns = false;
|
|
|
|
scanDirs.forEach((dir) => {
|
|
const fullPath = path.join(rootDir, dir);
|
|
if (!fs.existsSync(fullPath)) return;
|
|
|
|
const files = getAllTsxFiles(fullPath);
|
|
|
|
files.forEach((file) => {
|
|
const content = fs.readFileSync(file, 'utf8');
|
|
|
|
// Look for React components/hooks using .on() directly
|
|
if (
|
|
content.match(/function\s+\w+.*\{[\s\S]*?useEffect.*\.on\(/g) ||
|
|
content.match(/const\s+\w+\s*=.*=>[\s\S]*?useEffect.*\.on\(/g)
|
|
) {
|
|
const relativePath = path.relative(rootDir, file);
|
|
warn(`Possible anti-pattern (direct .on() in useEffect): ${relativePath}`);
|
|
foundAntiPatterns = true;
|
|
}
|
|
});
|
|
});
|
|
|
|
if (!foundAntiPatterns) {
|
|
pass('No obvious anti-patterns detected in scanned directories');
|
|
}
|
|
|
|
// ==============================================================================
|
|
// CHECK 6: Documentation Files
|
|
// ==============================================================================
|
|
section('6. Documentation');
|
|
|
|
const docs = [
|
|
'dev-docs/tasks/phase-0-foundation-stabalisation/TASK-009-verification-checklist/README.md',
|
|
'dev-docs/tasks/phase-0-foundation-stabalisation/TASK-010-eventlistener-verification/README.md',
|
|
'dev-docs/tasks/phase-0-foundation-stabalisation/TASK-011-react-event-pattern-guide/GOLDEN-PATTERN.md'
|
|
];
|
|
|
|
docs.forEach((doc) => {
|
|
const docPath = path.join(rootDir, doc);
|
|
if (fs.existsSync(docPath) && fs.statSync(docPath).size > 0) {
|
|
pass(`${path.basename(doc)} exists`);
|
|
} else {
|
|
warn(`${doc} missing or empty`);
|
|
}
|
|
});
|
|
|
|
// Check if .clinerules has EventListener section
|
|
const clinerulesPath = path.join(rootDir, '.clinerules');
|
|
if (fs.existsSync(clinerulesPath)) {
|
|
const clinerules = fs.readFileSync(clinerulesPath, 'utf8');
|
|
if (clinerules.includes('React + EventDispatcher') || clinerules.includes('useEventListener')) {
|
|
pass('.clinerules contains EventListener documentation');
|
|
} else {
|
|
warn('.clinerules missing EventListener section');
|
|
}
|
|
}
|
|
|
|
// ==============================================================================
|
|
// SUMMARY
|
|
// ==============================================================================
|
|
section('Health Check Summary');
|
|
|
|
console.log(`✅ Passed: ${checks.passed}`);
|
|
console.log(`⚠️ Warnings: ${checks.warnings}`);
|
|
console.log(`❌ Failed: ${checks.failed}\n`);
|
|
|
|
if (checks.failed > 0) {
|
|
console.log('❌ HEALTH CHECK FAILED');
|
|
console.log('Some critical Phase 0 fixes are missing or incorrect.');
|
|
console.log('Please review the failed checks above.\n');
|
|
} else if (checks.warnings > 0) {
|
|
console.log('⚠️ HEALTH CHECK PASSED WITH WARNINGS');
|
|
console.log('Foundation is stable but some improvements recommended.');
|
|
console.log('Review warnings above for details.\n');
|
|
} else {
|
|
console.log('✅ HEALTH CHECK PASSED');
|
|
console.log('Phase 0 Foundation is healthy!\n');
|
|
}
|
|
|
|
process.exit(exitCode);
|
|
|
|
// ==============================================================================
|
|
// Helpers
|
|
// ==============================================================================
|
|
|
|
function getAllTsxFiles(dir) {
|
|
let results = [];
|
|
|
|
try {
|
|
const list = fs.readdirSync(dir);
|
|
|
|
list.forEach((file) => {
|
|
const filePath = path.join(dir, file);
|
|
const stat = fs.statSync(filePath);
|
|
|
|
if (stat && stat.isDirectory()) {
|
|
if (!file.startsWith('.') && file !== 'node_modules') {
|
|
results = results.concat(getAllTsxFiles(filePath));
|
|
}
|
|
} else if (file.endsWith('.tsx') || file.endsWith('.ts')) {
|
|
results.push(filePath);
|
|
}
|
|
});
|
|
} catch (err) {
|
|
// Skip directories that can't be read
|
|
}
|
|
|
|
return results;
|
|
}
|