1
/* eslint-disable dot-notation */
2
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';
13
14
// TODO: Symbols that are referenced outside this module use dynamic accessor
15
// notation instead of dot notation to prevent Closure's advanced compilation
45
}
46
}
47
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
-
48
export function completeBoundary(suspenseBoundaryID, contentID, errorDigest) {
49
const contentNode = document.getElementById(contentID);
50
// We'll detach the content node so that regardless of what happens next we don't leave in the tree.