check-dependency-version-consistency
Version:
Ensures dependencies are on consistent versions across a monorepo.
86 lines (85 loc) • 3.07 kB
JavaScript
import semver from 'semver';
// This version doesn't throw for when we want to ignore invalid versions that might be present.
export function compareVersionRangesSafe(a, b) {
try {
return compareVersionRanges(a, b);
}
catch {
return 0;
}
}
// Compare semver version ranges like ^1.0.0, ~2.5.0, 3.0.0, etc.
export function compareVersionRanges(a, b) {
// Coerce to normalized version without any range prefix.
const aVersion = semver.coerce(a);
const bVersion = semver.coerce(b);
if (!aVersion) {
throw new Error(`Invalid Version: ${a}`);
}
if (!bVersion) {
throw new Error(`Invalid Version: ${b}`);
}
if (semver.eq(aVersion, bVersion)) {
// Same version, so decide which range is considered higher.
const aRange = versionRangeToRange(a);
const bRange = versionRangeToRange(b);
return compareRanges(aRange, bRange);
}
// Greater version considered higher.
return semver.gt(aVersion, bVersion) ? 1 : -1;
}
const RANGE_PRECEDENCE = ['~', '^']; // Lowest to highest.
// Compare semver ranges like ^, ~, etc.
export function compareRanges(aRange, bRange) {
const aRangePrecedence = RANGE_PRECEDENCE.indexOf(aRange);
const bRangePrecedence = RANGE_PRECEDENCE.indexOf(bRange);
if (aRangePrecedence > bRangePrecedence) {
return 1;
}
else if (aRangePrecedence < bRangePrecedence) {
return -1;
}
return 0;
}
// Example input: ^1.0.0, output: ^
export function versionRangeToRange(versionRange) {
const match = versionRange.match(/^\D+/);
return match ? match[0] : '';
}
export function getLatestVersion(versions) {
const sortedVersions = [...versions].sort(compareVersionRanges);
if (sortedVersions.length === 0) {
throw new Error('No versions provided.');
}
return sortedVersions.at(-1); // Latest version will be sorted to end of list.
}
// Example input: ['~', '^'], output: '^'
export function getHighestRangeType(ranges) {
const sorted = [...ranges].sort(compareRanges);
if (sorted.length === 0) {
throw new Error('No ranges provided.');
}
return sorted.at(-1); // Range with highest precedence will be sorted to end of list.
}
// Example input: ['1.5.0', '^1.0.0'], output: '^1.5.0'
export function getIncreasedLatestVersion(versions) {
const latestVersion = getLatestVersion(versions);
const latestVersionBare = semver.coerce(latestVersion);
let result = latestVersion;
let resultBare = latestVersionBare;
for (const version of versions) {
if (version === latestVersion) {
continue;
}
const versionBare = semver.coerce(version);
if (latestVersionBare &&
semver.satisfies(latestVersionBare, version) &&
versionBare &&
semver.gt(latestVersionBare, versionBare) &&
resultBare) {
result = version.replace(String(versionBare), String(resultBare));
resultBare = semver.coerce(result);
}
}
return result;
}