/* @flow */ import { warn, once, isDef, isUndef, isTrue, isObject, hasSymbol } from 'core/util/index' import { createEmptyVNode } from 'core/vdom/vnode' function ensureCtor (comp: any, base) { if ( comp.__esModule || (hasSymbol && comp[Symbol.toStringTag] === 'Module') ) { comp = comp.default } return isObject(comp) ? base.extend(comp) : comp } export function createAsyncPlaceholder ( factory: Function, data: ?VNodeData, context: Component, children: ?Array, tag: ?string ): VNode { const node = createEmptyVNode() node.asyncFactory = factory node.asyncMeta = { data, context, children, tag } return node } export function resolveAsyncComponent ( factory: Function, baseCtor: Class, context: Component ): Class | void { if (isTrue(factory.error) && isDef(factory.errorComp)) { return factory.errorComp } if (isDef(factory.resolved)) { return factory.resolved } if (isTrue(factory.loading) && isDef(factory.loadingComp)) { return factory.loadingComp } if (isDef(factory.contexts)) { // already pending factory.contexts.push(context) } else { const contexts = factory.contexts = [context] let sync = true const forceRender = () => { for (let i = 0, l = contexts.length; i < l; i++) { contexts[i].$forceUpdate() } } const resolve = once((res: Object | Class) => { // cache resolved factory.resolved = ensureCtor(res, baseCtor) // invoke callbacks only if this is not a synchronous resolve // (async resolves are shimmed as synchronous during SSR) if (!sync) { forceRender() } }) const reject = once(reason => { process.env.NODE_ENV !== 'production' && warn( `Failed to resolve async component: ${String(factory)}` + (reason ? `\nReason: ${reason}` : '') ) if (isDef(factory.errorComp)) { factory.error = true forceRender() } }) const res = factory(resolve, reject) if (isObject(res)) { if (typeof res.then === 'function') { // () => Promise if (isUndef(factory.resolved)) { res.then(resolve, reject) } } else if (isDef(res.component) && typeof res.component.then === 'function') { res.component.then(resolve, reject) if (isDef(res.error)) { factory.errorComp = ensureCtor(res.error, baseCtor) } if (isDef(res.loading)) { factory.loadingComp = ensureCtor(res.loading, baseCtor) if (res.delay === 0) { factory.loading = true } else { setTimeout(() => { if (isUndef(factory.resolved) && isUndef(factory.error)) { factory.loading = true forceRender() } }, res.delay || 200) } } if (isDef(res.timeout)) { setTimeout(() => { if (isUndef(factory.resolved)) { reject( process.env.NODE_ENV !== 'production' ? `timeout (${res.timeout}ms)` : null ) } }, res.timeout) } } } sync = false // return in case resolved synchronously return factory.loading ? factory.loadingComp : factory.resolved } }