Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 20 additions & 38 deletions packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,9 @@ const SUSPENSE_START_DATA = '$';
const SUSPENSE_END_DATA = '/$';
const SUSPENSE_PENDING_START_DATA = '$?';
const SUSPENSE_FALLBACK_START_DATA = '$!';
const PREAMBLE_CONTRIBUTION_HTML = 0b001;
const PREAMBLE_CONTRIBUTION_BODY = 0b010;
const PREAMBLE_CONTRIBUTION_HEAD = 0b100;
const PREAMBLE_CONTRIBUTION_HTML = 'html';
const PREAMBLE_CONTRIBUTION_BODY = 'body';
const PREAMBLE_CONTRIBUTION_HEAD = 'head';
const FORM_STATE_IS_MATCHING = 'F!';
const FORM_STATE_IS_NOT_MATCHING = 'F';

Expand Down Expand Up @@ -1054,7 +1054,6 @@ export function clearSuspenseBoundary(
suspenseInstance: SuspenseInstance,
): void {
let node: Node = suspenseInstance;
let possiblePreambleContribution: number = 0;
// Delete all nodes within this suspense boundary.
// There might be nested nodes so we need to keep track of how
// deep we are and only break out when we're back on top.
Expand All @@ -1065,36 +1064,6 @@ export function clearSuspenseBoundary(
if (nextNode && nextNode.nodeType === COMMENT_NODE) {
const data = ((nextNode: any).data: string);
if (data === SUSPENSE_END_DATA) {
if (
// represents 3 bits where at least one bit is set (1-7)
possiblePreambleContribution > 0 &&
possiblePreambleContribution < 8
) {
const code = possiblePreambleContribution;
// It's not normally possible to insert a comment immediately preceding Suspense boundary
// closing comment marker so we can infer that if the comment preceding starts with "1" through "7"
// then it is in fact a preamble contribution marker comment. We do this value test to avoid the case
// where the Suspense boundary is empty and the preceding comment marker is the Suspense boundary
// opening marker or the closing marker of an inner boundary. In those cases the first character won't
// have the requisite value to be interpreted as a Preamble contribution
const ownerDocument = parentInstance.ownerDocument;
if (code & PREAMBLE_CONTRIBUTION_HTML) {
const documentElement: Element =
(ownerDocument.documentElement: any);
releaseSingletonInstance(documentElement);
}
if (code & PREAMBLE_CONTRIBUTION_BODY) {
const body: Element = (ownerDocument.body: any);
releaseSingletonInstance(body);
}
if (code & PREAMBLE_CONTRIBUTION_HEAD) {
const head: Element = (ownerDocument.head: any);
releaseSingletonInstance(head);
// We need to clear the head because this is the only singleton that can have children that
// were part of this boundary but are not inside this boundary.
clearHead(head);
}
}
if (depth === 0) {
parentInstance.removeChild(nextNode);
// Retry if any event replaying was blocked on this.
Expand All @@ -1109,11 +1078,24 @@ export function clearSuspenseBoundary(
data === SUSPENSE_FALLBACK_START_DATA
) {
depth++;
} else {
possiblePreambleContribution = data.charCodeAt(0) - 48;
} else if (data === PREAMBLE_CONTRIBUTION_HTML) {
// If a preamble contribution marker is found within the bounds of this boundary,
// then it contributed to the html tag and we need to reset it.
const ownerDocument = parentInstance.ownerDocument;
const documentElement: Element = (ownerDocument.documentElement: any);
releaseSingletonInstance(documentElement);
} else if (data === PREAMBLE_CONTRIBUTION_HEAD) {
const ownerDocument = parentInstance.ownerDocument;
const head: Element = (ownerDocument.head: any);
releaseSingletonInstance(head);
// We need to clear the head because this is the only singleton that can have children that
// were part of this boundary but are not inside this boundary.
clearHead(head);
} else if (data === PREAMBLE_CONTRIBUTION_BODY) {
const ownerDocument = parentInstance.ownerDocument;
const body: Element = (ownerDocument.body: any);
releaseSingletonInstance(body);
}
} else {
possiblePreambleContribution = 0;
}
// $FlowFixMe[incompatible-type] we bail out when we get a null
node = nextNode;
Expand Down
78 changes: 22 additions & 56 deletions packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -692,23 +692,16 @@ export function completeResumableState(resumableState: ResumableState): void {
resumableState.bootstrapModules = undefined;
}

const NoContribution /* */ = 0b000;
const HTMLContribution /* */ = 0b001;
const BodyContribution /* */ = 0b010;
const HeadContribution /* */ = 0b100;

export type PreambleState = {
htmlChunks: null | Array<Chunk | PrecomputedChunk>,
headChunks: null | Array<Chunk | PrecomputedChunk>,
bodyChunks: null | Array<Chunk | PrecomputedChunk>,
contribution: number,
};
export function createPreambleState(): PreambleState {
return {
htmlChunks: null,
headChunks: null,
bodyChunks: null,
contribution: NoContribution,
};
}

Expand Down Expand Up @@ -3279,6 +3272,12 @@ function pushTitleImpl(
return null;
}

// These are used by the client if we clear a boundary and we find these, then we
// also clear the singleton as well.
const headPreambleContributionChunk = stringToPrecomputedChunk('<!--head-->');
const bodyPreambleContributionChunk = stringToPrecomputedChunk('<!--body-->');
const htmlPreambleContributionChunk = stringToPrecomputedChunk('<!--html-->');
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: how is this last??!? :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the order of the functions :D


function pushStartHead(
target: Array<Chunk | PrecomputedChunk>,
props: Object,
Expand All @@ -3293,6 +3292,12 @@ function pushStartHead(
if (preamble.headChunks) {
throw new Error(`The ${'`<head>`'} tag may only be rendered once.`);
}

// Insert a marker in the body where the contribution to the head was in case we need to clear it.
if (preambleState !== null) {
target.push(headPreambleContributionChunk);
}

preamble.headChunks = [];
return pushStartSingletonElement(preamble.headChunks, props, 'head');
} else {
Expand All @@ -3317,6 +3322,11 @@ function pushStartBody(
throw new Error(`The ${'`<body>`'} tag may only be rendered once.`);
}

// Insert a marker in the body where the contribution to the body tag was in case we need to clear it.
if (preambleState !== null) {
target.push(bodyPreambleContributionChunk);
}

preamble.bodyChunks = [];
return pushStartSingletonElement(preamble.bodyChunks, props, 'body');
} else {
Expand All @@ -3341,6 +3351,11 @@ function pushStartHtml(
throw new Error(`The ${'`<html>`'} tag may only be rendered once.`);
}

// Insert a marker in the body where the contribution to the head was in case we need to clear it.
if (preambleState !== null) {
target.push(htmlPreambleContributionChunk);
}

preamble.htmlChunks = [DOCTYPE];
return pushStartSingletonElement(preamble.htmlChunks, props, 'html');
} else {
Expand Down Expand Up @@ -4013,15 +4028,12 @@ export function hoistPreambleState(
const rootPreamble = renderState.preamble;
if (rootPreamble.htmlChunks === null && preambleState.htmlChunks) {
rootPreamble.htmlChunks = preambleState.htmlChunks;
preambleState.contribution |= HTMLContribution;
}
if (rootPreamble.headChunks === null && preambleState.headChunks) {
rootPreamble.headChunks = preambleState.headChunks;
preambleState.contribution |= HeadContribution;
}
if (rootPreamble.bodyChunks === null && preambleState.bodyChunks) {
rootPreamble.bodyChunks = preambleState.bodyChunks;
preambleState.contribution |= BodyContribution;
}
}

Expand Down Expand Up @@ -4101,11 +4113,7 @@ export function pushStartActivityBoundary(
export function pushEndActivityBoundary(
target: Array<Chunk | PrecomputedChunk>,
renderState: RenderState,
preambleState: null | PreambleState,
): void {
if (preambleState) {
pushPreambleContribution(target, preambleState);
}
target.push(endActivityBoundary);
}

Expand Down Expand Up @@ -4220,11 +4228,7 @@ export function writeStartClientRenderedSuspenseBoundary(
export function writeEndCompletedSuspenseBoundary(
destination: Destination,
renderState: RenderState,
preambleState: null | PreambleState,
): boolean {
if (preambleState) {
writePreambleContribution(destination, preambleState);
}
return writeChunkAndReturn(destination, endSuspenseBoundary);
}
export function writeEndPendingSuspenseBoundary(
Expand All @@ -4236,48 +4240,10 @@ export function writeEndPendingSuspenseBoundary(
export function writeEndClientRenderedSuspenseBoundary(
destination: Destination,
renderState: RenderState,
preambleState: null | PreambleState,
): boolean {
if (preambleState) {
writePreambleContribution(destination, preambleState);
}
return writeChunkAndReturn(destination, endSuspenseBoundary);
}

const boundaryPreambleContributionChunkStart = stringToPrecomputedChunk('<!--');
const boundaryPreambleContributionChunkEnd = stringToPrecomputedChunk('-->');

function pushPreambleContribution(
target: Array<Chunk | PrecomputedChunk>,
preambleState: PreambleState,
) {
// Same as writePreambleContribution but for the render phase.
const contribution = preambleState.contribution;
if (contribution !== NoContribution) {
target.push(
boundaryPreambleContributionChunkStart,
// This is a number type so we can do the fast path without coercion checking
// eslint-disable-next-line react-internal/safe-string-coercion
stringToChunk('' + contribution),
boundaryPreambleContributionChunkEnd,
);
}
}

function writePreambleContribution(
destination: Destination,
preambleState: PreambleState,
) {
const contribution = preambleState.contribution;
if (contribution !== NoContribution) {
writeChunk(destination, boundaryPreambleContributionChunkStart);
// This is a number type so we can do the fast path without coercion checking
// eslint-disable-next-line react-internal/safe-string-coercion
writeChunk(destination, stringToChunk('' + contribution));
writeChunk(destination, boundaryPreambleContributionChunkEnd);
}
}

const startSegmentHTML = stringToPrecomputedChunk('<div hidden id="');
const startSegmentHTML2 = stringToPrecomputedChunk('">');
const endSegmentHTML = stringToPrecomputedChunk('</div>');
Expand Down
17 changes: 3 additions & 14 deletions packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,11 @@ export function pushStartActivityBoundary(
export function pushEndActivityBoundary(
target: Array<Chunk | PrecomputedChunk>,
renderState: RenderState,
preambleState: null | PreambleState,
): void {
if (renderState.generateStaticMarkup) {
return;
}
pushEndActivityBoundaryImpl(target, renderState, preambleState);
pushEndActivityBoundaryImpl(target, renderState);
}

export function writeStartCompletedSuspenseBoundary(
Expand Down Expand Up @@ -269,30 +268,20 @@ export function writeStartClientRenderedSuspenseBoundary(
export function writeEndCompletedSuspenseBoundary(
destination: Destination,
renderState: RenderState,
preambleState: null | PreambleState,
): boolean {
if (renderState.generateStaticMarkup) {
return true;
}
return writeEndCompletedSuspenseBoundaryImpl(
destination,
renderState,
preambleState,
);
return writeEndCompletedSuspenseBoundaryImpl(destination, renderState);
}
export function writeEndClientRenderedSuspenseBoundary(
destination: Destination,
renderState: RenderState,
preambleState: null | PreambleState,
): boolean {
if (renderState.generateStaticMarkup) {
return true;
}
return writeEndClientRenderedSuspenseBoundaryImpl(
destination,
renderState,
preambleState,
);
return writeEndClientRenderedSuspenseBoundaryImpl(destination, renderState);
}

export type TransitionStatus = FormStatus;
Expand Down
3 changes: 0 additions & 3 deletions packages/react-markup/src/ReactFizzConfigMarkup.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ export function pushStartActivityBoundary(
export function pushEndActivityBoundary(
target: Array<Chunk | PrecomputedChunk>,
renderState: RenderState,
preambleState: null | PreambleState,
): void {
// Markup doesn't have any instructions.
return;
Expand Down Expand Up @@ -192,15 +191,13 @@ export function writeStartClientRenderedSuspenseBoundary(
export function writeEndCompletedSuspenseBoundary(
destination: Destination,
renderState: RenderState,
preambleState: null | PreambleState,
): boolean {
// Markup doesn't have any instructions.
return true;
}
export function writeEndClientRenderedSuspenseBoundary(
destination: Destination,
renderState: RenderState,
preambleState: null | PreambleState,
): boolean {
// Markup doesn't have any instructions.
return true;
Expand Down
1 change: 0 additions & 1 deletion packages/react-noop-renderer/src/ReactNoopServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ const ReactNoopServer = ReactFizzServer({
pushEndActivityBoundary(
target: Array<Uint8Array>,
renderState: RenderState,
preambleState: null | PreambleState,
): void {
target.push(POP);
},
Expand Down
13 changes: 2 additions & 11 deletions packages/react-server/src/ReactFizzServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2243,11 +2243,7 @@ function renderActivity(
renderNode(request, task, props.children, -1);
task.keyPath = prevKeyPath;
}
pushEndActivityBoundary(
segment.chunks,
request.renderState,
task.blockedPreamble,
);
pushEndActivityBoundary(segment.chunks, request.renderState);
segment.lastPushedText = false;
}
}
Expand Down Expand Up @@ -4908,7 +4904,6 @@ function flushSegment(
return writeEndClientRenderedSuspenseBoundary(
destination,
request.renderState,
boundary.fallbackPreamble,
);
} else if (boundary.status !== COMPLETED) {
if (boundary.status === PENDING) {
Expand Down Expand Up @@ -4979,11 +4974,7 @@ function flushSegment(
const contentSegment = completedSegments[0];
flushSegment(request, destination, contentSegment, hoistableState);

return writeEndCompletedSuspenseBoundary(
destination,
request.renderState,
boundary.contentPreamble,
);
return writeEndCompletedSuspenseBoundary(destination, request.renderState);
}
}

Expand Down