Npm-check-updates: Option to bump existing ranges to reflect latest supported version

Created on 11 Sep 2019  ·  21Comments  ·  Source: raineorshine/npm-check-updates

  • [x] I have read the list of known issues before filing this issue.
  • [x] I have searched for similiar issues before filing this issue.

  • node version: v12.10.0

  • npm version: 6.10.3
  • npm-check-updates version: 3.1.22

573 seems related, and #159 seems about same thing

I'm after an option that will bump existing ranges in package.json, so they point to the latest version (within a reflected range).

Currently ncu does that well, only if we use ^ and rely on latest major of given dependency.

However if there's a package where we rely on non-latest major, it automatically updates to latest major (which I find unwanted, as any major version upgrade should rather be done manually ensuring breaking changes are handled).

Explaining on example

There's a package where:

  • Latest version is 3.2.5
  • Latest v2 version is 2.8.5
  • Latest v2.4 version is 2.4.8
Case 1

Range in is package.json is ^2.4.5.

I'd love ncu to upgrade the range to ^2.8.5 (and not ^3.2.5 as it does now).

Case 2

Range in is package.json is ~2.4.5.

I'd love ncu to upgrade the range to ~2.4.8 (and not ^3.2.5 as it does now).


In #159 it was suggested that npm update does that. That's not true

npm update updates package.json only if:

  1. Given package __is installed in node_modules__
  2. Package __is installed at version lower than latest as supported by range__.

So in above scenario if in node_modules package is already installed at 2.8.5 (or is not installed at all), there's no way to forcenpm update to update the range in package.json.

enhancement

Most helpful comment

Good point! I will keep this open for the feature as requested.

All 21 comments

Thank you for the detailed explanation! And thank you for looking through existing issues.

I marked this as an enhancement and am open to PR's ~or +1's to signal interest if others are wanting this option~.

As a side note, I'm somewhat unclear what benefit there is to having the number changed in the package.json when the version range gives you the control you are looking for. This likely does not change the status of this ticket, but I thought I would mention that this came up for me.

what benefit there is to having the number changed in the package.json when the version range gives you the control you are looking for.

