UNPKG

@neurolint/cli

Version:

NeuroLint CLI for React/Next.js modernization with advanced 6-layer orchestration and intelligent AST transformations

427 lines (353 loc) 15 kB
import chalk from "chalk"; import ora from "ora"; import { table } from "table"; import inquirer from "inquirer"; import { CacheManager } from "../../lib/cache-manager"; import fs from "fs-extra"; import path from "path"; interface CacheOptions { verbose?: boolean; force?: boolean; // Stats options detailed?: boolean; // Clear options type?: string; // Warmup options pattern?: string; maxFiles?: number; } const cacheManager = new CacheManager(); export async function cacheCommand(action: string, options: CacheOptions = {}) { try { // Initialize cache manager await cacheManager.initialize(); switch (action) { case 'stats': case 'status': await showCacheStats(options); break; case 'clear': await clearCache(options); break; case 'warmup': await warmupCache(options); break; case 'cleanup': await performCleanup(options); break; case 'config': await showCacheConfig(options); break; default: await showCacheHelp(); } } catch (error) { console.error(chalk.red(`Cache command failed: ${error instanceof Error ? error.message : 'Unknown error'}`)); process.exit(1); } } async function showCacheStats(options: CacheOptions) { const spinner = ora('Loading cache statistics...').start(); try { const stats = cacheManager.getStatistics(); spinner.succeed('Cache statistics loaded'); console.log(chalk.white.bold('\nNeuroLint Cache Statistics\n')); // Overview Table const overviewData = [ ['Metric', 'Value'], ['Status', stats.initialized ? chalk.green('Initialized') : chalk.red('Not Initialized')], ['Cache Directory', stats.persistentCache.directory], ['Persistent Cache', stats.persistentCache.enabled ? chalk.green('Enabled') : chalk.gray('Disabled')], ['Memory Cache', stats.memoryCache.enabled ? chalk.green('Enabled') : chalk.gray('Disabled')], ['Total Cache Size', formatBytes(stats.persistentCache.size)], ['Total Cache Files', stats.persistentCache.files.toLocaleString()], ['Memory Cache Size', stats.memoryCache.size.toLocaleString()], ['Memory Hit Rate', `${(stats.memoryCache.hitRate * 100).toFixed(1)}%`] ]; console.log(table(overviewData, { border: { topBody: chalk.gray('─'), topJoin: chalk.gray('┬'), topLeft: chalk.gray('┌'), topRight: chalk.gray('┐'), bottomBody: chalk.gray('─'), bottomJoin: chalk.gray('┴'), bottomLeft: chalk.gray('└'), bottomRight: chalk.gray('┘'), bodyLeft: chalk.gray('│'), bodyRight: chalk.gray('│'), bodyJoin: chalk.gray('│'), joinBody: chalk.gray('─'), joinLeft: chalk.gray('├'), joinRight: chalk.gray('┤'), joinJoin: chalk.gray('┼') } })); if (options.detailed) { // Configuration Details console.log(chalk.blue.bold('\nConfiguration Details\n')); const configData = [ ['Setting', 'Value'], ['Max Cache Size', formatBytes(stats.config.maxCacheSize)], ['Max Age', formatDuration(stats.config.maxAge)], ['Compression', stats.config.compressionEnabled ? 'Enabled' : 'Disabled'], ['Max Memory Items', stats.config.maxMemoryItems.toLocaleString()], ['Hash Algorithm', stats.config.hashAlgorithm], ['Version', stats.config.version] ]; console.log(table(configData, { border: { topBody: chalk.gray('─'), topJoin: chalk.gray('┬'), topLeft: chalk.gray('┌'), topRight: chalk.gray('┐'), bottomBody: chalk.gray('─'), bottomJoin: chalk.gray('┴'), bottomLeft: chalk.gray('└'), bottomRight: chalk.gray('┘'), bodyLeft: chalk.gray('│'), bodyRight: chalk.gray('│'), bodyJoin: chalk.gray('│'), joinBody: chalk.gray('─'), joinLeft: chalk.gray('├'), joinRight: chalk.gray('┤'), joinJoin: chalk.gray('┼') } })); // Metadata if (stats.metadata) { console.log(chalk.blue.bold('\nCache Metadata\n')); const metadataData = [ ['Property', 'Value'], ['Created', new Date(stats.metadata.created).toLocaleString()], ['Last Cleanup', new Date(stats.metadata.lastCleanup).toLocaleString()], ['Version', stats.metadata.version] ]; console.log(table(metadataData, { border: { topBody: chalk.gray('─'), topJoin: chalk.gray('┬'), topLeft: chalk.gray('┌'), topRight: chalk.gray('┐'), bottomBody: chalk.gray('─'), bottomJoin: chalk.gray('┴'), bottomLeft: chalk.gray('└'), bottomRight: chalk.gray('┘'), bodyLeft: chalk.gray('│'), bodyRight: chalk.gray('│'), bodyJoin: chalk.gray('│'), joinBody: chalk.gray('─'), joinLeft: chalk.gray('├'), joinRight: chalk.gray('┤'), joinJoin: chalk.gray('┼') } })); } // Performance Recommendations console.log(chalk.yellow.bold('\nPerformance Recommendations\n')); const recommendations = generateRecommendations(stats); if (recommendations.length > 0) { recommendations.forEach((rec, index) => { console.log(`${chalk.yellow((index + 1).toString())}. ${rec}`); }); } else { console.log(chalk.green('Cache is optimally configured')); } } } catch (error) { spinner.fail('Failed to load cache statistics'); throw error; } } async function clearCache(options: CacheOptions) { if (!options.force) { const { confirmed } = await inquirer.prompt([ { type: 'confirm', name: 'confirmed', message: 'Are you sure you want to clear the cache? This cannot be undone.', default: false } ]); if (!confirmed) { console.log(chalk.yellow('Cache clear cancelled')); return; } } const spinner = ora('Clearing cache...').start(); try { const statsBefore = cacheManager.getStatistics(); await cacheManager.clearCache(); spinner.succeed('Cache cleared successfully'); console.log(chalk.green('\nCache Cleared\n')); console.log(chalk.gray(`Freed ${formatBytes(statsBefore.persistentCache.size)} of disk space`)); console.log(chalk.gray(`Removed ${statsBefore.persistentCache.files.toLocaleString()} cache files`)); console.log(chalk.gray(`Cleared ${statsBefore.memoryCache.size.toLocaleString()} memory cache entries`)); } catch (error) { spinner.fail('Failed to clear cache'); throw error; } } async function warmupCache(options: CacheOptions) { const spinner = ora('Scanning for files to warm up cache...').start(); try { // Find files to warmup const pattern = options.pattern || '**/*.{ts,tsx,js,jsx}'; const maxFiles = options.maxFiles || 100; spinner.text = `Finding files matching: ${pattern}`; // Get files from current directory const { glob } = await import('glob'); const files = await glob(pattern, { ignore: ['node_modules/**', 'dist/**', 'build/**', '.next/**'], absolute: true, cwd: process.cwd() }); const filesToProcess = files.slice(0, maxFiles); if (filesToProcess.length === 0) { spinner.warn('No files found to warm up cache'); return; } spinner.text = `Warming up cache for ${filesToProcess.length} files...`; await cacheManager.warmupCache(filesToProcess); spinner.succeed(`Cache warmed up for ${filesToProcess.length} files`); if (options.verbose) { console.log(chalk.gray('\nWarmed up files:')); filesToProcess.slice(0, 10).forEach(file => { console.log(chalk.gray(` ${path.relative(process.cwd(), file)}`)); }); if (filesToProcess.length > 10) { console.log(chalk.gray(` ... and ${filesToProcess.length - 10} more files`)); } } } catch (error) { spinner.fail('Cache warmup failed'); throw error; } } async function performCleanup(options: CacheOptions) { const spinner = ora('Performing cache cleanup...').start(); try { const statsBefore = cacheManager.getStatistics(); await cacheManager.performMaintenance(); const statsAfter = cacheManager.getStatistics(); const filesRemoved = statsBefore.persistentCache.files - statsAfter.persistentCache.files; const spaceFreed = statsBefore.persistentCache.size - statsAfter.persistentCache.size; spinner.succeed('Cache cleanup completed'); console.log(chalk.green('\nCache Cleanup Completed\n')); if (filesRemoved > 0 || spaceFreed > 0) { console.log(chalk.gray(`Removed ${filesRemoved.toLocaleString()} expired files`)); console.log(chalk.gray(`Freed ${formatBytes(spaceFreed)} of disk space`)); } else { console.log(chalk.gray('No cleanup was necessary')); } if (options.verbose) { console.log(chalk.gray(`\nCache files remaining: ${statsAfter.persistentCache.files.toLocaleString()}`)); console.log(chalk.gray(`Total cache size: ${formatBytes(statsAfter.persistentCache.size)}`)); } } catch (error) { spinner.fail('Cache cleanup failed'); throw error; } } async function showCacheConfig(options: CacheOptions) { const stats = cacheManager.getStatistics(); console.log(chalk.white.bold('\nCache Configuration\n')); const config = stats.config; console.log(chalk.blue('Cache Directory:')); console.log(chalk.gray(` ${config.cacheDir}`)); console.log(chalk.blue('\nStorage Settings:')); console.log(chalk.gray(` Max Cache Size: ${formatBytes(config.maxCacheSize)}`)); console.log(chalk.gray(` Max Age: ${formatDuration(config.maxAge)}`)); console.log(chalk.gray(` Compression: ${config.compressionEnabled ? 'Enabled' : 'Disabled'}`)); console.log(chalk.blue('\nMemory Cache:')); console.log(chalk.gray(` Enabled: ${config.memoryCache ? 'Yes' : 'No'}`)); console.log(chalk.gray(` Max Items: ${config.maxMemoryItems.toLocaleString()}`)); console.log(chalk.blue('\nSecurity:')); console.log(chalk.gray(` Hash Algorithm: ${config.hashAlgorithm}`)); console.log(chalk.gray(` Version: ${config.version}`)); if (options.verbose) { console.log(chalk.blue('\nAdvanced Settings:')); console.log(chalk.gray(` Persistent Cache: ${config.persistentCache ? 'Enabled' : 'Disabled'}`)); console.log(chalk.gray(` Auto-Cleanup: Enabled (every hour)`)); } console.log(chalk.yellow('\nConfiguration Tips:')); console.log(chalk.gray('• Increase maxCacheSize for larger projects')); console.log(chalk.gray('• Reduce maxAge for frequently changing codebases')); console.log(chalk.gray('• Enable compression to save disk space')); console.log(chalk.gray('• Configure in .neurolintrc.json under "cache" section')); } async function showCacheHelp() { console.log(chalk.white.bold('\nNeuroLint Cache Management\n')); console.log(chalk.white('Available Commands:')); console.log(chalk.gray(' stats Show cache statistics and status')); console.log(chalk.gray(' clear Clear all cached data')); console.log(chalk.gray(' warmup Pre-populate cache with project files')); console.log(chalk.gray(' cleanup Remove expired cache entries')); console.log(chalk.gray(' config Show cache configuration')); console.log(chalk.white('\nOptions:')); console.log(chalk.gray(' --verbose Show detailed information')); console.log(chalk.gray(' --force Skip confirmation prompts')); console.log(chalk.gray(' --detailed Show detailed statistics')); console.log(chalk.gray(' --pattern <glob> File pattern for warmup (default: **/*.{ts,tsx,js,jsx})')); console.log(chalk.gray(' --max-files <n> Maximum files to warmup (default: 100)')); console.log(chalk.white('\nExamples:')); console.log(chalk.gray(' neurolint cache stats --detailed')); console.log(chalk.gray(' neurolint cache clear --force')); console.log(chalk.gray(' neurolint cache warmup --pattern="src/**/*.tsx"')); console.log(chalk.gray(' neurolint cache cleanup --verbose')); console.log(chalk.white('\nCache Configuration:')); console.log(chalk.gray('Add to .neurolintrc.json:')); console.log(chalk.gray(`{ "cache": { "maxCacheSize": "100MB", "maxAge": "24h", "compressionEnabled": true, "memoryCache": true, "maxMemoryItems": 1000 } }`)); } // Helper functions function formatBytes(bytes: number): string { if (bytes === 0) return '0 B'; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(1024)); const size = bytes / Math.pow(1024, i); return `${size.toFixed(i === 0 ? 0 : 1)} ${sizes[i]}`; } function formatDuration(ms: number): string { const seconds = Math.floor(ms / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (days > 0) return `${days}d ${hours % 24}h`; if (hours > 0) return `${hours}h ${minutes % 60}m`; if (minutes > 0) return `${minutes}m ${seconds % 60}s`; return `${seconds}s`; } function generateRecommendations(stats: any): string[] { const recommendations: string[] = []; // Check cache hit rate if (stats.memoryCache.hitRate < 0.5) { recommendations.push('Low cache hit rate detected. Consider increasing memory cache size or cache duration.'); } // Check cache size const usageRatio = stats.persistentCache.size / stats.config.maxCacheSize; if (usageRatio > 0.9) { recommendations.push('Cache is nearly full. Consider increasing maxCacheSize or running cleanup more frequently.'); } // Check memory cache efficiency if (stats.memoryCache.enabled && stats.memoryCache.size < 10) { recommendations.push('Memory cache has few items. Consider running warmup to improve performance.'); } // Check if compression would help if (!stats.config.compressionEnabled && stats.persistentCache.size > 10 * 1024 * 1024) { recommendations.push('Enable compression to reduce cache disk usage for large caches.'); } // Check cache age const daysSinceCreation = (Date.now() - stats.metadata.created) / (1000 * 60 * 60 * 24); if (daysSinceCreation > 30 && stats.persistentCache.files > 1000) { recommendations.push('Consider running cleanup to remove old cache entries and improve performance.'); } return recommendations; }