diff --git a/src/to-vue-2.js b/src/to-vue-2.js index 4e46573..f8c8294 100644 --- a/src/to-vue-2.js +++ b/src/to-vue-2.js @@ -1,8 +1,16 @@ -import Vue from 'vue'; import frag from 'vue-frag'; -import {createApp, h} from 'vue3'; import {vue3ProxyNode} from './utils'; +let Vue2; +try { + Vue2 = require('vue'); +} catch {} + +let Vue3; +try { + Vue3 = require('vue3'); +} catch {} + const camelizeRE = /-(\w)/g; function normalizeEventName(eventName) { @@ -53,7 +61,7 @@ const renderVue2Vnode = /* Vue 3 component */ { mounted() { const vm = this; - this.vue2App = (new Vue({ + this.vue2App = (new Vue2({ beforeCreate() { this.$parent = vm.parent; }, @@ -82,14 +90,14 @@ const renderVue2Vnode = /* Vue 3 component */ { this.vue2App.$forceUpdate(); } - return h('div'); + return Vue3.h('div'); }, }; function interopSlots(ctx) { const scopedSlots = {}; for (const slotName in ctx.$scopedSlots) { - scopedSlots[slotName] = () => h(renderVue2Vnode, { + scopedSlots[slotName] = () => Vue3.h(renderVue2Vnode, { parent: ctx, vnode: ctx.$scopedSlots[slotName], }); @@ -127,8 +135,8 @@ const vue2WrapperBase = { // Delay until mounted for SSR mounted() { const vm = this; - this.v3app = createApp({ - render: () => h( + this.v3app = Vue3.createApp({ + render: () => Vue3.h( this.$options.component, mergeAttrsListeners(this), interopSlots(this), @@ -171,9 +179,32 @@ const vue2WrapperBase = { }; const toVue2 = vue3Component => { + if (!Vue2 && !Vue3) { + throw new Error('Vue 2 & 3 were not resolved with bare specifiers "vue" & "vue3". Register them with toVue3.register(Vue2, Vue3)'); + } + + if (!Vue2) { + throw new Error('Vue 2 was not resolved with bare specifier "vue". Register it with toVue3.register(Vue)'); + } + + if (!Vue3) { + throw new Error('Vue 3 was not resolved with bare specifier "vue3". Register it with toVue3.register(Vue3) or toVue3.register({ createApp, h })'); + } + const vue2Wrapper = Object.create(vue2WrapperBase); vue2Wrapper.component = vue3Component; return vue2Wrapper; }; +toVue2.register = function () { + for (let i = 0; i < arguments.length; i += 1) { // eslint-disable-line unicorn/no-for-loop + const Vue = arguments[i]; + if (typeof Vue === 'function' && Vue.version && Vue.version.startsWith('2')) { + Vue2 = Vue; + } else if (Vue.createApp && Vue.h) { + Vue3 = Vue; + } + } +}; + export default toVue2; diff --git a/src/to-vue-3.js b/src/to-vue-3.js index 08e7021..4263234 100644 --- a/src/to-vue-3.js +++ b/src/to-vue-3.js @@ -1,7 +1,15 @@ -import Vue from 'vue'; -import {createApp, h} from 'vue3'; import {vue3ProxyNode} from './utils'; +let Vue2; +try { + // Vue2 = require('vue'); +} catch {} + +let Vue3; +try { + // Vue3 = require('vue3'); +} catch {} + const hyphenateRE = /\B([A-Z])/g; const hyphenate = string => string.replace(hyphenateRE, '-$1').toLowerCase(); @@ -54,7 +62,7 @@ const renderVue3Vnode = { props: ['parent', 'vnode'], mounted() { - this.vue3App = createApp({ + this.vue3App = Vue3.createApp({ render: () => this.vnode(), }); @@ -112,7 +120,7 @@ const vue3WrapperBase = { const vm = this; const mountElement = this.$el; - this.v2 = new Vue({ + this.v2 = new Vue2({ provide() { return new Proxy(vm._.provides, { getOwnPropertyDescriptor(target, key) { @@ -170,7 +178,7 @@ const vue3WrapperBase = { this.v2.$forceUpdate(); } - return h('div'); + return Vue3.h('div'); }, }; @@ -181,6 +189,18 @@ const getProvidedMixin = { }; const toVue3 = vue2Component => { + if (!Vue2 && !Vue3) { + throw new Error('Vue 2 & 3 were not resolved with bare specifiers "vue" & "vue3". Register them with toVue3.register(Vue2, Vue3)'); + } + + if (!Vue2) { + throw new Error('Vue 2 was not resolved with bare specifier "vue". Register it with toVue3.register(Vue)'); + } + + if (!Vue3) { + throw new Error('Vue 3 was not resolved with bare specifier "vue3". Register it with toVue3.register(Vue3) or toVue3.register({ createApp, h })'); + } + const component = Object.create(vue2Component); component.mixins = [getProvidedMixin].concat(vue2Component.mixins || []); @@ -190,4 +210,15 @@ const toVue3 = vue2Component => { return vue3Wrapper; }; +toVue3.register = function () { + for (let i = 0; i < arguments.length; i += 1) { // eslint-disable-line unicorn/no-for-loop + const Vue = arguments[i]; + if (typeof Vue === 'function' && Vue.version && Vue.version.startsWith('2')) { + Vue2 = Vue; + } else if (Vue.createApp && Vue.h) { + Vue3 = Vue; + } + } +}; + export default toVue3; diff --git a/test/to-vue-2.spec.js b/test/to-vue-2.spec.js index 358b542..9d950db 100644 --- a/test/to-vue-2.spec.js +++ b/test/to-vue-2.spec.js @@ -1,6 +1,10 @@ const {default: toVue2} = require('vue-2-3/to-vue-2'); -const {createApp, provide, inject, nextTick} = require('vue3'); +const Vue = require('vue'); +const Vue3 = require('vue3'); const {mount} = require('@vue/test-utils'); +const {createApp, provide, inject, nextTick} = Vue3; + +toVue2.register(Vue, Vue3); describe('Error handling', () => { test('throw error when used in vue 3 app', () => { diff --git a/test/to-vue-3.spec.js b/test/to-vue-3.spec.js index dadcfba..907e9c3 100644 --- a/test/to-vue-3.spec.js +++ b/test/to-vue-3.spec.js @@ -1,8 +1,12 @@ const {default: toVue3} = require('vue-2-3/to-vue-3'); -const {createApp, nextTick} = require('vue3'); +const Vue3 = require('vue3'); const outdent = require('outdent'); const Vue = require('vue'); +const {createApp, nextTick} = Vue3; + +toVue3.register(Vue, Vue3); + let mountTarget; let app; beforeEach(() => {