diff --git a/lib/modules/manager/npm/__fixtures__/inputs/patch1.json b/lib/modules/manager/npm/__fixtures__/inputs/patch1.json new file mode 100644 index 0000000000000000000000000000000000000000..cecd931c74f8bed9e79f624f213b32412fb427bd --- /dev/null +++ b/lib/modules/manager/npm/__fixtures__/inputs/patch1.json @@ -0,0 +1,14 @@ +{ + "name": "renovate-repro", + "dependencies": { + "lodash": "^4.16.0" + }, + "resolutions": { + "lodash": "patch:lodash@npm:4.16.0#patches/lodash.patch" + }, + "dependenciesMeta": { + "lodash@4.16.0": { + "unplugged": true + } + } +} diff --git a/lib/modules/manager/npm/__fixtures__/inputs/patch2.json b/lib/modules/manager/npm/__fixtures__/inputs/patch2.json new file mode 100644 index 0000000000000000000000000000000000000000..48cc40edef147ab27396ec1978241171426f8641 --- /dev/null +++ b/lib/modules/manager/npm/__fixtures__/inputs/patch2.json @@ -0,0 +1,9 @@ +{ + "name": "renovate-repro", + "dependencies": { + "metro": "^0.58.0" + }, + "resolutions": { + "metro": "patch:metro@^0.58.0#./.patches/metro.patch" + } +} diff --git a/lib/modules/manager/npm/__fixtures__/outputs/patch1o.json b/lib/modules/manager/npm/__fixtures__/outputs/patch1o.json new file mode 100644 index 0000000000000000000000000000000000000000..01c60a6c2dae9dfd034201f485ef55c7fb99db7f --- /dev/null +++ b/lib/modules/manager/npm/__fixtures__/outputs/patch1o.json @@ -0,0 +1,14 @@ +{ + "name": "renovate-repro", + "dependencies": { + "lodash": "4.17.21" + }, + "resolutions": { + "lodash": "patch:lodash@npm:4.17.21#patches/lodash.patch" + }, + "dependenciesMeta": { + "lodash@4.16.0": { + "unplugged": true + } + } +} diff --git a/lib/modules/manager/npm/__fixtures__/outputs/patch2o.json b/lib/modules/manager/npm/__fixtures__/outputs/patch2o.json new file mode 100644 index 0000000000000000000000000000000000000000..250ccb545410e4b18b230224b7f78650a7d5baf8 --- /dev/null +++ b/lib/modules/manager/npm/__fixtures__/outputs/patch2o.json @@ -0,0 +1,9 @@ +{ + "name": "renovate-repro", + "dependencies": { + "metro": "^0.60.0" + }, + "resolutions": { + "metro": "patch:metro@^0.60.0#./.patches/metro.patch" + } +} diff --git a/lib/modules/manager/npm/update/dependency/index.spec.ts b/lib/modules/manager/npm/update/dependency/index.spec.ts index 080a61b6089923fafc4d334909d93bdf76425a10..78d19e20de161345fed6fc8ed616ae9954c06d5e 100644 --- a/lib/modules/manager/npm/update/dependency/index.spec.ts +++ b/lib/modules/manager/npm/update/dependency/index.spec.ts @@ -257,5 +257,31 @@ describe('modules/manager/npm/update/dependency/index', () => { expect(JSON.parse(testContent).resolutions.config).toBeUndefined(); expect(JSON.parse(testContent).resolutions['**/abc']).toBe('2.0.0'); }); + it('pins also the version in patch with npm protocol in resolutions', () => { + const upgrade = { + depType: 'dependencies', + depName: 'lodash', + newValue: '4.17.21', + }; + const outputContent = readFixture('outputs/patch1o.json'); + const testContent = npmUpdater.updateDependency({ + fileContent: readFixture('inputs/patch1.json'), + upgrade, + }); + expect(testContent).toEqual(outputContent); + }); + it('replaces also the version in patch with range in resolutions', () => { + const upgrade = { + depType: 'dependencies', + depName: 'metro', + newValue: '^0.60.0', + }; + const outputContent = readFixture('outputs/patch2o.json'); + const testContent = npmUpdater.updateDependency({ + fileContent: readFixture('inputs/patch2.json'), + upgrade, + }); + expect(testContent).toEqual(outputContent); + }); }); }); diff --git a/lib/modules/manager/npm/update/dependency/index.ts b/lib/modules/manager/npm/update/dependency/index.ts index 9de7e2d6e995a902658e327eec8cf840666239b1..4b411922005898e39d7150eea4f949debe2c1077 100644 --- a/lib/modules/manager/npm/update/dependency/index.ts +++ b/lib/modules/manager/npm/update/dependency/index.ts @@ -1,6 +1,7 @@ import { dequal } from 'dequal'; import type { PackageJson } from 'type-fest'; import { logger } from '../../../../../logger'; +import { escapeRegExp, regEx } from '../../../../../util/regex'; import { matchAt, replaceAt } from '../../../../../util/string'; import type { UpdateDependencyConfig } from '../../../types'; @@ -25,7 +26,17 @@ function replaceAsString( } // Look for the old version number const searchString = `"${oldValue}"`; - const newString = `"${newValue}"`; + let newString = `"${newValue}"`; + + const escapedDepName = escapeRegExp(depName); + const patchRe = regEx(`^(patch:${escapedDepName}@(npm:)?).*#`); + const match = patchRe.exec(oldValue); + if (match) { + const patch = oldValue.replace(match[0], `${match[1]}${newValue}#`); + parsedContents[depType][depName] = patch; + newString = `"${patch}"`; + } + // Skip ahead to depType section let searchIndex = fileContent.indexOf(`"${depType}"`) + depType.length; logger.trace(`Starting search at index ${searchIndex}`); @@ -91,6 +102,7 @@ export function updateDependency({ logger.trace('Version is already updated'); return fileContent; } + let newFileContent = replaceAsString( parsedContents, fileContent,