When working with JavaScript projects, we use the Node Package Manager (NPM) to manage our package dependencies. NPM is a Command Line Interface (CLI) tool that enables developers to add, remove, and update package dependencies in our projects.
Due to security vulnerabilities, bugs and enhancements, there is a high frequency of updates on these dependencies, and developers need to keep track of those updates to avoid accumulating technical debts on their projects, or even worse, to allow for a security vulnerability to continue to run on a production environment.
With this understanding, it is important to be familiar with the process to keep a JavaScript project up to date with the latest package updates. This enables us to clearly understand the steps that are required to check on the dependencies’ configuration, outdated versions, commands to manage the updates, and what to do to force an upgrade to major versions.
Understand the Project Dependencies
To get a better understanding of how to manage our project dependencies, we need to understand how a project is configured. When using NPM to manage a React, Angular or other JavaScript framework project, a package.json file is created. This file host both the release and development dependencies, the latter is used only for tooling to aid in the development and build effort and are not deployed.
The one area to notice from this file is how the semantic version
(semver) range rules are defined. Basically, these rules govern how far ahead
in new versions a dependency can be updated. For example, look at the following
configuration:
"scripts": { "build": "tsc", }, "dependencies":
{ "jsonwebtoken": "^8.5.1", "mongoose": "~5.3.1", "node-fetch": "^2.6.7" }, "devDependencies": { "@azure/functions": "^3.2.0", "@types/jsonwebtoken": "^8.5.9", "eslint": "^7.32.0", "jest": "^26.6.3", "typescript": "^4.8.2" } |
The dependency version is prefixed with a notation, most commons characters are the caret (^) for minor versions and tilde (~) for patch versions. These characters are designed to limit a project upgrade to only backward compatible versions, for example:
- ^8.5.1
Can only upgrade up to the max minor version 8.x.x but never to 9.x.x
- ~5.3.1 Can only upgrade to the max patch version 5.3.x but never to 5.4.x
It is important to follow the semver governance to maintain backward compatibility in your projects. Any attempts to upgrade to a major release will introduce breaking changes, which can require refactoring of the code.
Check for Outdated Versions
Now that we understand how a project is configured, we can now move forward to talk about how to check for outdated dependencies. To check all the version on our project, we can run the following npm command:
> npm outdated |
This command reads the package.json file and checks the version of
all the dependencies. In the image below, we can see the output from this
command:
The output shows each package name, its current version, the
wanted version which is governed by the semver range, and the latest package
available. Ideally, we want to upgrade to the latest package available, but if
that version is not within your semver range, there is the risk of many
breaking changes, which requires some code refactoring.
Note: Notice the font color on the package name, red indicates that an update is required
Update the Dependencies
So far, we have identified that some packages are behind in
updates or outdated. The next step is to use npm and apply the update to our
project, which is done by using another npm command:
> npm update |
Note: In Linux and WSL, if you see the EACCES error, grant the current user permissions by typing this command: sudo chmod 700 /folder/path
The update command reads all the packages and applies the new version following the semver range rules. After running the command, the output, if no errors were found, should look like the following images:
From this output, we can see that all the current versions match the wanted version. This basically means that the current version is updated with the latest minor release for that version. This is the safe way to update of the dependencies, but overtime, there will be a need to force your project to update to a new major release. How do we do that?
How to Upgrade to a Major Version
In some cases, there may be a security vulnerability, a feature
that does not exist in the minor version, or just is time to keep up with the
latest version, and there is a need to move up to the next major version or
even the latest version. Most of the time, it is sufficient to move to the next
major version when the project is not too far behind updates.
When this is the case, we can force update a version by running another npm command, which help us upgrade to a specific version or the latest one.
> npm install –save package-name@3.0.0 or > npm install –save package-name@latest |
The install command is not bound by the semver constraint. It
installs the selected version number or the latest version. We also provide the
–save parameter to save the changes to the package.json file, which is really
important for the next update. This will update the reference to the new
version number.
When upgrading to a new major version, there are some risks in introducing some breaking changes. Usually, these changes are manifested on deprecated functionality that may no longer exists or operate differently. This forces the dev team to have to refactor the code to meet the new technical specifications.
Verify the Updates
After applying the dependency update to a project, it is important
to verify that there are no issues with the update, especially when upgrading
to a major version. To verify that there are no issues, we need to build the
project. This is done by using the build script in the package.json file and
running the command npm run build
"scripts": { "build": "tsc", },
> npm run build |
The package.json file has a script node where we can define
commands to run the build, test cases and code formatting tasks. In this
example, tsc stand for TypeScript Compiler. It builds the project and check for
any compilation issues. If there are any compatibility problems, the output of
the build process will indicate where in the code to find the problem.
The npm run command enables us to run the script that are defined within the script node of the package.json file. In our case, it runs the tsc command to do a build. This command may look different in your project.
Conclusion
When we start a new project, we use the current package versions that are available from the npm repository at that time. Due to security vulnerabilities and software updates, there is a high frequency of updates in these JavaScript packages. Some of these new versions are backward compatibles, others are not. It is always an issue of technical debt when we let our projects get far behind in updates, so we most frequently check for outdated software and plan for major version updates when necessary. Therefore, become one with npm and use it to help manage a project package dependency.
npm run happy coding