Better package management with npm
How can npm help you remove unmaintained packages from a big repo?
TL&DR
You can use npm i <wrapper-old-package> --global-style
to prevent your teammates from accidentally importing old-package
.
Introduction
One of many approaches to removing a package from a big project that many developers contribute to, is to
- Keep existing functionality that rely on this package.
- Prevent these developers from relying on the package in focus.
In this short writing, we are going to look at how npm can help us achieve these two processes.
Sample repo
We are going to use Momentjs as an example since it is popular and its maintainers discourage its usage.
We will also be using node@16
and npm@8
Below is the structure of our sample repo
root
+-- index.mjs
+-- package.json
index.mjs
contains one function that calculates remaining time using Momentjs
package.json
contains entry for Momentjs as a dependency.
Keep existing functionality that rely on Momentjs.
Let us do some refactoring so that our calculateRemainingTime
function is moved to a workspace ( wrapper-moment
), then have the root level index.mjs
import this function from that workspace.
Now, our new repo should look like below.
root
+-- index.mjs
+-- package.json
+-- packages
+-- wrapper-moment
+-- index.mjs
+-- package.json
Prevent developers from relying on Momentjs.
At this point, we still have Momentjs inside the root level package.json
Let us run these two commands to move Momentjs inside the workspace’s package.json
npm uninstall moment
npm install moment --global-style --workspace=wrapper-moment
--global-style
will ensure that Momentjs is only accessible inside the workspace. Therefore, importing it from the root level index.mjs
should trigger an error.
We can also verify that moment is indeed now just part of the wrapper-moment
package by running npm explain moment
.
Why does this approach work?
Npm default installation process
npm cli implements a maximally naive deduplication
algorithm, which seeks to minimize the number of duplicated packages.
It does this by generating the shortest tree possible, which in turn forces many packages to become direct children of the root level node_modules
.
Node Module Resolution
Nodejs looks for the closest node_modules
folder to resolve a module.
Therefore, if a package was installed because of an inner dependency, then Nodejs is going to pick it up, even if the project does not intend to rely on it directly.
Illustration
--global-style
will help prevent the unintended module resolution.
Conclusion
Npm has evolved into an all purpose solution that allows developers to achieve almost any package management process they want.