UNPKG

depdrift

Version:

A tool to analyze dependency drift in JavaScript projects

230 lines (197 loc) 5.37 kB
/** * Reporter module for displaying drift analysis results * @module utils/reporter */ const fs = require('fs-extra'); const path = require('path'); const chalk = require('chalk'); const Table = require('cli-table3'); /** * Get color for drift status * @param {string} status - Drift status * @returns {Function} Chalk color function */ function getStatusColor (status) { switch (status.toLowerCase()) { case 'missing': case 'major': case 'extra': case 'critical': case 'high': case 'peer-missing': return chalk.red; case 'minor': case 'medium': return chalk.yellow; case 'patch': case 'low': return chalk.cyan; case 'safe': case 'none': case 'optional-missing': return chalk.green; case 'unknown': default: return chalk.gray; } } /** * Report dependency drift to console and/or file * @param {Array} records - Array of dependency records to report * @param {Object} options - Report options * @returns {Promise<void>} */ async function reportDrift (records, options = {}) { const { json = false, jsonFile = 'drift-report.json', showAll = false, sortBy = 'status', sortDirection = 'desc', title = 'Dependency Drift Report' } = options; if (!records || !Array.isArray(records) || records.length === 0) { console.log(chalk.yellow('No dependency drift records to report.')); if (json) { await fs.writeJson(jsonFile, { records: [] }, { spaces: 2 }); console.log(chalk.gray(`Empty report written to ${jsonFile}`)); } return; } // Sort the records const sortedRecords = [...records].sort((a, b) => { const statusRank = { 'missing': 50, 'peer-missing': 45, 'extra': 40, 'critical': 30, 'high': 25, 'major': 20, 'medium': 15, 'minor': 10, 'low': 5, 'patch': 3, 'optional-missing': 2, 'safe': 1, 'none': 0, 'unknown': -1 }; let comparison = 0; switch (sortBy) { case 'status': // Get status rank or default to -1 if not found const aRank = statusRank[a.status?.toLowerCase()] ?? -1; const bRank = statusRank[b.status?.toLowerCase()] ?? -1; comparison = bRank - aRank; break; case 'name': case 'package': comparison = a.package?.localeCompare(b.package) ?? 0; break; case 'expected': comparison = a.expected?.localeCompare(b.expected) ?? 0; break; case 'installed': comparison = a.installed?.localeCompare(b.installed) ?? 0; break; default: comparison = 0; } return sortDirection === 'asc' ? -comparison : comparison; }); // Create table const table = new Table({ head: [ chalk.bold('Package'), chalk.bold('Expected'), chalk.bold('Installed'), chalk.bold('Status') ], style: { head: [] } }); // Filter records if not showing all const recordsToShow = showAll ? sortedRecords : sortedRecords.filter(r => r.status && !['safe', 'none', 'optional-missing'].includes(r.status.toLowerCase()) ); // Add rows recordsToShow.forEach(record => { const colorFn = getStatusColor(record.status || 'unknown'); table.push([ record.package || '-', record.expected || '-', record.installed || '-', colorFn(record.status || 'unknown') ]); }); // Print title and table console.log(chalk.bold.underline(`\n${title}`)); console.log(`Total dependencies: ${records.length}`); console.log(`Issues found: ${recordsToShow.length}\n`); console.log(table.toString()); // Write JSON file if requested if (json) { await fs.writeJson(jsonFile, { records: sortedRecords }, { spaces: 2 }); console.log(chalk.green(`\nReport saved to ${jsonFile}`)); } } /** * Generate a summary of drift records * @param {Array} records - Array of dependency records * @returns {Object} Summary object */ function generateDriftSummary (records) { if (!records || !Array.isArray(records) || records.length === 0) { return { total: 0, issues: 0, statusCounts: {} }; } const summary = { total: records.length, issues: 0, statusCounts: {} }; // Count by status records.forEach(record => { const status = (record.status || 'unknown').toLowerCase(); // Increment status count summary.statusCounts[status] = (summary.statusCounts[status] || 0) + 1; // Count as issue if not safe/none/optional-missing if (!['safe', 'none', 'optional-missing'].includes(status)) { summary.issues++; } }); return summary; } /** * Save drift report to file * @param {Array} records - Drift records * @param {string} filePath - Path to save report * @param {Object} options - Report options * @returns {Promise<void>} */ async function saveDriftReport (records, filePath, options = {}) { const { format = 'json' } = options; try { if (format === 'json') { await fs.writeJson(filePath, { records }, { spaces: 2 }); } else { // For future support of other formats throw new Error(`Unsupported report format: ${format}`); } } catch (error) { throw new Error(`Failed to save drift report: ${error.message}`); } } module.exports = { reportDrift, generateDriftSummary, saveDriftReport, getStatusColor };