Skip to content

Instantly share code, notes, and snippets.

@kaisermann
Created July 3, 2020 19:34
Show Gist options
  • Save kaisermann/e918dab7a2f34f0e050e24c5462633ae to your computer and use it in GitHub Desktop.
Save kaisermann/e918dab7a2f34f0e050e24c5462633ae to your computer and use it in GitHub Desktop.
How to check what direct dependency of your project has a specific package in its dependency tree

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:

Debugging

Using npm ls

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.

├── classnames@2.2.6
├── debounce@1.2.0
├─┬ draftjs-md-converter@1.4.0
│ └─┬ @textlint/markdown-to-ast@6.1.6
│   ├── @textlint/ast-node-types@4.2.5
│   ├─┬ debug@4.1.1
│   │ └── ms@2.1.2
│   ├─┬ remark-frontmatter@1.3.2
│   │ ├─┬ fault@1.0.3
│   │ │ └── format@0.2.2
│   │ └── xtend@4.0.2
│   ├─┬ remark-parse@5.0.0
│   │ ├── collapse-white-space@1.0.5
│   │ ├── is-alphabetical@1.0.3
│   │ ├── is-decimal@1.0.3
│   │ ├── is-whitespace-character@1.0.3
│   │ ├── is-word-character@1.0.3
│   │ ├── markdown-escapes@1.0.3
│   │ ├─┬ parse-entities@1.2.2
│   │ │ ├── character-entities@1.2.3
│   │ │ ├── character-entities-legacy@1.1.3
│   │ │ ├── character-reference-invalid@1.1.3
│   │ │ ├─┬ is-alphanumerical@1.0.3
│   │ │ │ ├── is-alphabetical@1.0.3 deduped
│   │ │ │ └── is-decimal@1.0.3 deduped
│   │ │ ├── is-decimal@1.0.3 deduped
│   │ │ └── is-hexadecimal@1.0.3
│   │ ├── repeat-string@1.6.1
│   │ ├── state-toggle@1.0.2
│   │ ├── trim@0.0.1
│   │ ├── trim-trailing-lines@1.1.2
│   │ ├─┬ unherit@1.1.2
│   │ │ ├── inherits@2.0.4
│   │ │ └── xtend@4.0.2 deduped
│   │ ├─┬ unist-util-remove-position@1.1.4
│   │ │ └─┬ unist-util-visit@1.4.1
│   │ │   └─┬ unist-util-visit-parents@2.1.2
│   │ │     └── unist-util-is@3.0.0
│   │ ├── vfile-location@2.0.6
│   │ └── xtend@4.0.2 deduped
│   ├─┬ structured-source@3.0.2
│   │ └── boundary@1.0.1
│   ├── traverse@0.6.6
│   └─┬ unified@6.2.0
│     ├── bail@1.0.4
│     ├── extend@3.0.2
│     ├── is-plain-obj@1.1.0
│     ├── trough@1.0.4
│     ├─┬ vfile@2.3.0
│     │ ├── is-buffer@1.1.6
│     │ ├── replace-ext@1.0.0
│     │ ├── unist-util-stringify-position@1.1.2
│     │ └─┬ vfile-message@1.1.1
│     │   └── unist-util-stringify-position@1.1.2 deduped
│     └── x-is-string@0.1.0
├── js-base64@2.6.2
├─┬ query-string@5.1.1
│ ├── decode-uri-component@0.2.0
│ ├── object-assign@4.1.1
│ └── strict-uri-encode@1.1.0
└── ramda@0.26.1

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!

Using NPMGraph

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!

dependency graph example

Aha! You can't escape from us draftjs-md-converter!

Fixing

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!

dependency graph example 2

If an alternative is not found, you may have to get your hands dirty.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment