Skip to content

Commit e872907

Browse files
authored
Fix error overlay in Firefox (#749)
1 parent 2b7a4f1 commit e872907

File tree

1 file changed

+95
-40
lines changed

1 file changed

+95
-40
lines changed

packages/react-dev-utils/webpackHotDevClient.js

Lines changed: 95 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,10 @@ var colors = {
3939
};
4040
ansiHTML.setColors(colors);
4141

42-
function showErrorOverlay(message) {
43-
// Use an iframe so that document styles don't mess up the overlay.
44-
var iframeID = 'react-dev-utils-webpack-hot-dev-client-overlay';
45-
var iframe =
46-
document.getElementById(iframeID) ||
47-
document.createElement('iframe');
48-
iframe.id = iframeID;
42+
function createOverlayIframe(onIframeLoad) {
43+
var iframe = document.createElement('iframe');
44+
iframe.id = 'react-dev-utils-webpack-hot-dev-client-overlay';
45+
iframe.src = 'about:blank';
4946
iframe.style.position = 'fixed';
5047
iframe.style.left = 0;
5148
iframe.style.top = 0;
@@ -55,39 +52,74 @@ function showErrorOverlay(message) {
5552
iframe.style.height = '100vh';
5653
iframe.style.border = 'none';
5754
iframe.style.zIndex = 9999999999;
58-
document.body.appendChild(iframe);
59-
60-
// Inside, make a div.
61-
var overlayID = 'react-dev-utils-webpack-hot-dev-client-overlay-div';
62-
var overlay =
63-
iframe.contentDocument.getElementById(overlayID) ||
64-
iframe.contentDocument.createElement('div');
65-
overlay.id = overlayID;
66-
overlay.style.position = 'fixed';
67-
overlay.style.left = 0;
68-
overlay.style.top = 0;
69-
overlay.style.right = 0;
70-
overlay.style.bottom = 0;
71-
overlay.style.width = '100vw';
72-
overlay.style.height = '100vh';
73-
overlay.style.backgroundColor = 'black';
74-
overlay.style.color = '#E8E8E8';
75-
overlay.style.fontFamily = 'Menlo, Consolas, monospace';
76-
overlay.style.fontSize = 'large';
77-
overlay.style.padding = '2rem';
78-
overlay.style.lineHeight = '1.2';
79-
overlay.style.whiteSpace = 'pre-wrap';
80-
overlay.style.overflow = 'auto';
81-
82-
// Make it look similar to our terminal.
83-
overlay.innerHTML =
84-
'<span style="color: #' +
85-
colors.red +
86-
'">Failed to compile.</span><br><br>' +
87-
ansiHTML(entities.encode(message));
88-
89-
// Render!
90-
iframe.contentDocument.body.appendChild(overlay);
55+
iframe.onload = onIframeLoad;
56+
return iframe;
57+
}
58+
59+
function addOverlayDivTo(iframe) {
60+
var div = iframe.contentDocument.createElement('div');
61+
div.id = 'react-dev-utils-webpack-hot-dev-client-overlay-div';
62+
div.style.position = 'fixed';
63+
div.style.left = 0;
64+
div.style.top = 0;
65+
div.style.right = 0;
66+
div.style.bottom = 0;
67+
div.style.width = '100vw';
68+
div.style.height = '100vh';
69+
div.style.backgroundColor = 'black';
70+
div.style.color = '#E8E8E8';
71+
div.style.fontFamily = 'Menlo, Consolas, monospace';
72+
div.style.fontSize = 'large';
73+
div.style.padding = '2rem';
74+
div.style.lineHeight = '1.2';
75+
div.style.whiteSpace = 'pre-wrap';
76+
div.style.overflow = 'auto';
77+
iframe.contentDocument.body.appendChild(div);
78+
return div;
79+
}
80+
81+
var overlayIframe = null;
82+
var overlayDiv = null;
83+
var lastOnOverlayDivReady = null;
84+
85+
function ensureOverlayDivExists(onOverlayDivReady) {
86+
if (overlayDiv) {
87+
// Everything is ready, call the callback right away.
88+
onOverlayDivReady(overlayDiv);
89+
return;
90+
}
91+
92+
// Creating an iframe may be asynchronous so we'll schedule the callback.
93+
// In case of multiple calls, last callback wins.
94+
lastOnOverlayDivReady = onOverlayDivReady;
95+
96+
if (overlayIframe) {
97+
// We're already creating it.
98+
return;
99+
}
100+
101+
// Create iframe and, when it is ready, a div inside it.
102+
overlayIframe = createOverlayIframe(function onIframeLoad() {
103+
overlayDiv = addOverlayDivTo(overlayIframe);
104+
// Now we can talk!
105+
lastOnOverlayDivReady(overlayDiv);
106+
});
107+
108+
// Zalgo alert: onIframeLoad() will be called either synchronouly
109+
// or asynchronously depending on the browser.
110+
// We delay adding it so `overlayIframe` is set when `onIframeLoad` fires.
111+
document.body.appendChild(overlayIframe);
112+
}
113+
114+
function showErrorOverlay(message) {
115+
ensureOverlayDivExists(function onOverlayDivReady(overlayDiv) {
116+
// Make it look similar to our terminal.
117+
overlayDiv.innerHTML =
118+
'<span style="color: #' +
119+
colors.red +
120+
'">Failed to compile.</span><br><br>' +
121+
ansiHTML(entities.encode(message));
122+
});
91123
}
92124

93125
// Connect to WebpackDevServer via a socket.
@@ -105,11 +137,22 @@ var connection = new SockJS(url.format({
105137
// Remember some state related to hot module replacement.
106138
var isFirstCompilation = true;
107139
var mostRecentCompilationHash = null;
140+
var hasCompileErrors = false;
141+
142+
function clearOutdatedErrors() {
143+
// Clean up outdated compile errors, if any.
144+
if (hasCompileErrors && typeof console.clear === 'function') {
145+
console.clear();
146+
}
147+
}
108148

109149
// Successful compilation.
110150
function handleSuccess() {
151+
clearOutdatedErrors();
152+
111153
var isHotUpdate = !isFirstCompilation;
112154
isFirstCompilation = false;
155+
hasCompileErrors = false;
113156

114157
// Attempt to apply hot updates or reload.
115158
if (isHotUpdate) {
@@ -119,8 +162,11 @@ function handleSuccess() {
119162

120163
// Compilation with warnings (e.g. ESLint).
121164
function handleWarnings(warnings) {
165+
clearOutdatedErrors();
166+
122167
var isHotUpdate = !isFirstCompilation;
123168
isFirstCompilation = false;
169+
hasCompileErrors = false;
124170

125171
function printWarnings() {
126172
// Print warnings to the console.
@@ -144,7 +190,10 @@ function handleWarnings(warnings) {
144190

145191
// Compilation with errors (e.g. syntax error or missing modules).
146192
function handleErrors(errors) {
193+
clearOutdatedErrors();
194+
147195
isFirstCompilation = false;
196+
hasCompileErrors = true;
148197

149198
// "Massage" webpack messages.
150199
var formatted = formatWebpackMessages({
@@ -154,6 +203,12 @@ function handleErrors(errors) {
154203

155204
// Only show the first error.
156205
showErrorOverlay(formatted.errors[0]);
206+
207+
// Also log them to the console.
208+
for (var i = 0; i < formatted.errors.length; i++) {
209+
console.error(stripAnsi(formatted.errors[i]));
210+
}
211+
157212
// Do not attempt to reload now.
158213
// We will reload on next success instead.
159214
}

0 commit comments

Comments
 (0)