UNPKG

@regele/devtools

Version:

A collection of developer utilities for code processing and text analysis

270 lines (234 loc) 9.76 kB
#!/usr/bin/env node try { // Import the required modules const { Command } = require('commander'); const chalk = require('chalk'); const fs = require('fs'); const path = require('path'); // Import the CodeAnalyzer class const { CodeAnalyzer, SeverityLevel, CategoryType } = require('../dist/index'); // Create a new command const command = new Command('analyze-code'); // Configure the command command .description('Analyze code for potential issues, bugs, and improvements') .argument('<patterns...>', 'File patterns to analyze (e.g., "src/**/*.js")') .option('--severity <level>', 'Minimum severity level to report (info, warning, error, critical)', 'info') .option('--category <categories>', 'Categories to include (comma-separated: performance, security, maintainability, bugs, style)') .option('--ignore <pattern>', 'Files to ignore (comma-separated)') .option('--max-depth <depth>', 'Maximum depth for recursive analysis', parseInt) .option('--root-dir <path>', 'Root directory for analysis') .option('--output <path>', 'Write report to file (json or html)') .option('--format <format>', 'Output format (text, json, html)', 'text') .option('--silent', 'Suppress output', false) .action(async (patterns, options) => { try { // Parse severity level const severityLevel = parseSeverityLevel(options.severity); // Parse categories if provided const categories = options.category ? parseCategories(options.category) : undefined; // Parse ignore patterns if provided const ignorePatterns = options.ignore ? parseList(options.ignore) : undefined; // Create analyzer const analyzer = new CodeAnalyzer({ minSeverity: severityLevel, categories: categories, ignore: ignorePatterns, maxDepth: options.maxDepth, rootDir: options.rootDir, dryRun: options.dryRun }); // Start processing if (!options.silent) { console.log(chalk.cyan('⠋ Analyzing code...')); } // Process files const results = await analyzer.analyzeFiles(patterns); // Count findings const totalFindings = results.reduce( (sum, result) => sum + result.findings.length, 0 ); // Log results if (!options.silent) { console.log(chalk.green(`✓ Analyzed ${results.length} files, found ${totalFindings} issues`)); // Show detailed results const successResults = results.filter(r => r.success); const errorResults = results.filter(r => !r.success); // Log errors first if (errorResults.length > 0) { console.log('\n' + chalk.bold('Errors:')); errorResults.forEach(result => { console.log(chalk.red(`Failed to analyze ${result.filePath}: ${result.error}`)); }); } // Log findings if (totalFindings > 0) { console.log('\n' + chalk.bold('Findings:')); // Group findings by file const findingsByFile = {}; successResults.forEach(result => { if (result.findings.length > 0) { findingsByFile[result.filePath] = result.findings; } }); // Display findings Object.entries(findingsByFile).forEach(([filePath, findings]) => { console.log(`\n${chalk.underline(filePath)}`); // Sort findings by line number findings.sort((a, b) => a.line - b.line); findings.forEach(finding => { // Format severity with color let severityColor; switch (finding.severity) { case SeverityLevel.Critical: severityColor = chalk.bgRed.white; break; case SeverityLevel.Error: severityColor = chalk.red; break; case SeverityLevel.Warning: severityColor = chalk.yellow; break; default: severityColor = chalk.blue; } // Format category with color let categoryColor; switch (finding.category) { case CategoryType.Performance: categoryColor = chalk.magenta; break; case CategoryType.Security: categoryColor = chalk.red; break; case CategoryType.Maintainability: categoryColor = chalk.cyan; break; case CategoryType.Bugs: categoryColor = chalk.yellow; break; default: categoryColor = chalk.gray; } // Log finding header console.log( ` ${severityColor(finding.severity.toUpperCase())} ` + `${categoryColor(`[${finding.category}]`)} ` + `Line ${finding.line}${finding.column ? `:${finding.column}` : ''}: ` + `${finding.message}` ); // Log code snippet console.log(''); console.log(chalk.gray(' │ ' + finding.code.replace(/\n/g, '\n │ '))); console.log(''); // Log explanation console.log(chalk.gray(' ' + finding.explanation)); console.log(''); }); }); } // Generate summary console.log('\n' + chalk.bold('Summary:')); // Count by severity const severityCounts = { [SeverityLevel.Info]: 0, [SeverityLevel.Warning]: 0, [SeverityLevel.Error]: 0, [SeverityLevel.Critical]: 0 }; // Count by category const categoryCounts = { [CategoryType.Performance]: 0, [CategoryType.Security]: 0, [CategoryType.Maintainability]: 0, [CategoryType.Bugs]: 0, [CategoryType.Style]: 0 }; // Count findings successResults.forEach(result => { result.findings.forEach(finding => { severityCounts[finding.severity]++; categoryCounts[finding.category]++; }); }); // Log severity counts console.log(' Severity:'); console.log(` ${chalk.red('Critical')}: ${severityCounts[SeverityLevel.Critical]}`); console.log(` ${chalk.red('Error')}: ${severityCounts[SeverityLevel.Error]}`); console.log(` ${chalk.yellow('Warning')}: ${severityCounts[SeverityLevel.Warning]}`); console.log(` ${chalk.blue('Info')}: ${severityCounts[SeverityLevel.Info]}`); // Log category counts console.log(' Category:'); console.log(` ${chalk.magenta('Performance')}: ${categoryCounts[CategoryType.Performance]}`); console.log(` ${chalk.red('Security')}: ${categoryCounts[CategoryType.Security]}`); console.log(` ${chalk.cyan('Maintainability')}: ${categoryCounts[CategoryType.Maintainability]}`); console.log(` ${chalk.yellow('Bugs')}: ${categoryCounts[CategoryType.Bugs]}`); console.log(` ${chalk.gray('Style')}: ${categoryCounts[CategoryType.Style]}`); } } catch (error) { console.error(chalk.red('✗ ' + error.message)); process.exit(1); } }); // Parse command line arguments command.parse(process.argv); // Helper functions /** * Parse a comma-separated list * * @param {string} value - Comma-separated string * @returns {string[]} Array of strings */ function parseList(value) { return value.split(',').map(item => item.trim()); } /** * Parse categories from a comma-separated list * * @param {string} value - Comma-separated string * @returns {string[]} Array of categories */ function parseCategories(value) { const categories = value.split(',').map(item => item.trim().toLowerCase()); return categories.map(category => { switch (category) { case 'performance': return CategoryType.Performance; case 'security': return CategoryType.Security; case 'maintainability': return CategoryType.Maintainability; case 'bugs': return CategoryType.Bugs; case 'style': return CategoryType.Style; default: throw new Error(`Invalid category: ${category}`); } }); } /** * Parse severity level from string * * @param {string} value - Severity level string * @returns {string} Severity level enum */ function parseSeverityLevel(value) { switch (value.toLowerCase()) { case 'info': return SeverityLevel.Info; case 'warning': return SeverityLevel.Warning; case 'error': return SeverityLevel.Error; case 'critical': return SeverityLevel.Critical; default: throw new Error(`Invalid severity level: ${value}`); } } } catch (error) { console.error('Error executing command:', error.message); process.exit(1); }