-
Notifications
You must be signed in to change notification settings - Fork 47.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Fizz] Fork Fizz instruction set for inline script and external runti…
…me (#25862) ~~[Fizz] Duplicate completeBoundaryWithStyles to not reference globals~~ ## Summary Follow-up / cleanup PR to #25437 - `completeBoundaryWithStylesInlineLocals` is used by the Fizz external runtime, which bundles together all Fizz instruction functions (and is able to reference / rename `completeBoundary` and `resourceMap` as locals). - `completeBoundaryWithStylesInlineGlobals` is used by the Fizz inline script writer, which sends Fizz instruction functions on an as-needed basis. This version needs to reference `completeBoundary($RC)` and `resourceMap($RM)` as globals. Ideally, Closure would take care of inlining a shared implementation, but I couldn't figure out a zero-overhead inline due to lack of an `@inline` compiler directive. It seems that Closure thinks that a shared `completeBoundaryWithStyles` is too large and will always keep it as a separate function. I've also tried currying / writing a higher order function (`getCompleteBoundaryWithStyles`) with no luck ## How did you test this change? - generated Fizz inline instructions should be unchanged - bundle size for unstable_external_runtime should be slightly smaller (due to lack of globals) - `ReactDOMFizzServer-test.js` and `ReactDOMFloat-test.js` should be unaffected
- Loading branch information
Showing
9 changed files
with
249 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...ct-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInlineClientRenderBoundary.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
.../react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInlineCompleteBoundary.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...-bindings/src/server/fizz-instruction-set/ReactDOMFizzInlineCompleteBoundaryWithStyles.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...s/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInlineCompleteSegment.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 113 additions & 0 deletions
113
...dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
/* eslint-disable dot-notation */ | ||
|
||
// Instruction set for the Fizz external runtime | ||
|
||
import { | ||
clientRenderBoundary, | ||
completeBoundary, | ||
completeSegment, | ||
LOADED, | ||
ERRORED, | ||
} from './ReactDOMFizzInstructionSetShared'; | ||
|
||
export {clientRenderBoundary, completeBoundary, completeSegment}; | ||
|
||
const resourceMap = new Map(); | ||
|
||
// This function is almost identical to the version used by inline scripts | ||
// (ReactDOMFizzInstructionSetInlineSource), with the exception of how we read | ||
// completeBoundary and resourceMap | ||
export function completeBoundaryWithStyles( | ||
suspenseBoundaryID, | ||
contentID, | ||
styles, | ||
) { | ||
const precedences = new Map(); | ||
const thisDocument = document; | ||
let lastResource, node; | ||
|
||
// Seed the precedence list with existing resources | ||
const nodes = thisDocument.querySelectorAll( | ||
'link[data-precedence],style[data-precedence]', | ||
); | ||
for (let i = 0; (node = nodes[i++]); ) { | ||
precedences.set(node.dataset['precedence'], (lastResource = node)); | ||
} | ||
|
||
let i = 0; | ||
const 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['precedence'] = 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. | ||
const 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 { | ||
const 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', | ||
), | ||
); | ||
} |
116 changes: 116 additions & 0 deletions
116
...ct-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetInlineSource.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
/* eslint-disable dot-notation */ | ||
|
||
// Instruction set for Fizz inline scripts. | ||
// DO NOT DIRECTLY IMPORT THIS FILE. This is the source for the compiled and | ||
// minified code in ReactDOMFizzInstructionSetInlineCodeStrings. | ||
|
||
import { | ||
clientRenderBoundary, | ||
completeBoundary, | ||
completeSegment, | ||
LOADED, | ||
ERRORED, | ||
} from './ReactDOMFizzInstructionSetShared'; | ||
|
||
export {clientRenderBoundary, completeBoundary, completeSegment}; | ||
|
||
// This function is almost identical to the version used by the external | ||
// runtime (ReactDOMFizzInstructionSetExternalRuntime), with the exception of | ||
// how we read completeBoundaryImpl and resourceMap | ||
export function completeBoundaryWithStyles( | ||
suspenseBoundaryID, | ||
contentID, | ||
styles, | ||
) { | ||
const completeBoundaryImpl = window['$RC']; | ||
const resourceMap = window['$RM']; | ||
|
||
const precedences = new Map(); | ||
const thisDocument = document; | ||
let lastResource, node; | ||
|
||
// Seed the precedence list with existing resources | ||
const nodes = thisDocument.querySelectorAll( | ||
'link[data-precedence],style[data-precedence]', | ||
); | ||
for (let i = 0; (node = nodes[i++]); ) { | ||
precedences.set(node.dataset['precedence'], (lastResource = node)); | ||
} | ||
|
||
let i = 0; | ||
const 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['precedence'] = 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. | ||
const 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 { | ||
const head = thisDocument.head; | ||
head.insertBefore(resourceEl, head.firstChild); | ||
} | ||
} | ||
|
||
Promise.all(dependencies).then( | ||
completeBoundaryImpl.bind(null, suspenseBoundaryID, contentID, ''), | ||
completeBoundaryImpl.bind( | ||
null, | ||
suspenseBoundaryID, | ||
contentID, | ||
'Resource failed to load', | ||
), | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.