Patching an NPM dependency
So you’re working on an npm-based project and you’ve discovered one of your dependencies has a bug. What do you do?
The easiest thing — and I’d guess the thing most folks do — is open an issue on that package’s GitHub repository and hope the package maintainer gets around to fixing it.
But that could take a while, if it ever happens at all, and you’ve got a deadline coming up.
What now?
TL;DR
Fork the broken package’s repository on GitHub and push your fix to a new branch. Then update the package dependency declaration in your package.json
like this:
"dependencies": {
"some-broken-package": "me/some-broken-package#my-patch"
}
Now you and your teammates will all get the patched version when you do npm install
or npm update
. You can continue to maintain your fork and all the dependency management will keep working just like how you’re used to.
If you want to go through the whole process of arriving at this solution, read on…
Patch it locally
So a dependency is broken and you can’t afford to wait for the maintainer to maybe notice and fix your issue for you. What now?
Well, you do have all the source code to the package right there in your node_modules
folder. The Javascript ecosystem may be breathtakingly silly in a lot of ways, but one of the genuinely great things about it is that all this stuff is open-source. So let’s get to patching!
Of course, if you just dive into node_modules/some-broken-package
and start hacking, you might fix the problem locally, but nobody else on your team is going to get your fix. And if you’re smart, you’ll have gitignore’d node_modules
from the very start of your project, relying instead on your (checked in) package.json
/package_lock.json
plus npm install
for each user to sync their dependencies.
What you really need to do is get your fix into the broken package’s repository and published to npm, so when anyone else does npm install
(or npm update
) they also get your fix.
Make a pull request
If you open up node_modules/some-broken-package/package.json
you can find some useful information.
The homepage
entry will (often) take you right to the package repository on GitHub:
"homepage": "https://github.com/someuser/some-broken-package#readme",
Sometimes the homepage isn’t a GitHub URL and is instead a separate website (e.g. some-broken-package.com
). You could alternatively find the repository
section and get the actual git URL from there:
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/someuser/some-broken-package.git"
},
Either way, you should be able to find the package repository on GitHub (or maybe some other service; but it’ll probably be on GitHub) and fork it.
Now clone your fork — separately from your original project! — and create a branch for your patch:
git clone git+ssh://git@github.com/me/some-broken-package.git
git checkout -b my-patch
Hop in there and make your fix(es), commit, push, and open a pull request on the original repository. And you’re done!
…except that you’re not, because your original project still needs to pick up the fix, and the whole point of this article is that you don’t want to wait for the package maintainer to accept your PR (if they ever do).
So how do you get your fix into your original project?
Copy it over into node_modules
You could just copy over the changed files:
cp some-broken-package/lib/*.* my-project/node_modules/some-broken-project/
But that only works locally, not for your teammates, and your patch will just get silently stomped by npm install
or npm update
later anyway, so this is actually a terrible idea.
What you really want is for your package.json
to connect some-broken-project
to your fork instead of the original package.
Update your package.json
Fortunately, you can do this easily, but if you didn’t go spelunking into the darkest depths of the npm documentation you might not’ve discovered how.
Open up your package.json
(the one for your main project, not the one for the broken package) and find the dependency entry for the broken package:
"dependencies": {
"some-broken-package": "^1.0.0"
}
Replace the version bit with the git URL of your fork, including the name of your patch branch:
"dependencies": {
"some-broken-package": "git+ssh://git@github.com:me/some-broken-package.git#my-patch"
}
If your fork is on GitHub you can actually simplify this to just username/repository#branch
like so:
"dependencies": {
"some-broken-package": "me/some-broken-package#my-patch"
}
Now if you nuke node_modules/some-broken-package
and then run npm install
again you’ll see your fix come through. And that means your team members — and anyone else who clones your project — will also get your fix.
Furthermore, you can continue to make updates on your my-patch
branch, push them, and npm update
as needed.
Long-term considerations
You probably don’t want to publicly release a package that’s structured like this, as it kinda works around npm’s whole philosophy of package management, but this should work well as a temporary Band-Aid for critical issues in dependencies where you can’t afford to sit around and wait for the package maintainer to respond.
There are a couple ways this can play out in the long-term (i.e. as you approach release):
- Your PR is eventually accepted and the package maintainer publishes an update to npm. In this case, simply revert your dependency declaration in
package.json
(you could just delete it and thennpm install some-broken-package
to get the new, canonical version). - It’s time to launch and your PR still languishes in review hell. Maybe that package has even been completely abandoned! In this case you have a few options: publish your fork as an new npm package and link to that, or embed the forked version directly into your main project and don’t treat it as an npm dependency at all.
There might be better ways to handle this situation, but this is the best one I’ve found so far. I’m certainly open to alternatives!
Happy coding!