2022-11-02 00:04:50 +08:00
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
2022-12-01 02:22:08 +08:00
|
|
|
/* eslint-disable dot-notation */
|
2022-11-02 00:04:50 +08:00
|
|
|
|
|
|
|
|
// Imports are resolved statically by the closure compiler in release bundles
|
|
|
|
|
// and by rollup in jest unit tests
|
2022-10-15 11:29:17 +08:00
|
|
|
import {
|
|
|
|
|
clientRenderBoundary,
|
|
|
|
|
completeBoundaryWithStyles,
|
|
|
|
|
completeBoundary,
|
|
|
|
|
completeSegment,
|
2023-01-07 03:28:55 +08:00
|
|
|
} from './fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime';
|
2022-10-15 11:29:17 +08:00
|
|
|
|
2022-12-01 02:22:08 +08:00
|
|
|
if (!window.$RC) {
|
|
|
|
|
// TODO: Eventually remove, we currently need to set these globals for
|
|
|
|
|
// compatibility with ReactDOMFizzInstructionSet
|
|
|
|
|
window.$RC = completeBoundary;
|
|
|
|
|
window.$RM = new Map();
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-05 00:26:59 +08:00
|
|
|
if (document.body != null) {
|
|
|
|
|
if (document.readyState === 'loading') {
|
2022-12-01 02:22:08 +08:00
|
|
|
installFizzInstrObserver(document.body);
|
|
|
|
|
}
|
2023-03-05 00:26:59 +08:00
|
|
|
// $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 */));
|
2022-12-01 02:22:08 +08:00
|
|
|
|
2023-03-05 00:26:59 +08:00
|
|
|
// We can call disconnect without takeRecord here,
|
|
|
|
|
// since we only expect a single document.body
|
|
|
|
|
domBodyObserver.disconnect();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
// documentElement must already exist at this point
|
|
|
|
|
// $FlowFixMe[incompatible-call]
|
|
|
|
|
domBodyObserver.observe(document.documentElement, {childList: true});
|
|
|
|
|
}
|
2022-12-01 02:22:08 +08:00
|
|
|
|
2023-03-05 00:26:59 +08:00
|
|
|
function handleExistingNodes(target /*: HTMLElement */) {
|
|
|
|
|
const existingNodes = target.querySelectorAll('template');
|
2022-12-01 02:22:08 +08:00
|
|
|
for (let i = 0; i < existingNodes.length; i++) {
|
|
|
|
|
handleNode(existingNodes[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function installFizzInstrObserver(target /*: Node */) {
|
2023-02-22 01:10:23 +08:00
|
|
|
const handleMutations = (mutations /*: Array<MutationRecord> */) => {
|
2022-12-01 02:22:08 +08:00
|
|
|
for (let i = 0; i < mutations.length; i++) {
|
|
|
|
|
const addedNodes = mutations[i].addedNodes;
|
|
|
|
|
for (let j = 0; j < addedNodes.length; j++) {
|
2023-03-05 00:26:59 +08:00
|
|
|
if (addedNodes[j].parentNode) {
|
|
|
|
|
handleNode(addedNodes[j]);
|
2022-12-01 02:22:08 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-22 01:10:23 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const fizzInstrObserver = new MutationObserver(handleMutations);
|
2022-12-01 02:22:08 +08:00
|
|
|
// 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', () => {
|
2023-02-22 01:10:23 +08:00
|
|
|
handleMutations(fizzInstrObserver.takeRecords());
|
2022-12-01 02:22:08 +08:00
|
|
|
fizzInstrObserver.disconnect();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleNode(node_ /*: Node */) {
|
|
|
|
|
// $FlowFixMe[incompatible-cast]
|
2023-01-31 21:25:05 +08:00
|
|
|
if (node_.nodeType !== 1 || !(node_ /*: HTMLElement */).dataset) {
|
2022-12-01 02:22:08 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// $FlowFixMe[incompatible-cast]
|
2023-01-31 21:25:05 +08:00
|
|
|
const node = (node_ /*: HTMLElement */);
|
2022-12-01 02:22:08 +08:00
|
|
|
const dataset = node.dataset;
|
|
|
|
|
if (dataset['rxi'] != null) {
|
|
|
|
|
clientRenderBoundary(
|
|
|
|
|
dataset['bid'],
|
|
|
|
|
dataset['dgst'],
|
|
|
|
|
dataset['msg'],
|
|
|
|
|
dataset['stck'],
|
|
|
|
|
);
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|