vue-demi
vue-demi copied to clipboard
Shared `vue-demi` SFC Library & Yarn 3 Workspace Compatibility Issues
I've been having a difficult time working out how to build a universal component library with SFCs that can be used by both Vue2 and Vue3 apps bundled together in a Yarn 3 workspaces monorepo.
I have created a (failing) proof of concept project. The library builds, and the Vue3 app renders it correctly. However, the Vue 2 app will not build, and gives the following error:
failed to load config from <path to>demi-components-poc/packages/example-vue2/vite.config.ts
error when starting dev server:
Error:
Vue packages version mismatch:
- [email protected] (<path to>demi-components-poc/node_modules/vue/index.js)
- [email protected] (<path to>demi-components-poc/node_modules/vue-template-compiler/package.json)
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.
at Object.<anonymous> (<path to>demi-components-poc/node_modules/vue-template-compiler/index.js:10:9)
at Module._compile (node:internal/modules/cjs/loader:1101:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at Module.require (node:internal/modules/cjs/loader:1005:19)
at require (node:internal/modules/cjs/helpers:102:18)
at Object.<anonymous> (<path to>demi-components-poc/node_modules/vite-plugin-vue2/dist/utils/descriptorCache.js:30:42)
at Module._compile (node:internal/modules/cjs/loader:1101:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at Module.require (node:internal/modules/cjs/loader:1005:19)
at require (node:internal/modules/cjs/helpers:102:18)
at Object.<anonymous> (<path to>demi-components-poc/node_modules/vite-plugin-vue2/dist/main.js:29:27)
at Module._compile (node:internal/modules/cjs/loader:1101:14)
I've attempted various arrangements of vue-demi-switch
calls, but this exact error occurs regardless, so I've left that out of the PoC.
The library, as well as both Vue apps, are built using Vite. Is there an incompatibility with vue-demi
SFCs and Yarn workspaces, or maybe in how Vite loads Vue?
Here's the compiled JS:
import { defineComponent } from "vue-demi";
import { openBlock, createElementBlock } from "vue";
var _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props) {
target[key] = val;
}
return target;
};
const _sfc_main = defineComponent({
methods: {
action() {
console.log("Button Clicked");
}
}
});
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return openBlock(), createElementBlock("button", {
onClick: _cache[0] || (_cache[0] = (...args) => _ctx.action && _ctx.action(...args))
}, "Click");
}
var HelloWorld = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
export { HelloWorld };
But I'm not sure this has any bearing on anything. It seems to be more of a Vite and/or Yarn workspace dependency issue.
I think it more likely that vue-template-compiler
is grabing the v3 of vue
due to the yarn workspace hoisting. Maybe you can try adding this to your package.json
"installConfig": {
"hoistingLimits": "workspaces"
}
@antfu For some reason, that had no effect, but placing nmHoistingLimits: workspaces
in .yarnrc.yml
worked. After also adding a missing dependency, the Vue2 app builds and loads in the browser. However, the dependency from line 2 in the compiled JS causes an error, as it is still Vue3-specific.
The new error in the browser is:
Uncaught SyntaxError: The requested module '/node_modules/.vite/vue.js?v=c2bff7da' does not provide an export named 'createElementBlock'
Here's the new JS after adding the hoisting limits:
import { defineComponent } from "/node_modules/.vite/vue-demi.js?v=c2bff7da";
import { openBlock, createElementBlock } from "/node_modules/.vite/vue.js?v=c2bff7da";
var _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props) {
target[key] = val;
}
return target;
};
const _sfc_main = defineComponent({
methods: {
action() {
console.log("Button Clicked");
}
}
});
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return openBlock(), createElementBlock("button", {
onClick: _cache[0] || (_cache[0] = (...args) => _ctx.action && _ctx.action(...args))
}, "Click");
}
var HelloWorld = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
export { HelloWorld };
I think these are referenced in the Vue SFC compiler. Is there any way to get vue-demi
to pick these up during the compilation process, or is it not possible to use SFCs in a vue-demi
project?
I've updated the PoC repo, if you would like to poke at it.
I just found one project, vue3-sketch-ruler, uses a build process that creates separate libraries for 2 & 3, combined into a single package with a postinstall
process that determines which library should be used.
My best current working solution is in the PoC repo. It has the following:
- Two different version-specific packages, each with a version-specific build process
- Each package's build tool is pointed to an external, shared source directory, containing components that use
vue-demi
- Each package outputs their build artifacts to a third package
- The third package, which is the one that will be published, builds functionality that conditionally switches between the two versions at run-time
My only other thought to simplify this process is to have one library package that uses multiple build files, but that would require additional tooling like yarn-plugin-conditions (which currently does to seem to work with Vue3) to switch dependencies in the package manager itself.