diff --git a/packages/devkit/src/utils/package-json.spec.ts b/packages/devkit/src/utils/package-json.spec.ts index 530f5ed061..e1efe1171d 100644 --- a/packages/devkit/src/utils/package-json.spec.ts +++ b/packages/devkit/src/utils/package-json.spec.ts @@ -75,4 +75,72 @@ describe('addDependenciesToPackageJson', () => { }); expect(installTask).toBeDefined(); }); + + it('should not add dependencies when they exist in devDependencies or vice versa', () => { + // ARRANGE + writeJson(tree, 'package.json', { + dependencies: { + '@nrwl/angular': 'latest', + }, + devDependencies: { + '@nrwl/next': 'latest', + }, + }); + + // ACT + const installTask = addDependenciesToPackageJson( + tree, + { + '@nrwl/next': 'next', + }, + { + '@nrwl/angular': 'next', + } + ); + + // ASSERT + const { dependencies, devDependencies } = readJson(tree, 'package.json'); + expect(dependencies).toEqual({ + '@nrwl/angular': 'latest', + }); + expect(devDependencies).toEqual({ + '@nrwl/next': 'latest', + }); + expect(installTask).toBeDefined(); + }); + + it('should add additional dependencies when they dont exist in devDependencies or vice versa and not update the ones that do exist', () => { + // ARRANGE + writeJson(tree, 'package.json', { + dependencies: { + '@nrwl/angular': 'latest', + }, + devDependencies: { + '@nrwl/next': 'latest', + }, + }); + + // ACT + const installTask = addDependenciesToPackageJson( + tree, + { + '@nrwl/next': 'next', + '@nrwl/cypress': 'latest', + }, + { + '@nrwl/angular': 'next', + } + ); + + // ASSERT + const { dependencies, devDependencies } = readJson(tree, 'package.json'); + expect(dependencies).toEqual({ + '@nrwl/angular': 'latest', + '@nrwl/cypress': 'latest', + }); + expect(devDependencies).toEqual({ + '@nrwl/next': 'latest', + }); + expect(installTask).toBeDefined(); + }); }); diff --git a/packages/devkit/src/utils/package-json.ts b/packages/devkit/src/utils/package-json.ts index 797f212443..58326f0c3b 100644 --- a/packages/devkit/src/utils/package-json.ts +++ b/packages/devkit/src/utils/package-json.ts @@ -3,6 +3,19 @@ import { installPackagesTask } from '../tasks/install-packages-task'; import type { Tree } from 'nx/src/generators/tree'; import { GeneratorCallback } from 'nx/src/config/misc-interfaces'; +function filterExistingDependencies( + dependencies: Record, + existingDependencies: Record +) { + if (!existingDependencies) { + return dependencies; + } + + return Object.keys(dependencies ?? {}) + .filter((d) => !existingDependencies[d]) + .reduce((acc, d) => ({ ...acc, [d]: dependencies[d] }), {}); +} + /** * Add Dependencies and Dev Dependencies to package.json * @@ -26,18 +39,31 @@ export function addDependenciesToPackageJson( ): GeneratorCallback { const currentPackageJson = readJson(tree, packageJsonPath); + const filteredDependencies = filterExistingDependencies( + dependencies, + currentPackageJson.devDependencies + ); + const filteredDevDependencies = filterExistingDependencies( + devDependencies, + currentPackageJson.dependencies + ); + if ( - requiresAddingOfPackages(currentPackageJson, dependencies, devDependencies) + requiresAddingOfPackages( + currentPackageJson, + filteredDependencies, + filteredDevDependencies + ) ) { updateJson(tree, packageJsonPath, (json) => { json.dependencies = { ...(json.dependencies || {}), - ...dependencies, + ...filteredDependencies, ...(json.dependencies || {}), }; json.devDependencies = { ...(json.devDependencies || {}), - ...devDependencies, + ...filteredDevDependencies, ...(json.devDependencies || {}), }; json.dependencies = sortObjectByKeys(json.dependencies);