|
5 | 5 | import { |
6 | 6 | clientRenderBoundary, |
7 | 7 | completeBoundary, |
| 8 | + completeBoundaryWithStyles, |
8 | 9 | completeSegment, |
9 | 10 | listenToFormSubmissionsForReplaying, |
10 | 11 | } from './ReactDOMFizzInstructionSetShared'; |
11 | 12 |
|
12 | | -export {clientRenderBoundary, completeBoundary, completeSegment}; |
13 | | - |
14 | | -const resourceMap = new Map(); |
15 | | - |
16 | | -// This function is almost identical to the version used by inline scripts |
17 | | -// (ReactDOMFizzInstructionSetInlineSource), with the exception of how we read |
18 | | -// completeBoundary and resourceMap |
19 | | -export function completeBoundaryWithStyles( |
20 | | - suspenseBoundaryID, |
21 | | - contentID, |
22 | | - stylesheetDescriptors, |
23 | | -) { |
24 | | - const precedences = new Map(); |
25 | | - const thisDocument = document; |
26 | | - let lastResource, node; |
27 | | - |
28 | | - // Seed the precedence list with existing resources and collect hoistable style tags |
29 | | - const nodes = thisDocument.querySelectorAll( |
30 | | - 'link[data-precedence],style[data-precedence]', |
31 | | - ); |
32 | | - const styleTagsToHoist = []; |
33 | | - for (let i = 0; (node = nodes[i++]); ) { |
34 | | - if (node.getAttribute('media') === 'not all') { |
35 | | - styleTagsToHoist.push(node); |
36 | | - } else { |
37 | | - if (node.tagName === 'LINK') { |
38 | | - resourceMap.set(node.getAttribute('href'), node); |
39 | | - } |
40 | | - precedences.set(node.dataset['precedence'], (lastResource = node)); |
41 | | - } |
42 | | - } |
43 | | - |
44 | | - let i = 0; |
45 | | - const dependencies = []; |
46 | | - let href, precedence, attr, loadingState, resourceEl, media; |
47 | | - |
48 | | - function cleanupWith(cb) { |
49 | | - this['_p'] = null; |
50 | | - cb(); |
51 | | - } |
52 | | - |
53 | | - // Sheets Mode |
54 | | - let sheetMode = true; |
55 | | - while (true) { |
56 | | - if (sheetMode) { |
57 | | - // Sheet Mode iterates over the stylesheet arguments and constructs them if new or checks them for |
58 | | - // dependency if they already existed |
59 | | - const stylesheetDescriptor = stylesheetDescriptors[i++]; |
60 | | - if (!stylesheetDescriptor) { |
61 | | - // enter <style> Mode |
62 | | - sheetMode = false; |
63 | | - i = 0; |
64 | | - continue; |
65 | | - } |
66 | | - |
67 | | - let avoidInsert = false; |
68 | | - let j = 0; |
69 | | - href = stylesheetDescriptor[j++]; |
70 | | - |
71 | | - if ((resourceEl = resourceMap.get(href))) { |
72 | | - // We have an already inserted stylesheet. |
73 | | - loadingState = resourceEl['_p']; |
74 | | - avoidInsert = true; |
75 | | - } else { |
76 | | - // We haven't already processed this href so we need to construct a stylesheet and hoist it |
77 | | - // We construct it here and attach a loadingState. We also check whether it matches |
78 | | - // media before we include it in the dependency array. |
79 | | - resourceEl = thisDocument.createElement('link'); |
80 | | - resourceEl.href = href; |
81 | | - resourceEl.rel = 'stylesheet'; |
82 | | - resourceEl.dataset['precedence'] = precedence = |
83 | | - stylesheetDescriptor[j++]; |
84 | | - while ((attr = stylesheetDescriptor[j++])) { |
85 | | - resourceEl.setAttribute(attr, stylesheetDescriptor[j++]); |
86 | | - } |
87 | | - loadingState = resourceEl['_p'] = new Promise((resolve, reject) => { |
88 | | - resourceEl.onload = cleanupWith.bind(resourceEl, resolve); |
89 | | - resourceEl.onerror = cleanupWith.bind(resourceEl, reject); |
90 | | - }); |
91 | | - // Save this resource element so we can bailout if it is used again |
92 | | - resourceMap.set(href, resourceEl); |
93 | | - } |
94 | | - media = resourceEl.getAttribute('media'); |
95 | | - if (loadingState && (!media || window['matchMedia'](media).matches)) { |
96 | | - dependencies.push(loadingState); |
97 | | - } |
98 | | - if (avoidInsert) { |
99 | | - // We have a link that is already in the document. We don't want to fall through to the insert path |
100 | | - continue; |
101 | | - } |
102 | | - } else { |
103 | | - // <style> mode iterates over not-yet-hoisted <style> tags with data-precedence and hoists them. |
104 | | - resourceEl = styleTagsToHoist[i++]; |
105 | | - if (!resourceEl) { |
106 | | - // we are done with all style tags |
107 | | - break; |
108 | | - } |
109 | | - |
110 | | - precedence = resourceEl.getAttribute('data-precedence'); |
111 | | - resourceEl.removeAttribute('media'); |
112 | | - } |
113 | | - |
114 | | - // resourceEl is either a newly constructed <link rel="stylesheet" ...> or a <style> tag requiring hoisting |
115 | | - const prior = precedences.get(precedence) || lastResource; |
116 | | - if (prior === lastResource) { |
117 | | - lastResource = resourceEl; |
118 | | - } |
119 | | - precedences.set(precedence, resourceEl); |
120 | | - |
121 | | - // Finally, we insert the newly constructed instance at an appropriate location |
122 | | - // in the Document. |
123 | | - if (prior) { |
124 | | - prior.parentNode.insertBefore(resourceEl, prior.nextSibling); |
125 | | - } else { |
126 | | - const head = thisDocument.head; |
127 | | - head.insertBefore(resourceEl, head.firstChild); |
128 | | - } |
129 | | - } |
130 | | - |
131 | | - Promise.all(dependencies).then( |
132 | | - completeBoundary.bind(null, suspenseBoundaryID, contentID, ''), |
133 | | - completeBoundary.bind( |
134 | | - null, |
135 | | - suspenseBoundaryID, |
136 | | - contentID, |
137 | | - 'Resource failed to load', |
138 | | - ), |
139 | | - ); |
140 | | -} |
| 13 | +// This is a string so Closure's advanced compilation mode doesn't mangle it. |
| 14 | +// These will be renamed to local references by the external-runtime-plugin. |
| 15 | +window['$RM'] = new Map(); |
| 16 | +window['$RX'] = clientRenderBoundary; |
| 17 | +window['$RC'] = completeBoundary; |
| 18 | +window['$RR'] = completeBoundaryWithStyles; |
| 19 | +window['$RS'] = completeSegment; |
141 | 20 |
|
142 | 21 | listenToFormSubmissionsForReplaying(); |
0 commit comments