vueuse / vue-demi

🎩 Creates Universal Library for Vue 2 & 3
MIT License
2.98k stars 155 forks source link

Shared `vue-demi` SFC Library & Yarn 3 Workspace Compatibility Issues #139

Open phobetron opened 2 years ago

phobetron commented 2 years ago

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:

- vue@3.2.31 (<path to>demi-components-poc/node_modules/vue/index.js)
- vue-template-compiler@2.6.14 (<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?

phobetron commented 2 years ago

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.

antfu commented 2 years ago

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"
  }
phobetron commented 2 years ago

@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.

phobetron commented 2 years ago

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:

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.