From b70e61411ef942a555f8b4461dba10517a78353c Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Mon, 3 Feb 2025 18:28:14 -0300 Subject: [PATCH 1/5] feat(npm): Support for new option `replacementApproach` --- docs/usage/configuration-options.md | 10 +++++ lib/config/options/index.ts | 8 ++++ .../npm/update/dependency/index.spec.ts | 18 +++++++++ .../manager/npm/update/dependency/index.ts | 37 +++++++++++++------ lib/modules/manager/types.ts | 1 + 5 files changed, 62 insertions(+), 12 deletions(-) diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 042dc89b930e53..1307a9111feffc 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -3341,6 +3341,16 @@ For example to replace the npm package `jade` with version `2.0.0` of the packag } ``` +### replacementApproach + +For `npm` manager when `replacementApproach=alias` then instead of replacing `"foo": "1.2.3"` with `"@my/foo": "1.2.4"` we would instead replace it with `"foo": "npm:@my/foo@1.2.4"`. + +```json +{ + "replacementApproach": "alias" +} +``` + ### sourceDirectory Use this field to set the directory in which the package is present at the source of the package. diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index c5ad124c28110c..cc90c564b525f6 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -1465,6 +1465,14 @@ const options: RenovateOptions[] = [ cli: false, env: false, }, + { + name: 'replacementApproach', + description: 'Alter the behavior of how package renames are handled.', + type: 'string', + stage: 'branch', + allowedValues: ['replace', 'alias'], + default: 'replace', + }, { name: 'matchConfidence', description: diff --git a/lib/modules/manager/npm/update/dependency/index.spec.ts b/lib/modules/manager/npm/update/dependency/index.spec.ts index e02760aadc42e4..18d871756e6d89 100644 --- a/lib/modules/manager/npm/update/dependency/index.spec.ts +++ b/lib/modules/manager/npm/update/dependency/index.spec.ts @@ -1,5 +1,6 @@ import * as npmUpdater from '../..'; import { Fixtures } from '../../../../../../test/fixtures'; +import { type Upgrade } from '../../../types'; const readFixture = (x: string): string => Fixtures.get(x, '../..'); @@ -254,6 +255,23 @@ describe('modules/manager/npm/update/dependency/index', () => { expect(JSON.parse(testContent!).dependencies.abc).toBe('2.0.0'); }); + it('supports alias-based replacement', () => { + const upgrade: Upgrade = { + depType: 'dependencies', + depName: 'config', + newName: 'abc', + replacementApproach: 'alias', + newValue: '2.0.0', + }; + const testContent = npmUpdater.updateDependency({ + fileContent: input01Content, + upgrade, + }); + expect(JSON.parse(testContent!).dependencies.config).toBe( + 'npm:abc@2.0.0', + ); + }); + it('replaces glob package resolutions', () => { const upgrade = { depType: 'dependencies', diff --git a/lib/modules/manager/npm/update/dependency/index.ts b/lib/modules/manager/npm/update/dependency/index.ts index 0d4bb218f0205e..3f3e48f0d67d91 100644 --- a/lib/modules/manager/npm/update/dependency/index.ts +++ b/lib/modules/manager/npm/update/dependency/index.ts @@ -161,25 +161,38 @@ export function updateDependency({ } // TODO #22198 - let newFileContent = replaceAsString( - parsedContents, - fileContent, - depType as NpmDepType, - depName, - oldVersion!, - newValue!, - overrideDepParents, - ); - if (upgrade.newName) { + let newFileContent = fileContent; + if (upgrade.newName && upgrade.replacementApproach === 'alias') { newFileContent = replaceAsString( parsedContents, - newFileContent, + fileContent, depType as NpmDepType, depName, + oldVersion!, + `npm:${upgrade.newName}@${newValue}`, + overrideDepParents, + ); + } else { + newFileContent = replaceAsString( + parsedContents, + fileContent, + depType as NpmDepType, depName, - upgrade.newName, + oldVersion!, + newValue!, overrideDepParents, ); + if (upgrade.newName) { + newFileContent = replaceAsString( + parsedContents, + newFileContent, + depType as NpmDepType, + depName, + depName, + upgrade.newName, + overrideDepParents, + ); + } } // istanbul ignore if if (!newFileContent) { diff --git a/lib/modules/manager/types.ts b/lib/modules/manager/types.ts index 28ede22bda9518..3ce881369af1e5 100644 --- a/lib/modules/manager/types.ts +++ b/lib/modules/manager/types.ts @@ -192,6 +192,7 @@ export interface Upgrade> extends PackageDependency { registryUrls?: string[] | null; currentVersion?: string; replaceString?: string; + replacementApproach?: 'replace' | 'alias'; } export interface ArtifactNotice { From fb175638672e2fc86b1580e6a88eadaf84c3ccac Mon Sep 17 00:00:00 2001 From: Rhys Arkins Date: Tue, 4 Feb 2025 06:14:49 +0100 Subject: [PATCH 2/5] update --- docs/usage/configuration-options.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 1307a9111feffc..c4246ef26ce728 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -3252,6 +3252,16 @@ Here's an example of how you would define PR priority so that `devDependencies` } ``` +### replacementApproach + +For `npm` manager when `replacementApproach=alias` then instead of replacing `"foo": "1.2.3"` with `"@my/foo": "1.2.4"` we would instead replace it with `"foo": "npm:@my/foo@1.2.4"`. + +```json +{ + "replacementApproach": "alias" +} +``` + ### replacementName This config option only works with some managers. @@ -3341,16 +3351,6 @@ For example to replace the npm package `jade` with version `2.0.0` of the packag } ``` -### replacementApproach - -For `npm` manager when `replacementApproach=alias` then instead of replacing `"foo": "1.2.3"` with `"@my/foo": "1.2.4"` we would instead replace it with `"foo": "npm:@my/foo@1.2.4"`. - -```json -{ - "replacementApproach": "alias" -} -``` - ### sourceDirectory Use this field to set the directory in which the package is present at the source of the package. From 3d10cc5365e03511a2699af1c313ce65baacedf8 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Tue, 4 Feb 2025 12:47:31 -0300 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: Rhys Arkins --- lib/config/options/index.ts | 3 ++- lib/modules/manager/npm/update/dependency/index.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index cc90c564b525f6..9b9a33aed0a32a 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -1467,10 +1467,11 @@ const options: RenovateOptions[] = [ }, { name: 'replacementApproach', - description: 'Alter the behavior of how package renames are handled.', + description: 'Select whether to perform a direct replacement or alias replacement.', type: 'string', stage: 'branch', allowedValues: ['replace', 'alias'], + supportedManagers: ['npm'], default: 'replace', }, { diff --git a/lib/modules/manager/npm/update/dependency/index.ts b/lib/modules/manager/npm/update/dependency/index.ts index 3f3e48f0d67d91..c86ed856b20761 100644 --- a/lib/modules/manager/npm/update/dependency/index.ts +++ b/lib/modules/manager/npm/update/dependency/index.ts @@ -161,7 +161,7 @@ export function updateDependency({ } // TODO #22198 - let newFileContent = fileContent; + let newFileContent: string; if (upgrade.newName && upgrade.replacementApproach === 'alias') { newFileContent = replaceAsString( parsedContents, From 2c16235d29588fb4eb3a1231ee26148f18575fe3 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Tue, 4 Feb 2025 13:00:31 -0300 Subject: [PATCH 4/5] Fix doc --- docs/usage/configuration-options.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index c4246ef26ce728..987fdfa0293f1f 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -3252,16 +3252,6 @@ Here's an example of how you would define PR priority so that `devDependencies` } ``` -### replacementApproach - -For `npm` manager when `replacementApproach=alias` then instead of replacing `"foo": "1.2.3"` with `"@my/foo": "1.2.4"` we would instead replace it with `"foo": "npm:@my/foo@1.2.4"`. - -```json -{ - "replacementApproach": "alias" -} -``` - ### replacementName This config option only works with some managers. @@ -3859,6 +3849,16 @@ The field supports multiple URLs but it is datasource-dependent on whether only Add to this object if you wish to define rules that apply only to PRs that replace dependencies. +## replacementApproach + +For `npm` manager when `replacementApproach=alias` then instead of replacing `"foo": "1.2.3"` with `"@my/foo": "1.2.4"` we would instead replace it with `"foo": "npm:@my/foo@1.2.4"`. + +```json +{ + "replacementApproach": "alias" +} +``` + ## respectLatest Similar to `ignoreUnstable`, this option controls whether to update to versions that are greater than the version tagged as `latest` in the repository. From bf124abaa37eed40c71eecc775f5ff26c66e42f8 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Tue, 4 Feb 2025 13:03:17 -0300 Subject: [PATCH 5/5] Fix prettier --- lib/config/options/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index 9b9a33aed0a32a..6f20b33e45becb 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -1467,7 +1467,8 @@ const options: RenovateOptions[] = [ }, { name: 'replacementApproach', - description: 'Select whether to perform a direct replacement or alias replacement.', + description: + 'Select whether to perform a direct replacement or alias replacement.', type: 'string', stage: 'branch', allowedValues: ['replace', 'alias'],