Files
react/packages/react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js
T
Sebastian MarkbågeandGitHub b9149cc6e6 Include regular stack trace in serialized errors from Fizz (#28684)
We previously only included the component stack.
    
Cleaned up the fields in Fizz server that wasn't using consistent hidden
classes in dev vs prod.

Added a prefix to errors serialized from server rendering. It can be a
bit confusing to see where this error came from otherwise since it
didn't come from elsewhere on the client. It's really kind of confusing
with other recoverable errors that happen on the client too.
2024-03-30 11:08:54 -04:00

116 lines
3.6 KiB
JavaScript

/**
* This file is compiled to a standalone browser script by rollup and loaded by Fizz
* clients. Therefore, it should be fast and not have many external dependencies.
* @flow
*/
/* eslint-disable dot-notation */
// Imports are resolved statically by the closure compiler in release bundles
// and by rollup in jest unit tests
import {
clientRenderBoundary,
completeBoundaryWithStyles,
completeBoundary,
completeSegment,
} from './fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime';
if (!window.$RC) {
// TODO: Eventually remove, we currently need to set these globals for
// compatibility with ReactDOMFizzInstructionSet
window.$RC = completeBoundary;
window.$RM = new Map();
}
if (document.body != null) {
if (document.readyState === 'loading') {
installFizzInstrObserver(document.body);
}
// $FlowFixMe[incompatible-cast]
handleExistingNodes((document.body: HTMLElement));
} else {
// Document must be loading -- body may not exist yet if the fizz external
// runtime is sent in <head> (e.g. as a preinit resource)
// $FlowFixMe[recursive-definition]
const domBodyObserver = new MutationObserver(() => {
// We expect the body node to be stable once parsed / created
if (document.body != null) {
if (document.readyState === 'loading') {
installFizzInstrObserver(document.body);
}
// $FlowFixMe[incompatible-cast]
handleExistingNodes((document.body: HTMLElement));
// We can call disconnect without takeRecord here,
// since we only expect a single document.body
domBodyObserver.disconnect();
}
});
// documentElement must already exist at this point
domBodyObserver.observe(document.documentElement, {childList: true});
}
function handleExistingNodes(target: HTMLElement) {
const existingNodes = target.querySelectorAll('template');
for (let i = 0; i < existingNodes.length; i++) {
handleNode(existingNodes[i]);
}
}
function installFizzInstrObserver(target: Node) {
const handleMutations = (mutations: Array<MutationRecord>) => {
for (let i = 0; i < mutations.length; i++) {
const addedNodes = mutations[i].addedNodes;
for (let j = 0; j < addedNodes.length; j++) {
if (addedNodes[j].parentNode) {
handleNode(addedNodes[j]);
}
}
}
};
const fizzInstrObserver = new MutationObserver(handleMutations);
// We assume that instruction data nodes are eventually appended to the
// body, even if Fizz is streaming to a shell / subtree.
fizzInstrObserver.observe(target, {
childList: true,
});
window.addEventListener('DOMContentLoaded', () => {
handleMutations(fizzInstrObserver.takeRecords());
fizzInstrObserver.disconnect();
});
}
function handleNode(node_: Node) {
// $FlowFixMe[incompatible-cast]
if (node_.nodeType !== 1 || !(node_: HTMLElement).dataset) {
return;
}
// $FlowFixMe[incompatible-cast]
const node = (node_: HTMLElement);
const dataset = node.dataset;
if (dataset['rxi'] != null) {
clientRenderBoundary(
dataset['bid'],
dataset['dgst'],
dataset['msg'],
dataset['stck'],
dataset['cstck'],
);
node.remove();
} else if (dataset['rri'] != null) {
// Convert styles here, since its type is Array<Array<string>>
completeBoundaryWithStyles(
dataset['bid'],
dataset['sid'],
JSON.parse(dataset['sty']),
);
node.remove();
} else if (dataset['rci'] != null) {
completeBoundary(dataset['bid'], dataset['sid']);
node.remove();
} else if (dataset['rsi'] != null) {
completeSegment(dataset['sid'], dataset['pid']);
node.remove();
}
}