Bumping the ranges ensures that when user updates our package, its dependencies will be upgraded to latest versions (otherwise if ranges are not updated, npm won't do deep update).

Repeatedly I deal with a situation when some dependency of my project introduces accidentally a bug, and then quickly follows with a fix (note that buggy version cannot be removed from npm), then it's always recommended to bump the range so it gives no chance for buggy version to be installed.

(otherwise if ranges are not updated npm won't do deep update)

Does this help?

As of [email protected], the npm update will only inspect top-level packages. Prior versions of npm would also recursively inspect all dependencies. To get the old behavior, use npm --depth 9999 update.

https://docs.npmjs.com/cli/update.html

Does this help?

Not really, as it's not about how packages are being installed for maintainer of a package, but for projects that rely on it.

It's impossible to enforce users of my package (and packages that depend on it), to runnpm update --depth 9999 instead of npm install (or npm update)

Also just for maintainers npm update is not very useful.. as it requires two separate runs for _prod_ and _dev_ dependencies (there's no way to update both with one command run)

Good point! I will keep this open for the feature as requested.

Big +1 on my part!
It's actually what I was hoping to achieve with ncu --semverLevel major -u but have ncu ignore any hard-coded versions.

This is my goal and I believe it should be a flag called --packageRanges or something very clear.

I have a use case:

  • Some package introduced changed behaviour on a major update (eg. v1.2.0 introduced breaking changes on v1.3.0).

    • So we want to hard code the version to a specific version. Eg. set to "1.2.0" in package.json

    • this should make it so ncu does not touch this ever.

  • All other package we want to keep the latest versions in package.json explicitly (because of the reason above)

    • So that means, have ncu auto-update "^0.8.0" to "^0.9.0" in our package.json

TLDR;

So in conclusion I think a command ncu --packageRanges would be awesome!!

Sounds reasonable! Just need someone willing to work on this. I'm working on other things at the moment.

@raineorshine i can cry and make a PR for you!! 🙃 Can I request a little guidance on where you suggest me adding new code ?

I have a few questions for clarification.

It's actually what I was hoping to achieve with ncu --semverLevel major -u but have ncu ignore any hard-coded versions.

So you're looking for a flag that:

  1. Only affects version ranges, not exact versions
  2. Upgrades to the maxSatisfying version (i.e. respects semver)

@medikoo Is this the same as what you are looking for? Is --semverLevel major also inadequate for your use case?

I have a use case:

  • Some package introduced changed behaviour on a major update (eg. v1.2.0 introduced breaking changes on v1.3.0).

I'm a little confused by this: v1.2.0v1.3.0 is a minor version update, not a major version update. Are you saying the package author accidentally published breaking changes on a minor version?

  • So we want to hard code the version to a specific version. Eg. set to "1.2.0" in package.json
  • this should make it so ncu does not touch this ever.

This can be accomplished with an .ncurc file set to not touch that dependency:

{
  "reject": ["mypackage"]
}
  • All other package we want to keep the latest versions in package.json explicitly (because of the reason above)

    • So that means, have ncu auto-update "^0.8.0" to "^0.9.0" in our package.json

Upgrading "^0.8.0" to "^0.9.0" is the current behavior, right?

Sorry, I was confused and thought the name of the "middle" number was "major".

The current behaviour of NCU:

  • ncu --semverLevel major -u

This updates everything to latest "_minor_" (the middle one) version and it's currently my most used command.

This means, with these flags in package.json, the behaviour of this command is:

  • "~1.2.3" is upgraded to "~1.3.0"
  • "^1.2.3" is upgraded to "^1.3.0"
  • "1.2.3" is upgraded to "1.3.0"

This is the reason I was a bit confused on the naming, since we need to write _major_ but it only upgrades _minor_ versions, or is this a bug?

Upgrading "^0.8.0" to "^0.9.0" is the current behavior, right?

I'm not 100% sure what you mean by this. But, I believe ncu -u upgrades _everything_ even to _major_ versions, completely ignoring package ranges.

The behaviour I want with a new command:

ncu --packageRanges -u

I want it to upgrade everything as indicated in package.json. (also see npm ranges docs).

This means, with these flags in package.json, the behaviour of this command is:

  • "~1.2.3" should be upgraded to latest patch version like: "~1.2.4"
  • "^1.2.3" should be upgraded to latest minor version like: "^1.3.3"
  • "1.2.3" should never be touched

I think saving an extra special file just for when hard coding a package version is too confusing when working with teams. If authors can just have a single script like so:

  • "update-all-dependancies": "ncu --packageRanges -u && npm i"

This way the source of all truth _is_ just the package version range, and no extra files are needed that add clutter and can lead to discrepancies more easily.

I feel that ncu --packageRanges -u is so beautiful.
It's the command of commands. The command that will end all other commands.

NCU would basically offer:

  • ncu for checking new major versions and then go in an manually upgrade them on a case-by-case basis after checking breaking changes
  • ncu --packageRanges -u as the main command to upgrade everything in package.json. This can be done routinely every week.
  • once a version needs to be fixed, just do so in package.json and forget about the rest. ;)

NCU is so nice, especially in monorepos.

"~1.2.3" should be upgraded to latest patch version like: "~1.2.4"
"^1.2.3" should be upgraded to latest minor version like: "^1.3.3"
"1.2.3" should never be touched

@raineorshine ☝️ this is exactly the behavior I had in mind with this proposal.

This is the reason I was a bit confused on the naming, since we need to write _major_ but it only upgrades _minor_ versions, or is this a bug?

It was intended to convey that it locks upgrades to the current "major" version. I can see how it could be interpreted the other way though.

But, I believe ncu -u upgrades _everything_ even to _major_ versions, completely ignoring package ranges.

Yes, aside from semverLevel, that's correct.

I want it to upgrade everything as indicated in package.json. (also see npm ranges docs).

This means, with these flags in package.json, the behaviour of this command is:

  • "~1.2.3" should be upgraded to latest patch version like: "~1.2.4"
  • "^1.2.3" should be upgraded to latest minor version like: "^1.3.3"
  • "1.2.3" should never be touched

I think saving an extra special file just for when hard coding a package version is too confusing when working with teams. If authors can just have a single script like so:

  • "update-all-dependancies": "ncu --packageRanges -u && npm i"

This way the source of all truth _is_ just the package version range, and no extra files are needed that add clutter and can lead to discrepancies more easily.

This makes total sense. It's an interesting use case, as it wasn't the original intent of the library, but it is entirely coherent.

I feel that ncu --packageRanges -u is so beautiful.
It's the command of commands. The command that will end all other commands.

Ha. I am enjoying your hyperbolism.

I think --packageRanges can be combined with --semverLevel since they are mutually exclusive and do the same type of thing: setting the allowable range of upgrades. In fact, this also includes --greatest and --newest. Let's do --semverLevel semver for now and we can combine them all in a separate PR. I hope the name change does not curb your zeal in the slightest.

Can I request a little guidance on where you suggest me adding new code ?

Sure!

semverLevel gets converted to versionTarget in queryVersions. Start by adding semver as a valid versionTarget.

Then export a semver function from npm.js. This is where the different filtering logic will go. I would recommend copying greatestMajor and adapting it to use semver.maxSatisfying.

Unit tests can be added with the other --semverLevel tests.

@raineorshine I'll try to see if I can get a PR working.
I'm fine with keeping the --semverLevel flag, but

  • instead of the full param being:

    • --semverLevel semver

  • I would much much much more prefer this:

    • --semverLevel packageRanges

Just for clarity's sake.
Because the message I read in my head when comparing these two:

  • --semverLevel semver

    • "update packages according to the semver level as per the semver." (which really doesn't make much sense)

  • --semverLevel packageRanges

    • "update packages according to the semver level as per the package ranges." (which is clear and makes sense)

I hope it's not just in my head, but I think we'll avoid a loot of confusion with the latter as opposed to the former.

I hope you can agree with my opinion, let me know if I can get the green light on that naming scheme!

You have a point. --semverLevel semver is redundant and confusing. I still find that packageRanges to be less than clear. The option is there to constrain upgrades to latest, greatest, newest, or semver (the new option). All of them reference the package ranges, but each of them constrains the upgrades in a different way.

I would suggest one of the following two options:

  • --semverLevel maxsatisfying - Explicitly references the node-semver comparison
  • --target semver - Use a more generic option name, similar to versionTarget that is already used internally
  • --to semver - Another generic name that reads well literally.

Open to feedback.

@raineorshine
Thanks for your insights!
Personally I find --semverLevel maxsatisfying to be clearest in this case. : )

However, I re-read your previous comment and you mention:

In fact, this also includes --greatest and --newest. Let's do --semverLevel semver for now and we can combine them all in a separate PR.

and I think I kind of overlooked this: you want to also come up with alternatives for those --greatest and --newest flags, correct? In this case I will just go ahead with a PR, call it --semverLevel maxsatisfying for now, and you can revisit the naming scheme for everything at a later date.

@medikoo do you have any opinion on naming?

and I think I kind of overlooked this: you want to also come up with alternatives for those --greatest and --newest flags, correct?

Yes, I believe they should go under the same flag.

In this case I will just go ahead with a PR, call it --semverLevel maxsatisfying for now, and you can revisit the naming scheme for everything at a later date.

Yes! Agree

@mesqueeb Any update? Thanks!

FYI I am currently planning for the next major release consolidating --semverLevel, --greatest, and --newest into a single flag --target with possible values: latest, greatest, newest, patch, minor, major, semver.

Sounds exciting @raineorshine! I just tried v8.0.5 and could not find --target semver. What’s the option to upgrade to the latest available version within the given semver range? (e.g. ^1.2.3^1.99.0, ~1.2.3~1.2.99).

@kachkaev It hasn't been implemented yet. @mesqueeb had originally volunteered, but it's currently open for submissions.

I'll try to get to it eventually, but am a bit swamped in a couple of pending releases for some projects. 😰

if it's not yet implemented in a few weeks I'll probably eventually try my hand at it. seems like a fun journey!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vayurobins picture vayurobins  ·  5Comments

Simek picture Simek  ·  5Comments

mriehema picture mriehema  ·  7Comments

anonimoconiglio picture anonimoconiglio  ·  6Comments

dereklin picture dereklin  ·  6Comments