@@ -81,6 +81,7 @@ import {
81
81
completeBoundaryWithStyles as styleInsertionFunction ,
82
82
completeSegment as completeSegmentFunction ,
83
83
formReplaying as formReplayingRuntime ,
84
+ markShellTime ,
84
85
} from './fizz-instruction-set/ReactDOMFizzInstructionSetInlineCodeStrings' ;
85
86
86
87
import { getValueDescriptorExpectingObjectForWarning } from '../shared/ReactDOMResourceValidation' ;
@@ -120,13 +121,14 @@ const ScriptStreamingFormat: StreamingFormat = 0;
120
121
const DataStreamingFormat : StreamingFormat = 1 ;
121
122
122
123
export type InstructionState = number ;
123
- const NothingSent /* */ = 0b000000 ;
124
- const SentCompleteSegmentFunction /* */ = 0b000001 ;
125
- const SentCompleteBoundaryFunction /* */ = 0b000010 ;
126
- const SentClientRenderFunction /* */ = 0b000100 ;
127
- const SentStyleInsertionFunction /* */ = 0b001000 ;
128
- const SentFormReplayingRuntime /* */ = 0b010000 ;
129
- const SentCompletedShellId /* */ = 0b100000 ;
124
+ const NothingSent /* */ = 0b0000000 ;
125
+ const SentCompleteSegmentFunction /* */ = 0b0000001 ;
126
+ const SentCompleteBoundaryFunction /* */ = 0b0000010 ;
127
+ const SentClientRenderFunction /* */ = 0b0000100 ;
128
+ const SentStyleInsertionFunction /* */ = 0b0001000 ;
129
+ const SentFormReplayingRuntime /* */ = 0b0010000 ;
130
+ const SentCompletedShellId /* */ = 0b0100000 ;
131
+ const SentMarkShellTime /* */ = 0b1000000 ;
130
132
131
133
// Per request, global state that is not contextual to the rendering subtree.
132
134
// This cannot be resumed and therefore should only contain things that are
@@ -4107,21 +4109,53 @@ function writeBootstrap(
4107
4109
return true ;
4108
4110
}
4109
4111
4112
+ const shellTimeRuntimeScript = stringToPrecomputedChunk ( markShellTime ) ;
4113
+
4114
+ function writeShellTimeInstruction (
4115
+ destination : Destination ,
4116
+ resumableState : ResumableState ,
4117
+ renderState : RenderState ,
4118
+ ) : boolean {
4119
+ if (
4120
+ enableFizzExternalRuntime &&
4121
+ resumableState . streamingFormat !== ScriptStreamingFormat
4122
+ ) {
4123
+ // External runtime always tracks the shell time in the runtime.
4124
+ return true ;
4125
+ }
4126
+ if ( ( resumableState . instructions & SentMarkShellTime ) !== NothingSent ) {
4127
+ // We already sent this instruction.
4128
+ return true ;
4129
+ }
4130
+ resumableState . instructions |= SentMarkShellTime ;
4131
+ writeChunk ( destination , renderState . startInlineScript ) ;
4132
+ writeCompletedShellIdAttribute ( destination , resumableState ) ;
4133
+ writeChunk ( destination , endOfStartTag ) ;
4134
+ writeChunk ( destination , shellTimeRuntimeScript ) ;
4135
+ return writeChunkAndReturn ( destination , endInlineScript ) ;
4136
+ }
4137
+
4110
4138
export function writeCompletedRoot (
4111
4139
destination : Destination ,
4112
4140
resumableState : ResumableState ,
4113
4141
renderState : RenderState ,
4142
+ isComplete : boolean ,
4114
4143
) : boolean {
4144
+ if ( ! isComplete ) {
4145
+ // If we're not already fully complete, we might complete another boundary. If so,
4146
+ // we need to track the paint time of the shell so we know how much to throttle the reveal.
4147
+ writeShellTimeInstruction ( destination , resumableState , renderState ) ;
4148
+ }
4115
4149
const preamble = renderState . preamble ;
4116
4150
if ( preamble . htmlChunks || preamble . headChunks ) {
4117
4151
// If we rendered the whole document, then we emitted a rel="expect" that needs a
4118
4152
// matching target. Normally we use one of the bootstrap scripts for this but if
4119
4153
// there are none, then we need to emit a tag to complete the shell.
4120
4154
if ( ( resumableState . instructions & SentCompletedShellId ) === NothingSent ) {
4121
- const bootstrapChunks = renderState . bootstrapChunks ;
4122
- bootstrapChunks . push ( startChunkForTag ( 'template' ) ) ;
4123
- pushCompletedShellIdAttribute ( bootstrapChunks , resumableState ) ;
4124
- bootstrapChunks . push ( endOfStartTag , endChunkForTag ( 'template' ) ) ;
4155
+ writeChunk ( destination , startChunkForTag ( 'template' ) ) ;
4156
+ writeCompletedShellIdAttribute ( destination , resumableState ) ;
4157
+ writeChunk ( destination , endOfStartTag ) ;
4158
+ writeChunk ( destination , endChunkForTag ( 'template' ) ) ;
4125
4159
}
4126
4160
}
4127
4161
return writeBootstrap ( destination , renderState ) ;
@@ -4482,14 +4516,14 @@ export function writeCompletedSegmentInstruction(
4482
4516
}
4483
4517
}
4484
4518
4519
+ const completeBoundaryScriptFunctionOnly = stringToPrecomputedChunk (
4520
+ completeBoundaryFunction ,
4521
+ ) ;
4485
4522
const completeBoundaryScript1Full = stringToPrecomputedChunk (
4486
4523
completeBoundaryFunction + '$RC("' ,
4487
4524
) ;
4488
4525
const completeBoundaryScript1Partial = stringToPrecomputedChunk ( '$RC("' ) ;
4489
4526
4490
- const completeBoundaryWithStylesScript1FullBoth = stringToPrecomputedChunk (
4491
- completeBoundaryFunction + styleInsertionFunction + '$RR("' ,
4492
- ) ;
4493
4527
const completeBoundaryWithStylesScript1FullPartial = stringToPrecomputedChunk (
4494
4528
styleInsertionFunction + '$RR("' ,
4495
4529
) ;
@@ -4531,19 +4565,27 @@ export function writeCompletedBoundaryInstruction(
4531
4565
writeChunk ( destination , renderState . startInlineScript ) ;
4532
4566
writeChunk ( destination , endOfStartTag ) ;
4533
4567
if ( requiresStyleInsertion ) {
4568
+ if (
4569
+ ( resumableState . instructions & SentClientRenderFunction ) ===
4570
+ NothingSent
4571
+ ) {
4572
+ // The completeBoundaryWithStyles function depends on the client render function.
4573
+ resumableState . instructions |= SentClientRenderFunction ;
4574
+ writeChunk ( destination , clientRenderScriptFunctionOnly ) ;
4575
+ }
4534
4576
if (
4535
4577
( resumableState . instructions & SentCompleteBoundaryFunction ) ===
4536
4578
NothingSent
4537
4579
) {
4538
- resumableState . instructions |=
4539
- SentStyleInsertionFunction | SentCompleteBoundaryFunction ;
4540
- writeChunk ( destination , completeBoundaryWithStylesScript1FullBoth ) ;
4541
- } else if (
4580
+ // The completeBoundaryWithStyles function depends on the complete boundary function.
4581
+ resumableState . instructions |= SentCompleteBoundaryFunction ;
4582
+ writeChunk ( destination , completeBoundaryScriptFunctionOnly ) ;
4583
+ }
4584
+ if (
4542
4585
( resumableState . instructions & SentStyleInsertionFunction ) ===
4543
4586
NothingSent
4544
4587
) {
4545
4588
resumableState . instructions |= SentStyleInsertionFunction ;
4546
-
4547
4589
writeChunk ( destination , completeBoundaryWithStylesScript1FullPartial ) ;
4548
4590
} else {
4549
4591
writeChunk ( destination , completeBoundaryWithStylesScript1Partial ) ;
@@ -4608,6 +4650,9 @@ export function writeCompletedBoundaryInstruction(
4608
4650
return writeBootstrap ( destination , renderState ) && writeMore ;
4609
4651
}
4610
4652
4653
+ const clientRenderScriptFunctionOnly =
4654
+ stringToPrecomputedChunk ( clientRenderFunction ) ;
4655
+
4611
4656
const clientRenderScript1Full = stringToPrecomputedChunk (
4612
4657
clientRenderFunction + ';$RX("' ,
4613
4658
) ;
@@ -5004,6 +5049,21 @@ function writeBlockingRenderInstruction(
5004
5049
5005
5050
const completedShellIdAttributeStart = stringToPrecomputedChunk ( ' id="' ) ;
5006
5051
5052
+ function writeCompletedShellIdAttribute (
5053
+ destination : Destination ,
5054
+ resumableState : ResumableState ,
5055
+ ) : void {
5056
+ if ( ( resumableState . instructions & SentCompletedShellId ) !== NothingSent ) {
5057
+ return ;
5058
+ }
5059
+ resumableState . instructions |= SentCompletedShellId ;
5060
+ const idPrefix = resumableState . idPrefix ;
5061
+ const shellId = '\u00AB' + idPrefix + 'R\u00BB' ;
5062
+ writeChunk ( destination , completedShellIdAttributeStart ) ;
5063
+ writeChunk ( destination , stringToChunk ( escapeTextForBrowser ( shellId ) ) ) ;
5064
+ writeChunk ( destination , attributeEnd ) ;
5065
+ }
5066
+
5007
5067
function pushCompletedShellIdAttribute (
5008
5068
target : Array < Chunk | PrecomputedChunk > ,
5009
5069
resumableState : ResumableState ,
@@ -5029,15 +5089,10 @@ export function writePreambleStart(
5029
5089
destination : Destination ,
5030
5090
resumableState : ResumableState ,
5031
5091
renderState : RenderState ,
5032
- willFlushAllSegments : boolean ,
5033
5092
skipExpect ?: boolean , // Used as an override by ReactFizzConfigMarkup
5034
5093
) : void {
5035
5094
// This function must be called exactly once on every request
5036
- if (
5037
- enableFizzExternalRuntime &&
5038
- ! willFlushAllSegments &&
5039
- renderState . externalRuntimeScript
5040
- ) {
5095
+ if ( enableFizzExternalRuntime && renderState . externalRuntimeScript ) {
5041
5096
// If the root segment is incomplete due to suspended tasks
5042
5097
// (e.g. willFlushAllSegments = false) and we are using data
5043
5098
// streaming format, ensure the external runtime is sent.
0 commit comments