mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
[Fizz] Share code between inline and external runtime (#33066)
Stacked on #33065. The runtime is about to be a lot more complicated so we need to start sharing some more code. The problem with sharing code is that we want the inline runtime to as much as possible be isolated in its scope using only a few global variables to refer across runtimes. A problem with Closure Compiler is that it refuses to inline functions if they have closures inside of them. Which makes sense because of how VMs work it can cause memory leaks. However, in our cases this doesn't matter and code size matters more. So we can't use many clever tricks. So this just favors writing the source in the inline form. Then we add an extra compiler pass to turn those global variables into local variables in the external runtime.
This commit is contained in:
committed by
GitHub
parent
e9db3cc2d4
commit
bb57fa7351
@@ -7,12 +7,7 @@
|
||||
|
||||
// Imports are resolved statically by the closure compiler in release bundles
|
||||
// and by rollup in jest unit tests
|
||||
import {
|
||||
clientRenderBoundary,
|
||||
completeBoundaryWithStyles,
|
||||
completeBoundary,
|
||||
completeSegment,
|
||||
} from './fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime';
|
||||
import './fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime';
|
||||
|
||||
if (document.body != null) {
|
||||
if (document.readyState === 'loading') {
|
||||
@@ -82,7 +77,7 @@ function handleNode(node_: Node) {
|
||||
const node = (node_: HTMLElement);
|
||||
const dataset = node.dataset;
|
||||
if (dataset['rxi'] != null) {
|
||||
clientRenderBoundary(
|
||||
window['$RX'](
|
||||
dataset['bid'],
|
||||
dataset['dgst'],
|
||||
dataset['msg'],
|
||||
@@ -92,17 +87,13 @@ function handleNode(node_: Node) {
|
||||
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']),
|
||||
);
|
||||
window['$RR'](dataset['bid'], dataset['sid'], JSON.parse(dataset['sty']));
|
||||
node.remove();
|
||||
} else if (dataset['rci'] != null) {
|
||||
completeBoundary(dataset['bid'], dataset['sid']);
|
||||
window['$RC'](dataset['bid'], dataset['sid']);
|
||||
node.remove();
|
||||
} else if (dataset['rsi'] != null) {
|
||||
completeSegment(dataset['sid'], dataset['pid']);
|
||||
window['$RS'](dataset['sid'], dataset['pid']);
|
||||
node.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {clientRenderBoundary} from './ReactDOMFizzInstructionSetInlineSource';
|
||||
import {clientRenderBoundary} from './ReactDOMFizzInstructionSetShared';
|
||||
|
||||
// This is a string so Closure's advanced compilation mode doesn't mangle it.
|
||||
// eslint-disable-next-line dot-notation
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {completeBoundary} from './ReactDOMFizzInstructionSetInlineSource';
|
||||
import {completeBoundary} from './ReactDOMFizzInstructionSetShared';
|
||||
|
||||
// This is a string so Closure's advanced compilation mode doesn't mangle it.
|
||||
// eslint-disable-next-line dot-notation
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {completeBoundaryWithStyles} from './ReactDOMFizzInstructionSetInlineSource';
|
||||
import {completeBoundaryWithStyles} from './ReactDOMFizzInstructionSetShared';
|
||||
|
||||
// This is a string so Closure's advanced compilation mode doesn't mangle it.
|
||||
// eslint-disable-next-line dot-notation
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {completeSegment} from './ReactDOMFizzInstructionSetInlineSource';
|
||||
import {completeSegment} from './ReactDOMFizzInstructionSetShared';
|
||||
|
||||
// This is a string so Closure's advanced compilation mode doesn't mangle it.
|
||||
// eslint-disable-next-line dot-notation
|
||||
|
||||
@@ -5,138 +5,17 @@
|
||||
import {
|
||||
clientRenderBoundary,
|
||||
completeBoundary,
|
||||
completeBoundaryWithStyles,
|
||||
completeSegment,
|
||||
listenToFormSubmissionsForReplaying,
|
||||
} 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,
|
||||
stylesheetDescriptors,
|
||||
) {
|
||||
const precedences = new Map();
|
||||
const thisDocument = document;
|
||||
let lastResource, node;
|
||||
|
||||
// Seed the precedence list with existing resources and collect hoistable style tags
|
||||
const nodes = thisDocument.querySelectorAll(
|
||||
'link[data-precedence],style[data-precedence]',
|
||||
);
|
||||
const styleTagsToHoist = [];
|
||||
for (let i = 0; (node = nodes[i++]); ) {
|
||||
if (node.getAttribute('media') === 'not all') {
|
||||
styleTagsToHoist.push(node);
|
||||
} else {
|
||||
if (node.tagName === 'LINK') {
|
||||
resourceMap.set(node.getAttribute('href'), node);
|
||||
}
|
||||
precedences.set(node.dataset['precedence'], (lastResource = node));
|
||||
}
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
const dependencies = [];
|
||||
let href, precedence, attr, loadingState, resourceEl, media;
|
||||
|
||||
function cleanupWith(cb) {
|
||||
this['_p'] = null;
|
||||
cb();
|
||||
}
|
||||
|
||||
// Sheets Mode
|
||||
let sheetMode = true;
|
||||
while (true) {
|
||||
if (sheetMode) {
|
||||
// Sheet Mode iterates over the stylesheet arguments and constructs them if new or checks them for
|
||||
// dependency if they already existed
|
||||
const stylesheetDescriptor = stylesheetDescriptors[i++];
|
||||
if (!stylesheetDescriptor) {
|
||||
// enter <style> Mode
|
||||
sheetMode = false;
|
||||
i = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
let avoidInsert = false;
|
||||
let j = 0;
|
||||
href = stylesheetDescriptor[j++];
|
||||
|
||||
if ((resourceEl = resourceMap.get(href))) {
|
||||
// We have an already inserted stylesheet.
|
||||
loadingState = resourceEl['_p'];
|
||||
avoidInsert = true;
|
||||
} else {
|
||||
// We haven't already processed this href so we need to construct a stylesheet and hoist it
|
||||
// We construct it here and attach a loadingState. We also check whether it matches
|
||||
// media before we include it in the dependency array.
|
||||
resourceEl = thisDocument.createElement('link');
|
||||
resourceEl.href = href;
|
||||
resourceEl.rel = 'stylesheet';
|
||||
resourceEl.dataset['precedence'] = precedence =
|
||||
stylesheetDescriptor[j++];
|
||||
while ((attr = stylesheetDescriptor[j++])) {
|
||||
resourceEl.setAttribute(attr, stylesheetDescriptor[j++]);
|
||||
}
|
||||
loadingState = resourceEl['_p'] = new Promise((resolve, reject) => {
|
||||
resourceEl.onload = cleanupWith.bind(resourceEl, resolve);
|
||||
resourceEl.onerror = cleanupWith.bind(resourceEl, reject);
|
||||
});
|
||||
// Save this resource element so we can bailout if it is used again
|
||||
resourceMap.set(href, resourceEl);
|
||||
}
|
||||
media = resourceEl.getAttribute('media');
|
||||
if (loadingState && (!media || window['matchMedia'](media).matches)) {
|
||||
dependencies.push(loadingState);
|
||||
}
|
||||
if (avoidInsert) {
|
||||
// We have a link that is already in the document. We don't want to fall through to the insert path
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// <style> mode iterates over not-yet-hoisted <style> tags with data-precedence and hoists them.
|
||||
resourceEl = styleTagsToHoist[i++];
|
||||
if (!resourceEl) {
|
||||
// we are done with all style tags
|
||||
break;
|
||||
}
|
||||
|
||||
precedence = resourceEl.getAttribute('data-precedence');
|
||||
resourceEl.removeAttribute('media');
|
||||
}
|
||||
|
||||
// resourceEl is either a newly constructed <link rel="stylesheet" ...> or a <style> tag requiring hoisting
|
||||
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',
|
||||
),
|
||||
);
|
||||
}
|
||||
// This is a string so Closure's advanced compilation mode doesn't mangle it.
|
||||
// These will be renamed to local references by the external-runtime-plugin.
|
||||
window['$RM'] = new Map();
|
||||
window['$RX'] = clientRenderBoundary;
|
||||
window['$RC'] = completeBoundary;
|
||||
window['$RR'] = completeBoundaryWithStyles;
|
||||
window['$RS'] = completeSegment;
|
||||
|
||||
listenToFormSubmissionsForReplaying();
|
||||
|
||||
@@ -6,7 +6,7 @@ export const clientRenderBoundary =
|
||||
export const completeBoundary =
|
||||
'$RC=function(b,d,e){if(d=document.getElementById(d)){d.parentNode.removeChild(d);var a=document.getElementById(b);if(a){b=a.previousSibling;if(e)b.data="$!",a.setAttribute("data-dgst",e);else{e=b.parentNode;a=b.nextSibling;var f=0;do{if(a&&8===a.nodeType){var c=a.data;if("/$"===c||"/&"===c)if(0===f)break;else f--;else"$"!==c&&"$?"!==c&&"$!"!==c&&"&"!==c||f++}c=a.nextSibling;e.removeChild(a);a=c}while(a);for(;d.firstChild;)e.insertBefore(d.firstChild,a);b.data="$"}b._reactRetry&&b._reactRetry()}}};';
|
||||
export const completeBoundaryWithStyles =
|
||||
'$RM=new Map;\n$RR=function(t,u,y){function v(n){this._p=null;n()}for(var w=$RC,p=$RM,q=new Map,r=document,g,b,h=r.querySelectorAll("link[data-precedence],style[data-precedence]"),x=[],k=0;b=h[k++];)"not all"===b.getAttribute("media")?x.push(b):("LINK"===b.tagName&&p.set(b.getAttribute("href"),b),q.set(b.dataset.precedence,g=b));b=0;h=[];var l,a;for(k=!0;;){if(k){var e=y[b++];if(!e){k=!1;b=0;continue}var c=!1,m=0;var d=e[m++];if(a=p.get(d)){var f=a._p;c=!0}else{a=r.createElement("link");a.href=\nd;a.rel="stylesheet";for(a.dataset.precedence=l=e[m++];f=e[m++];)a.setAttribute(f,e[m++]);f=a._p=new Promise(function(n,z){a.onload=v.bind(a,n);a.onerror=v.bind(a,z)});p.set(d,a)}d=a.getAttribute("media");!f||d&&!matchMedia(d).matches||h.push(f);if(c)continue}else{a=x[b++];if(!a)break;l=a.getAttribute("data-precedence");a.removeAttribute("media")}c=q.get(l)||g;c===g&&(g=a);q.set(l,a);c?c.parentNode.insertBefore(a,c.nextSibling):(c=r.head,c.insertBefore(a,c.firstChild))}Promise.all(h).then(w.bind(null,\nt,u,""),w.bind(null,t,u,"Resource failed to load"))};';
|
||||
'$RM=new Map;\n$RR=function(r,t,w){function u(n){this._p=null;n()}for(var p=new Map,q=document,g,b,h=q.querySelectorAll("link[data-precedence],style[data-precedence]"),v=[],k=0;b=h[k++];)"not all"===b.getAttribute("media")?v.push(b):("LINK"===b.tagName&&$RM.set(b.getAttribute("href"),b),p.set(b.dataset.precedence,g=b));b=0;h=[];var l,a;for(k=!0;;){if(k){var e=w[b++];if(!e){k=!1;b=0;continue}var c=!1,m=0;var d=e[m++];if(a=$RM.get(d)){var f=a._p;c=!0}else{a=q.createElement("link");a.href=d;a.rel=\n"stylesheet";for(a.dataset.precedence=l=e[m++];f=e[m++];)a.setAttribute(f,e[m++]);f=a._p=new Promise(function(n,x){a.onload=u.bind(a,n);a.onerror=u.bind(a,x)});$RM.set(d,a)}d=a.getAttribute("media");!f||d&&!matchMedia(d).matches||h.push(f);if(c)continue}else{a=v[b++];if(!a)break;l=a.getAttribute("data-precedence");a.removeAttribute("media")}c=p.get(l)||g;c===g&&(g=a);p.set(l,a);c?c.parentNode.insertBefore(a,c.nextSibling):(c=q.head,c.insertBefore(a,c.firstChild))}Promise.all(h).then($RC.bind(null,\nr,t,""),$RC.bind(null,r,t,"Resource failed to load"))};';
|
||||
export const completeSegment =
|
||||
'$RS=function(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)};';
|
||||
export const formReplaying =
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
/* 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,
|
||||
} 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,
|
||||
stylesheetDescriptors,
|
||||
) {
|
||||
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 and collect hoistable style tags
|
||||
const nodes = thisDocument.querySelectorAll(
|
||||
'link[data-precedence],style[data-precedence]',
|
||||
);
|
||||
const styleTagsToHoist = [];
|
||||
for (let i = 0; (node = nodes[i++]); ) {
|
||||
if (node.getAttribute('media') === 'not all') {
|
||||
styleTagsToHoist.push(node);
|
||||
} else {
|
||||
if (node.tagName === 'LINK') {
|
||||
resourceMap.set(node.getAttribute('href'), node);
|
||||
}
|
||||
precedences.set(node.dataset['precedence'], (lastResource = node));
|
||||
}
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
const dependencies = [];
|
||||
let href, precedence, attr, loadingState, resourceEl, media;
|
||||
|
||||
function cleanupWith(cb) {
|
||||
this['_p'] = null;
|
||||
cb();
|
||||
}
|
||||
|
||||
// Sheets Mode
|
||||
let sheetMode = true;
|
||||
while (true) {
|
||||
if (sheetMode) {
|
||||
// Sheet Mode iterates over the stylesheet arguments and constructs them if new or checks them for
|
||||
// dependency if they already existed
|
||||
const stylesheetDescriptor = stylesheetDescriptors[i++];
|
||||
if (!stylesheetDescriptor) {
|
||||
// enter <style> Mode
|
||||
sheetMode = false;
|
||||
i = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
let avoidInsert = false;
|
||||
let j = 0;
|
||||
href = stylesheetDescriptor[j++];
|
||||
|
||||
if ((resourceEl = resourceMap.get(href))) {
|
||||
// We have an already inserted stylesheet.
|
||||
loadingState = resourceEl['_p'];
|
||||
avoidInsert = true;
|
||||
} else {
|
||||
// We haven't already processed this href so we need to construct a stylesheet and hoist it
|
||||
// We construct it here and attach a loadingState. We also check whether it matches
|
||||
// media before we include it in the dependency array.
|
||||
resourceEl = thisDocument.createElement('link');
|
||||
resourceEl.href = href;
|
||||
resourceEl.rel = 'stylesheet';
|
||||
resourceEl.dataset['precedence'] = precedence =
|
||||
stylesheetDescriptor[j++];
|
||||
while ((attr = stylesheetDescriptor[j++])) {
|
||||
resourceEl.setAttribute(attr, stylesheetDescriptor[j++]);
|
||||
}
|
||||
loadingState = resourceEl['_p'] = new Promise((resolve, reject) => {
|
||||
resourceEl.onload = cleanupWith.bind(resourceEl, resolve);
|
||||
resourceEl.onerror = cleanupWith.bind(resourceEl, reject);
|
||||
});
|
||||
// Save this resource element so we can bailout if it is used again
|
||||
resourceMap.set(href, resourceEl);
|
||||
}
|
||||
media = resourceEl.getAttribute('media');
|
||||
if (loadingState && (!media || window['matchMedia'](media).matches)) {
|
||||
dependencies.push(loadingState);
|
||||
}
|
||||
if (avoidInsert) {
|
||||
// We have a link that is already in the document. We don't want to fall through to the insert path
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// <style> mode iterates over not-yet-hoisted <style> tags with data-precedence and hoists them.
|
||||
resourceEl = styleTagsToHoist[i++];
|
||||
if (!resourceEl) {
|
||||
// we are done with all style tags
|
||||
break;
|
||||
}
|
||||
|
||||
precedence = resourceEl.getAttribute('data-precedence');
|
||||
resourceEl.removeAttribute('media');
|
||||
}
|
||||
|
||||
// resourceEl is either a newly constructed <link rel="stylesheet" ...> or a <style> tag requiring hoisting
|
||||
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',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -121,6 +121,129 @@ export function completeBoundary(suspenseBoundaryID, contentID, errorDigest) {
|
||||
}
|
||||
}
|
||||
|
||||
export function completeBoundaryWithStyles(
|
||||
suspenseBoundaryID,
|
||||
contentID,
|
||||
stylesheetDescriptors,
|
||||
) {
|
||||
const precedences = new Map();
|
||||
const thisDocument = document;
|
||||
let lastResource, node;
|
||||
|
||||
// Seed the precedence list with existing resources and collect hoistable style tags
|
||||
const nodes = thisDocument.querySelectorAll(
|
||||
'link[data-precedence],style[data-precedence]',
|
||||
);
|
||||
const styleTagsToHoist = [];
|
||||
for (let i = 0; (node = nodes[i++]); ) {
|
||||
if (node.getAttribute('media') === 'not all') {
|
||||
styleTagsToHoist.push(node);
|
||||
} else {
|
||||
if (node.tagName === 'LINK') {
|
||||
window['$RM'].set(node.getAttribute('href'), node);
|
||||
}
|
||||
precedences.set(node.dataset['precedence'], (lastResource = node));
|
||||
}
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
const dependencies = [];
|
||||
let href, precedence, attr, loadingState, resourceEl, media;
|
||||
|
||||
function cleanupWith(cb) {
|
||||
this['_p'] = null;
|
||||
cb();
|
||||
}
|
||||
|
||||
// Sheets Mode
|
||||
let sheetMode = true;
|
||||
while (true) {
|
||||
if (sheetMode) {
|
||||
// Sheet Mode iterates over the stylesheet arguments and constructs them if new or checks them for
|
||||
// dependency if they already existed
|
||||
const stylesheetDescriptor = stylesheetDescriptors[i++];
|
||||
if (!stylesheetDescriptor) {
|
||||
// enter <style> Mode
|
||||
sheetMode = false;
|
||||
i = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
let avoidInsert = false;
|
||||
let j = 0;
|
||||
href = stylesheetDescriptor[j++];
|
||||
|
||||
if ((resourceEl = window['$RM'].get(href))) {
|
||||
// We have an already inserted stylesheet.
|
||||
loadingState = resourceEl['_p'];
|
||||
avoidInsert = true;
|
||||
} else {
|
||||
// We haven't already processed this href so we need to construct a stylesheet and hoist it
|
||||
// We construct it here and attach a loadingState. We also check whether it matches
|
||||
// media before we include it in the dependency array.
|
||||
resourceEl = thisDocument.createElement('link');
|
||||
resourceEl.href = href;
|
||||
resourceEl.rel = 'stylesheet';
|
||||
resourceEl.dataset['precedence'] = precedence =
|
||||
stylesheetDescriptor[j++];
|
||||
while ((attr = stylesheetDescriptor[j++])) {
|
||||
resourceEl.setAttribute(attr, stylesheetDescriptor[j++]);
|
||||
}
|
||||
loadingState = resourceEl['_p'] = new Promise((resolve, reject) => {
|
||||
resourceEl.onload = cleanupWith.bind(resourceEl, resolve);
|
||||
resourceEl.onerror = cleanupWith.bind(resourceEl, reject);
|
||||
});
|
||||
// Save this resource element so we can bailout if it is used again
|
||||
window['$RM'].set(href, resourceEl);
|
||||
}
|
||||
media = resourceEl.getAttribute('media');
|
||||
if (loadingState && (!media || window['matchMedia'](media).matches)) {
|
||||
dependencies.push(loadingState);
|
||||
}
|
||||
if (avoidInsert) {
|
||||
// We have a link that is already in the document. We don't want to fall through to the insert path
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// <style> mode iterates over not-yet-hoisted <style> tags with data-precedence and hoists them.
|
||||
resourceEl = styleTagsToHoist[i++];
|
||||
if (!resourceEl) {
|
||||
// we are done with all style tags
|
||||
break;
|
||||
}
|
||||
|
||||
precedence = resourceEl.getAttribute('data-precedence');
|
||||
resourceEl.removeAttribute('media');
|
||||
}
|
||||
|
||||
// resourceEl is either a newly constructed <link rel="stylesheet" ...> or a <style> tag requiring hoisting
|
||||
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(
|
||||
window['$RC'].bind(null, suspenseBoundaryID, contentID, ''),
|
||||
window['$RC'].bind(
|
||||
null,
|
||||
suspenseBoundaryID,
|
||||
contentID,
|
||||
'Resource failed to load',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function completeSegment(containerID, placeholderID) {
|
||||
const segmentContainer = document.getElementById(containerID);
|
||||
const placeholderNode = document.getElementById(placeholderID);
|
||||
|
||||
@@ -21,6 +21,7 @@ const Sync = require('./sync');
|
||||
const sizes = require('./plugins/sizes-plugin');
|
||||
const useForks = require('./plugins/use-forks-plugin');
|
||||
const dynamicImports = require('./plugins/dynamic-imports');
|
||||
const externalRuntime = require('./plugins/external-runtime-plugin');
|
||||
const Packaging = require('./packaging');
|
||||
const {asyncRimRaf} = require('./utils');
|
||||
const codeFrame = require('@babel/code-frame').default;
|
||||
@@ -441,6 +442,8 @@ function getPlugins(
|
||||
__EXPERIMENTAL__,
|
||||
},
|
||||
}),
|
||||
// For the external runtime we turn global identifiers into local.
|
||||
entry.includes('server-external-runtime') && externalRuntime(),
|
||||
{
|
||||
name: 'top-level-definitions',
|
||||
renderChunk(source) {
|
||||
|
||||
@@ -46,7 +46,6 @@ async function main() {
|
||||
js: [
|
||||
require.resolve('./externs/closure-externs.js'),
|
||||
fullEntryPath,
|
||||
instructionDir + '/ReactDOMFizzInstructionSetInlineSource.js',
|
||||
instructionDir + '/ReactDOMFizzInstructionSetShared.js',
|
||||
],
|
||||
compilation_level: 'ADVANCED',
|
||||
|
||||
46
scripts/rollup/plugins/external-runtime-plugin.js
Normal file
46
scripts/rollup/plugins/external-runtime-plugin.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
module.exports = function externalRuntime() {
|
||||
// When generating the source code for the Fizz runtime chunks we use global identifiers to refer
|
||||
// to different parts of the implementation. When generating the external runtime we need to
|
||||
// replace those with local identifiers instead.
|
||||
return {
|
||||
name: 'scripts/rollup/plugins/dynamic-imports',
|
||||
renderChunk(source) {
|
||||
// This replaces "window['$globalVar']" with "$globalVar".
|
||||
const variables = new Set();
|
||||
source = source.replace(
|
||||
/window\[['"](\$[A-z0-9_]*)['"]\]/g,
|
||||
(_, variableName) => {
|
||||
variables.add(variableName);
|
||||
return variableName;
|
||||
}
|
||||
);
|
||||
const startOfFn = 'use strict';
|
||||
let index = source.indexOf(startOfFn);
|
||||
if (index === -1) {
|
||||
return source;
|
||||
}
|
||||
index += startOfFn.length + 2;
|
||||
|
||||
// Insert the declarations in the beginning of the function closure
|
||||
// to scope them to inside the runtime.
|
||||
let declarations = 'let ';
|
||||
variables.forEach(variable => {
|
||||
if (declarations !== 'let ') {
|
||||
declarations += ', ';
|
||||
}
|
||||
declarations += variable;
|
||||
});
|
||||
declarations += ';';
|
||||
source = source.slice(0, index) + declarations + source.slice(index);
|
||||
return source;
|
||||
},
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user