How to edit & replace NPM package dependency
Tags: npm, git, javascript
Published: 2023-08-15 12:08:50

Last week, my [first actual contribution into a public open source](https://github.com/unjs/unstorage/pull/269) project has been merged. Its actually only a small changes into [Unstorage](https://unstorage.unjs.io) library, which is used by [Nitros server engine](https://nitro.unjs.io/), which in turn used by the popular [NuxtJs](https://nuxt.com) 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.

```bash
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:

```jsx
// 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.

```jsx
// 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:


![Screenshot of our new dummy web app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zosdcu5k41hert0xhic2.png)


## Forking the depended package

Lets clone the unstorage package into our local env, then change it to our needs.

```jsx
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

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

```jsx
  // 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:

```jsx
  // 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:

![Content of our latest unstorage local folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lp2r31vdk5gkhhe6u7w8.png)

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

![Result of our latest changes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zvinmt8yb93jt2ud93py.png)

## 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](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#github-urls), 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. 

```bash
# 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](https://github.com/azophy/unstorage/tree/feat/vercelkv-ttl-build) 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:

```jsx
  // 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.