@boilerbuilder/deps-analyzer
Version:
CLI tool to analyze dependency evolution and release frequency
119 lines (94 loc) • 4.03 kB
JavaScript
// ============================================================================
// LIBYEAR-LIKE DRIFT/PULSE CALCULATIONS (based on libyear LGPL-3.0)
// ============================================================================
const cleanVersion = require('../utils/cleanVersion');
const findMaxAllowedVersion = require('../utils/findMaxAllowedVersion');
const DAYS_IN_YEAR = 365.2425;
/**
* Calculate drift (time since current version vs latest version)
* @param {string} currentVersionDate - ISO date string of current version
* @param {string} latestVersionDate - ISO date string of latest version
* @returns {number} Drift in years
*/
function calculateDrift(currentVersionDate, latestVersionDate) {
if (!currentVersionDate || !latestVersionDate) return 0;
try {
const currentDate = new Date(currentVersionDate);
const latestDate = new Date(latestVersionDate);
if (isNaN(currentDate.getTime()) || isNaN(latestDate.getTime())) return 0;
const diffMs = latestDate.getTime() - currentDate.getTime();
const diffDays = diffMs / (1000 * 60 * 60 * 24);
return Math.max(0, diffDays / DAYS_IN_YEAR);
} catch (error) {
return 0;
}
}
/**
* Calculate pulse (time since latest version was released)
* @param {string} latestVersionDate - ISO date string of latest version
* @returns {number} Pulse in years
*/
function calculatePulse(latestVersionDate) {
if (!latestVersionDate) return 0;
try {
const latestDate = new Date(latestVersionDate);
const now = new Date();
if (isNaN(latestDate.getTime())) return 0;
const diffMs = now.getTime() - latestDate.getTime();
const diffDays = diffMs / (1000 * 60 * 60 * 24);
return Math.max(0, diffDays / DAYS_IN_YEAR);
} catch (error) {
return 0;
}
}
/**
* Calculate drift and pulse for a dependency based on npm data and current version
* @param {Object} npmData - NPM package data with time field
* @param {string} currentVersion - Current version used in package.json
* @param {string} maxVersion - Optional maximum version constraint from max-deps.json only
* @returns {Object} Object with drift and pulse values, or error
*/
function calculateDependencyDriftPulse(npmData, currentVersion, maxVersion = null) {
if (!npmData || !npmData.time || !currentVersion) {
return { drift: 0, pulse: 0, error: 'Missing data' };
}
const cleanCurrent = cleanVersion(currentVersion);
const currentVersionDate = npmData.time[cleanCurrent];
if (!currentVersionDate) {
return { drift: 0, pulse: 0, error: `Version ${currentVersion} not found` };
}
// DRIFT: Always calculate based on latest version (shows real technical debt)
let driftTargetVersion = npmData.latestVersion;
let driftTargetDate = npmData.time[driftTargetVersion];
if (!driftTargetDate) {
return { drift: 0, pulse: 0, error: `Latest version ${driftTargetVersion} not found` };
}
// PULSE: Use constrained version if max-deps.json explicitly sets it, otherwise latest
let pulseTargetVersion = driftTargetVersion; // Default to latest
let pulseTargetDate = driftTargetDate;
let constrainedByMaxVersion = false;
// Only apply max-deps.json constraints (not package.json constraints like ^20)
if (maxVersion) {
const availableVersions = Object.keys(npmData.time).filter(v => v !== 'created' && v !== 'modified');
const maxAllowedVersion = findMaxAllowedVersion(maxVersion, availableVersions);
if (maxAllowedVersion && npmData.time[maxAllowedVersion]) {
pulseTargetVersion = maxAllowedVersion;
pulseTargetDate = npmData.time[maxAllowedVersion];
constrainedByMaxVersion = true;
}
}
const drift = calculateDrift(currentVersionDate, driftTargetDate);
const pulse = calculatePulse(pulseTargetDate);
return {
drift,
pulse,
targetVersion: pulseTargetVersion,
constrainedByMaxVersion
};
}
module.exports = {
calculateDrift,
calculatePulse,
calculateDependencyDriftPulse,
DAYS_IN_YEAR
};