renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
162 lines (161 loc) • 6.88 kB
JavaScript
import { logger } from "../../../../../../logger/index.js";
import api from "../../../../../versioning/npm/index.js";
import { updateDependency } from "../../dependency/index.js";
import { findFirstParentVersion } from "../common/parent-version.js";
import { findDepConstraints } from "./dep-constraints.js";
import { getLockedDependencies } from "./get-locked.js";
import detectIndent from "detect-indent";
//#region lib/modules/manager/npm/update/locked-dependency/package-lock/index.ts
async function updateLockedDependency(config, isParentUpdate = false) {
const { depName, currentVersion, newVersion, packageFile, packageFileContent, lockFile, lockFileContent, allowParentUpdates = true, allowHigherOrRemoved = false } = config;
logger.debug(`npm.updateLockedDependency: ${depName}@${currentVersion} -> ${newVersion} [${lockFile}]`);
try {
let packageJson;
let packageLockJson;
const detectedIndent = detectIndent(lockFileContent).indent || " ";
let newPackageJsonContent;
try {
packageJson = JSON.parse(packageFileContent);
packageLockJson = JSON.parse(lockFileContent);
} catch (err) {
logger.warn({ err }, "Failed to parse files");
return { status: "update-failed" };
}
const { lockfileVersion } = packageLockJson;
const lockedDeps = getLockedDependencies(packageLockJson, depName, currentVersion);
if (lockedDeps.some((dep) => dep.bundled)) {
logger.debug(`Package ${depName}@${currentVersion} is bundled and cannot be updated`);
return { status: "update-failed" };
}
if (!lockedDeps.length) {
const newLockedDeps = getLockedDependencies(packageLockJson, depName, newVersion);
let status;
if (newLockedDeps.length) {
logger.debug(`${depName}@${currentVersion} not found in ${lockFile} but ${depName}@${newVersion} was - looks like it's already updated`);
status = "already-updated";
} else if (lockfileVersion !== 1) {
logger.debug(`Found lockfileVersion ${packageLockJson.lockfileVersion}`);
status = "update-failed";
} else if (allowHigherOrRemoved) {
const anyVersionLocked = getLockedDependencies(packageLockJson, depName, null);
if (anyVersionLocked.length) if (anyVersionLocked.every((dep) => api.isGreaterThan(dep.version, newVersion))) {
logger.debug(`${depName} found in ${lockFile} with higher version - looks like it's already updated`);
status = "already-updated";
} else {
logger.debug({ anyVersionLocked }, `Found alternative versions of qs`);
status = "update-failed";
}
else {
logger.debug(`${depName} not found in ${lockFile} - looks like it's already removed`);
status = "already-updated";
}
} else {
logger.debug(`${depName}@${currentVersion} not found in ${lockFile} - cannot update`);
status = "update-failed";
}
/* v8 ignore next -- too hard to replicate */
if (isParentUpdate) {
const files = {};
files[packageFile] = packageFileContent;
files[lockFile] = lockFileContent;
return {
status,
files
};
}
return { status };
}
logger.debug(`Found matching dependencies with length ${lockedDeps.length}`);
const constraints = findDepConstraints(packageJson, packageLockJson, depName, currentVersion, newVersion);
logger.trace({
deps: lockedDeps,
constraints
}, "Matching details");
if (!constraints.length) {
logger.info({
depName,
currentVersion,
newVersion
}, "Could not find constraints for the locked dependency - cannot remediate");
return { status: "update-failed" };
}
const parentUpdates = [];
for (const { parentDepName, parentVersion, constraint, depType } of constraints)
// v8 ignore else -- TODO: add test #40625
if (api.matches(newVersion, constraint)) logger.debug(`${depName} can be updated to ${newVersion} in-range with matching constraint "${constraint}" in ${parentDepName ? `${parentDepName}@${parentVersion}` : packageFile}`);
else if (parentDepName && parentVersion) {
if (!allowParentUpdates) {
logger.debug(`Cannot update ${depName} to ${newVersion} without an update to ${parentDepName}`);
return { status: "update-failed" };
}
const parentNewVersion = await findFirstParentVersion(parentDepName, parentVersion, depName, newVersion);
if (parentNewVersion) if (parentNewVersion === parentVersion) logger.debug(`Update of ${depName} to ${newVersion} already achieved in parent ${parentDepName}@${parentNewVersion}`);
else {
logger.debug(`Update of ${depName} to ${newVersion} can be achieved due to parent ${parentDepName}`);
const parentUpdate = {
depName: parentDepName,
currentVersion: parentVersion,
newVersion: parentNewVersion
};
parentUpdates.push(parentUpdate);
}
else {
logger.debug(`Update of ${depName} to ${newVersion} cannot be achieved due to parent ${parentDepName}`);
return { status: "update-failed" };
}
} else if (depType) newPackageJsonContent = updateDependency({
fileContent: packageFileContent,
packageFile,
upgrade: {
depName,
depType,
newValue: api.getNewValue({
currentValue: constraint,
rangeStrategy: "replace",
currentVersion,
newVersion
})
}
});
for (const dependency of lockedDeps) {
dependency.version = newVersion;
delete dependency.resolved;
delete dependency.integrity;
}
let newLockFileContent = JSON.stringify(packageLockJson, null, detectedIndent);
for (const parentUpdate of parentUpdates) {
const parentUpdateResult = await updateLockedDependency({
...config,
...parentUpdate,
lockFileContent: newLockFileContent,
packageFileContent: newPackageJsonContent ?? packageFileContent
}, true);
/* v8 ignore next -- hard to test due to recursion */
if (!parentUpdateResult.files) {
logger.debug(`Update of ${depName} to ${newVersion} impossible due to failed update of parent ${parentUpdate.depName} to ${parentUpdate.newVersion}`);
return { status: "update-failed" };
}
newPackageJsonContent = parentUpdateResult.files[packageFile] || newPackageJsonContent;
/* v8 ignore next 2 -- hard to test */
newLockFileContent = parentUpdateResult.files[lockFile] || newLockFileContent;
}
const files = {};
// v8 ignore else -- TODO: add test #40625
if (newLockFileContent) files[lockFile] = newLockFileContent;
if (newPackageJsonContent) files[packageFile] = newPackageJsonContent;
else if (lockfileVersion !== 1) {
logger.debug("Remediations which change package-lock.json only are not supported unless lockfileVersion=1");
return { status: "unsupported" };
}
return {
status: "updated",
files
};
} catch (err) /* v8 ignore next -- TODO: add test #40625 */ {
logger.error({ err }, "updateLockedDependency() error");
return { status: "update-failed" };
}
}
//#endregion
export { updateLockedDependency };
//# sourceMappingURL=index.js.map