--- /dev/null
+import { isArray, isFunction, isObject, isPromise } from '@vue/shared/src'
+import { defineAsyncComponent } from '../apiAsyncComponent'
+import { Component, ComponentOptions, FunctionalComponent } from '../component'
+import { isVNode } from '../vnode'
+import { softAssertCompatEnabled } from './compatConfig'
+import { DeprecationTypes } from './deprecations'
+
+export function convertLegacyComponent(comp: any): Component {
+ // 2.x async component
+ if (
+ isFunction(comp) &&
+ softAssertCompatEnabled(DeprecationTypes.COMPONENT_ASYNC, comp)
+ ) {
+ return convertLegacyAsyncComponent(comp)
+ }
+
+ // 2.x functional component
+ if (
+ isObject(comp) &&
+ comp.functional &&
+ softAssertCompatEnabled(DeprecationTypes.COMPONENT_FUNCTIONAL, comp)
+ ) {
+ return convertLegacyFunctionalComponent(comp)
+ }
+
+ 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
+}
+
+function convertLegacyFunctionalComponent(comp: ComponentOptions) {
+ return comp.render as FunctionalComponent
+}
import {
formatComponentName,
+ getComponentName,
getCurrentInstance,
isRuntimeOnly
} from '../component'
ATTR_ENUMERATED_COERSION = 'ATTR_ENUMERATED_COERSION',
TRANSITION_CLASSES = 'TRANSITION_CLASSES',
- TRANSITION_GROUP_ROOT = 'TRANSITION_GROUP_ROOT'
+ TRANSITION_GROUP_ROOT = 'TRANSITION_GROUP_ROOT',
+
+ COMPONENT_ASYNC = 'COMPONENT_ASYNC',
+ COMPONENT_FUNCTIONAL = 'COMPONENT_FUNCTIONAL'
}
type DeprecationData = {
DeprecationTypes.TRANSITION_GROUP_ROOT
}: { enabled: false }})\n`,
link: `https://v3.vuejs.org/guide/migration/transition-group.html`
+ },
+
+ [DeprecationTypes.COMPONENT_ASYNC]: {
+ message: (comp: any) => {
+ const name = getComponentName(comp)
+ return (
+ `Async component${
+ name ? ` <${name}>` : `s`
+ } should be explicitly created via \`defineAsyncComponent()\` ` +
+ `in Vue 3. Plain functions will be treated as functional components in ` +
+ `non-compat build.`
+ )
+ },
+ link: `https://v3.vuejs.org/guide/migration/async-components.html`
+ },
+
+ [DeprecationTypes.COMPONENT_FUNCTIONAL]: {
+ message: (comp: any) => {
+ const name = getComponentName(comp)
+ return (
+ `Functional component${
+ name ? ` <${name}>` : `s`
+ } should be defined as a plain function in Vue 3. The "functional" ` +
+ `option has been removed.\n` +
+ `NOTE: Before migrating, ensure that all async ` +
+ `components have been upgraded to use \`defineAsyncComponent()\` and ` +
+ `then disable compat for legacy async components with:` +
+ `\n\n configureCompat({ ${
+ DeprecationTypes.COMPONENT_ASYNC
+ }: { enabled: false }})\n`
+ )
+ },
+ link: `https://v3.vuejs.org/guide/migration/functional-components.html`
}
}
import { NULL_DYNAMIC_COMPONENT } from './helpers/resolveAssets'
import { hmrDirtyComponents } from './hmr'
import { setCompiledSlotRendering } from './helpers/renderSlot'
+import { convertLegacyComponent } from './compat/component'
export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
__isFragment: true
type = type.__vccOpts
}
+ // 2.x async/functional component compat
+ if (__COMPAT__) {
+ type = convertLegacyComponent(type)
+ }
+
// class & style normalization.
if (props) {
// for reactive or proxy objects, we need to clone it to enable mutation.