Today I learned: Utilizing pnpm packageExtensions to fix broken dependencies
About 2 min reading time
I'm working on the migration of a monorepo from Vue.js 2 to Vue.js 3 right now. As we are in a monorepo we can migrate our applications stepwise but got into a situation where suddenly our Vue.js 2 tests failed with the following error:
Vue packages version mismatch: - email@example.com (...) - firstname.lastname@example.org (...) This may cause things to work incorrectly. Make sure to use the same version for both. If you are using vue-loader@>=10.0, simply update vue-template-compiler. If you are using vue-loader@<10.0 or vueify, re-installing vue-loader/vueify should bump `vue-template-compiler` to the latest.
This didn't make sense to me at first because we don't have any project that uses
vue-template-compiler and Vue.js in version 3. So, why do we see this conflict?
Cause 1: Hoisting
Hoisting in node package managers refers to the behavior where dependencies that are required by multiple packages in a project are installed only once, at the highest level possible in the package tree. This reduces duplication and helps conserve disk space, since each dependency only needs to be installed once. In our case, our package manager moved Vue.js and the
vue-template-compiler into the root
node_modules folder. So, we ended with Vue.js 2 and Vue.js 3 in our root
vue-template-compiler is designed with only one Vue.js version in the project
vue-template-compiler defines its dependency on Vue.js with the "version" "
file:../.." so it assumes that the Vue.js library is in the same
node_modules folder as the
vue-template-compiler and can refer it via a local path (valid in a
// vue-template-compiler doesn't specify a Vue.js version
This sounds strange, but it makes somehow sense: When
vue-template-compiler is installed as a dependency of a Vue.js 2 project, it needs to use the same version of Vue.js as the project itself, to ensure compatibility between the compiled templates and the runtime Vue.js library.
To achieve this,
vue-template-compiler uses a local path to the Vue.js package that is installed in the project's
node_modules directory, rather than relying on a global or external version of vue.
So, the problem was, that my package manager moves both dependencies to the root
node_modules folder of my project and the
vue-template-compiler tries to work with the wrong Vue.js version.
PNPM to the rescue
Now there are two ways to fix this with pnpm (thanks StackOverflow):
- you can use pnpm to disable hoisting, which was not possible in our project
- you can use pnpm packageExtensions to "fix" the dependencies
In your root
package.json this is possible by adding the following code:
"vue": "<your Vue.js version>"
Please note that you can do similar stuff with other package managers, too. I use pnpm a lot, so this did the trick for me. So if you are on a different package manager, maybe this helps you to find a good solution.
Basically, this fixed my setup, and it can be helpful if you need to adjust some libraries dependencies to work in your specific environment.