pury
Version:
š”ļø AI-powered security scanner with advanced threat detection, dual reporting system (detailed & summary), and comprehensive code analysis
147 lines ⢠6.41 kB
JavaScript
import { Command } from 'commander';
import { resolve } from 'path';
import { promises as fs } from 'fs';
import { FileScanner } from '../../scanner/file-scanner.js';
import { QualityAnalyzer } from '../../analyzers/quality-analyzer.js';
import { logger } from '../../utils/logger.js';
import { fileExists } from '../../utils/file-utils.js';
export function createCleanLogsCommand() {
return new Command('clean-logs')
.description('Remove console.log and other debug statements from code')
.argument('[path]', 'Path to clean (defaults to current directory)', '.')
.option('--apply', 'Actually apply the changes (default is dry-run)')
.option('--backup', 'Create backup files before modifying')
.option('--exclude <patterns...>', 'Patterns to exclude from cleaning')
.option('--include <patterns...>', 'Patterns to include in cleaning', [
'**/*.js',
'**/*.ts',
'**/*.jsx',
'**/*.tsx'
])
.action(async (path, options) => {
try {
await runCleanLogs(path, options);
}
catch (error) {
logger.error(`Clean logs failed: ${error.message}`);
process.exit(1);
}
});
}
async function runCleanLogs(cleanPath, options) {
const resolvedPath = resolve(cleanPath);
if (!(await fileExists(resolvedPath))) {
throw new Error(`Path does not exist: ${resolvedPath}`);
}
logger.info(`${options.apply ? 'Cleaning' : 'Analyzing'} debug statements in: ${resolvedPath}`);
// Initialize scanner
const scanner = new FileScanner(options.include || ['**/*.js', '**/*.ts', '**/*.jsx', '**/*.tsx'], options.exclude || ['node_modules/**', 'dist/**', '*.min.js']);
// Initialize quality analyzer
const analyzer = new QualityAnalyzer('high');
const spinner = logger.spinner('Scanning for debug statements...');
try {
// Scan files
const files = await scanner.scan({
path: resolvedPath,
recursive: true,
followSymlinks: false
});
spinner.succeed(`Scanned ${files.length} files`);
if (files.length === 0) {
logger.warn('No files found to process');
return;
}
// Analyze for console statements
logger.info('Analyzing for debug statements...');
const cleaningResult = await analyzer.cleanConsoleStatements(files);
// Show results
logger.info(`\nCleaning Results:`);
logger.info(`Files with debug statements: ${cleaningResult.filesModified}`);
logger.info(`Total statements found: ${cleaningResult.statementsRemoved}`);
if (cleaningResult.changes.length === 0) {
logger.success('No debug statements found - code is already clean!');
return;
}
// Show preview of changes
logger.info('\nPreview of changes:');
for (const change of cleaningResult.changes.slice(0, 10)) {
// Show first 10 changes
logger.info(`\nš ${change.file}:${change.line}`);
logger.info(` - ${change.original}`);
logger.info(` + ${change.modified}`);
}
if (cleaningResult.changes.length > 10) {
logger.info(`\n... and ${cleaningResult.changes.length - 10} more changes`);
}
if (options.apply) {
// Apply changes
logger.info('\nApplying changes...');
const changeSpinner = logger.spinner('Modifying files...');
try {
await applyChanges(cleaningResult.changes, options.backup);
changeSpinner.succeed(`Successfully cleaned ${cleaningResult.filesModified} files`);
logger.success(`\nā
Cleaning completed!`);
logger.success(`š Modified ${cleaningResult.filesModified} files`);
logger.success(`šļø Removed ${cleaningResult.statementsRemoved} debug statements`);
if (options.backup) {
logger.info('š¾ Backup files created with .backup extension');
}
}
catch (error) {
changeSpinner.fail('Failed to apply changes');
throw error;
}
}
else {
logger.info('\nš” This was a dry run. Use --apply to actually make changes.');
logger.info('š” Use --backup to create backup files before modifying.');
}
}
catch (error) {
spinner.fail('Failed to scan files');
throw error;
}
}
async function applyChanges(changes, createBackup) {
// Group changes by file
const changesByFile = new Map();
for (const change of changes) {
if (!changesByFile.has(change.file)) {
changesByFile.set(change.file, []);
}
changesByFile.get(change.file).push(change);
}
// Apply changes file by file
for (const [filePath, fileChanges] of changesByFile) {
try {
// Create backup if requested
if (createBackup) {
const originalContent = await fs.readFile(filePath, 'utf8');
await fs.writeFile(`${filePath}.backup`, originalContent, 'utf8');
}
// Read current file content
const content = await fs.readFile(filePath, 'utf8');
const lines = content.split('\n');
// Sort changes by line number in descending order to avoid line shifting issues
fileChanges.sort((a, b) => b.line - a.line);
// Apply changes
for (const change of fileChanges) {
const lineIndex = change.line - 1;
if (lineIndex >= 0 && lineIndex < lines.length && lines[lineIndex]) {
// Simple replacement - in practice, you might want more sophisticated matching
if (lines[lineIndex].includes(change.original.trim())) {
lines[lineIndex] = change.modified;
}
}
}
// Write modified content back
const modifiedContent = lines.join('\n');
await fs.writeFile(filePath, modifiedContent, 'utf8');
}
catch (error) {
logger.error(`Failed to modify file ${filePath}: ${error.message}`);
throw error;
}
}
}
//# sourceMappingURL=clean-logs.js.map