Techniques for Fixing npm Packages

If you’re a Javascript you likely make use of many npm packages. There are times a package has a bug or doesn’t do exactly what you want. This post details a few different ways to work around this.

I have often found myself wasting hours trying to make a one line fix, so I hope the strategies listed here will be of help to others. Many of the tools mentioned are also useful when developing a package.

Solutions

We’ll start with the obvious. The most basic thing you can do to solve your problems is to use a different package. If it’s a small piece of functionality, you can rewrite it yourself and avoid having to use the package.

This sometimes works, but if the package contains a lot of code this solution may not be practical. Beyond completely rewriting from scratch, you could copy and paste the contents of the package into your local project and edit as you need. The disadvantage of such an approach is that you won’t be able to easily follow future updates made by others to the package. Likely not the best solution in many instances.

Perhaps the first thing you should do before trying to fix a package yourself is post an issue in the GitHub repo. The functionality you are trying to add may already exist, or you are misusing the library and the package maintainers can point you in the right direction. If it really is a bug the package maintainers are likely the ones best suited to fix it.

Fix Locally

npm/yarn link

The first way to fix a package is to fork and clone it locally. You can then use npm link or yarn link to use it in your project. Any changes you make to the forked package will be reflected in your project.

You need to run two commands to link. Using yarn:

yarn link # in the root of the forked package
yarn link forked-package # in the root of your project

You will also need to run yarn install on the forked package and run any build/prepublish steps. In the past I have sometimes found the build/prepublish step to be annoying to get working. Your mileage may vary and it depends on how the package was written.

Another problem with this solution is that it only creates a local symlink to the package. This means that you’ve only solved the problem locally, but not for team members.

Once you’ve fixed the problem locally, you will want to publish it to GitHub and either rely on your forked version of the package, make a pull request to the main package, or publish the package to npm under your name.

yalc

Yalc is similar to npm link, but it publishes the forked package locally rather than creating a symlink. I have found that it often works better than npm link as it handles the full build process a package maintainer uses to publish to npm including npm pack.

Using yalc is similar to using npm/yarn link:

npm i yalc -g
yalc publish # in the root of the forked package
yalc add forked-package # in the root of your project

One problem I’ve run into using yalc is when you want to fix a package that is depended on by another package. For more discussion on that situation see this issue.

You can commit yalc changes to git to share the fix with other team members. This is useful to make a quick fix, although likely should not be used as a long term solution.

patch-package

Another solution is to use patch-package. To make use of it you don’t need to fork the buggy package. You can simply edit your node_modules directory with the changes you want.

To use patch-package:

yarn add patch-package postinstall-postinstall
# add to your package.json:
"scripts": {
"postinstall": "patch-package"
}
# fix the broken file in your project:
# e.g. node_modules/some-package/brokenFile.js
# then run the following to apply the patch:
yarn patch-package package-name

You can add the patches to your git repo to share the changes with the rest of the team. Patch-package applies these changes to the project itself by creating a patch file with the changes.

There are some problems with patch-package however. When a package author publishes a project to npm, many of the original files are lost in the process.

For example, if the project was written in TypeScript/ES6+, the original files may not have been published to npm, just the final transpiled version. This is the case if the package.json contains the files field, or the project contains a .npmignore file. Then not all the files in the project will be downloaded to your node_modules folder. Usually this is a good thing as it reduces the amount of data that needs to be downloaded, but it makes it difficult to use patch-package.

You can still edit the final ES5 or minified version using patch-package, but this is usually an uncomfortable experience.

Also, even if the src folder has been published to npm, you will still need to run the build steps locally. If some of the files necessary to build the project have been excluded, this will not be possible unless you grab the missing files from GitHub. At this stage it may just be easier to clone the package locally in a separate folder.

Beyond this, any fixes you make with patch-package are not shared with the wider community and anyone else using this package that may benefit from your changes.

Fork on GitHub

Another solution is to make the changes you need and publish them to GitHub/GitLab (or any other source control platform you use).

This can often be done in conjunction with the steps above. You may first make the fixes using npm/yarn link or yalc, check that all works as expected and then push the changes to your own Git repo. You can then run one of the following to add the package to your project and make it accessible to others (changing the url as appropriate):

yarn add https://github.com/fancyapps/fancybox [remote url]

yarn add https://github.com/fancyapps/fancybox#3.0 [branch/tag]

yarn add https://github.com/fancyapps/fancybox#5cda5b529ce3fb6c167a55d42ee5a316e921d95f [commit]

If you need to do any automated builds, you can take a look at this StackOverflow post.

At this point you can also make a Pull Request to the main repo to get the changes merged in for everyone using the package. Once the changes have been merged in you can go back to using the original package.

Along these lines, you can also republish the package under your own npm account and then install it as you would any npm package.

Other Solutions

If you are using a monorepo setup with something like Lerna or Rush, you can clone the package in question locally, and use it as you would any other Lerna package in your project.

The final solution available is monkey patching. You can read more about monkey patching in Node.js here.

Final Words

None of the solutions above are perfect. Either patch-package or a yalc followed by committing the changes to GitHub is my favoured solution of those listed above. But all the solutions have problems. After all these years, we are still missing the holy grail for simple package fixes.

If you know of some solutions that I’ve missed and know a better way to fix problems I would love to know in the comments below!

If you enjoyed this article be sure to give me like and follow for similar content in the future :). You can follow me on Twitter at:

Some good follow up discussion can be found here: