main
js 99 lines 3.24 KB
Raw
1 /**
2 * This file is compiled to a standalone browser script by rollup and loaded by Fizz
3 * clients. Therefore, it should be fast and not have many external dependencies.
4 * @flow
5 */
6 /* eslint-disable dot-notation */
7
8 // Imports are resolved statically by the closure compiler in release bundles
9 // and by rollup in jest unit tests
10 import './fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime';
11
12 if (document.body != null) {
13 if (document.readyState === 'loading') {
14 installFizzInstrObserver(document.body);
15 }
16 // $FlowFixMe[incompatible-type]
17 handleExistingNodes(document.body as HTMLElement);
18 } else {
19 // Document must be loading -- body may not exist yet if the fizz external
20 // runtime is sent in <head> (e.g. as a preinit resource)
21 // $FlowFixMe[recursive-definition]
22 const domBodyObserver = new MutationObserver(() => {
23 // We expect the body node to be stable once parsed / created
24 if (document.body != null) {
25 if (document.readyState === 'loading') {
26 installFizzInstrObserver(document.body);
27 }
28 // $FlowFixMe[incompatible-type]
29 handleExistingNodes(document.body as HTMLElement);
30
31 // We can call disconnect without takeRecord here,
32 // since we only expect a single document.body
33 domBodyObserver.disconnect();
34 }
35 });
36 // documentElement must already exist at this point
37 domBodyObserver.observe(document.documentElement, {childList: true});
38 }
39
40 function handleExistingNodes(target: HTMLElement) {
41 const existingNodes = target.querySelectorAll('template');
42 for (let i = 0; i < existingNodes.length; i++) {
43 handleNode(existingNodes[i]);
44 }
45 }
46
47 function installFizzInstrObserver(target: Node) {
48 const handleMutations = (mutations: Array<MutationRecord>) => {
49 for (let i = 0; i < mutations.length; i++) {
50 const addedNodes = mutations[i].addedNodes;
51 for (let j = 0; j < addedNodes.length; j++) {
52 if (addedNodes[j].parentNode) {
53 handleNode(addedNodes[j]);
54 }
55 }
56 }
57 };
58
59 const fizzInstrObserver = new MutationObserver(handleMutations);
60 // We assume that instruction data nodes are eventually appended to the
61 // body, even if Fizz is streaming to a shell / subtree.
62 fizzInstrObserver.observe(target, {
63 childList: true,
64 });
65 window.addEventListener('DOMContentLoaded', () => {
66 handleMutations(fizzInstrObserver.takeRecords());
67 fizzInstrObserver.disconnect();
68 });
69 }
70
71 function handleNode(node_: Node) {
72 // $FlowFixMe[incompatible-type]
73 if (node_.nodeType !== 1 || !(node_ as HTMLElement).dataset) {
74 return;
75 }
76 // $FlowFixMe[incompatible-type]
77 const node = node_ as HTMLElement;
78 const dataset = node.dataset;
79 if (dataset['rxi'] != null) {
80 window['$RX'](
81 dataset['bid'],
82 dataset['dgst'],
83 dataset['msg'],
84 dataset['stck'],
85 dataset['cstck'],
86 );
87 node.remove();
88 } else if (dataset['rri'] != null) {
89 // Convert styles here, since its type is Array<Array<string>>
90 window['$RR'](dataset['bid'], dataset['sid'], JSON.parse(dataset['sty']));
91 node.remove();
92 } else if (dataset['rci'] != null) {
93 window['$RC'](dataset['bid'], dataset['sid']);
94 node.remove();
95 } else if (dataset['rsi'] != null) {
96 window['$RS'](dataset['sid'], dataset['pid']);
97 node.remove();
98 }
99 }