@@ -15,6 +15,7 @@ import {
1515 currentInstance ,
1616 endMeasure ,
1717 expose ,
18+ getComponentName ,
1819 nextUid ,
1920 popWarningContext ,
2021 pushWarningContext ,
@@ -35,7 +36,13 @@ import {
3536 resetTracking ,
3637 unref ,
3738} from '@vue/reactivity'
38- import { EMPTY_OBJ , invokeArrayFns , isFunction , isString } from '@vue/shared'
39+ import {
40+ EMPTY_OBJ ,
41+ invokeArrayFns ,
42+ isFunction ,
43+ isPromise ,
44+ isString ,
45+ } from '@vue/shared'
3946import {
4047 type DynamicPropsSource ,
4148 type RawProps ,
@@ -137,6 +144,7 @@ export function createComponent(
137144 appContext : GenericAppContext = ( currentInstance &&
138145 currentInstance . appContext ) ||
139146 emptyContext ,
147+ parentSuspense ?: SuspenseBoundary | null ,
140148) : VaporComponentInstance {
141149 const _insertionParent = insertionParent
142150 const _insertionAnchor = insertionAnchor
@@ -180,6 +188,7 @@ export function createComponent(
180188 rawProps as RawProps ,
181189 rawSlots as RawSlots ,
182190 appContext ,
191+ parentSuspense ,
183192 )
184193
185194 if ( __DEV__ ) {
@@ -207,56 +216,24 @@ export function createComponent(
207216 ] ) || EMPTY_OBJ
208217 : EMPTY_OBJ
209218
210- if ( __DEV__ && ! isBlock ( setupResult ) ) {
211- if ( isFunction ( component ) ) {
212- warn ( `Functional vapor component must return a block directly.` )
213- instance . block = [ ]
214- } else if ( ! component . render ) {
219+ const isAsyncSetup = isPromise ( setupResult )
220+ if ( __FEATURE_SUSPENSE__ && isAsyncSetup ) {
221+ // async setup returned Promise.
222+ // bail here and wait for re-entry.
223+ instance . asyncDep = setupResult
224+ if ( __DEV__ && ! instance . suspense ) {
225+ const name = getComponentName ( component , true ) ?? 'Anonymous'
215226 warn (
216- `Vapor component setup() returned non-block value, and has no render function.` ,
227+ `Component <${ name } >: setup function returned a promise, but no ` +
228+ `<Suspense> boundary was found in the parent component tree. ` +
229+ `A component with async setup() must be nested in a <Suspense> ` +
230+ `in order to be rendered.` ,
217231 )
218- instance . block = [ ]
219- } else {
220- instance . devtoolsRawSetupState = setupResult
221- // TODO make the proxy warn non-existent property access during dev
222- instance . setupState = proxyRefs ( setupResult )
223- devRender ( instance )
224-
225- // HMR
226- if ( component . __hmrId ) {
227- registerHMR ( instance )
228- instance . isSingleRoot = isSingleRoot
229- instance . hmrRerender = hmrRerender . bind ( null , instance )
230- instance . hmrReload = hmrReload . bind ( null , instance )
231- }
232- }
233- } else {
234- // component has a render function but no setup function
235- // (typically components with only a template and no state)
236- if ( ! setupFn && component . render ) {
237- instance . block = callWithErrorHandling (
238- component . render ,
239- instance ,
240- ErrorCodes . RENDER_FUNCTION ,
241- )
242- } else {
243- // in prod result can only be block
244- instance . block = setupResult as Block
245232 }
246233 }
247234
248- // single root, inherit attrs
249- if (
250- instance . hasFallthrough &&
251- component . inheritAttrs !== false &&
252- instance . block instanceof Element &&
253- Object . keys ( instance . attrs ) . length
254- ) {
255- renderEffect ( ( ) => {
256- isApplyingFallthroughProps = true
257- setDynamicProps ( instance . block as Element , [ instance . attrs ] )
258- isApplyingFallthroughProps = false
259- } )
235+ if ( ! isAsyncSetup ) {
236+ handleSetupResult ( setupResult , component , instance , isSingleRoot , setupFn )
260237 }
261238
262239 resetTracking ( )
@@ -269,7 +246,7 @@ export function createComponent(
269246
270247 onScopeDispose ( ( ) => unmountComponent ( instance ) , true )
271248
272- if ( ! isHydrating && _insertionParent ) {
249+ if ( ! isHydrating && _insertionParent && ! isAsyncSetup ) {
273250 insert ( instance . block , _insertionParent , _insertionAnchor )
274251 }
275252
@@ -342,6 +319,9 @@ export class VaporComponentInstance implements GenericComponentInstance {
342319 ids : [ string , number , number ]
343320 // for suspense
344321 suspense : SuspenseBoundary | null
322+ suspenseId : number
323+ asyncDep : Promise < any > | null
324+ asyncResolved : boolean
345325
346326 hasFallthrough : boolean
347327
@@ -380,6 +360,7 @@ export class VaporComponentInstance implements GenericComponentInstance {
380360 rawProps ?: RawProps | null ,
381361 rawSlots ?: RawSlots | null ,
382362 appContext ?: GenericAppContext ,
363+ suspense ?: SuspenseBoundary | null ,
383364 ) {
384365 this . vapor = true
385366 this . uid = nextUid ( )
@@ -403,12 +384,13 @@ export class VaporComponentInstance implements GenericComponentInstance {
403384 this . emit = emit . bind ( null , this )
404385 this . expose = expose . bind ( null , this )
405386 this . refs = EMPTY_OBJ
406- this . emitted =
407- this . exposed =
408- this . exposeProxy =
409- this . propsDefaults =
410- this . suspense =
411- null
387+ this . emitted = this . exposed = this . exposeProxy = this . propsDefaults = null
388+
389+ // suspense related
390+ this . suspense = suspense !
391+ this . suspenseId = suspense ? suspense . pendingId : 0
392+ this . asyncDep = null
393+ this . asyncResolved = false
412394
413395 this . isMounted =
414396 this . isUnmounted =
@@ -545,3 +527,70 @@ export function getExposed(
545527 )
546528 }
547529}
530+
531+ export function handleSetupResult (
532+ setupResult : any ,
533+ component : VaporComponent ,
534+ instance : VaporComponentInstance ,
535+ isSingleRoot : boolean | undefined ,
536+ setupFn : VaporSetupFn | undefined ,
537+ ) : void {
538+ if ( __DEV__ ) {
539+ pushWarningContext ( instance )
540+ }
541+ if ( __DEV__ && ! isBlock ( setupResult ) ) {
542+ if ( isFunction ( component ) ) {
543+ warn ( `Functional vapor component must return a block directly.` )
544+ instance . block = [ ]
545+ } else if ( ! component . render ) {
546+ warn (
547+ `Vapor component setup() returned non-block value, and has no render function.` ,
548+ )
549+ instance . block = [ ]
550+ } else {
551+ instance . devtoolsRawSetupState = setupResult
552+ // TODO make the proxy warn non-existent property access during dev
553+ instance . setupState = proxyRefs ( setupResult )
554+ devRender ( instance )
555+
556+ // HMR
557+ if ( component . __hmrId ) {
558+ registerHMR ( instance )
559+ instance . isSingleRoot = isSingleRoot
560+ instance . hmrRerender = hmrRerender . bind ( null , instance )
561+ instance . hmrReload = hmrReload . bind ( null , instance )
562+ }
563+ }
564+ } else {
565+ // component has a render function but no setup function
566+ // (typically components with only a template and no state)
567+ if ( ! setupFn && component . render ) {
568+ instance . block = callWithErrorHandling (
569+ component . render ,
570+ instance ,
571+ ErrorCodes . RENDER_FUNCTION ,
572+ )
573+ } else {
574+ // in prod result can only be block
575+ instance . block = setupResult as Block
576+ }
577+ }
578+
579+ // single root, inherit attrs
580+ if (
581+ instance . hasFallthrough &&
582+ component . inheritAttrs !== false &&
583+ instance . block instanceof Element &&
584+ Object . keys ( instance . attrs ) . length
585+ ) {
586+ renderEffect ( ( ) => {
587+ isApplyingFallthroughProps = true
588+ setDynamicProps ( instance . block as Element , [ instance . attrs ] )
589+ isApplyingFallthroughProps = false
590+ } )
591+ }
592+
593+ if ( __DEV__ ) {
594+ popWarningContext ( )
595+ }
596+ }
0 commit comments