Skip to content

Commit c55f3ed

Browse files
committed
wip: fix render fn compat detection
1 parent a2f441d commit c55f3ed

File tree

9 files changed

+86
-20
lines changed

9 files changed

+86
-20
lines changed

packages/runtime-core/src/compat/compatConfig.ts

+5
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ export function isCompatEnabled(
3030
key: DeprecationTypes,
3131
instance: ComponentInternalInstance | null
3232
): boolean {
33+
// skip compat for built-in components
34+
if (instance && instance.type.__isBuiltIn) {
35+
return false
36+
}
37+
3338
const mode = getCompatConfigForKey('MODE', instance) || 2
3439
const val = getCompatConfigForKey(key, instance)
3540
if (mode === 2) {

packages/runtime-core/src/compat/component.ts

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export function convertLegacyComponent(
1919
comp: any,
2020
instance: ComponentInternalInstance | null
2121
): Component {
22+
if (comp.__isBuiltIn) {
23+
return comp
24+
}
25+
2226
// 2.x async component
2327
// since after disabling this, plain functions are still valid usage, do not
2428
// use softAssert here.

packages/runtime-core/src/compat/instance.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {
4949

5050
// overrides existing accessor
5151
$slots: i => {
52-
if (isCompatEnabled(DeprecationTypes.RENDER_FUNCTION, i)) {
52+
if (
53+
isCompatEnabled(DeprecationTypes.RENDER_FUNCTION, i) &&
54+
i.render &&
55+
i.render._compatWrapped
56+
) {
5357
return new Proxy(i.slots, legacySlotProxyHandlers)
5458
}
5559
return __DEV__ ? shallowReadonly(i.slots) : i.slots

packages/runtime-core/src/compat/renderFn.ts

+36-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import {
55
ShapeFlags,
66
toHandlerKey
77
} from '@vue/shared'
8-
import { Component, Data } from '../component'
8+
import {
9+
Component,
10+
ComponentInternalInstance,
11+
ComponentOptions,
12+
Data,
13+
InternalRenderFunction
14+
} from '../component'
915
import { DirectiveArguments, withDirectives } from '../directives'
1016
import {
1117
resolveDirective,
@@ -19,6 +25,35 @@ import {
1925
VNodeArrayChildren,
2026
VNodeProps
2127
} from '../vnode'
28+
import { checkCompatEnabled } from './compatConfig'
29+
import { DeprecationTypes } from './deprecations'
30+
31+
export function convertLegacyRenderFn(instance: ComponentInternalInstance) {
32+
const Component = instance.type as ComponentOptions
33+
const render = Component.render as InternalRenderFunction | undefined
34+
35+
// v3 runtime compiled, or already checked / wrapped
36+
if (!render || render._rc || render._compatChecked || render._compatWrapped) {
37+
return
38+
}
39+
40+
const string = render.toString()
41+
if (string.startsWith('function render(_ctx') || string.startsWith('(_ctx')) {
42+
// v3 pre-compiled function
43+
render._compatChecked = true
44+
return
45+
}
46+
47+
// v2 render function, try to provide compat
48+
if (checkCompatEnabled(DeprecationTypes.RENDER_FUNCTION, instance)) {
49+
const wrapped = (Component.render = function compatRender() {
50+
// @ts-ignore
51+
return render.call(this, compatH)
52+
})
53+
// @ts-ignore
54+
wrapped._compatWrapped = true
55+
}
56+
}
2257

2358
interface LegacyVNodeProps {
2459
key?: string | number

packages/runtime-core/src/component.ts

+11-12
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,7 @@ import { CompilerOptions } from '@vue/compiler-core'
5454
import { markAttrsAccessed } from './componentRenderUtils'
5555
import { currentRenderingInstance } from './componentRenderContext'
5656
import { startMeasure, endMeasure } from './profiling'
57-
import { checkCompatEnabled } from './compat/compatConfig'
58-
import { DeprecationTypes } from './compat/deprecations'
59-
import { compatH } from './compat/renderFn'
57+
import { convertLegacyRenderFn } from './compat/renderFn'
6058

6159
export type Data = Record<string, unknown>
6260

@@ -96,6 +94,10 @@ export interface ComponentInternalOptions {
9694
* @internal
9795
*/
9896
__hmrId?: string
97+
/**
98+
* Compat build only, for bailing out of certain compatibility behavior
99+
*/
100+
__isBuiltIn?: boolean
99101
/**
100102
* This one should be exposed so that devtools can make use of it
101103
*/
@@ -188,6 +190,10 @@ export type InternalRenderFunction = {
188190
$options: ComponentInternalInstance['ctx']
189191
): VNodeChild
190192
_rc?: boolean // isRuntimeCompiled
193+
194+
// __COMPAT__ only
195+
_compatChecked?: boolean // v3 and already checked for v2 compat
196+
_compatWrapped?: boolean // is wrapped for v2 compat
191197
}
192198

193199
/**
@@ -684,15 +690,8 @@ export function finishComponentSetup(
684690
) {
685691
const Component = instance.type as ComponentOptions
686692

687-
if (
688-
__COMPAT__ &&
689-
Component.render &&
690-
checkCompatEnabled(DeprecationTypes.RENDER_FUNCTION, instance)
691-
) {
692-
const originalRender = Component.render
693-
Component.render = function compatRender() {
694-
return originalRender.call(this, compatH)
695-
}
693+
if (__COMPAT__) {
694+
convertLegacyRenderFn(instance)
696695
}
697696

698697
// template / render function normalization

packages/runtime-core/src/components/BaseTransition.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import {
22
getCurrentInstance,
33
SetupContext,
4-
ComponentInternalInstance
4+
ComponentInternalInstance,
5+
ComponentOptions
56
} from '../component'
67
import {
78
cloneVNode,
@@ -110,7 +111,7 @@ export function useTransitionState(): TransitionState {
110111

111112
const TransitionHookValidator = [Function, Array]
112113

113-
const BaseTransitionImpl = {
114+
const BaseTransitionImpl: ComponentOptions = {
114115
name: `BaseTransition`,
115116

116117
props: {
@@ -250,6 +251,10 @@ const BaseTransitionImpl = {
250251
}
251252
}
252253

254+
if (__COMPAT__) {
255+
BaseTransitionImpl.__isBuiltIn = true
256+
}
257+
253258
// export the public type for h/tsx inference
254259
// also to avoid inline import() in generated d.ts files
255260
export const BaseTransition = (BaseTransitionImpl as any) as {

packages/runtime-core/src/components/KeepAlive.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
ComponentInternalInstance,
66
LifecycleHooks,
77
currentInstance,
8-
getComponentName
8+
getComponentName,
9+
ComponentOptions
910
} from '../component'
1011
import { VNode, cloneVNode, isVNode, VNodeProps } from '../vnode'
1112
import { warn } from '../warning'
@@ -62,7 +63,7 @@ export interface KeepAliveContext extends ComponentRenderContext {
6263
export const isKeepAlive = (vnode: VNode): boolean =>
6364
(vnode.type as any).__isKeepAlive
6465

65-
const KeepAliveImpl = {
66+
const KeepAliveImpl: ComponentOptions = {
6667
name: `KeepAlive`,
6768

6869
// Marker for special handling inside the renderer. We are not using a ===
@@ -298,6 +299,10 @@ const KeepAliveImpl = {
298299
}
299300
}
300301

302+
if (__COMPAT__) {
303+
KeepAliveImpl.__isBuildIn = true
304+
}
305+
301306
// export the public type for h/tsx inference
302307
// also to avoid inline import() in generated d.ts files
303308
export const KeepAlive = (KeepAliveImpl as any) as {

packages/runtime-dom/src/components/Transition.ts

+4
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ export const Transition: FunctionalComponent<TransitionProps> = (
4646

4747
Transition.displayName = 'Transition'
4848

49+
if (__COMPAT__) {
50+
Transition.__isBuiltIn = true
51+
}
52+
4953
const DOMTransitionPropsValidators = {
5054
name: String,
5155
type: String,

packages/runtime-dom/src/components/TransitionGroup.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import {
2222
SetupContext,
2323
toRaw,
2424
compatUtils,
25-
DeprecationTypes
25+
DeprecationTypes,
26+
ComponentOptions
2627
} from '@vue/runtime-core'
2728
import { extend } from '@vue/shared'
2829

@@ -39,7 +40,7 @@ export type TransitionGroupProps = Omit<TransitionProps, 'mode'> & {
3940
moveClass?: string
4041
}
4142

42-
const TransitionGroupImpl = {
43+
const TransitionGroupImpl: ComponentOptions = {
4344
name: 'TransitionGroup',
4445

4546
props: /*#__PURE__*/ extend({}, TransitionPropsValidators, {
@@ -145,6 +146,10 @@ const TransitionGroupImpl = {
145146
}
146147
}
147148

149+
if (__COMPAT__) {
150+
TransitionGroupImpl.__isBuiltIn = true
151+
}
152+
148153
/**
149154
* TransitionGroup does not support "mode" so we need to remove it from the
150155
* props declarations, but direct delete operation is considered a side effect

0 commit comments

Comments
 (0)