From e872907b50cfd9b76a47a7ea0e9116fccc4767e6 Mon Sep 17 00:00:00 2001 From: Dan Abramov <dan.abramov@gmail.com> Date: Sun, 25 Sep 2016 12:33:43 +0100 Subject: [PATCH] Fix error overlay in Firefox (#749) --- .../react-dev-utils/webpackHotDevClient.js | 135 ++++++++++++------ 1 file changed, 95 insertions(+), 40 deletions(-) diff --git a/packages/react-dev-utils/webpackHotDevClient.js b/packages/react-dev-utils/webpackHotDevClient.js index d0aa54adb..120b8680a 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); - - // 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'; - - // Make it look similar to our terminal. - overlay.innerHTML = - '<span style="color: #' + - colors.red + - '">Failed to compile.</span><br><br>' + - ansiHTML(entities.encode(message)); - - // Render! - iframe.contentDocument.body.appendChild(overlay); + iframe.onload = onIframeLoad; + return iframe; +} + +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; +} + +var overlayIframe = null; +var overlayDiv = null; +var lastOnOverlayDivReady = null; + +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 = + '<span style="color: #' + + colors.red + + '">Failed to compile.</span><br><br>' + + 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. } -- GitLab