key: DeprecationTypes,
instance: ComponentInternalInstance | null
): boolean {
+ // skip compat for built-in components
+ if (instance && instance.type.__isBuiltIn) {
+ return false
+ }
+
const mode = getCompatConfigForKey('MODE', instance) || 2
const val = getCompatConfigForKey(key, instance)
if (mode === 2) {
comp: any,
instance: ComponentInternalInstance | null
): Component {
+ if (comp.__isBuiltIn) {
+ return comp
+ }
+
// 2.x async component
// since after disabling this, plain functions are still valid usage, do not
// use softAssert here.
// overrides existing accessor
$slots: i => {
- if (isCompatEnabled(DeprecationTypes.RENDER_FUNCTION, i)) {
+ if (
+ isCompatEnabled(DeprecationTypes.RENDER_FUNCTION, i) &&
+ i.render &&
+ i.render._compatWrapped
+ ) {
return new Proxy(i.slots, legacySlotProxyHandlers)
}
return __DEV__ ? shallowReadonly(i.slots) : i.slots
ShapeFlags,
toHandlerKey
} from '@vue/shared'
-import { Component, Data } from '../component'
+import {
+ Component,
+ ComponentInternalInstance,
+ ComponentOptions,
+ Data,
+ InternalRenderFunction
+} from '../component'
import { DirectiveArguments, withDirectives } from '../directives'
import {
resolveDirective,
VNodeArrayChildren,
VNodeProps
} from '../vnode'
+import { checkCompatEnabled } from './compatConfig'
+import { DeprecationTypes } from './deprecations'
+
+export function convertLegacyRenderFn(instance: ComponentInternalInstance) {
+ const Component = instance.type as ComponentOptions
+ const render = Component.render as InternalRenderFunction | undefined
+
+ // v3 runtime compiled, or already checked / wrapped
+ if (!render || render._rc || render._compatChecked || render._compatWrapped) {
+ return
+ }
+
+ const string = render.toString()
+ if (string.startsWith('function render(_ctx') || string.startsWith('(_ctx')) {
+ // v3 pre-compiled function
+ render._compatChecked = true
+ return
+ }
+
+ // v2 render function, try to provide compat
+ if (checkCompatEnabled(DeprecationTypes.RENDER_FUNCTION, instance)) {
+ const wrapped = (Component.render = function compatRender() {
+ // @ts-ignore
+ return render.call(this, compatH)
+ })
+ // @ts-ignore
+ wrapped._compatWrapped = true
+ }
+}
interface LegacyVNodeProps {
key?: string | number
import { markAttrsAccessed } from './componentRenderUtils'
import { currentRenderingInstance } from './componentRenderContext'
import { startMeasure, endMeasure } from './profiling'
-import { checkCompatEnabled } from './compat/compatConfig'
-import { DeprecationTypes } from './compat/deprecations'
-import { compatH } from './compat/renderFn'
+import { convertLegacyRenderFn } from './compat/renderFn'
export type Data = Record<string, unknown>
* @internal
*/
__hmrId?: string
+ /**
+ * Compat build only, for bailing out of certain compatibility behavior
+ */
+ __isBuiltIn?: boolean
/**
* This one should be exposed so that devtools can make use of it
*/
$options: ComponentInternalInstance['ctx']
): VNodeChild
_rc?: boolean // isRuntimeCompiled
+
+ // __COMPAT__ only
+ _compatChecked?: boolean // v3 and already checked for v2 compat
+ _compatWrapped?: boolean // is wrapped for v2 compat
}
/**
) {
const Component = instance.type as ComponentOptions
- if (
- __COMPAT__ &&
- Component.render &&
- checkCompatEnabled(DeprecationTypes.RENDER_FUNCTION, instance)
- ) {
- const originalRender = Component.render
- Component.render = function compatRender() {
- return originalRender.call(this, compatH)
- }
+ if (__COMPAT__) {
+ convertLegacyRenderFn(instance)
}
// template / render function normalization
import {
getCurrentInstance,
SetupContext,
- ComponentInternalInstance
+ ComponentInternalInstance,
+ ComponentOptions
} from '../component'
import {
cloneVNode,
const TransitionHookValidator = [Function, Array]
-const BaseTransitionImpl = {
+const BaseTransitionImpl: ComponentOptions = {
name: `BaseTransition`,
props: {
}
}
+if (__COMPAT__) {
+ BaseTransitionImpl.__isBuiltIn = true
+}
+
// export the public type for h/tsx inference
// also to avoid inline import() in generated d.ts files
export const BaseTransition = (BaseTransitionImpl as any) as {
ComponentInternalInstance,
LifecycleHooks,
currentInstance,
- getComponentName
+ getComponentName,
+ ComponentOptions
} from '../component'
import { VNode, cloneVNode, isVNode, VNodeProps } from '../vnode'
import { warn } from '../warning'
export const isKeepAlive = (vnode: VNode): boolean =>
(vnode.type as any).__isKeepAlive
-const KeepAliveImpl = {
+const KeepAliveImpl: ComponentOptions = {
name: `KeepAlive`,
// Marker for special handling inside the renderer. We are not using a ===
}
}
+if (__COMPAT__) {
+ KeepAliveImpl.__isBuildIn = true
+}
+
// export the public type for h/tsx inference
// also to avoid inline import() in generated d.ts files
export const KeepAlive = (KeepAliveImpl as any) as {
Transition.displayName = 'Transition'
+if (__COMPAT__) {
+ Transition.__isBuiltIn = true
+}
+
const DOMTransitionPropsValidators = {
name: String,
type: String,
SetupContext,
toRaw,
compatUtils,
- DeprecationTypes
+ DeprecationTypes,
+ ComponentOptions
} from '@vue/runtime-core'
import { extend } from '@vue/shared'
moveClass?: string
}
-const TransitionGroupImpl = {
+const TransitionGroupImpl: ComponentOptions = {
name: 'TransitionGroup',
props: /*#__PURE__*/ extend({}, TransitionPropsValidators, {
}
}
+if (__COMPAT__) {
+ TransitionGroupImpl.__isBuiltIn = true
+}
+
/**
* TransitionGroup does not support "mode" so we need to remove it from the
* props declarations, but direct delete operation is considered a side effect