Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 95 additions & 40 deletions packages/react-dev-utils/webpackHotDevClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran into this issue when trying to setup dom measurement for css-layout browser testing. This was super annoying :)

// 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.
Expand All @@ -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) {
Expand All @@ -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.
Expand All @@ -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({
Expand All @@ -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.
}
Expand Down