@@ -2222,10 +2222,10 @@ function pushMeta(
2222
2222
target : Array < Chunk | PrecomputedChunk > ,
2223
2223
props : Object ,
2224
2224
renderState : RenderState ,
2225
- hoistableState : null | HoistableState ,
2226
2225
textEmbedded : boolean ,
2227
2226
insertionMode : InsertionMode ,
2228
2227
noscriptTagInScope : boolean ,
2228
+ isFallback : boolean ,
2229
2229
) : null {
2230
2230
if ( enableFloat ) {
2231
2231
if (
@@ -2241,31 +2241,26 @@ function pushMeta(
2241
2241
target . push ( textSeparator ) ;
2242
2242
}
2243
2243
2244
- if ( typeof props . charSet === 'string' ) {
2245
- return pushSelfClosing (
2246
- hoistableState
2247
- ? hoistableState . charsetChunks
2248
- : renderState . charsetChunks ,
2249
- props ,
2250
- 'meta' ,
2251
- ) ;
2244
+ if ( isFallback ) {
2245
+ // Hoistable Elements for fallbacks are simply omitted. we don't want to emit them early
2246
+ // because they are likely superceded by primary content and we want to avoid needing to clean
2247
+ // them up when the primary content is ready. They are never hydrated on the client anyway because
2248
+ // boundaries in fallback are awaited or client render, in either case there is never hydration
2249
+ return null ;
2250
+ } else if ( typeof props . charSet === 'string' ) {
2251
+ // "charset" Should really be config and not picked up from tags however since this is
2252
+ // the only way to embed the tag today we flush it on a special queue on the Request so it
2253
+ // can go before everything else. Like viewport this means that the tag will escape it's
2254
+ // parent container.
2255
+ return pushSelfClosing ( renderState . charsetChunks , props , 'meta' ) ;
2252
2256
} else if ( props . name === 'viewport' ) {
2253
- // "viewport" isn't related to preconnect but it has the right priority
2254
- return pushSelfClosing (
2255
- hoistableState
2256
- ? hoistableState . viewportChunks
2257
- : renderState . viewportChunks ,
2258
- props ,
2259
- 'meta' ,
2260
- ) ;
2257
+ // "viewport" is flushed on the Request so it can go earlier that Float resources that
2258
+ // might be affected by it. This means it can escape the boundary it is rendered within.
2259
+ // This is a pragmatic solution to viewport being incredibly sensitive to document order
2260
+ // without requiring all hoistables to be flushed too early.
2261
+ return pushSelfClosing ( renderState . viewportChunks , props , 'meta' ) ;
2261
2262
} else {
2262
- return pushSelfClosing (
2263
- hoistableState
2264
- ? hoistableState . hoistableChunks
2265
- : renderState . hoistableChunks ,
2266
- props ,
2267
- 'meta' ,
2268
- ) ;
2263
+ return pushSelfClosing ( renderState . hoistableChunks , props , 'meta' ) ;
2269
2264
}
2270
2265
}
2271
2266
} else {
@@ -2282,6 +2277,7 @@ function pushLink(
2282
2277
textEmbedded : boolean ,
2283
2278
insertionMode : InsertionMode ,
2284
2279
noscriptTagInScope : boolean ,
2280
+ isFallback : boolean ,
2285
2281
) : null {
2286
2282
if ( enableFloat ) {
2287
2283
const rel = props . rel ;
@@ -2437,10 +2433,15 @@ function pushLink(
2437
2433
target . push ( textSeparator ) ;
2438
2434
}
2439
2435
2440
- const hoistableChunks = hoistableState
2441
- ? hoistableState . hoistableChunks
2442
- : renderState . hoistableChunks ;
2443
- return pushLinkImpl ( hoistableChunks , props ) ;
2436
+ if ( isFallback ) {
2437
+ // Hoistable Elements for fallbacks are simply omitted. we don't want to emit them early
2438
+ // because they are likely superceded by primary content and we want to avoid needing to clean
2439
+ // them up when the primary content is ready. They are never hydrated on the client anyway because
2440
+ // boundaries in fallback are awaited or client render, in either case there is never hydration
2441
+ return null ;
2442
+ } else {
2443
+ return pushLinkImpl ( renderState . hoistableChunks , props ) ;
2444
+ }
2444
2445
}
2445
2446
} else {
2446
2447
return pushLinkImpl ( target , props ) ;
@@ -2894,9 +2895,9 @@ function pushTitle(
2894
2895
target : Array < Chunk | PrecomputedChunk > ,
2895
2896
props : Object ,
2896
2897
renderState : RenderState ,
2897
- hoistableState : null | HoistableState ,
2898
2898
insertionMode : InsertionMode ,
2899
2899
noscriptTagInScope : boolean ,
2900
+ isFallback : boolean ,
2900
2901
) : ReactNodeList {
2901
2902
if ( __DEV__ ) {
2902
2903
if ( hasOwnProperty . call ( props , 'children' ) ) {
@@ -2952,11 +2953,15 @@ function pushTitle(
2952
2953
! noscriptTagInScope &&
2953
2954
props . itemProp == null
2954
2955
) {
2955
- const hoistableTarget = hoistableState
2956
- ? hoistableState . hoistableChunks
2957
- : renderState . hoistableChunks ;
2958
- pushTitleImpl ( hoistableTarget , props ) ;
2959
- return null ;
2956
+ if ( isFallback ) {
2957
+ // Hoistable Elements for fallbacks are simply omitted. we don't want to emit them early
2958
+ // because they are likely superceded by primary content and we want to avoid needing to clean
2959
+ // them up when the primary content is ready. They are never hydrated on the client anyway because
2960
+ // boundaries in fallback are awaited or client render, in either case there is never hydration
2961
+ return null ;
2962
+ } else {
2963
+ pushTitleImpl ( renderState . hoistableChunks , props ) ;
2964
+ }
2960
2965
} else {
2961
2966
return pushTitleImpl ( target , props ) ;
2962
2967
}
@@ -3490,6 +3495,7 @@ export function pushStartInstance(
3490
3495
hoistableState : null | HoistableState ,
3491
3496
formatContext : FormatContext ,
3492
3497
textEmbedded : boolean ,
3498
+ isFallback : boolean ,
3493
3499
) : ReactNodeList {
3494
3500
if ( __DEV__ ) {
3495
3501
validateARIAProperties ( type , props ) ;
@@ -3556,9 +3562,9 @@ export function pushStartInstance(
3556
3562
target ,
3557
3563
props ,
3558
3564
renderState ,
3559
- hoistableState ,
3560
3565
formatContext . insertionMode ,
3561
3566
! ! ( formatContext . tagScope & NOSCRIPT_SCOPE ) ,
3567
+ isFallback ,
3562
3568
)
3563
3569
: pushStartTitle ( target , props ) ;
3564
3570
case 'link' :
@@ -3571,6 +3577,7 @@ export function pushStartInstance(
3571
3577
textEmbedded ,
3572
3578
formatContext . insertionMode ,
3573
3579
! ! ( formatContext . tagScope & NOSCRIPT_SCOPE ) ,
3580
+ isFallback ,
3574
3581
) ;
3575
3582
case 'script' :
3576
3583
return enableFloat
@@ -3600,10 +3607,10 @@ export function pushStartInstance(
3600
3607
target ,
3601
3608
props ,
3602
3609
renderState ,
3603
- hoistableState ,
3604
3610
textEmbedded ,
3605
3611
formatContext . insertionMode ,
3606
3612
! ! ( formatContext . tagScope & NOSCRIPT_SCOPE ) ,
3613
+ isFallback ,
3607
3614
) ;
3608
3615
// Newline eating tags
3609
3616
case 'listing' :
@@ -4469,7 +4476,7 @@ function hasStylesToHoist(stylesheet: StylesheetResource): boolean {
4469
4476
return false ;
4470
4477
}
4471
4478
4472
- export function writeHoistablesForPartialBoundary (
4479
+ export function writeHoistablesForBoundary (
4473
4480
destination : Destination ,
4474
4481
hoistableState : HoistableState ,
4475
4482
renderState : RenderState ,
@@ -4495,57 +4502,6 @@ export function writeHoistablesForPartialBoundary(
4495
4502
return destinationHasCapacity ;
4496
4503
}
4497
4504
4498
- export function writeHoistablesForCompletedBoundary (
4499
- destination : Destination ,
4500
- hoistableState : HoistableState ,
4501
- renderState : RenderState ,
4502
- ) : boolean {
4503
- // Reset these on each invocation, they are only safe to read in this function
4504
- currentlyRenderingBoundaryHasStylesToHoist = false ;
4505
- destinationHasCapacity = true ;
4506
-
4507
- // Flush style tags for each precedence this boundary depends on
4508
- hoistableState . styles . forEach ( flushStyleTagsLateForBoundary , destination ) ;
4509
-
4510
- // Determine if this boundary has stylesheets that need to be awaited upon completion
4511
- hoistableState . stylesheets . forEach ( hasStylesToHoist ) ;
4512
-
4513
- // Flush Hoistable Elements
4514
- let i ;
4515
- const charsetChunks = hoistableState . charsetChunks ;
4516
- for ( i = 0 ; i < charsetChunks . length - 1 ; i ++ ) {
4517
- writeChunk ( destination , charsetChunks [ i ] ) ;
4518
- }
4519
- if ( i < charsetChunks . length ) {
4520
- destinationHasCapacity = writeChunkAndReturn ( destination , charsetChunks [ i ] ) ;
4521
- }
4522
- const viewportChunks = hoistableState . viewportChunks ;
4523
- for ( i = 0 ; i < viewportChunks . length - 1 ; i ++ ) {
4524
- writeChunk ( destination , charsetChunks [ i ] ) ;
4525
- }
4526
- if ( i < viewportChunks . length ) {
4527
- destinationHasCapacity = writeChunkAndReturn (
4528
- destination ,
4529
- viewportChunks [ i ] ,
4530
- ) ;
4531
- }
4532
- const hoistableChunks = hoistableState . hoistableChunks ;
4533
- for ( i = 0 ; i < hoistableChunks . length - 1 ; i ++ ) {
4534
- writeChunk ( destination , hoistableChunks [ i ] ) ;
4535
- }
4536
- if ( i < hoistableChunks . length ) {
4537
- destinationHasCapacity = writeChunkAndReturn (
4538
- destination ,
4539
- hoistableChunks [ i ] ,
4540
- ) ;
4541
- }
4542
-
4543
- if ( currentlyRenderingBoundaryHasStylesToHoist ) {
4544
- renderState . stylesToHoist = true ;
4545
- }
4546
- return destinationHasCapacity ;
4547
- }
4548
-
4549
4505
function flushResource ( this : Destination , resource : Resource ) {
4550
4506
for ( let i = 0 ; i < resource . length ; i ++ ) {
4551
4507
writeChunk ( this , resource [ i ] ) ;
@@ -4741,26 +4697,35 @@ export function writePreamble(
4741
4697
}
4742
4698
hoistableChunks . length = 0 ;
4743
4699
4744
- // Flush closing head if necessary
4745
4700
if ( htmlChunks && headChunks === null ) {
4746
- // We have an <html> rendered but no <head> rendered. We however inserted
4747
- // a <head> up above so we need to emit the </head> now. This is safe because
4748
- // if the main content contained the </head> it would also have provided a
4749
- // <head>. This means that all the content inside <html> is either <body> or
4750
- // invalid HTML
4701
+ // we have an <html> but we inserted an implicit <head> tag. We need
4702
+ // to close it since the main content won't have it
4751
4703
writeChunk ( destination , endChunkForTag ( 'head' ) ) ;
4752
4704
}
4753
4705
}
4754
4706
4755
- // This is an opportunity to write hoistables however in the current implemention
4756
- // the only hoistables that make sense to write here are Resources. Hoistable Elements
4757
- // would have already been written as part of the preamble or will be written as part
4758
- // of a boundary completion and thus don't need to be written here .
4707
+ // We don't bother reporting backpressure at the moment because we expect to
4708
+ // flush the entire preamble in a single pass. This probably should be modified
4709
+ // in the future to be backpressure sensitive but that requires a larger refactor
4710
+ // of the flushing code in Fizz .
4759
4711
export function writeHoistables (
4760
4712
destination : Destination ,
4761
4713
resumableState : ResumableState ,
4762
4714
renderState : RenderState ,
4763
4715
) : void {
4716
+ let i = 0 ;
4717
+
4718
+ // Emit high priority Hoistables
4719
+
4720
+ // We omit charsetChunks because we have already sent the shell and if it wasn't
4721
+ // already sent it is too late now.
4722
+
4723
+ const viewportChunks = renderState . viewportChunks ;
4724
+ for ( i = 0 ; i < viewportChunks . length ; i ++ ) {
4725
+ writeChunk ( destination , viewportChunks [ i ] ) ;
4726
+ }
4727
+ viewportChunks . length = 0 ;
4728
+
4764
4729
renderState . preconnects . forEach ( flushResource , destination ) ;
4765
4730
renderState . preconnects . clear ( ) ;
4766
4731
@@ -4787,6 +4752,13 @@ export function writeHoistables(
4787
4752
4788
4753
renderState . bulkPreloads . forEach ( flushResource , destination ) ;
4789
4754
renderState . bulkPreloads . clear ( ) ;
4755
+
4756
+ // Write embedding hoistableChunks
4757
+ const hoistableChunks = renderState . hoistableChunks ;
4758
+ for ( i = 0 ; i < hoistableChunks . length ; i ++ ) {
4759
+ writeChunk ( destination , hoistableChunks [ i ] ) ;
4760
+ }
4761
+ hoistableChunks . length = 0 ;
4790
4762
}
4791
4763
4792
4764
export function writePostamble (
@@ -5259,10 +5231,6 @@ type StylesheetResource = {
5259
5231
export type HoistableState = {
5260
5232
styles : Set < StyleQueue > ,
5261
5233
stylesheets : Set < StylesheetResource > ,
5262
- // Hoistable chunks
5263
- charsetChunks : Array < Chunk | PrecomputedChunk > ,
5264
- viewportChunks : Array < Chunk | PrecomputedChunk > ,
5265
- hoistableChunks : Array < Chunk | PrecomputedChunk > ,
5266
5234
} ;
5267
5235
5268
5236
export type StyleQueue = {
@@ -5276,9 +5244,6 @@ export function createHoistableState(): HoistableState {
5276
5244
return {
5277
5245
styles : new Set ( ) ,
5278
5246
stylesheets : new Set ( ) ,
5279
- charsetChunks : [ ] ,
5280
- viewportChunks : [ ] ,
5281
- hoistableChunks : [ ] ,
5282
5247
} ;
5283
5248
}
5284
5249
@@ -6142,44 +6107,12 @@ function hoistStylesheetDependency(
6142
6107
this . stylesheets . add ( stylesheet ) ;
6143
6108
}
6144
6109
6145
- export function hoistToBoundary (
6110
+ export function hoistHoistables (
6146
6111
parentState : HoistableState ,
6147
6112
childState : HoistableState ,
6148
6113
) : void {
6149
6114
childState . styles . forEach ( hoistStyleQueueDependency , parentState ) ;
6150
6115
childState . stylesheets . forEach ( hoistStylesheetDependency , parentState ) ;
6151
- let i ;
6152
- const charsetChunks = childState . charsetChunks ;
6153
- for ( i = 0 ; i < charsetChunks . length ; i ++ ) {
6154
- parentState . charsetChunks . push ( charsetChunks [ i ] ) ;
6155
- }
6156
- const viewportChunks = childState . viewportChunks ;
6157
- for ( i = 0 ; i < charsetChunks . length ; i ++ ) {
6158
- parentState . viewportChunks . push ( viewportChunks [ i ] ) ;
6159
- }
6160
- const hoistableChunks = childState . hoistableChunks ;
6161
- for ( i = 0 ; i < hoistableChunks . length ; i ++ ) {
6162
- parentState . hoistableChunks . push ( hoistableChunks [ i ] ) ;
6163
- }
6164
- }
6165
-
6166
- export function hoistToRoot (
6167
- renderState : RenderState ,
6168
- hoistableState : HoistableState ,
6169
- ) : void {
6170
- let i ;
6171
- const charsetChunks = hoistableState . charsetChunks ;
6172
- for ( i = 0 ; i < charsetChunks . length ; i ++ ) {
6173
- renderState . charsetChunks . push ( charsetChunks [ i ] ) ;
6174
- }
6175
- const viewportChunks = hoistableState . viewportChunks ;
6176
- for ( i = 0 ; i < charsetChunks . length ; i ++ ) {
6177
- renderState . viewportChunks . push ( viewportChunks [ i ] ) ;
6178
- }
6179
- const hoistableChunks = hoistableState . hoistableChunks ;
6180
- for ( i = 0 ; i < hoistableChunks . length ; i ++ ) {
6181
- renderState . hoistableChunks . push ( hoistableChunks [ i ] ) ;
6182
- }
6183
6116
}
6184
6117
6185
6118
// This function is called at various times depending on whether we are rendering
0 commit comments