@regele/devtools
Version:
A collection of developer utilities for code processing and text analysis
270 lines (234 loc) • 9.76 kB
JavaScript
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);
}