vuejs / rollup-plugin-vue

Roll .vue files
https://vuejs.github.io/rollup-plugin-vue
MIT License
843 stars 148 forks source link

Use plugin along with vue-demi to write Universal Vue Libraries for Vue 2 & 3. #427

Open alvarosabu opened 3 years ago

alvarosabu commented 3 years ago

What problem does this feature solve?

vue-demi is an awesome utility that allows you to write Universal Vue Libraries for Vue 2 & 3.

At the moment, using rollup to build a library, will allow you to externalize Vue demi in the moment of build, similar to what Posva did here in vue-promise:

// rollup.config.js
...
output.globals = { 'vue-demi': 'VueDemi' }
...

const external = ['vue-demi']

However, this will only work if the lib you are implementing is only using typescript. If the lib requires to have .vue files for components, as an example, using rollup-plugin-vue in the build config will break the library for its use in vue 2 projects.

My wild guess is that this rollup plugin uses a specific version of vue because all errors prompt are related to rendering.

Steps to reproduce

  1. Clone/Fork this reproduction repo git clone https://github.com/alvarosaburido/vue-demi-universal-lib.git
  2. cd examples/vue-2-demo, yarn
  3. yarn serve

You will get a similar error in the console Screenshot 2021-01-04 at 09 01 50

In the main.js of the vue-2-demo (link here) you can toggle between the two versions of the lib:

Built with rollup-plugin-vue

import { createAwesomePlugin } from 'vue-demi-universal-lib';

or without it (This version doesn't have .vue files, only typescript ones and works correctly)

import { createAwesomePlugin } from 'vdul-next';

Let me know if it's something that could make sense for this plugin to have. It will open a gate for more universal plugins to avoid maintaining two codebases that double the workload for small scale libraries or new libraries that want to support both versions, doing bugfix or feature supplements twice is just no ideal.

Thanks

What does the proposed API look like?

My rough guess would be to use the isVue2 property from vue-demi to chose between compilers.

alvarosabu commented 3 years ago

@LinusBorg Thanks for the quick response:

Vue files for Vue 3 are compiled with @vue/sfc-compiler, while for Vue 2, vue-template-compiler is being used. these require different implementations in this rollup plugin, which is why, at least for now, the 6.0 major of this plugin supports Vue 3, and the 5.0 version supports Vue 2.

While theoretically possible to support both in one plugin, that would require a big rearchitecture that likely won't happen quickly.

What you can do though is install both versions of this plugin like this:

{ "dependencies": { "vue-demi": "^0.5.0" }, "devDependencies": { "rollup-plugin-vue": "^6.0.0", "rollup-plugin-vue2": "npm:rollup-plugin-vue@^5.0.0", "@vue/sfc-compiler": "^3.0.4", "vue-template-compiler": "^2.6.11", }, "scripts": { "build:vue2": "vue-demi-switch 2 && ROLLUP_VUE_VERSION=2 rollup -config rollup.config.js", "build:vue3": "vue-demi-switch 3 && ROLLUP_VUE_VERSION=3 rollup -config rollup.config.js" } } Then in rollup,config,js, use the right vue plugin depending on the env variable:

const vue2 = require('rollup-plugin-vue2') const vue = require('rollup-plugin-vue')

// ... let vuePlugin if (process.env.ROLLUP_VUE_VERSION === '2') { vuePlugin = vue2(/ options ... /) } else { vuePlugin = vue(/ options ... /) }

// ...

plugins: [ vuePlugin ] The above is untested, so likely needs some tweaking, but should be a valid roadpath for achieving your desired behavior.

I thought of something similar, but wouldn't this approach build 2 different bundles? (One esm for vue2 and the other for vue3). That would kinda kill the purpose of an isomorphic installation I guess.

I do understand that from an architectural level is no easy task tho.

LinusBorg commented 3 years ago

You will always need two different bundles as soon as components are involved, as Vue 2 and Vue 3 render functions are different.

And adding that, there are other breaking changes that will make it difficult to compile the exact same component for Vue 2 and Vue 3, i.e. v-model on a component expecting differently named events in Vue 2 vs. 3 (input vs update:modelValue).

So thinking about it, compiling components for both Vue 2 and 3 will only be possible for components that use shared APIs exclusively, and/or using additional bridging code that solves these kinds of changes - which might not be possible for all of them.

the use case of vue-demi is rather for vue plugins that don'T contain components.

alvarosabu commented 3 years ago

Make sense, thanks for the explanation.

beaubus commented 3 years ago

@LinusBorg Thanks for the quick response:

Vue files for Vue 3 are compiled with @vue/sfc-compiler, while for Vue 2, vue-template-compiler is being used. these require different implementations in this rollup plugin, which is why, at least for now, the 6.0 major of this plugin supports Vue 3, and the 5.0 version supports Vue 2.

While theoretically possible to support both in one plugin, that would require a big rearchitecture that likely won't happen quickly.

What you can do though is install both versions of this plugin like this:

{ "dependencies": { "vue-demi": "^0.5.0" }, "devDependencies": { "rollup-plugin-vue": "^6.0.0", "rollup-plugin-vue2": "npm:rollup-plugin-vue@^5.0.0", "@vue/sfc-compiler": "^3.0.4", "vue-template-compiler": "^2.6.11", }, "scripts": { "build:vue2": "vue-demi-switch 2 && ROLLUP_VUE_VERSION=2 rollup -config rollup.config.js", "build:vue3": "vue-demi-switch 3 && ROLLUP_VUE_VERSION=3 rollup -config rollup.config.js" } } Then in rollup,config,js, use the right vue plugin depending on the env variable:

const vue2 = require('rollup-plugin-vue2') const vue = require('rollup-plugin-vue')

// ... let vuePlugin if (process.env.ROLLUP_VUEVERSION === '2') { vuePlugin = vue2(/* options ... /) } else { vuePlugin = vue(/_ options ... */) }

// ...

plugins: [ vuePlugin ] The above is untested, so likely needs some tweaking, but should be a valid roadpath for achieving your desired behavior.

I thought of something similar, but wouldn't this approach build 2 different bundles? (One esm for vue2 and the other for vue3). That would kinda kill the purpose of an isomorphic installation I guess.

I do understand that from an architectural level is no easy task tho.

Created universal Vue2 & Vue3 component with a help of this thread: https://www.npmjs.com/package/@beaubus/single-file-upload-for-vue

Here is how I did it: https://www.beaubus.com/blog/make_npm_package_with_vue_single_file_component_sfc_working_with_vue_2_and_3.html