Debugging npm dependencies

Earlier today one of our projects randomly started failing tests on our continuous integration platform. This is not the first time something like this has happened. The cause - unpinned dependencies.

It wasn’t that we had not pinned our dependencies to a specific version. We always do. It was that one of our dependencies had an unpinned dependency that had updated.

Although this issues can be solved by using a lock file, or other package management tool / installer, I thought I’d explain how best to solve this in case other people come across similar issues.

The first thing we need to do is figure out which dependency is causing the problem. Luckily, in our case it was fairly obvious as the tests that were failing were snapshots, and the offending component had a props with the name of the library in it. In other cases you may have to look at a stack trace, and or manually figure out what libraries are being used in that area.

Here’s part of the output from our CI:

Received value does not match stored snapshot 1.

- Snapshot
+ Received

@@ -112,19 +112,27 @@
                      Object {
                        "display": "inline-block",
                      }
                    }
                  >
+                  <style
+                    dangerouslySetInnerHTML={
+                      Object {
+                        "__html": "input#undefined::-ms-clear {display: none;}",
+                      }
+                    }
+                  />
                    <input
                      aria-activedescendant="react-select-2--value"
                      aria-describedby={undefined}
                      aria-expanded="false"
                      aria-haspopup="false"
                      aria-label={undefined}
                      aria-labelledby={undefined}
                      aria-owns=""
                      className={undefined}
+                    id={undefined}
                      onBlur={[Function]}
                      onChange={[Function]}
                      onFocus={[Function]}
                      required={false}
                      role="combobox"
@@ -218,19 +226,27 @@
                      Object {
                        "display": "inline-block",
                      }
                    }
                  >

You can easily see here that the problem is with react-select. Don’t get me started on this library, I’ve had so many issues with it.

Anyway, if we have a look at the install logs from the CI we can find out what version of react-select was installed.

├── react-router-redux@4.0.8
├─┬ react-select@1.0.0-rc.5
│ └── react-input-autosize@1.2.0
├── react-test-renderer@15.5.4
├─┬ react-transition-group@1.1.3

Okay, so we’re using a pre-release version - 1.0.0-rc.5. Let’s find out what version I have installed locally, since the project is running fine there.

Running npm list will list all of the npm dependencies installed within the project, and then we can look for react-select in there.

├── react-router-redux@4.0.8
├─┬ react-select@1.0.0-rc.5
│ └── react-input-autosize@1.1.4
├── react-test-renderer@15.5.4
├─┬ react-transition-group@1.1.3

Hmmm… Same version of react-select is installed locally. What now? Oh, look, the version of react-input-autosize, one of react-select’s dependencies is different from that of the CI. It must not be pinned inside react-select. Not a problem. All we have to do is install a specific version to our dependencies and npm should always use this.

In this case I chose to use the version that I had installed locally because I knew it worked already, and did not have time to test a new version out since all of our CI tests failing was preventing any code getting merged.

npm install react-input-autosize@1.1.4 --save --save-exact

Problem solved.

…until another one of their dependencies releases a new version…

…maybe we should start using lock files.