renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
193 lines • 10.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateLockedDependency = updateLockedDependency;
const tslib_1 = require("tslib");
const detect_indent_1 = tslib_1.__importDefault(require("detect-indent"));
const logger_1 = require("../../../../../../logger");
const npm_1 = require("../../../../../versioning/npm");
const dependency_1 = require("../../dependency");
const parent_version_1 = require("../common/parent-version");
const dep_constraints_1 = require("./dep-constraints");
const get_locked_1 = require("./get-locked");
async function updateLockedDependency(config, isParentUpdate = false) {
const { depName, currentVersion, newVersion, packageFile, packageFileContent, lockFile, lockFileContent, allowParentUpdates = true, allowHigherOrRemoved = false, } = config;
logger_1.logger.debug(`npm.updateLockedDependency: ${depName}@${currentVersion} -> ${newVersion} [${lockFile}]`);
try {
let packageJson;
let packageLockJson;
// TODO #22198
const detectedIndent = (0, detect_indent_1.default)(lockFileContent).indent || ' ';
let newPackageJsonContent;
try {
// TODO #22198
packageJson = JSON.parse(packageFileContent);
packageLockJson = JSON.parse(lockFileContent);
}
catch (err) {
logger_1.logger.warn({ err }, 'Failed to parse files');
return { status: 'update-failed' };
}
const { lockfileVersion } = packageLockJson;
const lockedDeps = (0, get_locked_1.getLockedDependencies)(packageLockJson, depName, currentVersion);
if (lockedDeps.some((dep) => dep.bundled)) {
logger_1.logger.debug(`Package ${depName}@${currentVersion} is bundled and cannot be updated`);
return { status: 'update-failed' };
}
if (!lockedDeps.length) {
const newLockedDeps = (0, get_locked_1.getLockedDependencies)(packageLockJson, depName, newVersion);
let status;
if (newLockedDeps.length) {
logger_1.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_1.logger.debug(
// TODO: types (#22198)
`Found lockfileVersion ${packageLockJson.lockfileVersion}`);
status = 'update-failed';
}
else if (allowHigherOrRemoved) {
// it's acceptable if the package is no longer present
const anyVersionLocked = (0, get_locked_1.getLockedDependencies)(packageLockJson, depName, null);
if (anyVersionLocked.length) {
if (anyVersionLocked.every((dep) => npm_1.api.isGreaterThan(dep.version, newVersion))) {
logger_1.logger.debug(`${depName} found in ${lockFile} with higher version - looks like it's already updated`);
status = 'already-updated';
}
else {
logger_1.logger.debug({ anyVersionLocked }, `Found alternative versions of qs`);
status = 'update-failed';
}
}
else {
logger_1.logger.debug(`${depName} not found in ${lockFile} - looks like it's already removed`);
status = 'already-updated';
}
}
else {
logger_1.logger.debug(`${depName}@${currentVersion} not found in ${lockFile} - cannot update`);
status = 'update-failed';
}
}
// Don't return {} if we're a parent update or else the whole update will fail
/* v8 ignore start -- too hard to replicate */
if (isParentUpdate) {
const files = {};
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
files[packageFile] = packageFileContent;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
files[lockFile] = lockFileContent;
return { status, files };
} /* v8 ignore stop -- too hard to replicate */
return { status };
}
logger_1.logger.debug(`Found matching dependencies with length ${lockedDeps.length}`);
const constraints = (0, dep_constraints_1.findDepConstraints)(packageJson, packageLockJson, depName, currentVersion, newVersion);
logger_1.logger.trace({ deps: lockedDeps, constraints }, 'Matching details');
if (!constraints.length) {
logger_1.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) {
if (npm_1.api.matches(newVersion, constraint)) {
// Parent dependency is compatible with the new version we want
logger_1.logger.debug(`${depName} can be updated to ${newVersion} in-range with matching constraint "${constraint}" in ${
// TODO: types (#22198)
/* v8 ignore next -- hard to test */
parentDepName ? `${parentDepName}@${parentVersion}` : packageFile}`);
}
else if (parentDepName && parentVersion) {
if (!allowParentUpdates) {
logger_1.logger.debug(`Cannot update ${depName} to ${newVersion} without an update to ${parentDepName}`);
return { status: 'update-failed' };
}
// Parent dependency needs updating too
const parentNewVersion = await (0, parent_version_1.findFirstParentVersion)(parentDepName, parentVersion, depName, newVersion);
if (parentNewVersion) {
if (parentNewVersion === parentVersion) {
logger_1.logger.debug(`Update of ${depName} to ${newVersion} already achieved in parent ${parentDepName}@${parentNewVersion}`);
}
else {
// Update the parent dependency so that we can update this dependency
logger_1.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 {
// For some reason it's not possible to update the parent to a version compatible with our desired dep version
logger_1.logger.debug(`Update of ${depName} to ${newVersion} cannot be achieved due to parent ${parentDepName}`);
return { status: 'update-failed' };
}
}
else if (depType) {
// TODO: `newValue` can probably null
// The constraint comes from the package.json file, so we need to update it
const newValue = npm_1.api.getNewValue({
currentValue: constraint,
rangeStrategy: 'replace',
currentVersion,
newVersion,
});
newPackageJsonContent = (0, dependency_1.updateDependency)({
// TODO #22198
fileContent: packageFileContent,
upgrade: { depName, depType, newValue },
});
}
}
for (const dependency of lockedDeps) {
// Remove resolved and integrity fields for npm to fill in
dependency.version = newVersion;
delete dependency.resolved;
delete dependency.integrity;
}
let newLockFileContent = JSON.stringify(packageLockJson, null, detectedIndent);
// iterate through the parent updates first
for (const parentUpdate of parentUpdates) {
const parentUpdateConfig = {
...config,
...parentUpdate,
lockFileContent: newLockFileContent,
packageFileContent: newPackageJsonContent ?? packageFileContent,
};
const parentUpdateResult = await updateLockedDependency(parentUpdateConfig, true);
/* v8 ignore start -- hard to test due to recursion */
if (!parentUpdateResult.files) {
logger_1.logger.debug(
// TODO: types (#22198)
`Update of ${depName} to ${newVersion} impossible due to failed update of parent ${parentUpdate.depName} to ${parentUpdate.newVersion}`);
return { status: 'update-failed' };
} /* v8 ignore stop -- needs test */
newPackageJsonContent =
parentUpdateResult.files[packageFile] || newPackageJsonContent;
/* v8 ignore next 2 -- hard to test */
newLockFileContent =
parentUpdateResult.files[lockFile] || newLockFileContent;
}
const files = {};
if (newLockFileContent) {
files[lockFile] = newLockFileContent;
}
if (newPackageJsonContent) {
files[packageFile] = newPackageJsonContent;
}
else if (lockfileVersion !== 1) {
logger_1.logger.debug('Remediations which change package-lock.json only are not supported unless lockfileVersion=1');
return { status: 'unsupported' };
}
return { status: 'updated', files };
/* v8 ignore start -- needs test */
}
catch (err) {
logger_1.logger.error({ err }, 'updateLockedDependency() error');
return { status: 'update-failed' };
} /* v8 ignore stop -- needs test */
}
//# sourceMappingURL=index.js.map