@gordon1210/depup
Version:
a dependency upgrade tool for node projects
165 lines (164 loc) • 6.81 kB
JavaScript
import { useMemo, useState } from "react";
import semver from "semver";
import { getDisplayVersion } from "../utils.js";
export function usePackageController(packages) {
const [cursor, setCursor] = useState(0);
const [tabIndex, setTabIndex] = useState(0);
const grouped = useMemo(() => {
const groups = {};
for (const p of packages) {
const key = p.packagePath;
groups[key] ||= [];
groups[key].push(p);
}
const entries = Object.entries(groups).map(([path, packages]) => ({ path, packages }));
const isMonorepo = entries.length > 1;
if (isMonorepo) {
const sharedMap = new Map();
for (const pkg of packages) {
sharedMap.set(pkg.name, [...(sharedMap.get(pkg.name) || []), pkg]);
}
const sharedPackages = Array.from(sharedMap.entries())
.filter(([, pkgs]) => pkgs.length > 1)
.map(([, pkgs]) => pkgs)
.flat();
if (sharedPackages.length > 0) {
entries.push({ path: "__SHARED__", packages: sharedPackages });
}
}
return entries;
}, [packages]);
const currentGroup = grouped[tabIndex] || { path: "", packages: [] };
const handleTabChange = (direction) => {
if (direction === "prev") {
setTabIndex((prev) => Math.max(0, prev - 1));
}
else {
setTabIndex((prev) => Math.min(grouped.length - 1, prev + 1));
}
setCursor(0);
};
const toggleSelection = (pkgIndex) => {
const pkg = currentGroup.packages[pkgIndex];
const index = packages.findIndex((p) => p.name === pkg.name && p.packagePath === pkg.packagePath);
if (index !== -1) {
const updated = [...packages];
updated[index].selected = !updated[index].selected;
return updated;
}
return packages;
};
const changeVersionType = (pkgIndex, direction) => {
const pkg = currentGroup.packages[pkgIndex];
const index = packages.findIndex((p) => p.name === pkg.name && p.packagePath === pkg.packagePath);
if (index !== -1) {
const updated = [...packages];
const types = [
"patch",
"minor",
"latest",
"prerelease",
];
const typeIndex = types.indexOf(updated[index].targetVersionType);
const nextIndex = direction === "next"
? (typeIndex + 1) % types.length
: (typeIndex - 1 + types.length) % types.length;
updated[index].lastTargetVersionType = updated[index].targetVersionType;
updated[index].targetVersionType = types[nextIndex];
updated[index].displayVersion =
getDisplayVersion(updated[index]) || updated[index].currentVersion;
return updated;
}
return packages;
};
const changeGlobalVersionType = (direction) => {
if (!currentGroup || !currentGroup.packages.length) {
return packages;
}
const types = [
"patch",
"minor",
"latest",
"prerelease",
];
// Find the strategy of the currently selected package to use as reference
const referencePackage = currentGroup.packages[cursor];
const currentTypeIndex = types.indexOf(referencePackage.targetVersionType);
// Determine the next strategy in sequence
const nextTypeIndex = direction === "next"
? (currentTypeIndex + 1) % types.length
: (currentTypeIndex - 1 + types.length) % types.length;
const newStrategy = types[nextTypeIndex];
const updated = [...packages];
// Update all packages in the current group to the SAME new strategy
for (const pkg of currentGroup.packages) {
const index = updated.findIndex((p) => p.name === pkg.name && p.packagePath === pkg.packagePath);
if (index !== -1) {
updated[index].lastTargetVersionType = updated[index].targetVersionType;
updated[index].targetVersionType = newStrategy;
updated[index].displayVersion =
getDisplayVersion(updated[index]) || updated[index].currentVersion;
}
}
return updated;
};
const equalizeVersions = () => {
if (!currentGroup || !currentGroup.packages.length) {
return packages;
}
const current = currentGroup.packages[cursor];
return packages.map((pkg) => {
if (pkg.name === current.name) {
const targetVersionType = current.targetVersionType;
const displayVersion = getDisplayVersion({ ...pkg, targetVersionType }) ||
pkg.currentVersion;
return {
...pkg,
selected: true,
lastTargetVersionType: pkg.targetVersionType,
targetVersionType,
displayVersion,
};
}
return pkg;
});
};
const areVersionsEqual = (pkg) => {
return semver.eq(semver.minVersion(pkg.currentVersion || "") ?? "", semver.minVersion(pkg.displayVersion || "") ?? "");
};
const checkDivergingVersions = (packageName) => {
const allVersionsForDep = packages.filter((p) => p.name === packageName);
const versionsInUse = new Set(allVersionsForDep.map((p) => p.currentVersion));
return versionsInUse.size > 1;
};
const hasHigherUpdates = (pkg) => {
if (pkg.targetVersionType === "latest")
return false;
// If on patch and minor or latest exist, there are higher updates
if (pkg.targetVersionType === "patch") {
const hasMinorUpdate = pkg.minorVersion && pkg.minorVersion !== pkg.patchVersion;
const hasLatestUpdate = pkg.latestVersion && (!pkg.patchVersion || semver.gt(pkg.latestVersion, pkg.patchVersion));
return !!(hasMinorUpdate || hasLatestUpdate);
}
// If on minor and latest exists with higher version, there are higher updates
if (pkg.targetVersionType === "minor") {
return !!(pkg.latestVersion && (!pkg.minorVersion || semver.gt(pkg.latestVersion, pkg.minorVersion)));
}
return false;
};
return {
cursor,
setCursor,
tabIndex,
grouped,
currentGroup,
handleTabChange,
toggleSelection,
changeVersionType,
changeGlobalVersionType,
equalizeVersions,
areVersionsEqual,
checkDivergingVersions,
hasHigherUpdates,
};
}