Debugging Astro and Yarn Berry + nodeLinker pnpm
Hey, everyone! Hope you are all doing well, in this post, I’m going to do a quick share about my experiences in debugging stuff when I was building something with Astro and Yarn Berry (with `nodeLinker: pnpm`
).
What, I thought pnpm is a similar tool to npm and Yarn?
Yes, that is true, from a perspective. However, Yarn happens to have this thing called nodeLinker. There are 3 values available for this config:
`node-modules`
, which is the default one (used in Yarn Classic and npm).`pnp`
, which is a mode where we don’t use`node_modules`
. Instead, there are two files called`.pnp.cjs`
and`.pnp.loader.mjs`
. An example can be seen in this astro-yarn-berry-pnp repository. To have the best development experience, we are required to set up Editor SDKs.`pnpm`
, which is a somewhat middle ground between`node-modules`
and`pnp`
. While`pnp`
completely “flattens” the dependency structure and`node-modules`
mode has a chance for hoisting issues,`pnpm`
can be seen as the best of both worlds. It still creates a`node_modules`
folder, but it uses symlinks so that the packages and dependencies can find each other.
The issue: using Astro with React Integration
I have prepared this sandbox here, containing a setup of a broken Astro with React integration. The error is as follows:
This happened right after I did `yarn astro add react`
, which is exactly the command from the guide in the @astrojs/react documentation.
So, what exactly happens here?
From the error, we could see that somewhat the package `@vitejs/plugin-react`
can’t find `vite`
. Curious, right? Now let’s inspect the `package.json`
of `@vitejs/plugin-react`
deeper. Is the `vite`
dependency not included in the `package.json`
?
OK, it seems like `vite`
is defined in the `package.json`
, but as `peerDependencies`
. Now, what is `peerDependencies`
? According to the npm documentation of peerDependencies, it is a field in `package.json`
that specifies the list of dependencies that the “host project” should include.
Alright, with that in mind, we will ask, “Who installs this `@vitejs/plugin-react`
?” Since this error only started happening after we installed `@astrojs/react`
, we can be sure that it’s the culprit. So, let’s inspect the `package.json`
of `@astrojs/react`
.
Okay, so it defines `@vitejs/plugin-react`
as `dependencies`
and `vite`
as `devDependencies`
. What does this mean? This means that, when we’re installing from the package registry (e.g. npm registry), it will install `@vitejs/plugin-react`
and `ultrahtml`
, but not others that are defined in `devDependencies`
. This is because `dependencies`
are “transitive” dependencies, which are installed at all times, whereas `devDependencies`
will only be installed when we’re developing the package on our local machine.
So, that’s the answer to why `vite`
is not defined, because we don’t install it.
So, is the solution simply just to install vite in our host project?
Yes, installing `vite`
on the host project seems to do the trick, as could be seen in this sandbox, the Vite-related error doesn’t show up anymore. However, this seems to be fixed only if we’re working on a single-repo setup. What if we are using monorepo?
For monorepo, it’s more tricky. I have created a sandbox that reproduced the monorepo example. Here, we have a structure like the following:
Now, the difference between the monorepo structure and single project structure is that, I think in monorepo there are some kind of hoisting issues that caused `@astrojs/react`
to not be able to find `vite`
, despite that we have installed `vite`
inside the `astro`
monorepo package. So now, what do we do? I found out that we might need to play around a configuration in `yarnrc.yml`
called packageExtensions. Here’s what we’re adding:
What do the above additions do? So, we have learned that `@astrojs/react`
doesn’t have `vite`
transitive dependency in it, right? And in this case, we have also installed `vite`
in our `astro`
monorepo package. So, what’s missing is that, the “bridge” between `@astrojs/react`
and our `astro`
monorepo package. We can implement that “bridging” by “patching” the `peerDependencies`
of `@astrojs/react`
. By adding `vite`
into its `peerDependencies`
, we tell `@astrojs/react`
that, “Hey, the `vite`
package is installed, but not here. It’s installed in the host project.”
And, voila! Now you will be able to run Astro without any errors, because `@vitejs/plugin-react`
will ask `@astrojs/react`
for `vite`
, and `@astrojs/react`
will ask the monorepo package `astro`
for `vite`
, which we have installed already as a `devDependencies`
. This sandbox demonstrated the fix.
Summary
So, what do you do when you face dependency-related errors? Say, dependency A is missing.
- First, check if dependency A exists in the
`package.json`
of the dependency that requires it (say, dependency B). - If dependency A exists in dependency B’s
`package.json`
and somehow it’s missing from`node_modules`
, maybe it’s a good time to re-`yarn`
. - If dependency A exists in dependency B’s
`package.json`
but only as`peerDependencies`
, ensure that our project has dependency A installed. - If we have installed dependency A in our project but somehow we can’t still resolve the dependency, then we might need to “link” (or “patch”) dependency B’s
`package.json`
using`packageExtensions`
.
Hopefully, that’s useful. Until next time and stay safe!