Added strict mode to check version ranges

This commit is contained in:
Hein 2025-10-10 13:42:04 +02:00
parent 2ffa2bd0ca
commit 3ca78de94c
2 changed files with 74 additions and 23 deletions

View File

@ -52,9 +52,16 @@ const cli = async () => {
default: 'text',
type:'string'
})
.option('strict', {
alias: 's',
describe: 'Strict mode: only update packages with version ranges (~, ^, >=) if they are incompatible',
type: 'boolean',
default: false,
})
.example('$0 --check-versions', 'Show only version differences')
.example('$0 --check-missing', 'Show missing dependencies')
.example('$0 --update --dry-run', 'Show what would be updated')
.example('$0 --update --strict', 'Update only incompatible versions within ranges')
.example('$0 --packages ./packages,./other-packages', 'Check multiple package directories')
.example('$0 --packages ./pkg1/package.json,./pkg2/package.json', 'Check specific package.json files')
.example('$0 --packages ./packages,./other/package.json', 'Mix of directories and files')
@ -135,6 +142,7 @@ const cli = async () => {
format: argv.format as any,
checkVersions: argv.checkVersions,
checkMissing: argv.checkMissing,
strict: argv.strict,
});
} catch (error:any) {
console.error(chalk.red('Error during execution:'));

View File

@ -16,6 +16,7 @@ interface RunOptions {
format?: "text" | "json";
checkVersions?: boolean;
checkMissing?: boolean;
strict?: boolean;
}
interface VersionInfo {
@ -75,6 +76,7 @@ class DependencyChecker {
private packageJsonFiles: string[];
private dependencyMap: Map<string, DependencyInfo>;
private workspacePackages: Set<string>;
private strictMode: boolean;
constructor(appPackageJsonPath: string, packagesInput: string) {
this.appPackageJsonPath = appPackageJsonPath;
@ -82,6 +84,7 @@ class DependencyChecker {
this.packageJsonFiles = [];
this.dependencyMap = new Map();
this.workspacePackages = new Set();
this.strictMode = false;
}
private findWorkspacePackages(): void {
@ -147,6 +150,21 @@ class DependencyChecker {
return this.workspacePackages.has(packageName);
}
private hasVersionRange(version: string): boolean {
return /^[~^<>=]+/.test(version);
}
private isVersionCompatible(rangeVersion: string, targetVersion: string): boolean {
try {
const cleanTargetVersion = this.getSemverVersion(targetVersion);
if (!cleanTargetVersion) return false;
return semver.satisfies(cleanTargetVersion, rangeVersion);
} catch {
return false;
}
}
private shouldIncludeDependency(
depName: string,
version: string,
@ -590,30 +608,52 @@ class DependencyChecker {
if (appDependencies[dep]) {
const appVersion = appDependencies[dep];
// Extract just the version numbers for comparison
const cleanCurrentVersion = version.replace(/^[~^<>=]+\s*/g, "");
const cleanAppVersion = appVersion.replace(/^[~^<>=]+\s*/g, "");
if (cleanCurrentVersion !== cleanAppVersion) {
// Extract the version prefix (operators and spaces)
const versionPrefix = version.match(/^[~^<>=]+\s*/)?.[0] || '';
// Create new version string with original prefix but updated version number
const newVersion = versionPrefix + cleanAppVersion;
if (!dryRun) {
packageJson[section]![dep] = newVersion;
// In strict mode, check if versions are compatible within range constraints
if (this.strictMode && this.hasVersionRange(version)) {
if (!this.isVersionCompatible(version, appVersion)) {
// Only update if the app version doesn't satisfy the range
const cleanAppVersion = appVersion.replace(/^[~^<>=]+\s*/g, "");
const versionPrefix = version.match(/^[~^<>=]+\s*/)?.[0] || '';
const newVersion = versionPrefix + cleanAppVersion;
if (!dryRun) {
packageJson[section]![dep] = newVersion;
}
updates.push({
package: packageJson.name,
dependency: dep,
from: version,
to: newVersion,
type: section,
});
hasUpdates = true;
}
} else {
// Original behavior for non-strict mode
const cleanCurrentVersion = version.replace(/^[~^<>=]+\s*/g, "");
const cleanAppVersion = appVersion.replace(/^[~^<>=]+\s*/g, "");
if (cleanCurrentVersion !== cleanAppVersion) {
const versionPrefix = version.match(/^[~^<>=]+\s*/)?.[0] || '';
const newVersion = versionPrefix + cleanAppVersion;
if (!dryRun) {
packageJson[section]![dep] = newVersion;
}
updates.push({
package: packageJson.name,
dependency: dep,
from: version,
to: newVersion,
type: section,
});
hasUpdates = true;
}
updates.push({
package: packageJson.name,
dependency: dep,
from: version,
to: newVersion,
type: section,
});
hasUpdates = true;
}
}
});
@ -768,8 +808,11 @@ class DependencyChecker {
format = "text",
checkVersions = false,
checkMissing = false,
strict = false,
} = options;
this.strictMode = strict;
if (checkMissing) {
this.findPackageJsonFiles();
this.checkMissingDependencies();