angelogiuseppe / force-resolutions

This packages modifies package-lock.json to force the installation of specific version of a transitive dependency
MIT License
26 stars 3 forks source link

Support for multiple versions of same package #5

Open MBDesu opened 1 year ago

MBDesu commented 1 year ago

Essentially the same request as rogeriochaves/npm-force-resolutions#32, it would be very useful in projects with multiple versions of the same transient dependency to be able to independently define which version these should be updated to.

As mentioned in the thread for the aforementioned npm-force-resolutions issue, GHSA-3rfm-jhwj-7488 and GHSA-76p3-8jx3-jpfq are good use cases for such a feature.

Consider the following package.json:

{
  "name": "app",
  "version": "0.0.1",
  "sideEffects": false,
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build"
  }
  "dependencies": {
    "@angular/animations": "~13.2.0",
    "@angular/cdk": "~13.3.1",
    "@angular/common": "~13.2.0",
    "@angular/compiler": "~13.2.0",
    "@angular/core": "~13.2.0",
    "@angular/flex-layout": "^13.0.0-beta.38",
    "@angular/forms": "~13.2.0",
    "@angular/material": "~13.3.1",
    "@angular/platform-browser": "~13.2.0",
    "@angular/platform-browser-dynamic": "~13.2.0",
    "@angular/router": "~13.2.0",
    "@internal/library": "1.0.0",
    "angular-oauth2-oidc": "^12.1.0",
    "chart.js": "^3.7.1",
    "file-saver": "^2.0.5",
    "ng2-charts": "^3.0.9",
    "ngx-mask": "^13.1.12",
    "rxjs": "~7.5.0",
    "sass": "^1.53.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.11.4"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~13.2.2",
    "@angular-eslint/builder": "13.1.0",
    "@angular-eslint/eslint-plugin": "13.1.0",
    "@angular-eslint/eslint-plugin-template": "13.1.0",
    "@angular-eslint/schematics": "13.1.0",
    "@angular-eslint/template-parser": "13.1.0",
    "@angular/cli": "~13.2.2",
    "@angular/compiler-cli": "~13.2.0",
    "@types/file-saver": "^2.0.5",
    "@types/jasmine": "~3.10.0",
    "@types/node": "^12.11.1",
    "@typescript-eslint/eslint-plugin": "5.11.0",
    "@typescript-eslint/parser": "5.11.0",
    "compression-webpack-plugin": "^10.0.0",
    "eslint": "^8.2.0",
    "jasmine-core": "~4.0.0",
    "karma": "~6.3.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "~2.1.0",
    "karma-jasmine": "~4.0.0",
    "karma-jasmine-html-reporter": "~1.7.0",
    "ngx-build-plus": "^13.0.1",
    "typescript": "~4.5.2"
  }
}

This application is using Angular 13, which, to my knowledge, relies on versions of nx which requires npm 8.1.2, making using npm's native overrides out of the question (as overrides were introduced in 8.3).

Additionally, for the sake of this example, it is using an internal, closed-source package called @internal/library.

Looking at the dependency tree, you can see several versions of loader-utils are transitively required:

app@0.0.1
├─┬ @angular-devkit/build-angular@13.2.2
│ ├─┬ babel-loader@8.2.3
│ │ └── loader-utils@1.4.2
│ ├── loader-utils@3.2.0
│ └─┬ resolve-url-loader@5.0.0
│   ├─┬ adjust-sourcemap-loader@4.0.0
│   │ └── loader-utils@2.0.4
│   └── loader-utils@2.0.4
└─┬ @internal/library@1.0.0
  └─┬ @nrwl/angular@14.8.6
    ├─┬ @nrwl/cypress@14.8.6
    │ └─┬ @cypress/webpack-preprocessor@5.15.5
    │   └── loader-utils@2.0.4
    └─┬ @nrwl/webpack@14.8.6
      ├─┬ file-loader@6.2.0
      │ └── loader-utils@2.0.4
      ├── loader-utils@1.2.3
      └─┬ raw-loader@4.0.2
        └── loader-utils@2.0.4

In this case, loader-utils 1.2.3 (a vulnerable version) is a transitive dependency of @internal/library.

For the sake of remediating GHSA-3rfm-jhwj-7488 and/or GHSA-76p3-8jx3-jpfq in this scenario, it would be useful to be able to only update loader-utils 1.2.3 to 1.4.2. Updating all instances of loader-utils to 1.4.2, 2.0.4, or 3.2.0 (versions without vulnerabilities) may introduce breaking changes across major versions, and this tree contains 3 distinct major versions of loader-utils.

The mechanism by which this is controlled could vary, but even simply being able to specify which version is troublesome in resolutions would be helpful, e.g.:

"resolutions": {
  "loader-utils@1.2.3": "1.4.2"
}

Another possibility would be to take the approach npm did and allow specifying the dependency structure you'd like to override. In this case, ideally only the loader-utils whose direct parent is @nrwl/webpack would be forced to use loader-utils 1.4.2:

"resolutions": {
  "@nrwl/webpack": {
    "loader-utils": "1.4.2"
  }
}

Thanks for your consideration.

angelogiuseppe commented 1 year ago

Perfect, thanks for adding the information and examples, I understand the use case. I'll start looking into it in a couple of days. I can have a new version probably by the end of the week.

For faster communication please join the discord group listed on the README, we can test and ask questions faster there: https://discord.gg/fNEEthRGJu

Also any other feedback to improve the library is always welcome

MBDesu commented 1 year ago

Perfect, thanks for adding the information and examples, I understand the use case. I'll start looking into it in a couple of days. I can have a new version probably by the end of the week.

Of course! Thanks for your prompt response.

For faster communication please join the discord group listed on the README, we can test and ask questions faster there: https://discord.gg/fNEEthRGJu

Sure, I can do that in the next day or two.

Also any other feedback to improve the library is always welcome

Well, since you're asking...

For what it's worth, I would prefer the latter of my two proposed solutions. There are even a whole host of other ways you could do this, like allowing an external configuration script that does what this would do essentially, but I think npm's overrides work fantastically if you're able to use them, and matching functionality as equally as possible within reason is something to strive for.

See the NPM documentation for more details.

angelogiuseppe commented 1 year ago

Sure, I red the documentation of how NPM currently does it, I agree with you, it would be good to have the same functionality. I'll move in that direction 👍

JSMike commented 1 year ago

Hi, my team's project is struggling with the same exact scenario listed above, specifically with loader-utils 1.2.3, and trying to bump 1.2.3 to a higher version but not impact other major versions that are valid. Has there been any progress? I'll look to hop on discord as well.

angelogiuseppe commented 1 year ago

Currently I haven't had the opportunity to make some progress, my current work has been leaving me with not so much time.

If you guys have the opportunity to make a pull request it would be really helpful, so I can review it and then submit the new version

If there is no opportunity to make the the PR I can try adding that feature later this week, maybe on Friday

ninja7dev commented 2 months ago

is there any update to this? i have a similar scenario where i would need different versions of the same package, as indirect dependencies.