]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: fix render fn compat detection
authorEvan You <yyx990803@gmail.com>
Sun, 11 Apr 2021 15:15:40 +0000 (11:15 -0400)
committerEvan You <yyx990803@gmail.com>
Sun, 11 Apr 2021 15:15:40 +0000 (11:15 -0400)
packages/runtime-core/src/compat/compatConfig.ts
packages/runtime-core/src/compat/component.ts
packages/runtime-core/src/compat/instance.ts
packages/runtime-core/src/compat/renderFn.ts
packages/runtime-core/src/component.ts
packages/runtime-core/src/components/BaseTransition.ts
packages/runtime-core/src/components/KeepAlive.ts
packages/runtime-dom/src/components/Transition.ts
packages/runtime-dom/src/components/TransitionGroup.ts

index cc79a53930efce029421f3b298a6138725026af0..ee2bfcacb4bb3d6967e8d0a1987f58660090214c 100644 (file)
@@ -30,6 +30,11 @@ export function isCompatEnabled(
   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) {
index e02dfcd89a5cb1ec3a6b637ddb728a9f82201696..d7747789155be9479927fa08eff5eb0caf28d60d 100644 (file)
@@ -19,6 +19,10 @@ export function convertLegacyComponent(
   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.
index 1a25801ca6a93d1e73c9d17d212860087088dea9..2f9c254df899c2c93bffec734a8ac5cf429d3261 100644 (file)
@@ -49,7 +49,11 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {
 
     // 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
index 5bc720cf7420fec62c87c7dfe4a843b35237d42c..69e773f3c9be6f611c8bca6b5caff645880be059 100644 (file)
@@ -5,7 +5,13 @@ import {
   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,
@@ -19,6 +25,35 @@ import {
   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
index 2791fa952476b141689e6c0fa61934e3214801e3..d0e729457a5264e7f33e5a7e84a64933206a3bc8 100644 (file)
@@ -54,9 +54,7 @@ import { CompilerOptions } from '@vue/compiler-core'
 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>
 
@@ -96,6 +94,10 @@ export interface ComponentInternalOptions {
    * @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
    */
@@ -188,6 +190,10 @@ export type InternalRenderFunction = {
     $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
 }
 
 /**
@@ -684,15 +690,8 @@ export function finishComponentSetup(
 ) {
   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
index a6f2fb27f33d5a7845476a87eddab91e29ef001a..1cb47c8195eca8aea9abfdba0c643b5cec01d541 100644 (file)
@@ -1,7 +1,8 @@
 import {
   getCurrentInstance,
   SetupContext,
-  ComponentInternalInstance
+  ComponentInternalInstance,
+  ComponentOptions
 } from '../component'
 import {
   cloneVNode,
@@ -110,7 +111,7 @@ export function useTransitionState(): TransitionState {
 
 const TransitionHookValidator = [Function, Array]
 
-const BaseTransitionImpl = {
+const BaseTransitionImpl: ComponentOptions = {
   name: `BaseTransition`,
 
   props: {
@@ -250,6 +251,10 @@ const BaseTransitionImpl = {
   }
 }
 
+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 {
index cbba10fd755d665e1c7562a029c5fba640f59547..b9935525264bd00ce185dcfec7da0fe5ae29a260 100644 (file)
@@ -5,7 +5,8 @@ import {
   ComponentInternalInstance,
   LifecycleHooks,
   currentInstance,
-  getComponentName
+  getComponentName,
+  ComponentOptions
 } from '../component'
 import { VNode, cloneVNode, isVNode, VNodeProps } from '../vnode'
 import { warn } from '../warning'
@@ -62,7 +63,7 @@ export interface KeepAliveContext extends ComponentRenderContext {
 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 ===
@@ -298,6 +299,10 @@ const KeepAliveImpl = {
   }
 }
 
+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 {
index 1121af1ebf31095b603011637c27ad61a307082f..a253f51b435646519c2fee754dc4c928797815c2 100644 (file)
@@ -46,6 +46,10 @@ export const Transition: FunctionalComponent<TransitionProps> = (
 
 Transition.displayName = 'Transition'
 
+if (__COMPAT__) {
+  Transition.__isBuiltIn = true
+}
+
 const DOMTransitionPropsValidators = {
   name: String,
   type: String,
index f5d443e22e45fa7a8831eda537481450dfdc9f68..99f5fa9871d98069e9303ea030f4077c5da04b11 100644 (file)
@@ -22,7 +22,8 @@ import {
   SetupContext,
   toRaw,
   compatUtils,
-  DeprecationTypes
+  DeprecationTypes,
+  ComponentOptions
 } from '@vue/runtime-core'
 import { extend } from '@vue/shared'
 
@@ -39,7 +40,7 @@ export type TransitionGroupProps = Omit<TransitionProps, 'mode'> & {
   moveClass?: string
 }
 
-const TransitionGroupImpl = {
+const TransitionGroupImpl: ComponentOptions = {
   name: 'TransitionGroup',
 
   props: /*#__PURE__*/ extend({}, TransitionPropsValidators, {
@@ -145,6 +146,10 @@ const TransitionGroupImpl = {
   }
 }
 
+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