Npm packages get updated by their maintainers all the time. These updates could be bug fixes, security patches, new features, and major re-writes.
To help consumers of these packages understand how each new release will affect their project, package maintainers generally use what’s known as semantic versioning.
Semantic versioning looks like
MAJOR.MINOR.PATCH. For example, a package version could be set at
1.0.0. If a new release contains only bug fixes, then the version would be bumped to
1.0.1. If a new release contains new features that don’t break existing APIs, then the version would be bumped to
1.1.0. And if a new release contains breaking changes that consumers of the package will need to be aware of and adjust to in how they use the package, then the version would be bumped to
Your dependencies are specified in your
package.json file. For each package listed in your
devDependencies object, you can specify exactly how you want the package to be updated.
Including only the version number means you only want to use that exact package version only.
Prefixing the version number with a tilde (
~) means you only want to accept patch updates when they’re available.
Prefixing the version number with a caret (
^) means you want to accept minor and patch updates when they’re available.
If you are using Yarn to manage your packages, exact versions of each dependency that is installed in your project are stored in your
yarn.lock file. The
yarn.lock file’s main purpose is to ensure that the versions of each package remain consistent for each person that has pulled down your code onto their machine.
As mentioned above, npm packages get updated very frequently. This means that if you want to keep your project using the latest releases, you have to continually update your dependencies.
I try to update my project’s dependencies about once per week so that I don’t fall too far behind. Even in that timeframe, it’s common that I’ll have 10 or 20 packages that have new versions available.
Now, on to the crux of the issue: When running
yarn upgrade to upgrade your dependencies, the
yarn.lock file gets updated with the latest requested package versions, but the
package.json file does not!
For example, if you have a package
"something-cool": "^2.0.3" in your dependencies object in your
package.json file, and there is an available version for
2.4.0, and you run
yarn upgrade, then version
2.4.0 will be installed for your project, and version
2.4.0 will be shown as what’s installed in your
yarn.lock file. But, your
package.json file will still show
That’s because you’ve specified that you’re ok with installing the latest version of the package as long as it’s still part of the major version
2. That requirement holds true, so
package.json remains unchanged even though
yarn.lock changes and the later version is installed.
For me, this is a little counterintuitive. When I update the package from
2.4.0, I’d like the version to update in both
After doing a quick Google search, it seems that I’m not alone. Many other developers also expect this behavior.
So, is it possible to get this kind of behavior to happen?
The best solution I’ve found so far is to use the following command to update my packages:
yarn upgrade-interactive --latest.
Let’s break that down.
We already know that
yarn upgrade is used to upgrade package versions.
yarn upgrade-interactive instead puts you into a command line interface tool that allows you to pick and choose which packages you’d like to upgrade.
--latest flag is the key to getting the
package.json file to also be updated.
Now, it’s important to note that the
--latest flag updates your package version to the latest version, regardless of what rules you’ve specified for that package in your
package.json file. That means that if you have
"something-cool": "^2.0.3" specified, and a version
3.1.0 is available, running yarn upgrade
--latest would in fact update that package to version
3.1.0, despite the fact that you were only wanting to make minor and patch updates by default.
That’s why I use
yarn upgrade-interactive instead of just
yarn upgrade so that I can pick which packages I’d like to update. When choosing, I only choose those packages that have minor and patch updates available.
I update all those, and then run my linters and tests to make sure I haven’t broken anything.
If there are major versions available to upgrade, I generally handle those individually, one by one. That way it’s easy to know what package broke things if anything goes wrong.
Now you too can easily update your packages in
yarn.lock while keeping them in sync.