DEV Community

Jono M
Jono M

Posted on

Dependency hoisting in Yarn monorepos

I ran into a strange problem the other day which demonstrates one of the problems with monorepos, at least when using Yarn 1.

I noticed that adding @testing-library/react to one of our packages caused tests in another unrelated package to suddenly start failing. This turned out to be a weird quirk of how Yarn "hoists" dependencies.

As a general rule, if you only have one version of a dependency in a workspace, Yarn will put it in node_modules at the top level. This avoids duplicating packages unnecessarily. However, sometimes different packages will depend on different versions of a library, in which case Yarn will put the version used by the majority of packages at the top level and create separate node_modules folders in the individual package directories for packages which depend on a different version.

The cause of the failing tests turned out to be this strange issue:

  • In our monorepo, we have several packages built in React 18 and several built in React 17
  • This means we have packages depending on @testing-library/react versions 14 and 12, which are compatible with the respective React versions
  • We had more packages using React 18, but more packages using @testing-library/react version 12 (compatible with React 17)

This meant that we ended up with directories like this:

node_modules/
  @testing-library/
    react/ -- v12
      node_modules/
        react/ -- *v17, because the top level package is 18*

  react/ -- v18

packages/
  some-unrelated-package/
    node_modules/
      react/ -- v17
Enter fullscreen mode Exit fullscreen mode

You'll notice there are two copies of React 17 here - one because the package itself depends on React, and one because the testing library at the top level depends on a different version of React than the top level one.

The end result was that both versions of React were loaded in tests (one by the code and one by the testing library), which of course got React super confused and caused a bunch of strange errors.

Solutions

The best solution here would obviously be to upgrade everything to the same version of React. In fact, I'd say the moral of this story is to keep your dependencies in sync as much as possible in monorepos to avoid this kind of problem from the start.

But a couple of workarounds I found were

  • Just test more packages. If every package with React is tested with that library, the number of packages depending on each version matches and the right versions get hoisted
  • Use the nohoist property in package.json to force @testing-library/react not to be put at the top level. This means there will always be a copy per package, which will always use the nearest react (i.e. the same one as the package itself)

This is one of the trickier pitfalls I've run into with monorepos, and one you should absolutely try to avoid!

Top comments (0)