--- /dev/null
+import Vue from '@vue/compat'
+import {
+ DeprecationTypes,
+ deprecationData,
+ toggleDeprecationWarning
+} from '../compatConfig'
+
+beforeEach(() => {
+ toggleDeprecationWarning(true)
+ Vue.configureCompat({
+ MODE: 2,
+ GLOBAL_MOUNT: 'suppress-warning'
+ })
+})
+
+afterEach(() => {
+ toggleDeprecationWarning(false)
+ Vue.configureCompat({ MODE: 3 })
+})
+
+const timeout = (n: number) => new Promise(r => setTimeout(r, n))
+
+describe('COMPONENT_ASYNC', () => {
+ test('resolve/reject', async () => {
+ let resolve: any
+ const comp = (r: any) => {
+ resolve = r
+ }
+ const vm = new Vue({
+ template: `<div><comp/></div>`,
+ components: { comp }
+ }).$mount()
+ expect(vm.$el.innerHTML).toBe(`<!---->`)
+
+ resolve({ template: 'foo' })
+ await timeout(0)
+ expect(vm.$el.innerHTML).toBe(`foo`)
+
+ expect(
+ (deprecationData[DeprecationTypes.COMPONENT_ASYNC].message as Function)(
+ comp
+ )
+ ).toHaveBeenWarned()
+ })
+
+ test('Promise', async () => {
+ const comp = () => Promise.resolve({ template: 'foo' })
+ const vm = new Vue({
+ template: `<div><comp/></div>`,
+ components: { comp }
+ }).$mount()
+ expect(vm.$el.innerHTML).toBe(`<!---->`)
+ await timeout(0)
+ expect(vm.$el.innerHTML).toBe(`foo`)
+
+ expect(
+ (deprecationData[DeprecationTypes.COMPONENT_ASYNC].message as Function)(
+ comp
+ )
+ ).toHaveBeenWarned()
+ })
+})
--- /dev/null
+import Vue from '@vue/compat'
+import {
+ DeprecationTypes,
+ deprecationData,
+ toggleDeprecationWarning
+} from '../compatConfig'
+
+beforeEach(() => {
+ toggleDeprecationWarning(true)
+ Vue.configureCompat({
+ MODE: 2,
+ GLOBAL_MOUNT: 'suppress-warning'
+ })
+})
+
+afterEach(() => {
+ toggleDeprecationWarning(false)
+ Vue.configureCompat({ MODE: 3 })
+})
+
+test('COMPONENT_FUNCTIONAL', async () => {
+ const func = {
+ name: 'Func',
+ functional: true,
+ props: {
+ x: String
+ },
+ inject: ['foo'],
+ render: (h: any, { data, props, injections, slots }: any) => {
+ return h('div', { id: props.x, class: data.class }, [
+ h('div', { class: 'inject' }, injections.foo),
+ h('div', { class: 'slot' }, slots().default)
+ ])
+ }
+ }
+
+ const vm = new Vue({
+ provide() {
+ return {
+ foo: 123
+ }
+ },
+ components: {
+ func
+ },
+ template: `<func class="foo" x="foo">hello</func>`
+ }).$mount()
+
+ expect(vm.$el.id).toBe('foo')
+ expect(vm.$el.className).toBe('foo')
+ expect(vm.$el.querySelector('.inject').textContent).toBe('123')
+ expect(vm.$el.querySelector('.slot').textContent).toBe('hello')
+ expect(vm.$el.outerHTML).toMatchInlineSnapshot(
+ `"<div id=\\"foo\\" class=\\"foo\\"><div class=\\"inject\\">123</div><div class=\\"slot\\">hello</div></div>"`
+ )
+
+ expect(
+ (deprecationData[DeprecationTypes.COMPONENT_FUNCTIONAL]
+ .message as Function)(func)
+ ).toHaveBeenWarned()
+})
-import { isArray, isFunction, isObject, isPromise } from '@vue/shared'
-import { defineAsyncComponent } from '../apiAsyncComponent'
-import {
- Component,
- ComponentInternalInstance,
- ComponentOptions,
- FunctionalComponent,
- getCurrentInstance
-} from '../component'
-import { resolveInjections } from '../componentOptions'
-import { InternalSlots } from '../componentSlots'
-import { isVNode } from '../vnode'
+import { isFunction, isObject } from '@vue/shared'
+import { Component, ComponentInternalInstance } from '../component'
import {
checkCompatEnabled,
- softAssertCompatEnabled,
- DeprecationTypes
+ DeprecationTypes,
+ softAssertCompatEnabled
} from './compatConfig'
-import { getCompatListeners } from './instanceListeners'
-import { compatH } from './renderFn'
+import { convertLegacyAsyncComponent } from './componentAsync'
+import { convertLegacyFunctionalComponent } from './componentFunctional'
export function convertLegacyComponent(
comp: any,
return comp
}
-
-interface LegacyAsyncOptions {
- component: Promise<Component>
- loading?: Component
- error?: Component
- delay?: number
- timeout?: number
-}
-
-type LegacyAsyncReturnValue = Promise<Component> | LegacyAsyncOptions
-
-type LegacyAsyncComponent = (
- resolve?: (res: LegacyAsyncReturnValue) => void,
- reject?: (reason?: any) => void
-) => LegacyAsyncReturnValue | undefined
-
-const normalizedAsyncComponentMap = new Map<LegacyAsyncComponent, Component>()
-
-function convertLegacyAsyncComponent(comp: LegacyAsyncComponent) {
- if (normalizedAsyncComponentMap.has(comp)) {
- return normalizedAsyncComponentMap.get(comp)!
- }
-
- // we have to call the function here due to how v2's API won't expose the
- // options until we call it
- let resolve: (res: LegacyAsyncReturnValue) => void
- let reject: (reason?: any) => void
- const fallbackPromise = new Promise<Component>((r, rj) => {
- ;(resolve = r), (reject = rj)
- })
-
- const res = comp(resolve!, reject!)
-
- let converted: Component
- if (isPromise(res)) {
- converted = defineAsyncComponent(() => res)
- } else if (isObject(res) && !isVNode(res) && !isArray(res)) {
- converted = defineAsyncComponent({
- loader: () => res.component,
- loadingComponent: res.loading,
- errorComponent: res.error,
- delay: res.delay,
- timeout: res.timeout
- })
- } else if (res == null) {
- converted = defineAsyncComponent(() => fallbackPromise)
- } else {
- converted = comp as any // probably a v3 functional comp
- }
- normalizedAsyncComponentMap.set(comp, converted)
- return converted
-}
-
-const normalizedFunctionalComponentMap = new Map<
- ComponentOptions,
- FunctionalComponent
->()
-
-export const legacySlotProxyHandlers: ProxyHandler<InternalSlots> = {
- get(target, key: string) {
- const slot = target[key]
- return slot && slot()
- }
-}
-
-function convertLegacyFunctionalComponent(comp: ComponentOptions) {
- if (normalizedFunctionalComponentMap.has(comp)) {
- return normalizedFunctionalComponentMap.get(comp)!
- }
-
- const legacyFn = comp.render as any
-
- const Func: FunctionalComponent = (props, ctx) => {
- const instance = getCurrentInstance()!
-
- const legacyCtx = {
- props,
- children: instance.vnode.children || [],
- data: instance.vnode.props || {},
- scopedSlots: ctx.slots,
- parent: instance.parent && instance.parent.proxy,
- slots() {
- return new Proxy(ctx.slots, legacySlotProxyHandlers)
- },
- get listeners() {
- return getCompatListeners(instance)
- },
- get injections() {
- if (comp.inject) {
- const injections = {}
- resolveInjections(comp.inject, {})
- return injections
- }
- return {}
- }
- }
- return legacyFn(compatH, legacyCtx)
- }
- Func.props = comp.props
- Func.displayName = comp.name
- // v2 functional components do not inherit attrs
- Func.inheritAttrs = false
-
- normalizedFunctionalComponentMap.set(comp, Func)
- return Func
-}
--- /dev/null
+import { isArray, isObject, isPromise } from '@vue/shared'
+import { defineAsyncComponent } from '../apiAsyncComponent'
+import { Component } from '../component'
+import { isVNode } from '../vnode'
+
+interface LegacyAsyncOptions {
+ component: Promise<Component>
+ loading?: Component
+ error?: Component
+ delay?: number
+ timeout?: number
+}
+
+type LegacyAsyncReturnValue = Promise<Component> | LegacyAsyncOptions
+
+type LegacyAsyncComponent = (
+ resolve?: (res: LegacyAsyncReturnValue) => void,
+ reject?: (reason?: any) => void
+) => LegacyAsyncReturnValue | undefined
+
+const normalizedAsyncComponentMap = new Map<LegacyAsyncComponent, Component>()
+
+export function convertLegacyAsyncComponent(comp: LegacyAsyncComponent) {
+ if (normalizedAsyncComponentMap.has(comp)) {
+ return normalizedAsyncComponentMap.get(comp)!
+ }
+
+ // we have to call the function here due to how v2's API won't expose the
+ // options until we call it
+ let resolve: (res: LegacyAsyncReturnValue) => void
+ let reject: (reason?: any) => void
+ const fallbackPromise = new Promise<Component>((r, rj) => {
+ ;(resolve = r), (reject = rj)
+ })
+
+ const res = comp(resolve!, reject!)
+
+ let converted: Component
+ if (isPromise(res)) {
+ converted = defineAsyncComponent(() => res)
+ } else if (isObject(res) && !isVNode(res) && !isArray(res)) {
+ converted = defineAsyncComponent({
+ loader: () => res.component,
+ loadingComponent: res.loading,
+ errorComponent: res.error,
+ delay: res.delay,
+ timeout: res.timeout
+ })
+ } else if (res == null) {
+ converted = defineAsyncComponent(() => fallbackPromise)
+ } else {
+ converted = comp as any // probably a v3 functional comp
+ }
+ normalizedAsyncComponentMap.set(comp, converted)
+ return converted
+}
--- /dev/null
+import {
+ ComponentOptions,
+ FunctionalComponent,
+ getCurrentInstance
+} from '../component'
+import { resolveInjections } from '../componentOptions'
+import { InternalSlots } from '../componentSlots'
+import { getCompatListeners } from './instanceListeners'
+import { compatH } from './renderFn'
+
+const normalizedFunctionalComponentMap = new Map<
+ ComponentOptions,
+ FunctionalComponent
+>()
+
+export const legacySlotProxyHandlers: ProxyHandler<InternalSlots> = {
+ get(target, key: string) {
+ const slot = target[key]
+ return slot && slot()
+ }
+}
+
+export function convertLegacyFunctionalComponent(comp: ComponentOptions) {
+ if (normalizedFunctionalComponentMap.has(comp)) {
+ return normalizedFunctionalComponentMap.get(comp)!
+ }
+
+ const legacyFn = comp.render as any
+
+ const Func: FunctionalComponent = (props, ctx) => {
+ const instance = getCurrentInstance()!
+
+ const legacyCtx = {
+ props,
+ children: instance.vnode.children || [],
+ data: instance.vnode.props || {},
+ scopedSlots: ctx.slots,
+ parent: instance.parent && instance.parent.proxy,
+ slots() {
+ return new Proxy(ctx.slots, legacySlotProxyHandlers)
+ },
+ get listeners() {
+ return getCompatListeners(instance)
+ },
+ get injections() {
+ if (comp.inject) {
+ const injections = {}
+ resolveInjections(comp.inject, injections)
+ return injections
+ }
+ return {}
+ }
+ }
+ return legacyFn(compatH, legacyCtx)
+ }
+ Func.props = comp.props
+ Func.displayName = comp.name
+ // v2 functional components do not inherit attrs
+ Func.inheritAttrs = false
+
+ normalizedFunctionalComponentMap.set(comp, Func)
+ return Func
+}
import { off, on, once } from './instanceEventEmitter'
import { getCompatListeners } from './instanceListeners'
import { shallowReadonly } from '@vue/reactivity'
-import { legacySlotProxyHandlers } from './component'
+import { legacySlotProxyHandlers } from './componentFunctional'
import { compatH } from './renderFn'
import { createCommentVNode, createTextVNode } from '../vnode'
import { renderList } from '../helpers/renderList'