How to edit & replace NPM package dependency
Abdurrahman Shofy Adianto
Posted on August 15, 2023
Last week, my first actual contribution into a public open source project has been merged. Its actually only a small changes into Unstorage library, which is used by Nitros server engine, which in turn used by the popular NuxtJs framework. At the time, I was stuck in one of my side project which used Nuxt because Unstorage is lacking some functionalities that I need. However because Unstorage is 2 layers deep in the dependency tree, its not trivial for me to change the code there.
I imagine this is actually a common task when working with NPM. There are many reason for people to changes the library which is several layers deep in the dependency tree. Maybe they encounter a bug that need to be fixed, or there are some security issue so they wish to change the dependency into another library. However at the time I can’t find any complete tutorial for achieving this task. I only found some partial steps or incomplete docs that I need to piece together. So after some fiddling and some trials and errors, I finally found how to do it. Here I will share the methods so other people wouldn’t have to go through the same trouble as I did.
To show you how its done, I will demonstrate by doing basically the same thing. That is by creating a new Nuxt project, and then forking the unstorage package with my own changes. However in this example we would only add a simple change that wouldn’t affect the overall feature, just to illustrate how its done. You could ignore the NuxtJs parts as its not actually required for our main topic here.
Setting up the scene
First lets create a new Nuxtjs Project.
npx nuxi@latest init my-app
cd my-app
then lets setup a simple page to use unstorage feature. Lets create a simple counter page using it that would store into the default unstorage store (which is memory store). first we will edit app.vue
file:
// app.vue
<template>
<div>
Counter: {{ counter }}
<button @click="updateCounter()">
add
</button>
<br />
Server Counter: {{ server_counter }}
</div>
</template>
<script setup lang="ts">
const counter = useState('counter', () => 0)
const server_counter = useState('server_counter', () => 0)
async function updateCounter() {
counter.value++
const res = await $fetch('/api/add_counter', {
method: 'post',
body: { counter: counter.value },
})
server_counter.value = res.value
}
</script>
then we will add server/api/add_counter.post.ts
. Again, this is nuxtjs stuffs. Its not necessary to understand how to replace NPM dependency.
// server/api/add_counter.post.ts
export default defineEventHandler(async (event) => {
const { counter } = await readBody(event)
await useStorage().setItem('data:counter', counter)
const res = await useStorage().getItem('data:counter')
return { value: res }
})
now we have a simple dummy example for our demonstration:
Forking the depended package
Lets clone the unstorage package into our local env, then change it to our needs.
git clone https://github.com/unjs/unstorage.git
cd unstorage
npm install
to illustrate our change, lets just add a simple console.log in src/drivers/memory.ts
whenever we store new value
...
setItem(key, value) {
console.log(`setting key ${key} with value ${value}`)
data.set(key, value);
},
...
now run npm run build
to make sure our local changes is ready to use
Overwrite dependency in our main package with local changes
After we finished our local changes, now we are ready to overwrite our dependency. Open package.json
of our Nuxt project, then in devDependencies
section we’ll overried unstorage package with the local path to our forked unstorage package. Of course this depends on your needs whether to change devDependencies
or dependencies
section.
// package.json
// other settings...
"devDependencies": {
"unstorage" : "file:/full/path/to/your/forked-unstorage",
// other dependencies...
Notice that we use file:<path>
format for our dependency location. Now we add a new overrides
section to our package json. Here we need to adjust based on our dependency tree. In our case, unstorage is a dependency of Nitros server engine, which in turn is a dependency of NuxtJs. So our overrides
section would look like this:
// package.json
// other settings...
"overrides": {
"nuxt": {
"nitropack": {
"unstorage": "$unstorage"
}
}
}
}
now run npm update
our Nuxt folder to update our dependency, then restart our local nuxt server. We could confirm by running ls -al node_modules/unstorage
on our Nuxt folder and see that our unstorage folder has been changed into a link to our forked unstorage folder:
now when we try our nuxt app, it would print out our latest changes:
Publishing our changes temporarily
We have successfully override our dependency locally. Now hot to use it in our staging or production environment? We actually need to make our changes available publicly. The best ways is of course to merge it into the official upstream package. You could create a new pull request here to the upstream repo, and the repo maintainer would merge it into the codebase which you could use it in your project. Of course this rarely would happen quickly, so we the alternative is to just publish it in github. NPM actually support using github repo & branch as NPM package, so we would use this method here.
First lets create a new branch in our local folder, than commit the build folder into the new branch. Then push it into your own github repo.
# in our forked unstorage folder
git checkout -b my-build-branch
git add -f dist
git commit 'example npm build'
git push -o origin my-build-branch
You could see my branch in my github account for reference. There you could see that it contains dist
folder which contain my local build at the time. In my case, dist
folder is actually included in .gitignore
of unstorage repo so I need to run git add -f dist
to forcefully add the folder into my commit.
Now, we are ready to update our package.json
once more. Update our devDepencies
/ dependecies
section with our public github repo & branch. For example, in my case it would be:
// package.json
// other settings...
"devDependencies": {
"unstorage": "azophy/unstorage#my-build-branch",
then run npm update
once more. now you are ready to use your new fork on your project.
Conclusion
I hope this article could give you a clear outline about how to replace NPM dependency by yourself. Feel free to comment if you find anything lacking.
Posted on August 15, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.