+++ /dev/null
-import Vue from '@vue/compat'
-
-test('should work', async () => {
- const el = document.createElement('div')
- el.innerHTML = `{{ msg }}`
- new Vue({
- el,
- data() {
- return {
- msg: 'hello'
- }
- }
- })
- expect('global app bootstrapping API has changed').toHaveBeenWarned()
- expect(el.innerHTML).toBe('hello')
-})
--- /dev/null
+import Vue from '@vue/compat'
+
+describe('compat: global API', () => {
+ test('should work', async () => {
+ const el = document.createElement('div')
+ el.innerHTML = `{{ msg }}`
+ new Vue({
+ el,
+ data() {
+ return {
+ msg: 'hello'
+ }
+ }
+ })
+ expect('global app bootstrapping API has changed').toHaveBeenWarned()
+ expect(el.innerHTML).toBe('hello')
+ })
+})
--- /dev/null
+import { ShapeFlags } from '@vue/shared/src'
+import { createComponentInstance } from '../../component'
+import { setCurrentRenderingInstance } from '../../componentRenderContext'
+import { DirectiveBinding } from '../../directives'
+import { createVNode } from '../../vnode'
+import { compatH as h } from '../renderFn'
+
+describe('compat: render function', () => {
+ const mockDir = {}
+ const mockChildComp = {}
+ const mockComponent = {
+ directives: {
+ mockDir
+ },
+ components: {
+ foo: mockChildComp
+ }
+ }
+ const mockInstance = createComponentInstance(
+ createVNode(mockComponent),
+ null,
+ null
+ )
+ beforeEach(() => {
+ setCurrentRenderingInstance(mockInstance)
+ })
+ afterEach(() => {
+ setCurrentRenderingInstance(null)
+ })
+
+ test('string component lookup', () => {
+ expect(h('foo')).toMatchObject({
+ type: mockChildComp
+ })
+ })
+
+ test('class / style / attrs / domProps / props', () => {
+ expect(
+ h('div', {
+ class: 'foo',
+ style: { color: 'red' },
+ attrs: {
+ id: 'foo'
+ },
+ domProps: {
+ innerHTML: 'hi'
+ },
+ props: {
+ myProp: 'foo'
+ }
+ })
+ ).toMatchObject({
+ props: {
+ class: 'foo',
+ style: { color: 'red' },
+ id: 'foo',
+ innerHTML: 'hi',
+ myProp: 'foo'
+ }
+ })
+ })
+
+ test('on / nativeOn', () => {
+ const fn = () => {}
+ expect(
+ h('div', {
+ on: {
+ click: fn,
+ fooBar: fn
+ },
+ nativeOn: {
+ click: fn,
+ 'bar-baz': fn
+ }
+ })
+ ).toMatchObject({
+ props: {
+ onClick: fn, // should dedupe
+ onFooBar: fn,
+ 'onBar-baz': fn
+ }
+ })
+ })
+
+ test('directives', () => {
+ expect(
+ h('div', {
+ directives: [
+ {
+ name: 'mock-dir',
+ value: '2',
+ // expression: '1 + 1',
+ arg: 'foo',
+ modifiers: {
+ bar: true
+ }
+ }
+ ]
+ })
+ ).toMatchObject({
+ dirs: [
+ {
+ dir: mockDir,
+ instance: mockInstance.proxy,
+ value: '2',
+ oldValue: void 0,
+ arg: 'foo',
+ modifiers: {
+ bar: true
+ }
+ }
+ ] as DirectiveBinding[]
+ })
+ })
+
+ test('scopedSlots', () => {
+ const scopedSlots = {
+ default() {}
+ }
+ const vnode = h(mockComponent, {
+ scopedSlots
+ })
+ expect(vnode).toMatchObject({
+ children: scopedSlots
+ })
+ expect('scopedSlots' in vnode.props!).toBe(false)
+ expect(vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN).toBeTruthy()
+ })
+
+ test('legacy named slot', () => {
+ const vnode = h(mockComponent, [
+ 'text',
+ h('div', { slot: 'foo' }, 'one'),
+ h('div', { slot: 'bar' }, 'two'),
+ h('div', { slot: 'foo' }, 'three'),
+ h('div', 'four')
+ ])
+ expect(vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN).toBeTruthy()
+ const slots = vnode.children as any
+
+ // default
+ expect(slots.default()).toMatchObject(['text', { children: 'four' }])
+ expect(slots.foo()).toMatchObject([
+ { children: 'one' },
+ { children: 'three' }
+ ])
+ expect(slots.bar()).toMatchObject([{ children: 'two' }])
+ })
+})
import { extend } from '@vue/shared'
-import { ComponentOptions, getCurrentInstance } from '../component'
+import { ComponentInternalInstance, ComponentOptions } from '../component'
import { DeprecationTypes, warnDeprecation } from './deprecations'
export type CompatConfig = Partial<
extend(globalCompatConfig, config)
}
-export function getCompatConfigForKey(key: DeprecationTypes | 'MODE') {
- const instance = getCurrentInstance()
+export function getCompatConfigForKey(
+ key: DeprecationTypes | 'MODE',
+ instance: ComponentInternalInstance | null
+) {
const instanceConfig =
instance && (instance.type as ComponentOptions).compatConfig
if (instanceConfig && key in instanceConfig) {
return globalCompatConfig[key]
}
-export function isCompatEnabled(key: DeprecationTypes): boolean {
- const mode = getCompatConfigForKey('MODE') || 2
- const val = getCompatConfigForKey(key)
+export function isCompatEnabled(
+ key: DeprecationTypes,
+ instance: ComponentInternalInstance | null
+): boolean {
+ const mode = getCompatConfigForKey('MODE', instance) || 2
+ const val = getCompatConfigForKey(key, instance)
if (mode === 2) {
return val !== false
} else {
}
}
-export function assertCompatEnabled(key: DeprecationTypes, ...args: any[]) {
- if (!isCompatEnabled(key)) {
+export function assertCompatEnabled(
+ key: DeprecationTypes,
+ instance: ComponentInternalInstance | null,
+ ...args: any[]
+) {
+ if (!isCompatEnabled(key, instance)) {
throw new Error(`${key} compat has been disabled.`)
} else if (__DEV__) {
- warnDeprecation(key, ...args)
+ warnDeprecation(key, instance, ...args)
}
}
-export function softAssertCompatEnabled(key: DeprecationTypes, ...args: any[]) {
+export function softAssertCompatEnabled(
+ key: DeprecationTypes,
+ instance: ComponentInternalInstance | null,
+ ...args: any[]
+) {
if (__DEV__) {
- warnDeprecation(key, ...args)
+ warnDeprecation(key, instance, ...args)
}
- return isCompatEnabled(key)
+ return isCompatEnabled(key, instance)
}
// disable features that conflict with v3 behavior
import { defineAsyncComponent } from '../apiAsyncComponent'
import {
Component,
+ ComponentInternalInstance,
ComponentOptions,
FunctionalComponent,
getCurrentInstance
import { getCompatListeners } from './instanceListeners'
import { compatH } from './renderFn'
-export function convertLegacyComponent(comp: any): Component {
+export function convertLegacyComponent(
+ comp: any,
+ instance: ComponentInternalInstance | null
+): Component {
// 2.x async component
// since after disabling this, plain functions are still valid usage, do not
// use softAssert here.
- if (isFunction(comp) && isCompatEnabled(DeprecationTypes.COMPONENT_ASYNC)) {
- __DEV__ && warnDeprecation(DeprecationTypes.COMPONENT_ASYNC, comp)
+ if (
+ isFunction(comp) &&
+ isCompatEnabled(DeprecationTypes.COMPONENT_ASYNC, instance)
+ ) {
+ __DEV__ && warnDeprecation(DeprecationTypes.COMPONENT_ASYNC, instance, comp)
return convertLegacyAsyncComponent(comp)
}
if (
isObject(comp) &&
comp.functional &&
- softAssertCompatEnabled(DeprecationTypes.COMPONENT_FUNCTIONAL, comp)
+ softAssertCompatEnabled(
+ DeprecationTypes.COMPONENT_FUNCTIONAL,
+ instance,
+ comp
+ )
) {
return convertLegacyFunctionalComponent(comp)
}
FunctionalComponent
>()
-const legacySlotProxyHandlers: ProxyHandler<InternalSlots> = {
+export const legacySlotProxyHandlers: ProxyHandler<InternalSlots> = {
get(target, key: string) {
const slot = target[key]
return slot && slot()
import { isArray } from '@vue/shared'
+import { ComponentInternalInstance } from '../component'
import { ObjectDirective, DirectiveHook } from '../directives'
import { softAssertCompatEnabled } from './compatConfig'
import { DeprecationTypes } from './deprecations'
export function mapCompatDirectiveHook(
name: keyof ObjectDirective,
- dir: ObjectDirective & LegacyDirective
+ dir: ObjectDirective & LegacyDirective,
+ instance: ComponentInternalInstance | null
): DirectiveHook | DirectiveHook[] | undefined {
const mappedName = legacyDirectiveHookMap[name]
if (mappedName) {
mappedName.forEach(name => {
const mappedHook = dir[name]
if (mappedHook) {
- softAssertCompatEnabled(DeprecationTypes.CUSTOM_DIR, mappedName, name)
+ softAssertCompatEnabled(
+ DeprecationTypes.CUSTOM_DIR,
+ instance,
+ mappedName,
+ name
+ )
hook.push(mappedHook)
}
})
return hook.length ? hook : undefined
} else {
if (dir[mappedName]) {
- softAssertCompatEnabled(DeprecationTypes.CUSTOM_DIR, mappedName, name)
+ softAssertCompatEnabled(
+ DeprecationTypes.CUSTOM_DIR,
+ instance,
+ mappedName,
+ name
+ )
}
return dir[mappedName]
}
import { isPlainObject } from '@vue/shared'
+import { ComponentInternalInstance } from '../component'
import { DeprecationTypes, warnDeprecation } from './deprecations'
-export function deepMergeData(to: any, from: any) {
+export function deepMergeData(
+ to: any,
+ from: any,
+ instance: ComponentInternalInstance
+) {
for (const key in from) {
const toVal = to[key]
const fromVal = from[key]
if (key in to && isPlainObject(toVal) && isPlainObject(fromVal)) {
- __DEV__ && warnDeprecation(DeprecationTypes.OPTIONS_DATA_MERGE, key)
- deepMergeData(toVal, fromVal)
+ __DEV__ &&
+ warnDeprecation(DeprecationTypes.OPTIONS_DATA_MERGE, instance, key)
+ deepMergeData(toVal, fromVal, instance)
} else {
to[key] = fromVal
}
import {
+ ComponentInternalInstance,
formatComponentName,
getComponentName,
getCurrentInstance,
TRANSITION_GROUP_ROOT = 'TRANSITION_GROUP_ROOT',
COMPONENT_ASYNC = 'COMPONENT_ASYNC',
- COMPONENT_FUNCTIONAL = 'COMPONENT_FUNCTIONAL'
+ COMPONENT_FUNCTIONAL = 'COMPONENT_FUNCTIONAL',
+
+ RENDER_FUNCTION = 'RENDER_FUNCTION'
}
type DeprecationData = {
)
},
link: `https://v3.vuejs.org/guide/migration/functional-components.html`
+ },
+
+ [DeprecationTypes.RENDER_FUNCTION]: {
+ message:
+ `Vue 3's render function API has changed. ` +
+ `You can opt-in to the new API with:` +
+ `\n\n configureCompat({ ${
+ DeprecationTypes.RENDER_FUNCTION
+ }: false })\n` +
+ `\n (This can also be done per-component via the "compatConfig" option.)`,
+ link: `https://v3.vuejs.org/guide/migration/render-function-api.html`
}
}
const instanceWarned: Record<string, true> = Object.create(null)
const warnCount: Record<string, number> = Object.create(null)
-export function warnDeprecation(key: DeprecationTypes, ...args: any[]) {
+export function warnDeprecation(
+ key: DeprecationTypes,
+ instance: ComponentInternalInstance | null,
+ ...args: any[]
+) {
if (!__DEV__) {
return
}
+ instance = instance || getCurrentInstance()
+
// check user config
- const config = getCompatConfigForKey(key)
+ const config = getCompatConfigForKey(key, instance)
if (config === 'suppress-warning') {
return
}
const dupKey = key + args.join('')
- const instance = getCurrentInstance()
const compName = instance && formatComponentName(instance, instance.type)
// skip if the same warning is emitted for the same component type
typeof message === 'function' ? message(...args) : message
}${link ? `\n Details: ${link}` : ``}`
)
- if (!isCompatEnabled(key)) {
+ if (!isCompatEnabled(key, instance)) {
console.error(
`^ The above deprecation's compat behavior is disabled and will likely ` +
`lead to runtime errors.`
const singletonApp = createApp({})
function createCompatApp(options: ComponentOptions = {}, Ctor: any) {
- assertCompatEnabled(DeprecationTypes.GLOBAL_MOUNT)
+ assertCompatEnabled(DeprecationTypes.GLOBAL_MOUNT, null)
const { data } = options
if (
data &&
!isFunction(data) &&
- softAssertCompatEnabled(DeprecationTypes.OPTIONS_DATA_FN)
+ softAssertCompatEnabled(DeprecationTypes.OPTIONS_DATA_FN, null)
) {
options.data = () => data
}
// copy prototype augmentations as config.globalProperties
const isPrototypeEnabled = isCompatEnabled(
- DeprecationTypes.GLOBAL_PROTOTYPE
+ DeprecationTypes.GLOBAL_PROTOTYPE,
+ null
)
let hasPrototypeAugmentations = false
for (const key in Ctor.prototype) {
}
}
if (__DEV__ && hasPrototypeAugmentations) {
- warnDeprecation(DeprecationTypes.GLOBAL_PROTOTYPE)
+ warnDeprecation(DeprecationTypes.GLOBAL_PROTOTYPE, null)
}
const vm = app._createRoot!(options)
Vue.nextTick = nextTick
Vue.extend = ((options: ComponentOptions = {}) => {
- assertCompatEnabled(DeprecationTypes.GLOBAL_EXTEND)
+ assertCompatEnabled(DeprecationTypes.GLOBAL_EXTEND, null)
function SubVue(inlineOptions?: ComponentOptions) {
if (!inlineOptions) {
}) as any
Vue.set = (target, key, value) => {
- assertCompatEnabled(DeprecationTypes.GLOBAL_SET)
+ assertCompatEnabled(DeprecationTypes.GLOBAL_SET, null)
target[key] = value
}
Vue.delete = (target, key) => {
- assertCompatEnabled(DeprecationTypes.GLOBAL_DELETE)
+ assertCompatEnabled(DeprecationTypes.GLOBAL_DELETE, null)
delete target[key]
}
Vue.observable = (target: any) => {
- assertCompatEnabled(DeprecationTypes.GLOBAL_OBSERVABLE)
+ assertCompatEnabled(DeprecationTypes.GLOBAL_OBSERVABLE, null)
return reactive(target)
}
for (let i = 0; i < container.attributes.length; i++) {
const attr = container.attributes[i]
if (attr.name !== 'v-cloak' && /^(v-|:|@)/.test(attr.name)) {
- warnDeprecation(DeprecationTypes.GLOBAL_MOUNT_CONTAINER)
+ warnDeprecation(DeprecationTypes.GLOBAL_MOUNT_CONTAINER, null)
break
}
}
},
set(newVal) {
if (!isCopyingConfig) {
- warnDeprecation(legacyConfigOptions[key])
+ warnDeprecation(legacyConfigOptions[key], null)
}
val = newVal
// compat for runtime ignoredElements -> isCustomElement
if (
key === 'ignoredElements' &&
- isCompatEnabled(DeprecationTypes.CONFIG_IGNORED_ELEMENTS) &&
+ isCompatEnabled(DeprecationTypes.CONFIG_IGNORED_ELEMENTS, null) &&
!isRuntimeOnly() &&
isArray(newVal)
) {
import { extend, NOOP } from '@vue/shared'
import { PublicPropertiesMap } from '../componentPublicInstance'
import { getCompatChildren } from './instanceChildren'
-import { assertCompatEnabled } from './compatConfig'
+import { assertCompatEnabled, isCompatEnabled } from './compatConfig'
import { DeprecationTypes, warnDeprecation } from './deprecations'
import { off, on, once } from './instanceEventEmitter'
import { getCompatListeners } from './instanceListeners'
import { shallowReadonly } from '@vue/reactivity'
+import { legacySlotProxyHandlers } from './component'
export function installCompatInstanceProperties(map: PublicPropertiesMap) {
const set = (target: any, key: any, val: any) => {
}
extend(map, {
- $set: () => {
- assertCompatEnabled(DeprecationTypes.INSTANCE_SET)
+ $set: i => {
+ assertCompatEnabled(DeprecationTypes.INSTANCE_SET, i)
return set
},
- $delete: () => {
- assertCompatEnabled(DeprecationTypes.INSTANCE_DELETE)
+ $delete: i => {
+ assertCompatEnabled(DeprecationTypes.INSTANCE_DELETE, i)
return del
},
$mount: i => {
- assertCompatEnabled(DeprecationTypes.GLOBAL_MOUNT)
+ assertCompatEnabled(
+ DeprecationTypes.GLOBAL_MOUNT,
+ null /* this warning is global */
+ )
// root mount override from ./global.ts in installCompatMount
return i.ctx._compat_mount || NOOP
},
$destroy: i => {
- assertCompatEnabled(DeprecationTypes.INSTANCE_DESTROY)
+ assertCompatEnabled(DeprecationTypes.INSTANCE_DESTROY, i)
// root destroy override from ./global.ts in installCompatMount
return i.ctx._compat_destroy || NOOP
},
+ // overrides existing accessor
+ $slots: i => {
+ if (isCompatEnabled(DeprecationTypes.RENDER_FUNCTION, i)) {
+ return new Proxy(i.slots, legacySlotProxyHandlers)
+ }
+ return i.slots
+ },
+
$scopedSlots: i => {
- assertCompatEnabled(DeprecationTypes.INSTANCE_SCOPED_SLOTS)
+ assertCompatEnabled(DeprecationTypes.INSTANCE_SCOPED_SLOTS, i)
return __DEV__ ? shallowReadonly(i.slots) : i.slots
},
// overrides existing accessor
$attrs: i => {
if (__DEV__ && i.type.inheritAttrs === false) {
- warnDeprecation(DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE)
+ warnDeprecation(DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE, i)
}
return __DEV__ ? shallowReadonly(i.attrs) : i.attrs
},
export function getCompatChildren(
instance: ComponentInternalInstance
): ComponentPublicInstance[] {
- assertCompatEnabled(DeprecationTypes.INSTANCE_CHILDREN)
+ assertCompatEnabled(DeprecationTypes.INSTANCE_CHILDREN, instance)
const root = instance.subTree
const children: ComponentPublicInstance[] = []
if (root) {
event.forEach(e => on(instance, e, fn))
} else {
if (event.startsWith('hook:')) {
- assertCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS)
+ assertCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
} else {
- assertCompatEnabled(DeprecationTypes.INSTANCE_EVENT_EMITTER)
+ assertCompatEnabled(DeprecationTypes.INSTANCE_EVENT_EMITTER, instance)
}
const events = getRegistry(instance)
;(events[event] || (events[event] = [])).push(fn)
event?: string,
fn?: Function
) {
- assertCompatEnabled(DeprecationTypes.INSTANCE_EVENT_EMITTER)
+ assertCompatEnabled(DeprecationTypes.INSTANCE_EVENT_EMITTER, instance)
const vm = instance.proxy
// all
if (!arguments.length) {
import { DeprecationTypes } from './deprecations'
export function getCompatListeners(instance: ComponentInternalInstance) {
- assertCompatEnabled(DeprecationTypes.INSTANCE_LISTENERS)
+ assertCompatEnabled(DeprecationTypes.INSTANCE_LISTENERS, instance)
const listeners: Record<string, Function | Function[]> = {}
const rawProps = instance.vnode.props
{},
{
get() {
- warnDeprecation(DeprecationTypes.PROPS_DEFAULT_THIS, propKey)
+ warnDeprecation(DeprecationTypes.PROPS_DEFAULT_THIS, null, propKey)
}
}
)
-import { isArray, isObject } from '@vue/shared'
+import {
+ extend,
+ isArray,
+ isObject,
+ ShapeFlags,
+ toHandlerKey
+} from '@vue/shared'
import { Component, Data } from '../component'
+import { DirectiveArguments, withDirectives } from '../directives'
+import {
+ resolveDirective,
+ resolveDynamicComponent
+} from '../helpers/resolveAssets'
import {
createVNode,
isVNode,
+ normalizeChildren,
VNode,
VNodeArrayChildren,
VNodeProps
nativeOn?: Record<string, Function | Function[]>
directives?: LegacyVNodeDirective[]
+ // component only
+ props?: Record<string, unknown>
slot?: string
scopedSlots?: Record<string, Function>
}
propsOrChildren?: any,
children?: any
): VNode {
+ // to support v2 string component name lookup
+ type = resolveDynamicComponent(type)
+
const l = arguments.length
if (l === 2) {
if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
return convertLegacySlots(createVNode(type, null, [propsOrChildren]))
}
// props without children
- return convertLegacyDirectives(
- createVNode(type, convertLegacyProps(propsOrChildren)),
- propsOrChildren
+ return convertLegacySlots(
+ convertLegacyDirectives(
+ createVNode(type, convertLegacyProps(propsOrChildren)),
+ propsOrChildren
+ )
)
} else {
// omit props
}
}
-function convertLegacyProps(props: LegacyVNodeProps): Data & VNodeProps {
- // TODO
- return props as any
+function convertLegacyProps(legacyProps?: LegacyVNodeProps): Data & VNodeProps {
+ const converted: Data & VNodeProps = {}
+
+ for (const key in legacyProps) {
+ if (key === 'attrs' || key === 'domProps' || key === 'props') {
+ extend(converted, legacyProps[key])
+ } else if (key === 'on' || key === 'nativeOn') {
+ const listeners = legacyProps[key]
+ for (const event in listeners) {
+ const handlerKey = toHandlerKey(event)
+ const existing = converted[handlerKey]
+ const incoming = listeners[event]
+ if (existing !== incoming) {
+ converted[handlerKey] = existing
+ ? [].concat(existing as any, incoming as any)
+ : incoming
+ }
+ }
+ } else {
+ converted[key] = legacyProps[key as keyof LegacyVNodeProps]
+ }
+ }
+
+ return converted
}
-function convertLegacyDirectives(vnode: VNode, props: LegacyVNodeProps): VNode {
- // TODO
+function convertLegacyDirectives(
+ vnode: VNode,
+ props?: LegacyVNodeProps
+): VNode {
+ if (props && props.directives) {
+ return withDirectives(
+ vnode,
+ props.directives.map(({ name, value, arg, modifiers }) => {
+ return [
+ resolveDirective(name)!,
+ value,
+ arg,
+ modifiers
+ ] as DirectiveArguments[number]
+ })
+ )
+ }
return vnode
}
function convertLegacySlots(vnode: VNode): VNode {
- // TODO
+ const { props, children } = vnode
+
+ let slots: Record<string, any> | undefined
+
+ if (vnode.shapeFlag & ShapeFlags.COMPONENT && isArray(children)) {
+ slots = {}
+ // check "slot" property on vnodes and turn them into v3 function slots
+ for (let i = 0; i < children.length; i++) {
+ const child = children[i]
+ const slotName =
+ (isVNode(child) && child.props && child.props.slot) || 'default'
+ ;(slots[slotName] || (slots[slotName] = [] as any[])).push(child)
+ }
+ if (slots) {
+ for (const key in slots) {
+ const slotChildren = slots[key]
+ slots[key] = () => slotChildren
+ }
+ }
+ }
+
+ const scopedSlots = props && props.scopedSlots
+ if (scopedSlots) {
+ delete props!.scopedSlots
+ if (slots) {
+ extend(slots, scopedSlots)
+ } else {
+ slots = scopedSlots
+ }
+ }
+
+ if (slots) {
+ normalizeChildren(vnode, slots)
+ }
+
return vnode
}
import { markAttrsAccessed } from './componentRenderUtils'
import { currentRenderingInstance } from './componentRenderContext'
import { startMeasure, endMeasure } from './profiling'
+import { isCompatEnabled } from './compat/compatConfig'
+import { DeprecationTypes, warnDeprecation } from './compat/deprecations'
+import { compatH } from './compat/renderFn'
export type Data = Record<string, unknown>
) {
const Component = instance.type as ComponentOptions
+ if (
+ __COMPAT__ &&
+ Component.render &&
+ isCompatEnabled(DeprecationTypes.RENDER_FUNCTION)
+ ) {
+ warnDeprecation(DeprecationTypes.RENDER_FUNCTION)
+ const originalRender = Component.render
+ Component.render = function compatRender() {
+ return originalRender.call(this, compatH)
+ }
+ }
+
// template / render function normalization
if (__NODE_JS__ && isSSR) {
// 1. the render function may already exist, returned by `setup`
if (__COMPAT__) {
if (
beforeDestroy &&
- softAssertCompatEnabled(DeprecationTypes.OPTIONS_BEFORE_DESTROY)
+ softAssertCompatEnabled(DeprecationTypes.OPTIONS_BEFORE_DESTROY, instance)
) {
onBeforeUnmount(beforeDestroy.bind(publicThis))
}
if (
destroyed &&
- softAssertCompatEnabled(DeprecationTypes.OPTIONS_DESTROYED)
+ softAssertCompatEnabled(DeprecationTypes.OPTIONS_DESTROYED, instance)
) {
onUnmounted(destroyed.bind(publicThis))
}
instance.data = reactive(data)
} else {
// existing data: this is a mixin or extends.
- if (__COMPAT__ && isCompatEnabled(DeprecationTypes.OPTIONS_DATA_MERGE)) {
- deepMergeData(instance.data, data)
+ if (
+ __COMPAT__ &&
+ isCompatEnabled(DeprecationTypes.OPTIONS_DATA_MERGE, instance)
+ ) {
+ deepMergeData(instance.data, data, instance)
} else {
extend(instance.data, data)
}
}
let hook = binding.dir[name] as DirectiveHook | DirectiveHook[] | undefined
if (__COMPAT__ && !hook) {
- hook = mapCompatDirectiveHook(name, binding.dir)
+ hook = mapCompatDirectiveHook(name, binding.dir, instance)
}
if (hook) {
callWithAsyncErrorHandling(hook, instance, ErrorCodes.DIRECTIVE_HOOK, [
// 2.x async/functional component compat
if (__COMPAT__) {
- type = convertLegacyComponent(type)
+ type = convertLegacyComponent(type, currentRenderingInstance)
}
// class & style normalization.
const incoming = toMerge[key]
if (existing !== incoming) {
ret[key] = existing
- ? [].concat(existing as any, toMerge[key] as any)
+ ? [].concat(existing as any, incoming as any)
: incoming
}
} else if (key !== '') {