Skip to content

Commit 3feb3cd

Browse files
committed
Fork to two instruction sets
Inline scripts and external runtime may require divergent implementations (or hand optimizations for code size). Since we do not need a bundle time fork, I split this to two different modules.
1 parent 5379b61 commit 3feb3cd

9 files changed

Lines changed: 249 additions & 113 deletions

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
completeBoundaryWithStyles,
1313
completeBoundary,
1414
completeSegment,
15-
} from './fizz-instruction-set/ReactDOMFizzInstructionSet';
15+
} from './fizz-instruction-set/ReactDOMFizzInstructionSetExternalRuntime';
1616

1717
if (!window.$RC) {
1818
// TODO: Eventually remove, we currently need to set these globals for

packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInlineClientRenderBoundary.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {clientRenderBoundary} from './ReactDOMFizzInstructionSet';
1+
import {clientRenderBoundary} from './ReactDOMFizzInstructionSetInlineSource';
22

33
// This is a string so Closure's advanced compilation mode doesn't mangle it.
44
// eslint-disable-next-line dot-notation

packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInlineCompleteBoundary.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {completeBoundary} from './ReactDOMFizzInstructionSet';
1+
import {completeBoundary} from './ReactDOMFizzInstructionSetInlineSource';
22

33
// This is a string so Closure's advanced compilation mode doesn't mangle it.
44
// eslint-disable-next-line dot-notation

packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInlineCompleteBoundaryWithStyles.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {completeBoundaryWithStyles} from './ReactDOMFizzInstructionSet';
1+
import {completeBoundaryWithStyles} from './ReactDOMFizzInstructionSetInlineSource';
22

33
// This is a string so Closure's advanced compilation mode doesn't mangle it.
44
// eslint-disable-next-line dot-notation

packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInlineCompleteSegment.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {completeSegment} from './ReactDOMFizzInstructionSet';
1+
import {completeSegment} from './ReactDOMFizzInstructionSetInlineSource';
22

33
// This is a string so Closure's advanced compilation mode doesn't mangle it.
44
// eslint-disable-next-line dot-notation
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/* eslint-disable dot-notation */
2+
3+
// Instruction set for the Fizz external runtime
4+
5+
import {
6+
clientRenderBoundary,
7+
completeBoundary,
8+
completeSegment,
9+
LOADED,
10+
ERRORED,
11+
} from './ReactDOMFizzInstructionSetShared';
12+
13+
export {clientRenderBoundary, completeBoundary, completeSegment};
14+
15+
const resourceMap = new Map();
16+
17+
// This function is almost identical to the version used by inline scripts
18+
// (ReactDOMFizzInstructionSetInlineSource), with the exception of how we read
19+
// completeBoundary and resourceMap
20+
export function completeBoundaryWithStyles(
21+
suspenseBoundaryID,
22+
contentID,
23+
styles,
24+
) {
25+
const precedences = new Map();
26+
const thisDocument = document;
27+
let lastResource, node;
28+
29+
// Seed the precedence list with existing resources
30+
const nodes = thisDocument.querySelectorAll(
31+
'link[data-precedence],style[data-precedence]',
32+
);
33+
for (let i = 0; (node = nodes[i++]); ) {
34+
precedences.set(node.dataset['precedence'], (lastResource = node));
35+
}
36+
37+
let i = 0;
38+
const dependencies = [];
39+
let style, href, precedence, attr, loadingState, resourceEl;
40+
41+
function setStatus(s) {
42+
this['s'] = s;
43+
}
44+
45+
while ((style = styles[i++])) {
46+
let j = 0;
47+
href = style[j++];
48+
// We check if this resource is already in our resourceMap and reuse it if so.
49+
// If it is already loaded we don't return it as a depenendency since there is nothing
50+
// to wait for
51+
loadingState = resourceMap.get(href);
52+
if (loadingState) {
53+
if (loadingState['s'] !== 'l') {
54+
dependencies.push(loadingState);
55+
}
56+
continue;
57+
}
58+
59+
// We construct our new resource element, looping over remaining attributes if any
60+
// setting them to the Element.
61+
resourceEl = thisDocument.createElement('link');
62+
resourceEl.href = href;
63+
resourceEl.rel = 'stylesheet';
64+
resourceEl.dataset['precedence'] = precedence = style[j++];
65+
while ((attr = style[j++])) {
66+
resourceEl.setAttribute(attr, style[j++]);
67+
}
68+
69+
// We stash a pending promise in our map by href which will resolve or reject
70+
// when the underlying resource loads or errors. We add it to the dependencies
71+
// array to be returned.
72+
loadingState = resourceEl['_p'] = new Promise((re, rj) => {
73+
resourceEl.onload = re;
74+
resourceEl.onerror = rj;
75+
});
76+
loadingState.then(
77+
setStatus.bind(loadingState, LOADED),
78+
setStatus.bind(loadingState, ERRORED),
79+
);
80+
resourceMap.set(href, loadingState);
81+
dependencies.push(loadingState);
82+
83+
// The prior style resource is the last one placed at a given
84+
// precedence or the last resource itself which may be null.
85+
// We grab this value and then update the last resource for this
86+
// precedence to be the inserted element, updating the lastResource
87+
// pointer if needed.
88+
const prior = precedences.get(precedence) || lastResource;
89+
if (prior === lastResource) {
90+
lastResource = resourceEl;
91+
}
92+
precedences.set(precedence, resourceEl);
93+
94+
// Finally, we insert the newly constructed instance at an appropriate location
95+
// in the Document.
96+
if (prior) {
97+
prior.parentNode.insertBefore(resourceEl, prior.nextSibling);
98+
} else {
99+
const head = thisDocument.head;
100+
head.insertBefore(resourceEl, head.firstChild);
101+
}
102+
}
103+
104+
Promise.all(dependencies).then(
105+
completeBoundary.bind(null, suspenseBoundaryID, contentID, ''),
106+
completeBoundary.bind(
107+
null,
108+
suspenseBoundaryID,
109+
contentID,
110+
'Resource failed to load',
111+
),
112+
);
113+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/* eslint-disable dot-notation */
2+
3+
// Instruction set for Fizz inline scripts.
4+
// DO NOT DIRECTLY IMPORT THIS FILE. This is the source for the compiled and
5+
// minified code in ReactDOMFizzInstructionSetInlineCodeStrings.
6+
7+
import {
8+
clientRenderBoundary,
9+
completeBoundary,
10+
completeSegment,
11+
LOADED,
12+
ERRORED,
13+
} from './ReactDOMFizzInstructionSetShared';
14+
15+
export {clientRenderBoundary, completeBoundary, completeSegment};
16+
17+
// This function is almost identical to the version used by the external
18+
// runtime (ReactDOMFizzInstructionSetExternalRuntime), with the exception of
19+
// how we read completeBoundaryImpl and resourceMap
20+
export function completeBoundaryWithStyles(
21+
suspenseBoundaryID,
22+
contentID,
23+
styles,
24+
) {
25+
const completeBoundaryImpl = window['$RC'];
26+
const resourceMap = window['$RM'];
27+
28+
const precedences = new Map();
29+
const thisDocument = document;
30+
let lastResource, node;
31+
32+
// Seed the precedence list with existing resources
33+
const nodes = thisDocument.querySelectorAll(
34+
'link[data-precedence],style[data-precedence]',
35+
);
36+
for (let i = 0; (node = nodes[i++]); ) {
37+
precedences.set(node.dataset['precedence'], (lastResource = node));
38+
}
39+
40+
let i = 0;
41+
const dependencies = [];
42+
let style, href, precedence, attr, loadingState, resourceEl;
43+
44+
function setStatus(s) {
45+
this['s'] = s;
46+
}
47+
48+
while ((style = styles[i++])) {
49+
let j = 0;
50+
href = style[j++];
51+
// We check if this resource is already in our resourceMap and reuse it if so.
52+
// If it is already loaded we don't return it as a depenendency since there is nothing
53+
// to wait for
54+
loadingState = resourceMap.get(href);
55+
if (loadingState) {
56+
if (loadingState['s'] !== 'l') {
57+
dependencies.push(loadingState);
58+
}
59+
continue;
60+
}
61+
62+
// We construct our new resource element, looping over remaining attributes if any
63+
// setting them to the Element.
64+
resourceEl = thisDocument.createElement('link');
65+
resourceEl.href = href;
66+
resourceEl.rel = 'stylesheet';
67+
resourceEl.dataset['precedence'] = precedence = style[j++];
68+
while ((attr = style[j++])) {
69+
resourceEl.setAttribute(attr, style[j++]);
70+
}
71+
72+
// We stash a pending promise in our map by href which will resolve or reject
73+
// when the underlying resource loads or errors. We add it to the dependencies
74+
// array to be returned.
75+
loadingState = resourceEl['_p'] = new Promise((re, rj) => {
76+
resourceEl.onload = re;
77+
resourceEl.onerror = rj;
78+
});
79+
loadingState.then(
80+
setStatus.bind(loadingState, LOADED),
81+
setStatus.bind(loadingState, ERRORED),
82+
);
83+
resourceMap.set(href, loadingState);
84+
dependencies.push(loadingState);
85+
86+
// The prior style resource is the last one placed at a given
87+
// precedence or the last resource itself which may be null.
88+
// We grab this value and then update the last resource for this
89+
// precedence to be the inserted element, updating the lastResource
90+
// pointer if needed.
91+
const prior = precedences.get(precedence) || lastResource;
92+
if (prior === lastResource) {
93+
lastResource = resourceEl;
94+
}
95+
precedences.set(precedence, resourceEl);
96+
97+
// Finally, we insert the newly constructed instance at an appropriate location
98+
// in the Document.
99+
if (prior) {
100+
prior.parentNode.insertBefore(resourceEl, prior.nextSibling);
101+
} else {
102+
const head = thisDocument.head;
103+
head.insertBefore(resourceEl, head.firstChild);
104+
}
105+
}
106+
107+
Promise.all(dependencies).then(
108+
completeBoundaryImpl.bind(null, suspenseBoundaryID, contentID, ''),
109+
completeBoundaryImpl.bind(
110+
null,
111+
suspenseBoundaryID,
112+
contentID,
113+
'Resource failed to load',
114+
),
115+
);
116+
}

packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSet.js renamed to packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetShared.js

Lines changed: 10 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
/* eslint-disable dot-notation */
22

3-
const COMMENT_NODE = 8;
4-
const SUSPENSE_START_DATA = '$';
5-
const SUSPENSE_END_DATA = '/$';
6-
const SUSPENSE_PENDING_START_DATA = '$?';
7-
const SUSPENSE_FALLBACK_START_DATA = '$!';
8-
const LOADED = 'l';
9-
const ERRORED = 'e';
3+
// Shared implementation and constants between the inline script and external
4+
// runtime instruction sets.
5+
6+
export const COMMENT_NODE = 8;
7+
export const SUSPENSE_START_DATA = '$';
8+
export const SUSPENSE_END_DATA = '/$';
9+
export const SUSPENSE_PENDING_START_DATA = '$?';
10+
export const SUSPENSE_FALLBACK_START_DATA = '$!';
11+
export const LOADED = 'l';
12+
export const ERRORED = 'e';
1013

1114
// TODO: Symbols that are referenced outside this module use dynamic accessor
1215
// notation instead of dot notation to prevent Closure's advanced compilation
@@ -42,106 +45,6 @@ export function clientRenderBoundary(
4245
}
4346
}
4447

45-
export function completeBoundaryWithStyles(
46-
suspenseBoundaryID,
47-
contentID,
48-
styles,
49-
) {
50-
// TODO: In the non-inline version of the runtime, these don't need to be read
51-
// from the global scope.
52-
const completeBoundaryImpl = window['$RC'];
53-
const resourceMap = window['$RM'];
54-
55-
const precedences = new Map();
56-
const thisDocument = document;
57-
let lastResource, node;
58-
59-
// Seed the precedence list with existing resources
60-
const nodes = thisDocument.querySelectorAll(
61-
'link[data-precedence],style[data-precedence]',
62-
);
63-
for (let i = 0; (node = nodes[i++]); ) {
64-
precedences.set(node.dataset['precedence'], (lastResource = node));
65-
}
66-
67-
let i = 0;
68-
const dependencies = [];
69-
let style, href, precedence, attr, loadingState, resourceEl;
70-
71-
function setStatus(s) {
72-
this['s'] = s;
73-
}
74-
75-
while ((style = styles[i++])) {
76-
let j = 0;
77-
href = style[j++];
78-
// We check if this resource is already in our resourceMap and reuse it if so.
79-
// If it is already loaded we don't return it as a depenendency since there is nothing
80-
// to wait for
81-
loadingState = resourceMap.get(href);
82-
if (loadingState) {
83-
if (loadingState['s'] !== 'l') {
84-
dependencies.push(loadingState);
85-
}
86-
continue;
87-
}
88-
89-
// We construct our new resource element, looping over remaining attributes if any
90-
// setting them to the Element.
91-
resourceEl = thisDocument.createElement('link');
92-
resourceEl.href = href;
93-
resourceEl.rel = 'stylesheet';
94-
resourceEl.dataset['precedence'] = precedence = style[j++];
95-
while ((attr = style[j++])) {
96-
resourceEl.setAttribute(attr, style[j++]);
97-
}
98-
99-
// We stash a pending promise in our map by href which will resolve or reject
100-
// when the underlying resource loads or errors. We add it to the dependencies
101-
// array to be returned.
102-
loadingState = resourceEl['_p'] = new Promise((re, rj) => {
103-
resourceEl.onload = re;
104-
resourceEl.onerror = rj;
105-
});
106-
loadingState.then(
107-
setStatus.bind(loadingState, LOADED),
108-
setStatus.bind(loadingState, ERRORED),
109-
);
110-
resourceMap.set(href, loadingState);
111-
dependencies.push(loadingState);
112-
113-
// The prior style resource is the last one placed at a given
114-
// precedence or the last resource itself which may be null.
115-
// We grab this value and then update the last resource for this
116-
// precedence to be the inserted element, updating the lastResource
117-
// pointer if needed.
118-
const prior = precedences.get(precedence) || lastResource;
119-
if (prior === lastResource) {
120-
lastResource = resourceEl;
121-
}
122-
precedences.set(precedence, resourceEl);
123-
124-
// Finally, we insert the newly constructed instance at an appropriate location
125-
// in the Document.
126-
if (prior) {
127-
prior.parentNode.insertBefore(resourceEl, prior.nextSibling);
128-
} else {
129-
const head = thisDocument.head;
130-
head.insertBefore(resourceEl, head.firstChild);
131-
}
132-
}
133-
134-
Promise.all(dependencies).then(
135-
completeBoundaryImpl.bind(null, suspenseBoundaryID, contentID, ''),
136-
completeBoundaryImpl.bind(
137-
null,
138-
suspenseBoundaryID,
139-
contentID,
140-
'Resource failed to load',
141-
),
142-
);
143-
}
144-
14548
export function completeBoundary(suspenseBoundaryID, contentID, errorDigest) {
14649
const contentNode = document.getElementById(contentID);
14750
// We'll detach the content node so that regardless of what happens next we don't leave in the tree.

0 commit comments

Comments
 (0)