#!/usr/bin/env node /** * Script to replace console.log statements with proper winston logging * This addresses immediate-4 from the improvement roadmap */ const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); // Configuration const BACKEND_DIR = path.join(__dirname, '..', 'src'); const FRONTEND_DIR = path.join(__dirname, '..', '..', 'frontend', 'src'); const LOGGER_IMPORT = "import { logger } from '../utils/logger';"; // Console.log replacement patterns const CONSOLE_REPLACEMENTS = { 'console.log': 'logger.info', 'console.error': 'logger.error', 'console.warn': 'logger.warn', 'console.info': 'logger.info', 'console.debug': 'logger.debug' }; // Enhanced logging patterns for specific contexts const ENHANCED_LOGGING_PATTERNS = { // Upload-related logging 'console.log.*upload.*start': 'logger.info(\'Upload started\', { category: \'upload\', operation: \'upload_start\' })', 'console.log.*upload.*complete': 'logger.info(\'Upload completed\', { category: \'upload\', operation: \'upload_success\' })', 'console.log.*upload.*error': 'logger.error(\'Upload failed\', { category: \'upload\', operation: \'upload_error\' })', // Processing-related logging 'console.log.*process.*start': 'logger.info(\'Processing started\', { category: \'processing\', operation: \'processing_start\' })', 'console.log.*process.*complete': 'logger.info(\'Processing completed\', { category: \'processing\', operation: \'processing_success\' })', 'console.log.*process.*error': 'logger.error(\'Processing failed\', { category: \'processing\', operation: \'processing_error\' })', // Authentication-related logging 'console.log.*auth': 'logger.info(\'Authentication event\', { category: \'auth\' })', 'console.log.*token': 'logger.debug(\'Token operation\', { category: \'auth\' })', // API-related logging 'console.log.*api': 'logger.info(\'API operation\', { category: \'api\' })', 'console.log.*request': 'logger.debug(\'API request\', { category: \'api\' })', 'console.log.*response': 'logger.debug(\'API response\', { category: \'api\' })', // Database-related logging 'console.log.*database': 'logger.info(\'Database operation\', { category: \'database\' })', 'console.log.*query': 'logger.debug(\'Database query\', { category: \'database\' })', // Error-related logging 'console.log.*error': 'logger.error(\'Error occurred\', { category: \'error\' })', 'console.log.*fail': 'logger.error(\'Operation failed\', { category: \'error\' })', }; function findFiles(dir, extensions = ['.ts', '.tsx', '.js', '.jsx']) { const files = []; function traverse(currentDir) { const items = fs.readdirSync(currentDir); for (const item of items) { const fullPath = path.join(currentDir, item); const stat = fs.statSync(fullPath); if (stat.isDirectory() && !item.startsWith('.') && item !== 'node_modules') { traverse(fullPath); } else if (stat.isFile() && extensions.includes(path.extname(item))) { files.push(fullPath); } } } traverse(dir); return files; } function addLoggerImport(filePath, content) { const lines = content.split('\n'); const importLines = []; let lastImportIndex = -1; // Find the last import statement for (let i = 0; i < lines.length; i++) { if (lines[i].trim().startsWith('import ')) { lastImportIndex = i; } } // Check if logger is already imported const hasLoggerImport = lines.some(line => line.includes('import') && line.includes('logger') ); if (!hasLoggerImport) { // Add logger import after the last import statement if (lastImportIndex >= 0) { lines.splice(lastImportIndex + 1, 0, LOGGER_IMPORT); } else { // No imports found, add at the beginning lines.unshift(LOGGER_IMPORT); } } return lines.join('\n'); } function replaceConsoleLogs(content) { let modifiedContent = content; // Replace basic console methods for (const [consoleMethod, loggerMethod] of Object.entries(CONSOLE_REPLACEMENTS)) { const regex = new RegExp(`\\b${consoleMethod}\\b`, 'g'); modifiedContent = modifiedContent.replace(regex, loggerMethod); } // Replace enhanced patterns for (const [pattern, replacement] of Object.entries(ENHANCED_LOGGING_PATTERNS)) { const regex = new RegExp(pattern, 'gi'); modifiedContent = modifiedContent.replace(regex, replacement); } // Handle console.log with string literals modifiedContent = modifiedContent.replace( /console\.log\((['"`])(.*?)\1(,\s*(.+))?\)/g, (match, quote, message, args) => { if (args) { return `logger.info(${quote}${message}${quote}, ${args.trim()})`; } else { return `logger.info(${quote}${message}${quote})`; } } ); // Handle console.log with template literals modifiedContent = modifiedContent.replace( /console\.log\(`([^`]+)`(,\s*(.+))?\)/g, (match, message, args) => { if (args) { return `logger.info(\`${message}\`, ${args.trim()})`; } else { return `logger.info(\`${message}\`)`; } } ); return modifiedContent; } function processFile(filePath) { try { const content = fs.readFileSync(filePath, 'utf8'); // Check if file contains console.log statements if (!content.includes('console.log') && !content.includes('console.error') && !content.includes('console.warn') && !content.includes('console.info') && !content.includes('console.debug')) { return false; } console.log(`Processing: ${filePath}`); // Replace console.log statements let modifiedContent = replaceConsoleLogs(content); // Add logger import if needed if (modifiedContent !== content) { modifiedContent = addLoggerImport(filePath, modifiedContent); } // Write back to file fs.writeFileSync(filePath, modifiedContent, 'utf8'); return true; } catch (error) { console.error(`Error processing ${filePath}:`, error.message); return false; } } function main() { console.log('šŸ”§ Starting console.log replacement process...'); const backendFiles = findFiles(BACKEND_DIR); const frontendFiles = findFiles(FRONTEND_DIR); let processedCount = 0; let errorCount = 0; // Process backend files console.log(`\nšŸ“ Processing ${backendFiles.length} backend files...`); for (const file of backendFiles) { try { if (processFile(file)) { processedCount++; } } catch (error) { errorCount++; console.error(`Error processing ${file}:`, error.message); } } // Process frontend files (with different logger import) console.log(`\nšŸ“ Processing ${frontendFiles.length} frontend files...`); for (const file of frontendFiles) { try { // For frontend, we'll use a different approach since it doesn't have winston const content = fs.readFileSync(file, 'utf8'); if (content.includes('console.log')) { console.log(`Frontend file with console.log: ${file}`); // For now, just log that we found console.log statements // Frontend logging will be handled separately } } catch (error) { errorCount++; console.error(`Error processing ${file}:`, error.message); } } console.log(`\nāœ… Console.log replacement completed!`); console.log(`šŸ“Š Files processed: ${processedCount}`); console.log(`āŒ Errors: ${errorCount}`); // Run linting to check for any issues console.log('\nšŸ” Running linting check...'); try { execSync('npm run lint', { cwd: path.join(__dirname, '..'), stdio: 'inherit' }); console.log('āœ… Linting passed!'); } catch (error) { console.log('āš ļø Linting found issues - please review and fix manually'); } } if (require.main === module) { main(); } module.exports = { processFile, replaceConsoleLogs, findFiles };