diff --git a/packages/react-dev-utils/webpackHotDevClient.js b/packages/react-dev-utils/webpackHotDevClient.js
index d0aa54ad..120b8680 100644
--- a/packages/react-dev-utils/webpackHotDevClient.js
+++ b/packages/react-dev-utils/webpackHotDevClient.js
@@ -39,13 +39,10 @@ var colors = {
};
ansiHTML.setColors(colors);
-function showErrorOverlay(message) {
- // Use an iframe so that document styles don't mess up the overlay.
- var iframeID = 'react-dev-utils-webpack-hot-dev-client-overlay';
- var iframe =
- document.getElementById(iframeID) ||
- document.createElement('iframe');
- iframe.id = iframeID;
+function createOverlayIframe(onIframeLoad) {
+ var iframe = document.createElement('iframe');
+ iframe.id = 'react-dev-utils-webpack-hot-dev-client-overlay';
+ iframe.src = 'about:blank';
iframe.style.position = 'fixed';
iframe.style.left = 0;
iframe.style.top = 0;
@@ -55,39 +52,74 @@ function showErrorOverlay(message) {
iframe.style.height = '100vh';
iframe.style.border = 'none';
iframe.style.zIndex = 9999999999;
- document.body.appendChild(iframe);
+ iframe.onload = onIframeLoad;
+ return iframe;
+}
- // Inside, make a div.
- var overlayID = 'react-dev-utils-webpack-hot-dev-client-overlay-div';
- var overlay =
- iframe.contentDocument.getElementById(overlayID) ||
- iframe.contentDocument.createElement('div');
- overlay.id = overlayID;
- overlay.style.position = 'fixed';
- overlay.style.left = 0;
- overlay.style.top = 0;
- overlay.style.right = 0;
- overlay.style.bottom = 0;
- overlay.style.width = '100vw';
- overlay.style.height = '100vh';
- overlay.style.backgroundColor = 'black';
- overlay.style.color = '#E8E8E8';
- overlay.style.fontFamily = 'Menlo, Consolas, monospace';
- overlay.style.fontSize = 'large';
- overlay.style.padding = '2rem';
- overlay.style.lineHeight = '1.2';
- overlay.style.whiteSpace = 'pre-wrap';
- overlay.style.overflow = 'auto';
+function addOverlayDivTo(iframe) {
+ var div = iframe.contentDocument.createElement('div');
+ div.id = 'react-dev-utils-webpack-hot-dev-client-overlay-div';
+ div.style.position = 'fixed';
+ div.style.left = 0;
+ div.style.top = 0;
+ div.style.right = 0;
+ div.style.bottom = 0;
+ div.style.width = '100vw';
+ div.style.height = '100vh';
+ div.style.backgroundColor = 'black';
+ div.style.color = '#E8E8E8';
+ div.style.fontFamily = 'Menlo, Consolas, monospace';
+ div.style.fontSize = 'large';
+ div.style.padding = '2rem';
+ div.style.lineHeight = '1.2';
+ div.style.whiteSpace = 'pre-wrap';
+ div.style.overflow = 'auto';
+ iframe.contentDocument.body.appendChild(div);
+ return div;
+}
- // Make it look similar to our terminal.
- overlay.innerHTML =
- 'Failed to compile.
' +
- ansiHTML(entities.encode(message));
+var overlayIframe = null;
+var overlayDiv = null;
+var lastOnOverlayDivReady = null;
- // Render!
- iframe.contentDocument.body.appendChild(overlay);
+function ensureOverlayDivExists(onOverlayDivReady) {
+ if (overlayDiv) {
+ // Everything is ready, call the callback right away.
+ onOverlayDivReady(overlayDiv);
+ return;
+ }
+
+ // Creating an iframe may be asynchronous so we'll schedule the callback.
+ // In case of multiple calls, last callback wins.
+ lastOnOverlayDivReady = onOverlayDivReady;
+
+ if (overlayIframe) {
+ // We're already creating it.
+ return;
+ }
+
+ // Create iframe and, when it is ready, a div inside it.
+ overlayIframe = createOverlayIframe(function onIframeLoad() {
+ overlayDiv = addOverlayDivTo(overlayIframe);
+ // Now we can talk!
+ lastOnOverlayDivReady(overlayDiv);
+ });
+
+ // Zalgo alert: onIframeLoad() will be called either synchronouly
+ // or asynchronously depending on the browser.
+ // We delay adding it so `overlayIframe` is set when `onIframeLoad` fires.
+ document.body.appendChild(overlayIframe);
+}
+
+function showErrorOverlay(message) {
+ ensureOverlayDivExists(function onOverlayDivReady(overlayDiv) {
+ // Make it look similar to our terminal.
+ overlayDiv.innerHTML =
+ 'Failed to compile.
' +
+ ansiHTML(entities.encode(message));
+ });
}
// Connect to WebpackDevServer via a socket.
@@ -105,11 +137,22 @@ var connection = new SockJS(url.format({
// Remember some state related to hot module replacement.
var isFirstCompilation = true;
var mostRecentCompilationHash = null;
+var hasCompileErrors = false;
+
+function clearOutdatedErrors() {
+ // Clean up outdated compile errors, if any.
+ if (hasCompileErrors && typeof console.clear === 'function') {
+ console.clear();
+ }
+}
// Successful compilation.
function handleSuccess() {
+ clearOutdatedErrors();
+
var isHotUpdate = !isFirstCompilation;
isFirstCompilation = false;
+ hasCompileErrors = false;
// Attempt to apply hot updates or reload.
if (isHotUpdate) {
@@ -119,8 +162,11 @@ function handleSuccess() {
// Compilation with warnings (e.g. ESLint).
function handleWarnings(warnings) {
+ clearOutdatedErrors();
+
var isHotUpdate = !isFirstCompilation;
isFirstCompilation = false;
+ hasCompileErrors = false;
function printWarnings() {
// Print warnings to the console.
@@ -144,7 +190,10 @@ function handleWarnings(warnings) {
// Compilation with errors (e.g. syntax error or missing modules).
function handleErrors(errors) {
+ clearOutdatedErrors();
+
isFirstCompilation = false;
+ hasCompileErrors = true;
// "Massage" webpack messages.
var formatted = formatWebpackMessages({
@@ -154,6 +203,12 @@ function handleErrors(errors) {
// Only show the first error.
showErrorOverlay(formatted.errors[0]);
+
+ // Also log them to the console.
+ for (var i = 0; i < formatted.errors.length; i++) {
+ console.error(stripAnsi(formatted.errors[i]));
+ }
+
// Do not attempt to reload now.
// We will reload on next success instead.
}