Skip to content

Commit a6bf466

Browse files
acdlitemofeiZ
andauthored
Extract Fizz instruction set to build macro (#25457)
We're adding an option to Fizz to support an alternate output format that doesn't rely on inline script tags (see #25437). The two outputs will share the same "instruction set" of functions. These functions are currently inlined into the source file; to make this a bit more maintainable, and eventually have a single source of truth, in preparation for the new option, this commit moves the instruction set to a separate files that are imported. In the future, we could improve this further by running Closure on the instruction set and generating it at build time. This isn't an urgent improvement, though, because we rarely modify the instruction set. Co-authored-by: Mofei Zhang <[email protected]> Co-authored-by: Mofei Zhang <[email protected]>
1 parent ea5bc6b commit a6bf466

File tree

5 files changed

+256
-213
lines changed

5 files changed

+256
-213
lines changed

packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js

+5-213
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ export {
7474
hoistResourcesToRoot,
7575
} from './ReactDOMFloatServer';
7676

77+
import completeSegmentFunction from './fizz-instruction-set/completeSegmentFunctionString';
78+
import completeBoundaryFunction from './fizz-instruction-set/completeBoundaryFunctionString';
79+
import styleInsertionFunction from './fizz-instruction-set/styleInsertionFunctionString';
80+
import clientRenderFunction from './fizz-instruction-set/clientRenderFunctionString';
81+
7782
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
7883
const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher;
7984

@@ -2021,219 +2026,6 @@ export function writeEndSegment(
20212026
}
20222027
}
20232028

2024-
// Instruction Set
2025-
2026-
// The following code is the source scripts that we then minify and inline below,
2027-
// with renamed function names that we hope don't collide:
2028-
2029-
// const COMMENT_NODE = 8;
2030-
// const SUSPENSE_START_DATA = '$';
2031-
// const SUSPENSE_END_DATA = '/$';
2032-
// const SUSPENSE_PENDING_START_DATA = '$?';
2033-
// const SUSPENSE_FALLBACK_START_DATA = '$!';
2034-
// const LOADED = 'l';
2035-
// const ERRORED = 'e';
2036-
2037-
// function clientRenderBoundary(suspenseBoundaryID, errorDigest, errorMsg, errorComponentStack) {
2038-
// // Find the fallback's first element.
2039-
// const suspenseIdNode = document.getElementById(suspenseBoundaryID);
2040-
// if (!suspenseIdNode) {
2041-
// // The user must have already navigated away from this tree.
2042-
// // E.g. because the parent was hydrated.
2043-
// return;
2044-
// }
2045-
// // Find the boundary around the fallback. This is always the previous node.
2046-
// const suspenseNode = suspenseIdNode.previousSibling;
2047-
// // Tag it to be client rendered.
2048-
// suspenseNode.data = SUSPENSE_FALLBACK_START_DATA;
2049-
// // assign error metadata to first sibling
2050-
// let dataset = suspenseIdNode.dataset;
2051-
// if (errorDigest) dataset.dgst = errorDigest;
2052-
// if (errorMsg) dataset.msg = errorMsg;
2053-
// if (errorComponentStack) dataset.stck = errorComponentStack;
2054-
// // Tell React to retry it if the parent already hydrated.
2055-
// if (suspenseNode._reactRetry) {
2056-
// suspenseNode._reactRetry();
2057-
// }
2058-
// }
2059-
2060-
// resourceMap = new Map();
2061-
// function completeBoundaryWithStyles(suspenseBoundaryID, contentID, styles) {
2062-
// const precedences = new Map();
2063-
// const thisDocument = document;
2064-
// let lastResource, node;
2065-
2066-
// // Seed the precedence list with existing resources
2067-
// let nodes = thisDocument.querySelectorAll('link[data-rprec]');
2068-
// for (let i = 0;node = nodes[i++];) {
2069-
// precedences.set(node.dataset.rprec, lastResource = node);
2070-
// }
2071-
2072-
// let i = 0;
2073-
// let dependencies = [];
2074-
// let style, href, precedence, attr, loadingState, resourceEl;
2075-
2076-
// function setStatus(s) {
2077-
// this.s = s;
2078-
// }
2079-
2080-
// while (style = styles[i++]) {
2081-
// let j = 0;
2082-
// href = style[j++];
2083-
// // We check if this resource is already in our resourceMap and reuse it if so.
2084-
// // If it is already loaded we don't return it as a depenendency since there is nothing
2085-
// // to wait for
2086-
// loadingState = resourceMap.get(href);
2087-
// if (loadingState) {
2088-
// if (loadingState.s !== 'l') {
2089-
// dependencies.push(loadingState);
2090-
// }
2091-
// continue;
2092-
// }
2093-
2094-
// // We construct our new resource element, looping over remaining attributes if any
2095-
// // setting them to the Element.
2096-
// resourceEl = thisDocument.createElement("link");
2097-
// resourceEl.href = href;
2098-
// resourceEl.rel = 'stylesheet';
2099-
// resourceEl.dataset.rprec = precedence = style[j++];
2100-
// while(attr = style[j++]) {
2101-
// resourceEl.setAttribute(attr, style[j++]);
2102-
// }
2103-
2104-
// // We stash a pending promise in our map by href which will resolve or reject
2105-
// // when the underlying resource loads or errors. We add it to the dependencies
2106-
// // array to be returned.
2107-
// loadingState = resourceEl._p = new Promise((re, rj) => {
2108-
// resourceEl.onload = re;
2109-
// resourceEl.onerror = rj;
2110-
// })
2111-
// loadingState.then(
2112-
// setStatus.bind(loadingState, LOADED),
2113-
// setStatus.bind(loadingState, ERRORED)
2114-
// );
2115-
// resourceMap.set(href, loadingState);
2116-
// dependencies.push(loadingState);
2117-
2118-
// // The prior style resource is the last one placed at a given
2119-
// // precedence or the last resource itself which may be null.
2120-
// // We grab this value and then update the last resource for this
2121-
// // precedence to be the inserted element, updating the lastResource
2122-
// // pointer if needed.
2123-
// let prior = precedences.get(precedence) || lastResource;
2124-
// if (prior === lastResource) {
2125-
// lastResource = resourceEl
2126-
// }
2127-
// precedences.set(precedence, resourceEl)
2128-
2129-
// // Finally, we insert the newly constructed instance at an appropriate location
2130-
// // in the Document.
2131-
// if (prior) {
2132-
// prior.parentNode.insertBefore(resourceEl, prior.nextSibling);
2133-
// } else {
2134-
// let head = thisDocument.head;
2135-
// head.insertBefore(resourceEl, head.firstChild);
2136-
// }
2137-
// }
2138-
2139-
// Promise.all(dependencies).then(
2140-
// completeBoundary.bind(null, suspenseBoundaryID, contentID, ''),
2141-
// completeBoundary.bind(null, suspenseBoundaryID, contentID, "Resource failed to load")
2142-
// );
2143-
// }
2144-
2145-
// function completeBoundary(suspenseBoundaryID, contentID, errorDigest) {
2146-
// const contentNode = document.getElementById(contentID);
2147-
// // We'll detach the content node so that regardless of what happens next we don't leave in the tree.
2148-
// // This might also help by not causing recalcing each time we move a child from here to the target.
2149-
// contentNode.parentNode.removeChild(contentNode);
2150-
2151-
// // Find the fallback's first element.
2152-
// const suspenseIdNode = document.getElementById(suspenseBoundaryID);
2153-
// if (!suspenseIdNode) {
2154-
// // The user must have already navigated away from this tree.
2155-
// // E.g. because the parent was hydrated. That's fine there's nothing to do
2156-
// // but we have to make sure that we already deleted the container node.
2157-
// return;
2158-
// }
2159-
// // Find the boundary around the fallback. This is always the previous node.
2160-
// const suspenseNode = suspenseIdNode.previousSibling;
2161-
2162-
// if (!errorDigest) {
2163-
// // Clear all the existing children. This is complicated because
2164-
// // there can be embedded Suspense boundaries in the fallback.
2165-
// // This is similar to clearSuspenseBoundary in ReactDOMHostConfig.
2166-
// // TODO: We could avoid this if we never emitted suspense boundaries in fallback trees.
2167-
// // They never hydrate anyway. However, currently we support incrementally loading the fallback.
2168-
// const parentInstance = suspenseNode.parentNode;
2169-
// let node = suspenseNode.nextSibling;
2170-
// let depth = 0;
2171-
// do {
2172-
// if (node && node.nodeType === COMMENT_NODE) {
2173-
// const data = node.data;
2174-
// if (data === SUSPENSE_END_DATA) {
2175-
// if (depth === 0) {
2176-
// break;
2177-
// } else {
2178-
// depth--;
2179-
// }
2180-
// } else if (
2181-
// data === SUSPENSE_START_DATA ||
2182-
// data === SUSPENSE_PENDING_START_DATA ||
2183-
// data === SUSPENSE_FALLBACK_START_DATA
2184-
// ) {
2185-
// depth++;
2186-
// }
2187-
// }
2188-
2189-
// const nextNode = node.nextSibling;
2190-
// parentInstance.removeChild(node);
2191-
// node = nextNode;
2192-
// } while (node);
2193-
2194-
// const endOfBoundary = node;
2195-
2196-
// // Insert all the children from the contentNode between the start and end of suspense boundary.
2197-
// while (contentNode.firstChild) {
2198-
// parentInstance.insertBefore(contentNode.firstChild, endOfBoundary);
2199-
// }
2200-
2201-
// suspenseNode.data = SUSPENSE_START_DATA;
2202-
// } else {
2203-
// suspenseNode.data = SUSPENSE_FALLBACK_START_DATA;
2204-
// suspenseIdNode.setAttribute('data-dgst', errorDigest)
2205-
// }
2206-
2207-
// if (suspenseNode._reactRetry) {
2208-
// suspenseNode._reactRetry();
2209-
// }
2210-
// }
2211-
2212-
// function completeSegment(containerID, placeholderID) {
2213-
// const segmentContainer = document.getElementById(containerID);
2214-
// const placeholderNode = document.getElementById(placeholderID);
2215-
// // We always expect both nodes to exist here because, while we might
2216-
// // have navigated away from the main tree, we still expect the detached
2217-
// // tree to exist.
2218-
// segmentContainer.parentNode.removeChild(segmentContainer);
2219-
// while (segmentContainer.firstChild) {
2220-
// placeholderNode.parentNode.insertBefore(
2221-
// segmentContainer.firstChild,
2222-
// placeholderNode,
2223-
// );
2224-
// }
2225-
// placeholderNode.parentNode.removeChild(placeholderNode);
2226-
// }
2227-
2228-
const completeSegmentFunction =
2229-
'function $RS(a,b){a=document.getElementById(a);b=document.getElementById(b);for(a.parentNode.removeChild(a);a.firstChild;)b.parentNode.insertBefore(a.firstChild,b);b.parentNode.removeChild(b)}';
2230-
const completeBoundaryFunction =
2231-
'function $RC(b,c,d){c=document.getElementById(c);c.parentNode.removeChild(c);var a=document.getElementById(b);if(a){b=a.previousSibling;if(d)b.data="$!",a.setAttribute("data-dgst",d);else{d=b.parentNode;a=b.nextSibling;var e=0;do{if(a&&a.nodeType===8){var h=a.data;if(h==="/$")if(0===e)break;else e--;else h!=="$"&&h!=="$?"&&h!=="$!"||e++}h=a.nextSibling;d.removeChild(a);a=h}while(a);for(;c.firstChild;)d.insertBefore(c.firstChild,a);b.data="$"}b._reactRetry&&b._reactRetry()}}';
2232-
const styleInsertionFunction =
2233-
'$RM=new Map;function $RR(p,q,t){function r(l){this.s=l}for(var m=new Map,n=document,g,e,f=n.querySelectorAll("link[data-rprec]"),d=0;e=f[d++];)m.set(e.dataset.rprec,g=e);e=0;f=[];for(var c,h,b,a;c=t[e++];){var k=0;h=c[k++];if(b=$RM.get(h))"l"!==b.s&&f.push(b);else{a=n.createElement("link");a.href=h;a.rel="stylesheet";for(a.dataset.rprec=d=c[k++];b=c[k++];)a.setAttribute(b,c[k++]);b=a._p=new Promise(function(l,u){a.onload=l;a.onerror=u});b.then(r.bind(b,"l"),r.bind(b,"e"));$RM.set(h,b);f.push(b);c=m.get(d)||g;c===g&&(g=a);m.set(d,a);c?c.parentNode.insertBefore(a,c.nextSibling):(d=n.head,d.insertBefore(a,d.firstChild))}}Promise.all(f).then($RC.bind(null,p,q,""),$RC.bind(null,p,q,"Resource failed to load"))}';
2234-
const clientRenderFunction =
2235-
'function $RX(b,c,d,e){var a=document.getElementById(b);a&&(b=a.previousSibling,b.data="$!",a=a.dataset,c&&(a.dgst=c),d&&(a.msg=d),e&&(a.stck=e),b._reactRetry&&b._reactRetry())}';
2236-
22372029
const completeSegmentScript1Full = stringToPrecomputedChunk(
22382030
completeSegmentFunction + ';$RS("',
22392031
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Instruction Set
2+
3+
// The following code is the source scripts that we then minify and inline below,
4+
// with renamed function names that we hope don't collide:
5+
6+
// const COMMENT_NODE = 8;
7+
// const SUSPENSE_START_DATA = '$';
8+
// const SUSPENSE_END_DATA = '/$';
9+
// const SUSPENSE_PENDING_START_DATA = '$?';
10+
// const SUSPENSE_FALLBACK_START_DATA = '$!';
11+
// const LOADED = 'l';
12+
// const ERRORED = 'e';
13+
14+
// function clientRenderBoundary(suspenseBoundaryID, errorDigest, errorMsg, errorComponentStack) {
15+
// // Find the fallback's first element.
16+
// const suspenseIdNode = document.getElementById(suspenseBoundaryID);
17+
// if (!suspenseIdNode) {
18+
// // The user must have already navigated away from this tree.
19+
// // E.g. because the parent was hydrated.
20+
// return;
21+
// }
22+
// // Find the boundary around the fallback. This is always the previous node.
23+
// const suspenseNode = suspenseIdNode.previousSibling;
24+
// // Tag it to be client rendered.
25+
// suspenseNode.data = SUSPENSE_FALLBACK_START_DATA;
26+
// // assign error metadata to first sibling
27+
// let dataset = suspenseIdNode.dataset;
28+
// if (errorDigest) dataset.dgst = errorDigest;
29+
// if (errorMsg) dataset.msg = errorMsg;
30+
// if (errorComponentStack) dataset.stck = errorComponentStack;
31+
// // Tell React to retry it if the parent already hydrated.
32+
// if (suspenseNode._reactRetry) {
33+
// suspenseNode._reactRetry();
34+
// }
35+
// }
36+
37+
// TODO: Generate this file with a build step.
38+
export default 'function $RX(b,c,d,e){var a=document.getElementById(b);a&&(b=a.previousSibling,b.data="$!",a=a.dataset,c&&(a.dgst=c),d&&(a.msg=d),e&&(a.stck=e),b._reactRetry&&b._reactRetry())}';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Instruction Set
2+
3+
// The following code is the source scripts that we then minify and inline below,
4+
// with renamed function names that we hope don't collide:
5+
6+
// const COMMENT_NODE = 8;
7+
// const SUSPENSE_START_DATA = '$';
8+
// const SUSPENSE_END_DATA = '/$';
9+
// const SUSPENSE_PENDING_START_DATA = '$?';
10+
// const SUSPENSE_FALLBACK_START_DATA = '$!';
11+
// const LOADED = 'l';
12+
// const ERRORED = 'e';
13+
14+
// function completeBoundary(suspenseBoundaryID, contentID, errorDigest) {
15+
// const contentNode = document.getElementById(contentID);
16+
// // We'll detach the content node so that regardless of what happens next we don't leave in the tree.
17+
// // This might also help by not causing recalcing each time we move a child from here to the target.
18+
// contentNode.parentNode.removeChild(contentNode);
19+
20+
// // Find the fallback's first element.
21+
// const suspenseIdNode = document.getElementById(suspenseBoundaryID);
22+
// if (!suspenseIdNode) {
23+
// // The user must have already navigated away from this tree.
24+
// // E.g. because the parent was hydrated. That's fine there's nothing to do
25+
// // but we have to make sure that we already deleted the container node.
26+
// return;
27+
// }
28+
// // Find the boundary around the fallback. This is always the previous node.
29+
// const suspenseNode = suspenseIdNode.previousSibling;
30+
31+
// if (!errorDigest) {
32+
// // Clear all the existing children. This is complicated because
33+
// // there can be embedded Suspense boundaries in the fallback.
34+
// // This is similar to clearSuspenseBoundary in ReactDOMHostConfig.
35+
// // TODO: We could avoid this if we never emitted suspense boundaries in fallback trees.
36+
// // They never hydrate anyway. However, currently we support incrementally loading the fallback.
37+
// const parentInstance = suspenseNode.parentNode;
38+
// let node = suspenseNode.nextSibling;
39+
// let depth = 0;
40+
// do {
41+
// if (node && node.nodeType === COMMENT_NODE) {
42+
// const data = node.data;
43+
// if (data === SUSPENSE_END_DATA) {
44+
// if (depth === 0) {
45+
// break;
46+
// } else {
47+
// depth--;
48+
// }
49+
// } else if (
50+
// data === SUSPENSE_START_DATA ||
51+
// data === SUSPENSE_PENDING_START_DATA ||
52+
// data === SUSPENSE_FALLBACK_START_DATA
53+
// ) {
54+
// depth++;
55+
// }
56+
// }
57+
58+
// const nextNode = node.nextSibling;
59+
// parentInstance.removeChild(node);
60+
// node = nextNode;
61+
// } while (node);
62+
63+
// const endOfBoundary = node;
64+
65+
// // Insert all the children from the contentNode between the start and end of suspense boundary.
66+
// while (contentNode.firstChild) {
67+
// parentInstance.insertBefore(contentNode.firstChild, endOfBoundary);
68+
// }
69+
70+
// suspenseNode.data = SUSPENSE_START_DATA;
71+
// } else {
72+
// suspenseNode.data = SUSPENSE_FALLBACK_START_DATA;
73+
// suspenseIdNode.setAttribute('data-dgst', errorDigest)
74+
// }
75+
76+
// if (suspenseNode._reactRetry) {
77+
// suspenseNode._reactRetry();
78+
// }
79+
// }
80+
81+
// TODO: Generate this file with a build step.
82+
export default 'function $RC(b,c,d){c=document.getElementById(c);c.parentNode.removeChild(c);var a=document.getElementById(b);if(a){b=a.previousSibling;if(d)b.data="$!",a.setAttribute("data-dgst",d);else{d=b.parentNode;a=b.nextSibling;var e=0;do{if(a&&a.nodeType===8){var h=a.data;if(h==="/$")if(0===e)break;else e--;else h!=="$"&&h!=="$?"&&h!=="$!"||e++}h=a.nextSibling;d.removeChild(a);a=h}while(a);for(;c.firstChild;)d.insertBefore(c.firstChild,a);b.data="$"}b._reactRetry&&b._reactRetry()}}';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Instruction Set
2+
3+
// The following code is the source scripts that we then minify and inline below,
4+
// with renamed function names that we hope don't collide:
5+
6+
// const COMMENT_NODE = 8;
7+
// const SUSPENSE_START_DATA = '$';
8+
// const SUSPENSE_END_DATA = '/$';
9+
// const SUSPENSE_PENDING_START_DATA = '$?';
10+
// const SUSPENSE_FALLBACK_START_DATA = '$!';
11+
// const LOADED = 'l';
12+
// const ERRORED = 'e';
13+
14+
// function completeSegment(containerID, placeholderID) {
15+
// const segmentContainer = document.getElementById(containerID);
16+
// const placeholderNode = document.getElementById(placeholderID);
17+
// // We always expect both nodes to exist here because, while we might
18+
// // have navigated away from the main tree, we still expect the detached
19+
// // tree to exist.
20+
// segmentContainer.parentNode.removeChild(segmentContainer);
21+
// while (segmentContainer.firstChild) {
22+
// placeholderNode.parentNode.insertBefore(
23+
// segmentContainer.firstChild,
24+
// placeholderNode,
25+
// );
26+
// }
27+
// placeholderNode.parentNode.removeChild(placeholderNode);
28+
// }
29+
30+
// TODO: Generate this file with a build step.
31+
export default 'function $RS(a,b){a=document.getElementById(a);b=document.getElementById(b);for(a.parentNode.removeChild(a);a.firstChild;)b.parentNode.insertBefore(a.firstChild,b);b.parentNode.removeChild(b)}';

0 commit comments

Comments
 (0)