diff --git a/docs/generated/devkit/addDependenciesToPackageJson.md b/docs/generated/devkit/addDependenciesToPackageJson.md index 5e13d71a7a7108..d881ee458edf58 100644 --- a/docs/generated/devkit/addDependenciesToPackageJson.md +++ b/docs/generated/devkit/addDependenciesToPackageJson.md @@ -1,6 +1,6 @@ # Function: addDependenciesToPackageJson -▸ **addDependenciesToPackageJson**(`tree`, `dependencies`, `devDependencies`, `packageJsonPath?`): [`GeneratorCallback`](../../devkit/documents/GeneratorCallback) +▸ **addDependenciesToPackageJson**(`tree`, `dependencies`, `devDependencies`, `packageJsonPath?`, `keepExistingVersions?`): [`GeneratorCallback`](../../devkit/documents/GeneratorCallback) Add Dependencies and Dev Dependencies to package.json @@ -14,12 +14,13 @@ This will **add** `react` and `jest` to the dependencies and devDependencies sec #### Parameters -| Name | Type | Description | -| :----------------- | :------------------------------------ | :---------------------------------------------------------------------- | -| `tree` | [`Tree`](../../devkit/documents/Tree) | Tree representing file system to modify | -| `dependencies` | `Record`\<`string`, `string`\> | Dependencies to be added to the dependencies section of package.json | -| `devDependencies` | `Record`\<`string`, `string`\> | Dependencies to be added to the devDependencies section of package.json | -| `packageJsonPath?` | `string` | Path to package.json | +| Name | Type | Description | +| :---------------------- | :------------------------------------ | :-------------------------------------------------------------------------- | +| `tree` | [`Tree`](../../devkit/documents/Tree) | Tree representing file system to modify | +| `dependencies` | `Record`\<`string`, `string`\> | Dependencies to be added to the dependencies section of package.json | +| `devDependencies` | `Record`\<`string`, `string`\> | Dependencies to be added to the devDependencies section of package.json | +| `packageJsonPath?` | `string` | Path to package.json | +| `keepExistingVersions?` | `boolean` | If true, prevents existing dependencies from being bumped to newer versions | #### Returns diff --git a/packages/devkit/src/utils/package-json.spec.ts b/packages/devkit/src/utils/package-json.spec.ts index 607df2bec761a2..2ec5f7d36cc07c 100644 --- a/packages/devkit/src/utils/package-json.spec.ts +++ b/packages/devkit/src/utils/package-json.spec.ts @@ -446,6 +446,29 @@ describe('addDependenciesToPackageJson', () => { }); expect(installTask).toBeDefined(); }); + + it('should allow existing versions to be kept', () => { + writeJson(tree, 'package.json', { + dependencies: { + foo: '1.0.0', + }, + }); + + addDependenciesToPackageJson( + tree, + { + foo: '2.0.0', + }, + {}, + undefined, + true + ); + + const result = readJson(tree, 'package.json'); + expect(result.dependencies).toEqual({ + foo: '1.0.0', + }); + }); }); describe('ensurePackage', () => { diff --git a/packages/devkit/src/utils/package-json.ts b/packages/devkit/src/utils/package-json.ts index 0a2f7d4d550036..77811b0eb72379 100644 --- a/packages/devkit/src/utils/package-json.ts +++ b/packages/devkit/src/utils/package-json.ts @@ -132,13 +132,15 @@ function updateExistingDependenciesVersion( * @param dependencies Dependencies to be added to the dependencies section of package.json * @param devDependencies Dependencies to be added to the devDependencies section of package.json * @param packageJsonPath Path to package.json + * @param keepExistingVersions If true, prevents existing dependencies from being bumped to newer versions * @returns Callback to install dependencies only if necessary, no-op otherwise */ export function addDependenciesToPackageJson( tree: Tree, dependencies: Record, devDependencies: Record, - packageJsonPath: string = 'package.json' + packageJsonPath: string = 'package.json', + keepExistingVersions?: boolean ): GeneratorCallback { const currentPackageJson = readJson(tree, packageJsonPath); @@ -155,6 +157,7 @@ export function addDependenciesToPackageJson( // filtered dependencies should consist of: // - dependencies of the same type that are not present + // by default, filtered dependencies also include these (unless keepExistingVersions is true): // - dependencies of the same type that have greater version // - specified dependencies of the other type that have greater version and are already installed as current type filteredDependencies = { @@ -178,14 +181,25 @@ export function addDependenciesToPackageJson( ), }; - filteredDependencies = removeLowerVersions( - filteredDependencies, - currentPackageJson.dependencies - ); - filteredDevDependencies = removeLowerVersions( - filteredDevDependencies, - currentPackageJson.devDependencies - ); + if (keepExistingVersions) { + filteredDependencies = removeExistingDependencies( + filteredDependencies, + currentPackageJson.dependencies + ); + filteredDevDependencies = removeExistingDependencies( + filteredDevDependencies, + currentPackageJson.devDependencies + ); + } else { + filteredDependencies = removeLowerVersions( + filteredDependencies, + currentPackageJson.dependencies + ); + filteredDevDependencies = removeLowerVersions( + filteredDevDependencies, + currentPackageJson.devDependencies + ); + } if ( requiresAddingOfPackages( @@ -227,12 +241,24 @@ function removeLowerVersions( ) { return Object.keys(incomingDeps).reduce((acc, d) => { if ( - existingDeps?.[d] && - !isIncomingVersionGreater(incomingDeps[d], existingDeps[d]) + !existingDeps?.[d] || + isIncomingVersionGreater(incomingDeps[d], existingDeps[d]) ) { - return acc; + acc[d] = incomingDeps[d]; + } + return acc; + }, {}); +} + +function removeExistingDependencies( + incomingDeps: Record, + existingDeps: Record +): Record { + return Object.keys(incomingDeps).reduce((acc, d) => { + if (!existingDeps?.[d]) { + acc[d] = incomingDeps[d]; } - return { ...acc, [d]: incomingDeps[d] }; + return acc; }, {}); }