@@ -782,13 +782,14 @@ const HTML_COLGROUP_MODE = 9;
782
782
783
783
type InsertionMode = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ;
784
784
785
- const NO_SCOPE = /* */ 0b000000 ;
786
- const NOSCRIPT_SCOPE = /* */ 0b000001 ;
787
- const PICTURE_SCOPE = /* */ 0b000010 ;
788
- const FALLBACK_SCOPE = /* */ 0b000100 ;
789
- const EXIT_SCOPE = /* */ 0b001000 ; // A direct Instance below a Suspense fallback is the only thing that can "exit"
790
- const ENTER_SCOPE = /* */ 0b010000 ; // A direct Instance below Suspense content is the only thing that can "enter"
791
- const UPDATE_SCOPE = /* */ 0b100000 ; // Inside a scope that applies "update" ViewTransitions if anything mutates here.
785
+ const NO_SCOPE = /* */ 0b0000000 ;
786
+ const NOSCRIPT_SCOPE = /* */ 0b0000001 ;
787
+ const PICTURE_SCOPE = /* */ 0b0000010 ;
788
+ const FALLBACK_SCOPE = /* */ 0b0000100 ;
789
+ const EXIT_SCOPE = /* */ 0b0001000 ; // A direct Instance below a Suspense fallback is the only thing that can "exit"
790
+ const ENTER_SCOPE = /* */ 0b0010000 ; // A direct Instance below Suspense content is the only thing that can "enter"
791
+ const UPDATE_SCOPE = /* */ 0b0100000 ; // Inside a scope that applies "update" ViewTransitions if anything mutates here.
792
+ const APPEARING_SCOPE = /* */ 0b1000000 ; // Below Suspense content subtree which might appear in an "enter" animation or "shared" animation.
792
793
793
794
// Everything not listed here are tracked for the whole subtree as opposed to just
794
795
// until the next Instance.
@@ -987,11 +988,20 @@ export function getSuspenseContentFormatContext(
987
988
resumableState : ResumableState ,
988
989
parentContext : FormatContext ,
989
990
) : FormatContext {
991
+ const viewTransition = getSuspenseViewTransition (
992
+ parentContext . viewTransition ,
993
+ ) ;
994
+ let subtreeScope = parentContext . tagScope | ENTER_SCOPE ;
995
+ if ( viewTransition !== null && viewTransition . share !== 'none' ) {
996
+ // If we have a ViewTransition wrapping Suspense then the appearing animation
997
+ // will be applied just like an "enter" below. Mark it as animating.
998
+ subtreeScope |= APPEARING_SCOPE ;
999
+ }
990
1000
return createFormatContext (
991
1001
parentContext . insertionMode ,
992
1002
parentContext . selectedValue ,
993
- parentContext . tagScope | ENTER_SCOPE ,
994
- getSuspenseViewTransition ( parentContext . viewTransition ) ,
1003
+ subtreeScope ,
1004
+ viewTransition ,
995
1005
) ;
996
1006
}
997
1007
@@ -1063,6 +1073,9 @@ export function getViewTransitionFormatContext(
1063
1073
} else {
1064
1074
subtreeScope &= ~ UPDATE_SCOPE ;
1065
1075
}
1076
+ if ( enter !== 'none' ) {
1077
+ subtreeScope |= APPEARING_SCOPE ;
1078
+ }
1066
1079
return createFormatContext (
1067
1080
parentContext . insertionMode ,
1068
1081
parentContext . selectedValue ,
@@ -3289,6 +3302,7 @@ function pushImg(
3289
3302
props : Object ,
3290
3303
resumableState : ResumableState ,
3291
3304
renderState : RenderState ,
3305
+ hoistableState : null | HoistableState ,
3292
3306
formatContext : FormatContext ,
3293
3307
) : null {
3294
3308
const pictureOrNoScriptTagInScope =
@@ -3321,6 +3335,19 @@ function pushImg(
3321
3335
) {
3322
3336
// We have a suspensey image and ought to preload it to optimize the loading of display blocking
3323
3337
// resumableState.
3338
+
3339
+ if ( hoistableState !== null ) {
3340
+ // Mark this boundary's state as having suspensey images.
3341
+ // Only do that if we have a ViewTransition that might trigger a parent Suspense boundary
3342
+ // to animate its appearing. Since that's the only case we'd actually apply suspensey images
3343
+ // for SSR reveals.
3344
+ const isInSuspenseWithEnterViewTransition =
3345
+ formatContext . tagScope & APPEARING_SCOPE ;
3346
+ if ( isInSuspenseWithEnterViewTransition ) {
3347
+ hoistableState . suspenseyImages = true ;
3348
+ }
3349
+ }
3350
+
3324
3351
const sizes = typeof props . sizes === 'string' ? props . sizes : undefined ;
3325
3352
const key = getImageResourceKey ( src , srcSet , sizes ) ;
3326
3353
@@ -4255,7 +4282,14 @@ export function pushStartInstance(
4255
4282
return pushStartPreformattedElement ( target , props , type , formatContext ) ;
4256
4283
}
4257
4284
case 'img' : {
4258
- return pushImg ( target , props , resumableState , renderState , formatContext ) ;
4285
+ return pushImg (
4286
+ target ,
4287
+ props ,
4288
+ resumableState ,
4289
+ renderState ,
4290
+ hoistableState ,
4291
+ formatContext ,
4292
+ ) ;
4259
4293
}
4260
4294
// Omitted close tags
4261
4295
case 'base' :
@@ -6125,6 +6159,7 @@ type StylesheetResource = {
6125
6159
export type HoistableState = {
6126
6160
styles : Set < StyleQueue > ,
6127
6161
stylesheets : Set < StylesheetResource > ,
6162
+ suspenseyImages : boolean ,
6128
6163
} ;
6129
6164
6130
6165
export type StyleQueue = {
@@ -6138,6 +6173,7 @@ export function createHoistableState(): HoistableState {
6138
6173
return {
6139
6174
styles : new Set ( ) ,
6140
6175
stylesheets : new Set ( ) ,
6176
+ suspenseyImages : false ,
6141
6177
} ;
6142
6178
}
6143
6179
@@ -6995,6 +7031,18 @@ export function hoistHoistables(
6995
7031
) : void {
6996
7032
childState . styles . forEach ( hoistStyleQueueDependency , parentState ) ;
6997
7033
childState . stylesheets . forEach ( hoistStylesheetDependency , parentState ) ;
7034
+ if ( childState . suspenseyImages ) {
7035
+ // If the child has suspensey images, the parent now does too if it's inlined.
7036
+ // Similarly, if a SuspenseList row has a suspensey image then effectively
7037
+ // the next row should be blocked on it as well since the next row can't show
7038
+ // earlier. In practice, since the child will be outlined this transferring
7039
+ // may never matter but is conceptually correct.
7040
+ parentState . suspenseyImages = true ;
7041
+ }
7042
+ }
7043
+
7044
+ export function hasSuspenseyContent ( hoistableState : HoistableState ) : boolean {
7045
+ return hoistableState . stylesheets . size > 0 || hoistableState . suspenseyImages ;
6998
7046
}
6999
7047
7000
7048
// This function is called at various times depending on whether we are rendering
0 commit comments