Search code examples
angularnpm

npm unable to resolve dependency tree untill deletion of node_modules


try to understand an error report of npm and fail to find the problem - surprisingly the deletion of the node_modules directory fixed it. We were in the process to update angular to 18+ and nx to 19+.

Found: @angular-devkit/[email protected]
node_modules/@angular-devkit/build-angular
  dev @angular-devkit/build-angular@"18.0.4" from the root project
  peer @angular-devkit/build-angular@">= 15.0.0 < 18.0.0" from @nx/[email protected]
  node_modules/@nx/angular
    @nx/angular@"19.3.0" from the root project
    @nx/angular@"18.3.4" from @nrwl/[email protected]
    node_modules/@nrwl/angular
      @nrwl/angular@"18.3.4" from @nx/[email protected]
  peer @angular-devkit/build-angular@">=15.0.0 <18.0.0" from [email protected]
  node_modules/jest-preset-angular
    dev jest-preset-angular@"14.1.1" from the root project

Could not resolve dependency:
dev @angular-devkit/build-angular@"18.0.4" from the root project

Conflicting peer dependency: @angular/[email protected]
node_modules/@angular/compiler-cli
  peer @angular/compiler-cli@"^18.0.0" from @angular-devkit/[email protected]
  node_modules/@angular-devkit/build-angular
    dev @angular-devkit/build-angular@"18.0.4" from the root project

How i understand the last part:

  1. @angular-devkit/[email protected] found in package.json
  2. package has peer dependency to @angular/compiler-cli@^18.0.0
  3. failed to resolve @angular/[email protected] (which is also in package.json)

Why does it fail to resolve @angular/[email protected] when its clearly a valid version ?

How i understand the first part:

  1. [email protected] found in package.json
  2. version 14.0.3 of this package has peer dependency to @angular-devkit/build-angular@>=15.0.0 <18.0.0
  3. (resolving errors based on wrong version range)

Why does npm use the old version of jest-preset-angular and nx/angular for resolving ? it seems like it ignore versions specified in the package.json and use the current installed.

(deleting nod_modules to fix an npm dependency problem seem like a bug in npm itself to me)

package.json

"dependencies": {
    "@abp/ng.components": "8.1.3",
    "@abp/ng.core": "8.1.3",
    "@abp/ng.oauth": "8.1.3",
    "@abp/ng.theme.shared": "8.1.3",
    "@angular/animations": "18.0.3",
    "@angular/common": "18.0.3",
    "@angular/compiler": "18.0.3",
    "@angular/core": "18.0.3",
    "@angular/forms": "18.0.3",
    "@angular/localize": "18.0.3",
    "@angular/platform-browser": "18.0.3",
    "@angular/platform-browser-dynamic": "18.0.3",
    "@angular/router": "18.0.3",
    "@ngrx/effects": "18.0.0",
    "@ngrx/store": "18.0.0",
    "@ngrx/operators": "18.0.0",
    "@nx/angular": "19.3.0",
    "@puschie286/golden-layout": "^2.6.2",
    "@types/lodash-es": "^4.17.12",
    "angular-oauth2-oidc": "^17.0.2",
    "bootstrap-icons": "^1.11.3",
    "lodash-es": "^4.17.21",
    "primeicons": "^7.0.0",
    "primeng": "^17.18.1",
    "primeng-locale": "github:primefaces/primelocale",
    "primeng-sass-theme": "github:primefaces/primeng-sass-theme",
    "rxjs": "~7.8.1",
    "ts-debounce": "^4.0.0",
    "tslib": "^2.6.2",
    "zone.js": "0.14.4"
  },
  "devDependencies": {
    "@abp/ng.schematics": "^8.1.3",
    "@abp/nx.generators": "^8.1.3",
    "@angular-devkit/build-angular": "18.0.4",
    "@angular-devkit/core": "18.0.4",
    "@angular-devkit/schematics": "18.0.4",
    "@angular-eslint/eslint-plugin": "18.0.1",
    "@angular-eslint/eslint-plugin-template": "18.0.1",
    "@angular-eslint/template-parser": "18.0.1",
    "@angular/cli": "~18.0.0",
    "@angular/compiler-cli": "18.0.3",
    "@angular/language-service": "18.0.3",
    "@ngrx/eslint-plugin": "18.0.0",
    "@ngrx/schematics": "18.0.0",
    "@ngrx/store-devtools": "18.0.0",
    "@nx-dotnet/core": "^2.2.0",
    "@nx/cypress": "19.3.0",
    "@nx/eslint": "19.3.0",
    "@nx/eslint-plugin": "19.3.0",
    "@nx/jest": "19.3.0",
    "@nx/js": "19.3.0",
    "@nx/plugin": "19.3.0",
    "@nx/web": "19.3.0",
    "@nx/workspace": "19.3.0",
    "@schematics/angular": "18.0.4",
    "@swc-node/register": "1.9.2",
    "@swc/core": "1.5.7",
    "@types/jest": "^29.5.12",
    "@types/node": "^20.11.17",
    "@typescript-eslint/eslint-plugin": "7.3.0",
    "@typescript-eslint/parser": "7.3.0",
    "@typescript-eslint/utils": "^8.0.0-alpha.28",
    "autoprefixer": "^10.4.17",
    "cypress": "13.8.1",
    "daisyui": "^4.6.3",
    "eslint": "8.57.0",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-cypress": "^2.15.1",
    "jasmine-marbles": "~0.9.2",
    "jest": "^29.7.0",
    "jest-environment-jsdom": "^29.7.0",
    "jest-preset-angular": "14.1.1",
    "jsonc-eslint-parser": "^2.4.0",
    "nx": "19.3.0",
    "postcss": "^8.4.38",
    "prettier": "^3.2.5",
    "tailwindcss": "^3.4.3",
    "ts-jest": "^29.1.2",
    "ts-node": "10.9.2",
    "typescript": "5.4.3"
  }

Solution

  • @nx/[email protected] and [email protected] both specify that they need a @angular-devkit/build-angular that is >= 15.0.0 < 18.0.0.

    Unfortunately, this range does not include any version of 18.x.x. The latest version matching that range is 17.3.8 according to the NPM semver calculator.

    For @nx/angular it looks like this was fixed in https://github.com/nrwl/nx/pull/22509. For jest-preset-angular it was fixed in https://github.com/thymikee/jest-preset-angular/blob/main/CHANGELOG.md#1410-2024-05-21.

    It uses the old versions because you likely have a package-lock.json which is storing the transitive versions of these dependencies from a previous install. This is by design and best practice -- but it requires some extra work to manage in complex upgrade scenarios. Please see package-lock.json.

    With the latest NPM, you need to do something like npm update --save @nx/angular jest-preset-angular.

    And ensure the resulting changes to the package-lock.json and (possibly, this will only change if they were direct deps) package.json are committed. Make sure in CI that it fails the build if someone commits a change that should have caused a package-lock.json update, but it was not included. Practically that means in CI you use npm ci and never npm install.

    And of course, never commit node_modules.