@samitouri / QOS-React-1 / commits / 8a3c5e1a8d

Emit Preamble Contribution inline instead of the end of a boundary (#32850)

This lets us write them early in the render phase. This should be safe because even if we write them deeply, then they still can't be wrapped by a element because then they'd no longer be in the document scope anymore. They end up flat in the body and so when we search the content we'll discover them.

Sebastian Markbåge committed Apr 10, 2025 at 19:42 UTC 8a3c5e1a8d1d89a68ca36c6959c1f253710f6cef
6 files changed +47 -123
packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
+20 -38
@@ -230,9 +230,9 @@ const SUSPENSE_START_DATA = '$';
230 const SUSPENSE_END_DATA = '/$';
231 const SUSPENSE_PENDING_START_DATA = '$?';
232 const SUSPENSE_FALLBACK_START_DATA = '$!';
233 -const PREAMBLE_CONTRIBUTION_HTML = 0b001;
234 -const PREAMBLE_CONTRIBUTION_BODY = 0b010;
235 -const PREAMBLE_CONTRIBUTION_HEAD = 0b100;
233 +const PREAMBLE_CONTRIBUTION_HTML = 'html';
234 +const PREAMBLE_CONTRIBUTION_BODY = 'body';
235 +const PREAMBLE_CONTRIBUTION_HEAD = 'head';
236 const FORM_STATE_IS_MATCHING = 'F!';
237 const FORM_STATE_IS_NOT_MATCHING = 'F';
238
@@ -1054,7 +1054,6 @@ export function clearSuspenseBoundary(
1054 suspenseInstance: SuspenseInstance,
1055 ): void {
1056 let node: Node = suspenseInstance;
1057 - let possiblePreambleContribution: number = 0;
1057 // Delete all nodes within this suspense boundary.
1058 // There might be nested nodes so we need to keep track of how
1059 // deep we are and only break out when we're back on top.
@@ -1065,36 +1064,6 @@ export function clearSuspenseBoundary(
1064 if (nextNode && nextNode.nodeType === COMMENT_NODE) {
1065 const data = ((nextNode: any).data: string);
1066 if (data === SUSPENSE_END_DATA) {
1068 - if (
1069 - // represents 3 bits where at least one bit is set (1-7)
1070 - possiblePreambleContribution > 0 &&
1071 - possiblePreambleContribution < 8
1072 - ) {
1073 - const code = possiblePreambleContribution;
1074 - // It's not normally possible to insert a comment immediately preceding Suspense boundary
1075 - // closing comment marker so we can infer that if the comment preceding starts with "1" through "7"
1076 - // then it is in fact a preamble contribution marker comment. We do this value test to avoid the case
1077 - // where the Suspense boundary is empty and the preceding comment marker is the Suspense boundary
1078 - // opening marker or the closing marker of an inner boundary. In those cases the first character won't
1079 - // have the requisite value to be interpreted as a Preamble contribution
1080 - const ownerDocument = parentInstance.ownerDocument;
1081 - if (code & PREAMBLE_CONTRIBUTION_HTML) {
1082 - const documentElement: Element =
1083 - (ownerDocument.documentElement: any);
1084 - releaseSingletonInstance(documentElement);
1085 - }
1086 - if (code & PREAMBLE_CONTRIBUTION_BODY) {
1087 - const body: Element = (ownerDocument.body: any);
1088 - releaseSingletonInstance(body);
1089 - }
1090 - if (code & PREAMBLE_CONTRIBUTION_HEAD) {
1091 - const head: Element = (ownerDocument.head: any);
1092 - releaseSingletonInstance(head);
1093 - // We need to clear the head because this is the only singleton that can have children that
1094 - // were part of this boundary but are not inside this boundary.
1095 - clearHead(head);
1096 - }
1097 - }
1067 if (depth === 0) {
1068 parentInstance.removeChild(nextNode);
1069 // Retry if any event replaying was blocked on this.
@@ -1109,11 +1078,24 @@ export function clearSuspenseBoundary(
1078 data === SUSPENSE_FALLBACK_START_DATA
1079 ) {
1080 depth++;
1112 - } else {
1113 - possiblePreambleContribution = data.charCodeAt(0) - 48;
1081 + } else if (data === PREAMBLE_CONTRIBUTION_HTML) {
1082 + // If a preamble contribution marker is found within the bounds of this boundary,
1083 + // then it contributed to the html tag and we need to reset it.
1084 + const ownerDocument = parentInstance.ownerDocument;
1085 + const documentElement: Element = (ownerDocument.documentElement: any);
1086 + releaseSingletonInstance(documentElement);
1087 + } else if (data === PREAMBLE_CONTRIBUTION_HEAD) {
1088 + const ownerDocument = parentInstance.ownerDocument;
1089 + const head: Element = (ownerDocument.head: any);
1090 + releaseSingletonInstance(head);
1091 + // We need to clear the head because this is the only singleton that can have children that
1092 + // were part of this boundary but are not inside this boundary.
1093 + clearHead(head);
1094 + } else if (data === PREAMBLE_CONTRIBUTION_BODY) {
1095 + const ownerDocument = parentInstance.ownerDocument;
1096 + const body: Element = (ownerDocument.body: any);
1097 + releaseSingletonInstance(body);
1098 }
1115 - } else {
1116 - possiblePreambleContribution = 0;
1099 }
1100 // $FlowFixMe[incompatible-type] we bail out when we get a null
1101 node = nextNode;
packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
+22 -56
@@ -692,23 +692,16 @@ export function completeResumableState(resumableState: ResumableState): void {
692 resumableState.bootstrapModules = undefined;
693 }
694
695 -const NoContribution /* */ = 0b000;
696 -const HTMLContribution /* */ = 0b001;
697 -const BodyContribution /* */ = 0b010;
698 -const HeadContribution /* */ = 0b100;
699 -
695 export type PreambleState = {
696 htmlChunks: null | Array<Chunk | PrecomputedChunk>,
697 headChunks: null | Array<Chunk | PrecomputedChunk>,
698 bodyChunks: null | Array<Chunk | PrecomputedChunk>,
704 - contribution: number,
699 };
700 export function createPreambleState(): PreambleState {
701 return {
702 htmlChunks: null,
703 headChunks: null,
704 bodyChunks: null,
711 - contribution: NoContribution,
705 };
706 }
707
@@ -3279,6 +3272,12 @@ function pushTitleImpl(
3272 return null;
3273 }
3274
3275 +// These are used by the client if we clear a boundary and we find these, then we
3276 +// also clear the singleton as well.
3277 +const headPreambleContributionChunk = stringToPrecomputedChunk('<!--head-->');
3278 +const bodyPreambleContributionChunk = stringToPrecomputedChunk('<!--body-->');
3279 +const htmlPreambleContributionChunk = stringToPrecomputedChunk('<!--html-->');
3280 +
3281 function pushStartHead(
3282 target: Array<Chunk | PrecomputedChunk>,
3283 props: Object,
@@ -3293,6 +3292,12 @@ function pushStartHead(
3292 if (preamble.headChunks) {
3293 throw new Error(`The ${'`<head>`'} tag may only be rendered once.`);
3294 }
3295 +
3296 + // Insert a marker in the body where the contribution to the head was in case we need to clear it.
3297 + if (preambleState !== null) {
3298 + target.push(headPreambleContributionChunk);
3299 + }
3300 +
3301 preamble.headChunks = [];
3302 return pushStartSingletonElement(preamble.headChunks, props, 'head');
3303 } else {
@@ -3317,6 +3322,11 @@ function pushStartBody(
3322 throw new Error(`The ${'`<body>`'} tag may only be rendered once.`);
3323 }
3324
3325 + // Insert a marker in the body where the contribution to the body tag was in case we need to clear it.
3326 + if (preambleState !== null) {
3327 + target.push(bodyPreambleContributionChunk);
3328 + }
3329 +
3330 preamble.bodyChunks = [];
3331 return pushStartSingletonElement(preamble.bodyChunks, props, 'body');
3332 } else {
@@ -3341,6 +3351,11 @@ function pushStartHtml(
3351 throw new Error(`The ${'`<html>`'} tag may only be rendered once.`);
3352 }
3353
3354 + // Insert a marker in the body where the contribution to the head was in case we need to clear it.
3355 + if (preambleState !== null) {
3356 + target.push(htmlPreambleContributionChunk);
3357 + }
3358 +
3359 preamble.htmlChunks = [DOCTYPE];
3360 return pushStartSingletonElement(preamble.htmlChunks, props, 'html');
3361 } else {
@@ -4013,15 +4028,12 @@ export function hoistPreambleState(
4028 const rootPreamble = renderState.preamble;
4029 if (rootPreamble.htmlChunks === null && preambleState.htmlChunks) {
4030 rootPreamble.htmlChunks = preambleState.htmlChunks;
4016 - preambleState.contribution |= HTMLContribution;
4031 }
4032 if (rootPreamble.headChunks === null && preambleState.headChunks) {
4033 rootPreamble.headChunks = preambleState.headChunks;
4020 - preambleState.contribution |= HeadContribution;
4034 }
4035 if (rootPreamble.bodyChunks === null && preambleState.bodyChunks) {
4036 rootPreamble.bodyChunks = preambleState.bodyChunks;
4024 - preambleState.contribution |= BodyContribution;
4037 }
4038 }
4039
@@ -4101,11 +4113,7 @@ export function pushStartActivityBoundary(
4113 export function pushEndActivityBoundary(
4114 target: Array<Chunk | PrecomputedChunk>,
4115 renderState: RenderState,
4104 - preambleState: null | PreambleState,
4116 ): void {
4106 - if (preambleState) {
4107 - pushPreambleContribution(target, preambleState);
4108 - }
4117 target.push(endActivityBoundary);
4118 }
4119
@@ -4220,11 +4228,7 @@ export function writeStartClientRenderedSuspenseBoundary(
4228 export function writeEndCompletedSuspenseBoundary(
4229 destination: Destination,
4230 renderState: RenderState,
4223 - preambleState: null | PreambleState,
4231 ): boolean {
4225 - if (preambleState) {
4226 - writePreambleContribution(destination, preambleState);
4227 - }
4232 return writeChunkAndReturn(destination, endSuspenseBoundary);
4233 }
4234 export function writeEndPendingSuspenseBoundary(
@@ -4236,48 +4240,10 @@ export function writeEndPendingSuspenseBoundary(
4240 export function writeEndClientRenderedSuspenseBoundary(
4241 destination: Destination,
4242 renderState: RenderState,
4239 - preambleState: null | PreambleState,
4243 ): boolean {
4241 - if (preambleState) {
4242 - writePreambleContribution(destination, preambleState);
4243 - }
4244 return writeChunkAndReturn(destination, endSuspenseBoundary);
4245 }
4246
4247 -const boundaryPreambleContributionChunkStart = stringToPrecomputedChunk('<!--');
4248 -const boundaryPreambleContributionChunkEnd = stringToPrecomputedChunk('-->');
4249 -
4250 -function pushPreambleContribution(
4251 - target: Array<Chunk | PrecomputedChunk>,
4252 - preambleState: PreambleState,
4253 -) {
4254 - // Same as writePreambleContribution but for the render phase.
4255 - const contribution = preambleState.contribution;
4256 - if (contribution !== NoContribution) {
4257 - target.push(
4258 - boundaryPreambleContributionChunkStart,
4259 - // This is a number type so we can do the fast path without coercion checking
4260 - // eslint-disable-next-line react-internal/safe-string-coercion
4261 - stringToChunk('' + contribution),
4262 - boundaryPreambleContributionChunkEnd,
4263 - );
4264 - }
4265 -}
4266 -
4267 -function writePreambleContribution(
4268 - destination: Destination,
4269 - preambleState: PreambleState,
4270 -) {
4271 - const contribution = preambleState.contribution;
4272 - if (contribution !== NoContribution) {
4273 - writeChunk(destination, boundaryPreambleContributionChunkStart);
4274 - // This is a number type so we can do the fast path without coercion checking
4275 - // eslint-disable-next-line react-internal/safe-string-coercion
4276 - writeChunk(destination, stringToChunk('' + contribution));
4277 - writeChunk(destination, boundaryPreambleContributionChunkEnd);
4278 - }
4279 -}
4280 -
4247 const startSegmentHTML = stringToPrecomputedChunk('<div hidden id="');
4248 const startSegmentHTML2 = stringToPrecomputedChunk('">');
4249 const endSegmentHTML = stringToPrecomputedChunk('</div>');
packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js
+3 -14
@@ -224,12 +224,11 @@ export function pushStartActivityBoundary(
224 export function pushEndActivityBoundary(
225 target: Array<Chunk | PrecomputedChunk>,
226 renderState: RenderState,
227 - preambleState: null | PreambleState,
227 ): void {
228 if (renderState.generateStaticMarkup) {
229 return;
230 }
232 - pushEndActivityBoundaryImpl(target, renderState, preambleState);
231 + pushEndActivityBoundaryImpl(target, renderState);
232 }
233
234 export function writeStartCompletedSuspenseBoundary(
@@ -269,30 +268,20 @@ export function writeStartClientRenderedSuspenseBoundary(
268 export function writeEndCompletedSuspenseBoundary(
269 destination: Destination,
270 renderState: RenderState,
272 - preambleState: null | PreambleState,
271 ): boolean {
272 if (renderState.generateStaticMarkup) {
273 return true;
274 }
277 - return writeEndCompletedSuspenseBoundaryImpl(
278 - destination,
279 - renderState,
280 - preambleState,
281 - );
275 + return writeEndCompletedSuspenseBoundaryImpl(destination, renderState);
276 }
277 export function writeEndClientRenderedSuspenseBoundary(
278 destination: Destination,
279 renderState: RenderState,
286 - preambleState: null | PreambleState,
280 ): boolean {
281 if (renderState.generateStaticMarkup) {
282 return true;
283 }
291 - return writeEndClientRenderedSuspenseBoundaryImpl(
292 - destination,
293 - renderState,
294 - preambleState,
295 - );
284 + return writeEndClientRenderedSuspenseBoundaryImpl(destination, renderState);
285 }
286
287 export type TransitionStatus = FormStatus;
packages/react-markup/src/ReactFizzConfigMarkup.js
-3
@@ -162,7 +162,6 @@ export function pushStartActivityBoundary(
162 export function pushEndActivityBoundary(
163 target: Array<Chunk | PrecomputedChunk>,
164 renderState: RenderState,
165 - preambleState: null | PreambleState,
165 ): void {
166 // Markup doesn't have any instructions.
167 return;
@@ -192,7 +191,6 @@ export function writeStartClientRenderedSuspenseBoundary(
191 export function writeEndCompletedSuspenseBoundary(
192 destination: Destination,
193 renderState: RenderState,
195 - preambleState: null | PreambleState,
194 ): boolean {
195 // Markup doesn't have any instructions.
196 return true;
@@ -200,7 +198,6 @@ export function writeEndCompletedSuspenseBoundary(
198 export function writeEndClientRenderedSuspenseBoundary(
199 destination: Destination,
200 renderState: RenderState,
203 - preambleState: null | PreambleState,
201 ): boolean {
202 // Markup doesn't have any instructions.
203 return true;
packages/react-noop-renderer/src/ReactNoopServer.js
-1
@@ -181,7 +181,6 @@ const ReactNoopServer = ReactFizzServer({
181 pushEndActivityBoundary(
182 target: Array<Uint8Array>,
183 renderState: RenderState,
184 - preambleState: null | PreambleState,
184 ): void {
185 target.push(POP);
186 },
packages/react-server/src/ReactFizzServer.js
+2 -11
@@ -2243,11 +2243,7 @@ function renderActivity(
2243 renderNode(request, task, props.children, -1);
2244 task.keyPath = prevKeyPath;
2245 }
2246 - pushEndActivityBoundary(
2247 - segment.chunks,
2248 - request.renderState,
2249 - task.blockedPreamble,
2250 - );
2246 + pushEndActivityBoundary(segment.chunks, request.renderState);
2247 segment.lastPushedText = false;
2248 }
2249 }
@@ -4908,7 +4904,6 @@ function flushSegment(
4904 return writeEndClientRenderedSuspenseBoundary(
4905 destination,
4906 request.renderState,
4911 - boundary.fallbackPreamble,
4907 );
4908 } else if (boundary.status !== COMPLETED) {
4909 if (boundary.status === PENDING) {
@@ -4979,11 +4974,7 @@ function flushSegment(
4974 const contentSegment = completedSegments[0];
4975 flushSegment(request, destination, contentSegment, hoistableState);
4976
4982 - return writeEndCompletedSuspenseBoundary(
4983 - destination,
4984 - request.renderState,
4985 - boundary.contentPreamble,
4986 - );
4977 + return writeEndCompletedSuspenseBoundary(destination, request.renderState);
4978 }
4979 }
4980