How to edit & replace NPM package dependency

azophy

Abdurrahman Shofy Adianto

Posted on August 15, 2023

How to edit & replace NPM package dependency

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


Enter fullscreen mode Exit fullscreen mode

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>


Enter fullscreen mode Exit fullscreen mode

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 }
})


Enter fullscreen mode Exit fullscreen mode

now we have a simple dummy example for our demonstration:

Screenshot of our new dummy web app

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


Enter fullscreen mode Exit fullscreen mode

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);
    },
    ... 


Enter fullscreen mode Exit fullscreen mode

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...


Enter fullscreen mode Exit fullscreen mode

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"
      }
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

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:

Content of our latest unstorage local folder

now when we try our nuxt app, it would print out our latest changes:

Result of 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


Enter fullscreen mode Exit fullscreen mode

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",


Enter fullscreen mode Exit fullscreen mode

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.

💖 💪 🙅 🚩
azophy
Abdurrahman Shofy Adianto

Posted on August 15, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related