Ever come back to a project you haven’t touched for a while, only to find out there’s a lot of outdated npm packages that you want to update? This is a situation I occasionally find myself in and I’d never thought of a good way to tackle it.
Finding Outdated Packages
First off, how do you know what’s outdated? We can use
npm outdated for that and it’ll return something like this:
If you want some more information you can provide the
--long flag and get more output, such as whether the package is in the
If the update is within the semver filter you have in your
package.json, it’s easy to upgrade with
npm upgrade, but if you’re in a situation like I found myself in from the above list, there’s a lot of major version upgrades needing to be done, and since they are beyond the allowed semver range it’s a non-starter.
Upgrading Beyond SemVer Ranges
How do we go upgrading beyond our allowed semver range? By treating it as a new install and specifying the
@latest tag (or specific version), like so:
Doing this will install the latest version of TypeScript (
4.1.2 at the time of writing) which is a major version “upgrade”, and it’s easy enough to do if you’ve only got one or two packages to upgrade, but I was looking at 19 packages in my repo to upgrade, so it would be a lot of copy/pasting.
Upgrading from Output
Something worth noting about the
npm outdated command is that if you pass
--json it will return you a JSON output, rather than a human readable one, and this got me thinking.
If we’ve got JSON, we can use
jq to manipulate it and build up a command to run from the command line.
The output JSON from
npm outdated --json --long is going to look like this:
We’re starting with an object, but we want to treat each sub-object as a separate node in the data set, we’ll turn it into an array using
to_entities, which gives us this new output:
This gives us a dictionary where the
key is the package name and
value is the information about the upgrade for the package. As it’s now an array we can choose to filter it using whatever heuristics we want, and for the moment we’ll upgrade the
dependencies separate from the
devDependencies. We do that using the
select function in jq:
selectfunction allows you to do whatever filtering you want, for example if you wanted to only update the TypeScript type definitions you could change the
select(.key | startswith("@types")).
Running this will give you a filtered output on the terminal, showing only the packages that match your
select condition. The last step is to generate the new package install version:
This update specified the
@latest tag, but you could use
.key + "@" + .value.latest if you wanted to install the specific version for more tighter semver pinning. The output in the terminal will now look like this:
All that’s left to do is to pass the packages to
npm install, so you’d possibly think we can just pipe the output:
npm install doesn’t accept command line arguments provided by standard input, so instead we’ll use
xargs to convert the standard input into command line arguments:
And with that, our upgrade is fully underway!
I’m going to keep this snippet handy for when I’m coming back to projects that I haven’t worked on for a while, as it’s an easy way to do a large number of updated.
An alternative option you can look at is
npm-check-updates, which is a command line utility that will update in a similar manner to above, but also has other feature to how it controls updates.