diff --git a/packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js b/packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js index 22429ac70d183..ae5ccf75e7109 100644 --- a/packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js +++ b/packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js @@ -74,6 +74,11 @@ export { hoistResourcesToRoot, } from './ReactDOMFloatServer'; +import completeSegmentFunction from './fizz-instruction-set/completeSegmentFunctionString'; +import completeBoundaryFunction from './fizz-instruction-set/completeBoundaryFunctionString'; +import styleInsertionFunction from './fizz-instruction-set/styleInsertionFunctionString'; +import clientRenderFunction from './fizz-instruction-set/clientRenderFunctionString'; + import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals'; const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher; @@ -2021,219 +2026,6 @@ export function writeEndSegment( } } -// Instruction Set - -// The following code is the source scripts that we then minify and inline below, -// with renamed function names that we hope don't collide: - -// const COMMENT_NODE = 8; -// const SUSPENSE_START_DATA = '$'; -// const SUSPENSE_END_DATA = '/$'; -// const SUSPENSE_PENDING_START_DATA = '$?'; -// const SUSPENSE_FALLBACK_START_DATA = '$!'; -// const LOADED = 'l'; -// const ERRORED = 'e'; - -// function clientRenderBoundary(suspenseBoundaryID, errorDigest, errorMsg, errorComponentStack) { -// // Find the fallback's first element. -// const suspenseIdNode = document.getElementById(suspenseBoundaryID); -// if (!suspenseIdNode) { -// // The user must have already navigated away from this tree. -// // E.g. because the parent was hydrated. -// return; -// } -// // Find the boundary around the fallback. This is always the previous node. -// const suspenseNode = suspenseIdNode.previousSibling; -// // Tag it to be client rendered. -// suspenseNode.data = SUSPENSE_FALLBACK_START_DATA; -// // assign error metadata to first sibling -// let dataset = suspenseIdNode.dataset; -// if (errorDigest) dataset.dgst = errorDigest; -// if (errorMsg) dataset.msg = errorMsg; -// if (errorComponentStack) dataset.stck = errorComponentStack; -// // Tell React to retry it if the parent already hydrated. -// if (suspenseNode._reactRetry) { -// suspenseNode._reactRetry(); -// } -// } - -// resourceMap = new Map(); -// function completeBoundaryWithStyles(suspenseBoundaryID, contentID, styles) { -// const precedences = new Map(); -// const thisDocument = document; -// let lastResource, node; - -// // Seed the precedence list with existing resources -// let nodes = thisDocument.querySelectorAll('link[data-rprec]'); -// for (let i = 0;node = nodes[i++];) { -// precedences.set(node.dataset.rprec, lastResource = node); -// } - -// let i = 0; -// let dependencies = []; -// let style, href, precedence, attr, loadingState, resourceEl; - -// function setStatus(s) { -// this.s = s; -// } - -// while (style = styles[i++]) { -// let j = 0; -// href = style[j++]; -// // We check if this resource is already in our resourceMap and reuse it if so. -// // If it is already loaded we don't return it as a depenendency since there is nothing -// // to wait for -// loadingState = resourceMap.get(href); -// if (loadingState) { -// if (loadingState.s !== 'l') { -// dependencies.push(loadingState); -// } -// continue; -// } - -// // We construct our new resource element, looping over remaining attributes if any -// // setting them to the Element. -// resourceEl = thisDocument.createElement("link"); -// resourceEl.href = href; -// resourceEl.rel = 'stylesheet'; -// resourceEl.dataset.rprec = precedence = style[j++]; -// while(attr = style[j++]) { -// resourceEl.setAttribute(attr, style[j++]); -// } - -// // We stash a pending promise in our map by href which will resolve or reject -// // when the underlying resource loads or errors. We add it to the dependencies -// // array to be returned. -// loadingState = resourceEl._p = new Promise((re, rj) => { -// resourceEl.onload = re; -// resourceEl.onerror = rj; -// }) -// loadingState.then( -// setStatus.bind(loadingState, LOADED), -// setStatus.bind(loadingState, ERRORED) -// ); -// resourceMap.set(href, loadingState); -// dependencies.push(loadingState); - -// // The prior style resource is the last one placed at a given -// // precedence or the last resource itself which may be null. -// // We grab this value and then update the last resource for this -// // precedence to be the inserted element, updating the lastResource -// // pointer if needed. -// let prior = precedences.get(precedence) || lastResource; -// if (prior === lastResource) { -// lastResource = resourceEl -// } -// precedences.set(precedence, resourceEl) - -// // Finally, we insert the newly constructed instance at an appropriate location -// // in the Document. -// if (prior) { -// prior.parentNode.insertBefore(resourceEl, prior.nextSibling); -// } else { -// let head = thisDocument.head; -// head.insertBefore(resourceEl, head.firstChild); -// } -// } - -// Promise.all(dependencies).then( -// completeBoundary.bind(null, suspenseBoundaryID, contentID, ''), -// completeBoundary.bind(null, suspenseBoundaryID, contentID, "Resource failed to load") -// ); -// } - -// function completeBoundary(suspenseBoundaryID, contentID, errorDigest) { -// const contentNode = document.getElementById(contentID); -// // We'll detach the content node so that regardless of what happens next we don't leave in the tree. -// // This might also help by not causing recalcing each time we move a child from here to the target. -// contentNode.parentNode.removeChild(contentNode); - -// // Find the fallback's first element. -// const suspenseIdNode = document.getElementById(suspenseBoundaryID); -// if (!suspenseIdNode) { -// // The user must have already navigated away from this tree. -// // E.g. because the parent was hydrated. That's fine there's nothing to do -// // but we have to make sure that we already deleted the container node. -// return; -// } -// // Find the boundary around the fallback. This is always the previous node. -// const suspenseNode = suspenseIdNode.previousSibling; - -// if (!errorDigest) { -// // Clear all the existing children. This is complicated because -// // there can be embedded Suspense boundaries in the fallback. -// // This is similar to clearSuspenseBoundary in ReactDOMHostConfig. -// // TODO: We could avoid this if we never emitted suspense boundaries in fallback trees. -// // They never hydrate anyway. However, currently we support incrementally loading the fallback. -// const parentInstance = suspenseNode.parentNode; -// let node = suspenseNode.nextSibling; -// let depth = 0; -// do { -// if (node && node.nodeType === COMMENT_NODE) { -// const data = node.data; -// if (data === SUSPENSE_END_DATA) { -// if (depth === 0) { -// break; -// } else { -// depth--; -// } -// } else if ( -// data === SUSPENSE_START_DATA || -// data === SUSPENSE_PENDING_START_DATA || -// data === SUSPENSE_FALLBACK_START_DATA -// ) { -// depth++; -// } -// } - -// const nextNode = node.nextSibling; -// parentInstance.removeChild(node); -// node = nextNode; -// } while (node); - -// const endOfBoundary = node; - -// // Insert all the children from the contentNode between the start and end of suspense boundary. -// while (contentNode.firstChild) { -// parentInstance.insertBefore(contentNode.firstChild, endOfBoundary); -// } - -// suspenseNode.data = SUSPENSE_START_DATA; -// } else { -// suspenseNode.data = SUSPENSE_FALLBACK_START_DATA; -// suspenseIdNode.setAttribute('data-dgst', errorDigest) -// } - -// if (suspenseNode._reactRetry) { -// suspenseNode._reactRetry(); -// } -// } - -// function completeSegment(containerID, placeholderID) { -// const segmentContainer = document.getElementById(containerID); -// const placeholderNode = document.getElementById(placeholderID); -// // We always expect both nodes to exist here because, while we might -// // have navigated away from the main tree, we still expect the detached -// // tree to exist. -// segmentContainer.parentNode.removeChild(segmentContainer); -// while (segmentContainer.firstChild) { -// placeholderNode.parentNode.insertBefore( -// segmentContainer.firstChild, -// placeholderNode, -// ); -// } -// placeholderNode.parentNode.removeChild(placeholderNode); -// } - -const completeSegmentFunction = - '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)}'; -const completeBoundaryFunction = - '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()}}'; -const styleInsertionFunction = - '$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"))}'; -const clientRenderFunction = - '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())}'; - const completeSegmentScript1Full = stringToPrecomputedChunk( completeSegmentFunction + ';$RS("', ); diff --git a/packages/react-dom-bindings/src/server/fizz-instruction-set/clientRenderFunctionString.js b/packages/react-dom-bindings/src/server/fizz-instruction-set/clientRenderFunctionString.js new file mode 100644 index 0000000000000..d2c2a4b8efbd4 --- /dev/null +++ b/packages/react-dom-bindings/src/server/fizz-instruction-set/clientRenderFunctionString.js @@ -0,0 +1,38 @@ +// Instruction Set + +// The following code is the source scripts that we then minify and inline below, +// with renamed function names that we hope don't collide: + +// const COMMENT_NODE = 8; +// const SUSPENSE_START_DATA = '$'; +// const SUSPENSE_END_DATA = '/$'; +// const SUSPENSE_PENDING_START_DATA = '$?'; +// const SUSPENSE_FALLBACK_START_DATA = '$!'; +// const LOADED = 'l'; +// const ERRORED = 'e'; + +// function clientRenderBoundary(suspenseBoundaryID, errorDigest, errorMsg, errorComponentStack) { +// // Find the fallback's first element. +// const suspenseIdNode = document.getElementById(suspenseBoundaryID); +// if (!suspenseIdNode) { +// // The user must have already navigated away from this tree. +// // E.g. because the parent was hydrated. +// return; +// } +// // Find the boundary around the fallback. This is always the previous node. +// const suspenseNode = suspenseIdNode.previousSibling; +// // Tag it to be client rendered. +// suspenseNode.data = SUSPENSE_FALLBACK_START_DATA; +// // assign error metadata to first sibling +// let dataset = suspenseIdNode.dataset; +// if (errorDigest) dataset.dgst = errorDigest; +// if (errorMsg) dataset.msg = errorMsg; +// if (errorComponentStack) dataset.stck = errorComponentStack; +// // Tell React to retry it if the parent already hydrated. +// if (suspenseNode._reactRetry) { +// suspenseNode._reactRetry(); +// } +// } + +// TODO: Generate this file with a build step. +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())}'; diff --git a/packages/react-dom-bindings/src/server/fizz-instruction-set/completeBoundaryFunctionString.js b/packages/react-dom-bindings/src/server/fizz-instruction-set/completeBoundaryFunctionString.js new file mode 100644 index 0000000000000..cd1def26fcecb --- /dev/null +++ b/packages/react-dom-bindings/src/server/fizz-instruction-set/completeBoundaryFunctionString.js @@ -0,0 +1,82 @@ +// Instruction Set + +// The following code is the source scripts that we then minify and inline below, +// with renamed function names that we hope don't collide: + +// const COMMENT_NODE = 8; +// const SUSPENSE_START_DATA = '$'; +// const SUSPENSE_END_DATA = '/$'; +// const SUSPENSE_PENDING_START_DATA = '$?'; +// const SUSPENSE_FALLBACK_START_DATA = '$!'; +// const LOADED = 'l'; +// const ERRORED = 'e'; + +// function completeBoundary(suspenseBoundaryID, contentID, errorDigest) { +// const contentNode = document.getElementById(contentID); +// // We'll detach the content node so that regardless of what happens next we don't leave in the tree. +// // This might also help by not causing recalcing each time we move a child from here to the target. +// contentNode.parentNode.removeChild(contentNode); + +// // Find the fallback's first element. +// const suspenseIdNode = document.getElementById(suspenseBoundaryID); +// if (!suspenseIdNode) { +// // The user must have already navigated away from this tree. +// // E.g. because the parent was hydrated. That's fine there's nothing to do +// // but we have to make sure that we already deleted the container node. +// return; +// } +// // Find the boundary around the fallback. This is always the previous node. +// const suspenseNode = suspenseIdNode.previousSibling; + +// if (!errorDigest) { +// // Clear all the existing children. This is complicated because +// // there can be embedded Suspense boundaries in the fallback. +// // This is similar to clearSuspenseBoundary in ReactDOMHostConfig. +// // TODO: We could avoid this if we never emitted suspense boundaries in fallback trees. +// // They never hydrate anyway. However, currently we support incrementally loading the fallback. +// const parentInstance = suspenseNode.parentNode; +// let node = suspenseNode.nextSibling; +// let depth = 0; +// do { +// if (node && node.nodeType === COMMENT_NODE) { +// const data = node.data; +// if (data === SUSPENSE_END_DATA) { +// if (depth === 0) { +// break; +// } else { +// depth--; +// } +// } else if ( +// data === SUSPENSE_START_DATA || +// data === SUSPENSE_PENDING_START_DATA || +// data === SUSPENSE_FALLBACK_START_DATA +// ) { +// depth++; +// } +// } + +// const nextNode = node.nextSibling; +// parentInstance.removeChild(node); +// node = nextNode; +// } while (node); + +// const endOfBoundary = node; + +// // Insert all the children from the contentNode between the start and end of suspense boundary. +// while (contentNode.firstChild) { +// parentInstance.insertBefore(contentNode.firstChild, endOfBoundary); +// } + +// suspenseNode.data = SUSPENSE_START_DATA; +// } else { +// suspenseNode.data = SUSPENSE_FALLBACK_START_DATA; +// suspenseIdNode.setAttribute('data-dgst', errorDigest) +// } + +// if (suspenseNode._reactRetry) { +// suspenseNode._reactRetry(); +// } +// } + +// TODO: Generate this file with a build step. +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()}}'; diff --git a/packages/react-dom-bindings/src/server/fizz-instruction-set/completeSegmentFunctionString.js b/packages/react-dom-bindings/src/server/fizz-instruction-set/completeSegmentFunctionString.js new file mode 100644 index 0000000000000..d88785341b739 --- /dev/null +++ b/packages/react-dom-bindings/src/server/fizz-instruction-set/completeSegmentFunctionString.js @@ -0,0 +1,31 @@ +// Instruction Set + +// The following code is the source scripts that we then minify and inline below, +// with renamed function names that we hope don't collide: + +// const COMMENT_NODE = 8; +// const SUSPENSE_START_DATA = '$'; +// const SUSPENSE_END_DATA = '/$'; +// const SUSPENSE_PENDING_START_DATA = '$?'; +// const SUSPENSE_FALLBACK_START_DATA = '$!'; +// const LOADED = 'l'; +// const ERRORED = 'e'; + +// function completeSegment(containerID, placeholderID) { +// const segmentContainer = document.getElementById(containerID); +// const placeholderNode = document.getElementById(placeholderID); +// // We always expect both nodes to exist here because, while we might +// // have navigated away from the main tree, we still expect the detached +// // tree to exist. +// segmentContainer.parentNode.removeChild(segmentContainer); +// while (segmentContainer.firstChild) { +// placeholderNode.parentNode.insertBefore( +// segmentContainer.firstChild, +// placeholderNode, +// ); +// } +// placeholderNode.parentNode.removeChild(placeholderNode); +// } + +// TODO: Generate this file with a build step. +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)}'; diff --git a/packages/react-dom-bindings/src/server/fizz-instruction-set/styleInsertionFunctionString.js b/packages/react-dom-bindings/src/server/fizz-instruction-set/styleInsertionFunctionString.js new file mode 100644 index 0000000000000..d143400aa0ebe --- /dev/null +++ b/packages/react-dom-bindings/src/server/fizz-instruction-set/styleInsertionFunctionString.js @@ -0,0 +1,100 @@ +// Instruction Set + +// The following code is the source scripts that we then minify and inline below, +// with renamed function names that we hope don't collide: + +// const COMMENT_NODE = 8; +// const SUSPENSE_START_DATA = '$'; +// const SUSPENSE_END_DATA = '/$'; +// const SUSPENSE_PENDING_START_DATA = '$?'; +// const SUSPENSE_FALLBACK_START_DATA = '$!'; +// const LOADED = 'l'; +// const ERRORED = 'e'; + +// resourceMap = new Map(); +// function completeBoundaryWithStyles(suspenseBoundaryID, contentID, styles) { +// const precedences = new Map(); +// const thisDocument = document; +// let lastResource, node; + +// // Seed the precedence list with existing resources +// let nodes = thisDocument.querySelectorAll('link[data-rprec]'); +// for (let i = 0;node = nodes[i++];) { +// precedences.set(node.dataset.rprec, lastResource = node); +// } + +// let i = 0; +// let dependencies = []; +// let style, href, precedence, attr, loadingState, resourceEl; + +// function setStatus(s) { +// this.s = s; +// } + +// while (style = styles[i++]) { +// let j = 0; +// href = style[j++]; +// // We check if this resource is already in our resourceMap and reuse it if so. +// // If it is already loaded we don't return it as a depenendency since there is nothing +// // to wait for +// loadingState = resourceMap.get(href); +// if (loadingState) { +// if (loadingState.s !== 'l') { +// dependencies.push(loadingState); +// } +// continue; +// } + +// // We construct our new resource element, looping over remaining attributes if any +// // setting them to the Element. +// resourceEl = thisDocument.createElement("link"); +// resourceEl.href = href; +// resourceEl.rel = 'stylesheet'; +// resourceEl.dataset.rprec = precedence = style[j++]; +// while(attr = style[j++]) { +// resourceEl.setAttribute(attr, style[j++]); +// } + +// // We stash a pending promise in our map by href which will resolve or reject +// // when the underlying resource loads or errors. We add it to the dependencies +// // array to be returned. +// loadingState = resourceEl._p = new Promise((re, rj) => { +// resourceEl.onload = re; +// resourceEl.onerror = rj; +// }) +// loadingState.then( +// setStatus.bind(loadingState, LOADED), +// setStatus.bind(loadingState, ERRORED) +// ); +// resourceMap.set(href, loadingState); +// dependencies.push(loadingState); + +// // The prior style resource is the last one placed at a given +// // precedence or the last resource itself which may be null. +// // We grab this value and then update the last resource for this +// // precedence to be the inserted element, updating the lastResource +// // pointer if needed. +// let prior = precedences.get(precedence) || lastResource; +// if (prior === lastResource) { +// lastResource = resourceEl +// } +// precedences.set(precedence, resourceEl) + +// // Finally, we insert the newly constructed instance at an appropriate location +// // in the Document. +// if (prior) { +// prior.parentNode.insertBefore(resourceEl, prior.nextSibling); +// } else { +// let head = thisDocument.head; +// head.insertBefore(resourceEl, head.firstChild); +// } +// } + +// Promise.all(dependencies).then( +// completeBoundary.bind(null, suspenseBoundaryID, contentID, ''), +// completeBoundary.bind(null, suspenseBoundaryID, contentID, "Resource failed to load") +// ); +// } + +// TODO: Generate this file with a build step. +export default '$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"))}';