Is there a quick and easy out-of-the-box way to update all the npm packages in your project? Yes. Does it quite work as you expect? Not always.
For those in camp TL;DR; write and execute a node script(source below) to rewrite all the versions listed in package.json.
npm outdated
The outdated command will list any packages that are not up to date ?
Is there not an npm command to upgrade these?
npm update –save/–save-dev
Yes. That is it. It will only update packages so far though. The update function respects semver. For example, if I have a package which is at version 1.3.5, but the latest version is 3.0.5, the package would only update to the latest minor version. In our example, 1.9.9 may be the highest version prior to 2.0.0.
There is good reason for this. New major versions may introduce breaking changes that could break your project. You want to avoid this scenario. The restricted power of npm update promotes manual updates. In turn, manual updates make it easier to spot the culprit if something should break ?
Sooo, why would I want to do this then?
It?s a valid question.
Ever have a project that you didn?t touch for some time? And when you returned to it, the package dependencies were very out of date?
It may be risky, but, sometimes you want a quick solution to update all dependencies. A solution that updates every dependency to its latest version so you can see what happens.
Something like the following would be ideal;
npm update –save/–save-dev -f
This would be intuitive. You?d assume this would force packages to update to their latest version.
How do we do it then?
A solution could be to use npm run scripts. We can define a script, let?s call it update:packages. This script executes a node script that wipes all defined versions in our package.json file. It then removes our installed node modules. Finally, it updates them all saving the new versions to package.json.
?scripts?: { ?update:packages?: ?node wipe-dependencies.js && rm -rf node_modules && npm update –save-dev && npm update –save?},
To run this on the command line;
npm run update:packages
Once updated, you can then revert to using the npm update command as you are now up to date.
That node script? wipe-dependencies.js?
Small ? Using fs, read our package.json file, modify the content and write them back to the file.
const fs = require(‘fs’)const wipeDependencies = () => { const file = fs.readFileSync(‘package.json’) const content = JSON.parse(file) for (const devDep in content.devDependencies) { content.devDependencies[devDep] = ‘*’ } for (const dep in content.dependencies) { content.dependencies[dep] = ‘*’ } fs.writeFileSync(‘package.json’, JSON.stringify(content))}if (require.main === module) { wipeDependencies()} else { module.exports = wipeDependencies}
Wipe the version numbers replacing them with an asterisk.
This version covers both devDependencies and dependencies. You could modify the script to only update devDependencies. This could be less risky for the actual output of your project to start with.
BONUS: Only update packages in the npm registry
Vidur raised a great point in one of the responses about packages that are not part of the npm registry. This is a great spot! We will likely want to avoid those.
When first tackling this problem, I was more concerned catching packages that we don?t want to update. For example, ignore git endpoints by implementing a check before asterisking the version.
const fs = require(‘fs’)const wipeDependencies = () => { const file = fs.readFileSync(‘package.json’) const content = JSON.parse(file) for (var devDep in content.devDependencies) { if (!content.devDependencies[devDep].includes(git)) { content.devDependencies[devDep] = ‘*’ } } for (var dep in content.dependencies) { if (!content.dependencies[dep].includes(git)) { content.dependencies[dep] = ‘*’ } } fs.writeFileSync(‘package.json’, JSON.stringify(content))}if (require.main === module) { wipeDependencies()} else { module.exports = wipeDependencies}
That?s OK. But, it?s not great ? It only checks for git endpoints. If there are other patterns we wish to add in future, they must be more checks implemented.
One thing we do know, any package in the npm registry will adhere to SemVer. Its version number will in most cases only contain numbers and symbols such as 15.0.0 or 3.21.2 preceded by some symbols. There are also cases where the version may contain prerelease strings such as alpha or beta.
Flip our thinking on its head. Instead of excluding versions that contain patterns, include those that adhere to SemVer. Create a registered expression and use it against the version. Do this to determine whether a version requires updating. This means that versions will only get updated if they match SemVer ?
const fs = require(‘fs’)const wipeDependencies = () => { const file = fs.readFileSync(‘package.json’) const content = JSON.parse(file) for (var devDep in content.devDependencies) { if (content.devDependencies[devDep].match(/W+d+.d+.d+-?((alpha|beta|rc)?.d+)?/g)) { content.devDependencies[devDep] = ‘*’; } } for (var dep in content.dependencies) { if (content.dependencies[dep].match(/W+d+.d+.d+-?((alpha|beta|rc)?.d+)?/g)) { content.dependencies[dep] = ‘*’; } } fs.writeFileSync(‘package.json’, JSON.stringify(content))}if (require.main === module) { wipeDependencies()} else { module.exports = wipeDependencies}
That?s it!
A quick and easy, albeit risky way to update all npm packages in your project at once.
As always, any questions or suggestions, please feel free to leave a response or tweet me ?!