The modern web package ecosssystem is overloaded with packages and it's very easy to forget that each dependency of our project can have tons of other dependencies.
Sometimes you get an error in a file of a dependency that you've never heard of and it's not listed in your package.json
. How can we find the direct dependency of our project that has, down its tree, this elusive dependency?
As an example, assume we are using webpack
as the bundler for our web application and this is the project's package.json
dependencies:
...,
"dependencies": {
"classnames": "^2.2.5",
"debounce": "^1.2.0",
"draftjs-md-converter": "^1.4.0",
"js-base64": "^2.4.9",
"query-string": "^5.1.1",
"ramda": "^0.26.1"
},
...
After running the build script, you get the following error:
./node_modules/replace-ext/index.js
Module not found: Error: Can't resolve 'path' in './node_modules/replace-ext'
resolve 'path' in './node_modules/replace-ext'
Parsed request is a module
using description file: ./node_modules/replace-ext/package.json (relative path: .)
It seems that some package called replace-ext
is trying to import the native path
module from Node. However, we're building a web application, so it doesn't make sense to use path
because browsers don't have a native path
library.
Also, the replace-ext
is not listed in our package.json
, so it must be a dependency of some of our dependencies! So, how can we find what package has this library down its dependency tree?
Well, there's a handful of ways! Let's go over two of them:
We can use the npm ls
command, which lists every package installed by our package.json
definitions, to help us find this elusive dependency.
Go to your project's root directory and type the command:
$ npm ls --prod > tree.txt
The output will consist of the dependency tree of every package listed in your dependencies
properties.
Open the tree.txt
file in any text editor and do a string search for replace-ext
. After finding it, backtrack to it's parent packages until your find the direct dependency of our project.
├── [email protected]
├── [email protected]
├─┬ [email protected]
│ └─┬ @textlint/[email protected]
│ ├── @textlint/[email protected]
│ ├─┬ [email protected]
│ │ └── [email protected]
│ ├─┬ [email protected]
│ │ ├─┬ [email protected]
│ │ │ └── [email protected]
│ │ └── [email protected]
│ ├─┬ [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├─┬ [email protected]
│ │ │ ├── [email protected]
│ │ │ ├── [email protected]
│ │ │ ├── [email protected]
│ │ │ ├─┬ [email protected]
│ │ │ │ ├── [email protected] deduped
│ │ │ │ └── [email protected] deduped
│ │ │ ├── [email protected] deduped
│ │ │ └── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├─┬ [email protected]
│ │ │ ├── [email protected]
│ │ │ └── [email protected] deduped
│ │ ├─┬ [email protected]
│ │ │ └─┬ [email protected]
│ │ │ └─┬ [email protected]
│ │ │ └── [email protected]
│ │ ├── [email protected]
│ │ └── [email protected] deduped
│ ├─┬ [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ └─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ ├── [email protected]
│ │ └─┬ [email protected]
│ │ └── [email protected] deduped
│ └── [email protected]
├── [email protected]
├─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ └── [email protected]
└── [email protected]
There you go! We've found that the draftjs-md-converter
package, which is being imported somewhere in our web application, is the one trying to use a Node library!
The website npm.broofa.com allows you to upload a package.json
and show your dependency tree as a graph! If we upload our example package.json
we will see all packages being listed in the right side panel. Look for replace-ext
there and click it. You'll see that the replace-ext
node of our graph is now highlighted ✨! Now backtrack from that node until until you find the dependency listed explicitly in our package.json
!
Aha! You can't escape from us draftjs-md-converter
!
Unfortunately, the fix for this kind of issue is not straightforward. It completely depends on what your app does and what libraries are available for you to choose.
Following our example, we found that the draftjs-md-converter
package is the root of all our problem. The first thing we should do is to think if this package was made to be used in the browser. If it's a module that supposedly supports browser environments, you're probably another victim of the current web ecossystem. If it's supposed to run only in a Node environment, there's your problem.
Regardless, looking for an alternative module is the only solution apart from trying to fix the issue yourself in these modules. Not all libraries will have alternatives that work in browsers, but in this example we're in luck! After doing some research, we found that markdown-draft-js
is another library with the same purpose and with no dependencies using node related libraries!
If an alternative is not found, you may have to get your hands dirty.