1 | const RuntimeErrorFooter = require('./components/RuntimeErrorFooter.js');
|
2 | const RuntimeErrorHeader = require('./components/RuntimeErrorHeader.js');
|
3 | const CompileErrorContainer = require('./containers/CompileErrorContainer.js');
|
4 | const RuntimeErrorContainer = require('./containers/RuntimeErrorContainer.js');
|
5 | const theme = require('./theme.js');
|
6 | const utils = require('./utils.js');
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | let iframeRoot = null;
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | let rootDocument = null;
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | let root = null;
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | let scheduledRenderFn = null;
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 | let currentCompileErrorMessage = '';
|
41 |
|
42 |
|
43 |
|
44 |
|
45 | let currentRuntimeErrorIndex = 0;
|
46 |
|
47 |
|
48 |
|
49 |
|
50 | let currentRuntimeErrors = [];
|
51 |
|
52 |
|
53 |
|
54 |
|
55 | let currentMode = null;
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 | function IframeRoot(document, root, props) {
|
71 | const iframe = document.createElement('iframe');
|
72 | iframe.id = 'react-refresh-overlay';
|
73 | iframe.src = 'about:blank';
|
74 |
|
75 | iframe.style.border = 'none';
|
76 | iframe.style.height = '100vh';
|
77 | iframe.style.left = '0';
|
78 | iframe.style.position = 'fixed';
|
79 | iframe.style.top = '0';
|
80 | iframe.style.width = '100vw';
|
81 | iframe.style.zIndex = '2147483647';
|
82 | iframe.addEventListener('load', function onLoad() {
|
83 |
|
84 | iframe.contentDocument.body.style.margin = '0';
|
85 | props.onIframeLoad();
|
86 | });
|
87 |
|
88 |
|
89 |
|
90 | return iframe;
|
91 | }
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 | function OverlayRoot(document, root) {
|
100 | const div = document.createElement('div');
|
101 | div.id = 'react-refresh-overlay-error';
|
102 |
|
103 |
|
104 | div.style.backgroundColor = '#' + theme.grey;
|
105 | div.style.boxSizing = 'border-box';
|
106 | div.style.color = '#' + theme.white;
|
107 | div.style.fontFamily = [
|
108 | '-apple-system',
|
109 | 'BlinkMacSystemFont',
|
110 | '"Segoe UI"',
|
111 | '"Helvetica Neue"',
|
112 | 'Helvetica',
|
113 | 'Arial',
|
114 | 'sans-serif',
|
115 | '"Apple Color Emoji"',
|
116 | '"Segoe UI Emoji"',
|
117 | 'Segoe UI Symbol',
|
118 | ].join(', ');
|
119 | div.style.fontSize = '0.875rem';
|
120 | div.style.height = '100vh';
|
121 | div.style.lineHeight = '1.3';
|
122 | div.style.overflow = 'auto';
|
123 | div.style.padding = '1rem 1.5rem 0';
|
124 | div.style.width = '100vw';
|
125 |
|
126 | root.appendChild(div);
|
127 | return div;
|
128 | }
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 | function ensureRootExists(renderFn) {
|
137 | if (root) {
|
138 |
|
139 | renderFn();
|
140 | return;
|
141 | }
|
142 |
|
143 |
|
144 |
|
145 | scheduledRenderFn = renderFn;
|
146 |
|
147 | if (iframeRoot) {
|
148 |
|
149 | return;
|
150 | }
|
151 |
|
152 |
|
153 | iframeRoot = IframeRoot(document, document.body, {
|
154 | onIframeLoad: function onIframeLoad() {
|
155 | rootDocument = iframeRoot.contentDocument;
|
156 | root = OverlayRoot(rootDocument, rootDocument.body);
|
157 | scheduledRenderFn();
|
158 | },
|
159 | });
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | document.body.appendChild(iframeRoot);
|
165 | }
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 | function render() {
|
172 | ensureRootExists(function () {
|
173 | const currentFocus = rootDocument.activeElement;
|
174 | let currentFocusId;
|
175 | if (currentFocus.localName === 'button' && currentFocus.id) {
|
176 | currentFocusId = currentFocus.id;
|
177 | }
|
178 |
|
179 | utils.removeAllChildren(root);
|
180 |
|
181 | if (currentCompileErrorMessage) {
|
182 | currentMode = 'compileError';
|
183 |
|
184 | CompileErrorContainer(rootDocument, root, {
|
185 | errorMessage: currentCompileErrorMessage,
|
186 | });
|
187 | } else if (currentRuntimeErrors.length) {
|
188 | currentMode = 'runtimeError';
|
189 |
|
190 | RuntimeErrorHeader(rootDocument, root, {
|
191 | currentErrorIndex: currentRuntimeErrorIndex,
|
192 | totalErrors: currentRuntimeErrors.length,
|
193 | });
|
194 | RuntimeErrorContainer(rootDocument, root, {
|
195 | currentError: currentRuntimeErrors[currentRuntimeErrorIndex],
|
196 | });
|
197 | RuntimeErrorFooter(rootDocument, root, {
|
198 | initialFocus: currentFocusId,
|
199 | multiple: currentRuntimeErrors.length > 1,
|
200 | onClickCloseButton: function onClose() {
|
201 | clearRuntimeErrors();
|
202 | },
|
203 | onClickNextButton: function onNext() {
|
204 | if (currentRuntimeErrorIndex === currentRuntimeErrors.length - 1) {
|
205 | return;
|
206 | }
|
207 | currentRuntimeErrorIndex += 1;
|
208 | ensureRootExists(render);
|
209 | },
|
210 | onClickPrevButton: function onPrev() {
|
211 | if (currentRuntimeErrorIndex === 0) {
|
212 | return;
|
213 | }
|
214 | currentRuntimeErrorIndex -= 1;
|
215 | ensureRootExists(render);
|
216 | },
|
217 | });
|
218 | }
|
219 | });
|
220 | }
|
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 | function cleanup() {
|
227 |
|
228 | document.body.removeChild(iframeRoot);
|
229 | scheduledRenderFn = null;
|
230 | root = null;
|
231 | iframeRoot = null;
|
232 | }
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 | function clearCompileError() {
|
239 | if (!root || currentMode !== 'compileError') {
|
240 | return;
|
241 | }
|
242 |
|
243 | currentCompileErrorMessage = '';
|
244 | currentMode = null;
|
245 | cleanup();
|
246 | }
|
247 |
|
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 | function clearRuntimeErrors(dismissOverlay) {
|
254 | if (!root || currentMode !== 'runtimeError') {
|
255 | return;
|
256 | }
|
257 |
|
258 | currentRuntimeErrorIndex = 0;
|
259 | currentRuntimeErrors = [];
|
260 |
|
261 | if (typeof dismissOverlay === 'undefined' || dismissOverlay) {
|
262 | currentMode = null;
|
263 | cleanup();
|
264 | }
|
265 | }
|
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 |
|
272 | function showCompileError(message) {
|
273 | if (!message) {
|
274 | return;
|
275 | }
|
276 |
|
277 | currentCompileErrorMessage = message;
|
278 |
|
279 | render();
|
280 | }
|
281 |
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 | function showRuntimeErrors(errors) {
|
288 | if (!errors || !errors.length) {
|
289 | return;
|
290 | }
|
291 |
|
292 | currentRuntimeErrors = errors;
|
293 |
|
294 | render();
|
295 | }
|
296 |
|
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 |
|
303 | const debouncedShowRuntimeErrors = utils.debounce(showRuntimeErrors, 30);
|
304 |
|
305 |
|
306 |
|
307 |
|
308 |
|
309 |
|
310 | function isWebpackCompileError(error) {
|
311 | return /Module [A-z ]+\(from/.test(error.message) || /Cannot find module/.test(error.message);
|
312 | }
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 | function handleRuntimeError(error) {
|
321 | if (error && !isWebpackCompileError(error) && currentRuntimeErrors.indexOf(error) === -1) {
|
322 | currentRuntimeErrors = currentRuntimeErrors.concat(error);
|
323 | }
|
324 | debouncedShowRuntimeErrors(currentRuntimeErrors);
|
325 | }
|
326 |
|
327 | module.exports = Object.freeze({
|
328 | clearCompileError: clearCompileError,
|
329 | clearRuntimeErrors: clearRuntimeErrors,
|
330 | handleRuntimeError: handleRuntimeError,
|
331 | showCompileError: showCompileError,
|
332 | showRuntimeErrors: showRuntimeErrors,
|
333 | });
|