wip: test for legacy component compat
This commit is contained in:
parent
3963f2e963
commit
6db7c00b42
@ -0,0 +1,62 @@
|
||||
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()
|
||||
})
|
||||
})
|
@ -0,0 +1,61 @@
|
||||
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()
|
||||
})
|
@ -1,22 +1,12 @@
|
||||
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,
|
||||
@ -56,109 +46,3 @@ export function convertLegacyComponent(
|
||||
|
||||
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
|
||||
}
|
||||
|
56
packages/runtime-core/src/compat/componentAsync.ts
Normal file
56
packages/runtime-core/src/compat/componentAsync.ts
Normal file
@ -0,0 +1,56 @@
|
||||
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
|
||||
}
|
63
packages/runtime-core/src/compat/componentFunctional.ts
Normal file
63
packages/runtime-core/src/compat/componentFunctional.ts
Normal file
@ -0,0 +1,63 @@
|
||||
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
|
||||
}
|
@ -19,7 +19,7 @@ import {
|
||||
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'
|
||||
|
Loading…
Reference in New Issue
Block a user