Skip to content

Commit

Permalink
feat: add option to close overlay (#3433)
Browse files Browse the repository at this point in the history
  • Loading branch information
anshumanv committed Jun 18, 2021
1 parent 639cd75 commit 307f2e7
Show file tree
Hide file tree
Showing 23 changed files with 3,019 additions and 257 deletions.
10 changes: 5 additions & 5 deletions client-src/index.js
Expand Up @@ -87,7 +87,7 @@ const onSocketMessage = {

// Fixes #1042. overlay doesn't clear if errors are fixed but warnings remain.
if (options.overlay) {
overlay.clear();
overlay.hide();
}

sendMessage('Invalid');
Expand Down Expand Up @@ -121,7 +121,7 @@ const onSocketMessage = {
log.info('Nothing changed.');

if (options.overlay) {
overlay.clear();
overlay.hide();
}

sendMessage('StillOk');
Expand All @@ -130,7 +130,7 @@ const onSocketMessage = {
sendMessage('Ok');

if (options.overlay) {
overlay.clear();
overlay.hide();
}

if (options.initial) {
Expand Down Expand Up @@ -177,7 +177,7 @@ const onSocketMessage = {
: options.overlay && options.overlay.warnings;

if (needShowOverlay) {
overlay.showMessage(warnings);
overlay.show(warnings, 'warnings');
}

if (options.initial) {
Expand Down Expand Up @@ -205,7 +205,7 @@ const onSocketMessage = {
: options.overlay && options.overlay.errors;

if (needShowOverlay) {
overlay.showMessage(errors);
overlay.show(errors, 'errors');
}

options.initial = false;
Expand Down
192 changes: 109 additions & 83 deletions client-src/overlay.js
Expand Up @@ -19,113 +19,139 @@ const colors = {
darkgrey: '6D7891',
};

let overlayIframe = null;
let overlayDiv = null;
let lastOnOverlayDivReady = null;
let iframeContainerElement;
let containerElement;
let onLoadQueue = [];

ansiHTML.setColors(colors);

function createOverlayIframe(onIframeLoad) {
const iframe = document.createElement('iframe');

iframe.id = 'webpack-dev-server-client-overlay';
iframe.src = 'about:blank';
iframe.style.position = 'fixed';
iframe.style.left = 0;
iframe.style.top = 0;
iframe.style.right = 0;
iframe.style.bottom = 0;
iframe.style.width = '100vw';
iframe.style.height = '100vh';
iframe.style.border = 'none';
iframe.style.zIndex = 9999999999;
iframe.onload = onIframeLoad;

return iframe;
function createContainer() {
iframeContainerElement = document.createElement('iframe');
iframeContainerElement.id = 'webpack-dev-server-client-overlay';
iframeContainerElement.src = 'about:blank';
iframeContainerElement.style.position = 'fixed';
iframeContainerElement.style.left = 0;
iframeContainerElement.style.top = 0;
iframeContainerElement.style.right = 0;
iframeContainerElement.style.bottom = 0;
iframeContainerElement.style.width = '100vw';
iframeContainerElement.style.height = '100vh';
iframeContainerElement.style.border = 'none';
iframeContainerElement.style.zIndex = 9999999999;
iframeContainerElement.onload = () => {
containerElement =
iframeContainerElement.contentDocument.createElement('div');
containerElement.id = 'webpack-dev-server-client-overlay-div';
containerElement.style.position = 'fixed';
containerElement.style.boxSizing = 'border-box';
containerElement.style.left = 0;
containerElement.style.top = 0;
containerElement.style.right = 0;
containerElement.style.bottom = 0;
containerElement.style.width = '100vw';
containerElement.style.height = '100vh';
containerElement.style.backgroundColor = 'rgba(0, 0, 0, 0.85)';
containerElement.style.color = '#E8E8E8';
containerElement.style.fontFamily = 'Menlo, Consolas, monospace';
containerElement.style.fontSize = 'large';
containerElement.style.padding = '2rem';
containerElement.style.lineHeight = '1.2';
containerElement.style.whiteSpace = 'pre-wrap';
containerElement.style.overflow = 'auto';

const headerElement = document.createElement('span');

headerElement.innerText = 'Compiled with problems:';

const closeButtonElement = document.createElement('button');

closeButtonElement.innerText = 'X';
closeButtonElement.style.background = 'transparent';
closeButtonElement.style.border = 'none';
closeButtonElement.style.fontSize = '20px';
closeButtonElement.style.fontWeight = 'bold';
closeButtonElement.style.color = 'white';
closeButtonElement.style.cursor = 'pointer';
closeButtonElement.style.cssFloat = 'right';
closeButtonElement.style.styleFloat = 'right';
closeButtonElement.addEventListener('click', () => {
hide();
});

containerElement.appendChild(headerElement);
containerElement.appendChild(closeButtonElement);
containerElement.appendChild(document.createElement('br'));
containerElement.appendChild(document.createElement('br'));

iframeContainerElement.contentDocument.body.appendChild(containerElement);

onLoadQueue.forEach((onLoad) => {
onLoad(containerElement);
});
onLoadQueue = [];

iframeContainerElement.onload = null;
};

document.body.appendChild(iframeContainerElement);
}

function addOverlayDivTo(iframe) {
const div = iframe.contentDocument.createElement('div');

div.id = 'webpack-dev-server-client-overlay-div';
div.style.position = 'fixed';
div.style.boxSizing = 'border-box';
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 = 'rgba(0, 0, 0, 0.85)';
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;
}

function ensureOverlayDivExists(onOverlayDivReady) {
if (overlayDiv) {
function ensureOverlayExists(callback) {
if (containerElement) {
// Everything is ready, call the callback right away.
onOverlayDivReady(overlayDiv);
callback(containerElement);

return;
}

// Creating an iframe may be asynchronous so we'll schedule the callback.
// In case of multiple calls, last callback wins.
lastOnOverlayDivReady = onOverlayDivReady;
onLoadQueue.push(callback);

if (overlayIframe) {
// We've already created it.
if (iframeContainerElement) {
return;
}

// Create iframe and, when it is ready, a div inside it.
overlayIframe = createOverlayIframe(() => {
overlayDiv = addOverlayDivTo(overlayIframe);
// Now we can talk!
lastOnOverlayDivReady(overlayDiv);
});

// Zalgo alert: onIframeLoad() will be called either synchronously
// or asynchronously depending on the browser.
// We delay adding it so `overlayIframe` is set when `onIframeLoad` fires.
document.body.appendChild(overlayIframe);
createContainer();
}

// Successful compilation.
function clear() {
if (!overlayDiv) {
// It is not there in the first place.
function hide() {
if (!iframeContainerElement) {
return;
}

// Clean up and reset internal state.
document.body.removeChild(overlayIframe);
document.body.removeChild(iframeContainerElement);

overlayDiv = null;
overlayIframe = null;
lastOnOverlayDivReady = null;
iframeContainerElement = null;
containerElement = null;
}

// Compilation with errors (e.g. syntax error or missing modules).
function showMessage(messages) {
ensureOverlayDivExists((div) => {
// Make it look similar to our terminal.
const errorMessage = messages[0].message || messages[0];
const text = ansiHTML(encode(errorMessage));

div.innerHTML = `<span style="color: #${colors.red}">Failed to compile.</span><br><br>${text}`;
function show(messages, type) {
ensureOverlayExists(() => {
messages.forEach((message) => {
const entryElement = document.createElement('div');
const typeElement = document.createElement('span');

typeElement.innerText = type === 'warnings' ? 'Warning:' : 'Error:';
typeElement.style.color = `#${colors.red}`;

// Make it look similar to our terminal.
const errorMessage = message.message || messages[0];
const text = ansiHTML(encode(errorMessage));
const messageTextNode = document.createTextNode(text);

entryElement.appendChild(typeElement);
entryElement.appendChild(document.createElement('br'));
entryElement.appendChild(document.createElement('br'));
entryElement.appendChild(messageTextNode);
entryElement.appendChild(document.createElement('br'));
entryElement.appendChild(document.createElement('br'));
entryElement.appendChild(document.createElement('br'));

containerElement.appendChild(entryElement);
});
});
}

module.exports = {
clear,
showMessage,
};
module.exports = { show, hide };
14 changes: 5 additions & 9 deletions lib/Server.js
Expand Up @@ -982,13 +982,13 @@ class Server {
this.sendMessage(webSocketConnections, 'hash', stats.hash);

if (stats.errors.length > 0 || stats.warnings.length > 0) {
if (stats.errors.length > 0) {
this.sendMessage(webSocketConnections, 'errors', stats.errors);
}

if (stats.warnings.length > 0) {
this.sendMessage(webSocketConnections, 'warnings', stats.warnings);
}

if (stats.errors.length > 0) {
this.sendMessage(webSocketConnections, 'errors', stats.errors);
}
} else {
this.sendMessage(webSocketConnections, 'ok');
}
Expand Down Expand Up @@ -1031,11 +1031,7 @@ class Server {
// disabling refreshing on changing the content
if (this.options.liveReload) {
watcher.on('change', (item) => {
this.sendMessage(
this.webSocketConnections,
'static-changed',
item
);
this.sendMessage(this.webSocketConnections, 'static-changed', item);
});
}

Expand Down
6 changes: 6 additions & 0 deletions lib/utils/normalizeOptions.js
Expand Up @@ -132,6 +132,12 @@ function normalizeOptions(compiler, options, logger) {
// Enable client overlay by default
if (typeof options.client.overlay === 'undefined') {
options.client.overlay = true;
} else if (typeof options.client.overlay !== 'boolean') {
options.client.overlay = {
errors: true,
warnings: true,
...options.client.overlay,
};
}

// client.hotEntry
Expand Down
22 changes: 0 additions & 22 deletions test/client/__snapshots__/index.test.js.snap.webpack4
Expand Up @@ -14,30 +14,8 @@ exports[`index should run onSocketMessage.close 2`] = `"Close"`;

exports[`index should run onSocketMessage.error 1`] = `"error!!"`;

exports[`index should run onSocketMessage.errors 1`] = `"Errors while compiling. Reload prevented."`;

exports[`index should run onSocketMessage.errors 2`] = `"Errors"`;

exports[`index should run onSocketMessage.errors 3`] = `
Array [
Array [
"error1",
],
Array [
"error2",
],
Array [
"error3",
],
]
`;

exports[`index should run onSocketMessage.hot 1`] = `"Hot Module Replacement enabled."`;

exports[`index should run onSocketMessage.invalid 1`] = `"App updated. Recompiling..."`;

exports[`index should run onSocketMessage.invalid 2`] = `"Invalid"`;

exports[`index should run onSocketMessage.liveReload 1`] = `"Live Reloading enabled."`;

exports[`index should run onSocketMessage.ok 1`] = `"Ok"`;
Expand Down
22 changes: 0 additions & 22 deletions test/client/__snapshots__/index.test.js.snap.webpack5
Expand Up @@ -14,30 +14,8 @@ exports[`index should run onSocketMessage.close 2`] = `"Close"`;

exports[`index should run onSocketMessage.error 1`] = `"error!!"`;

exports[`index should run onSocketMessage.errors 1`] = `"Errors while compiling. Reload prevented."`;

exports[`index should run onSocketMessage.errors 2`] = `"Errors"`;

exports[`index should run onSocketMessage.errors 3`] = `
Array [
Array [
"error1",
],
Array [
"error2",
],
Array [
"error3",
],
]
`;

exports[`index should run onSocketMessage.hot 1`] = `"Hot Module Replacement enabled."`;

exports[`index should run onSocketMessage.invalid 1`] = `"App updated. Recompiling..."`;

exports[`index should run onSocketMessage.invalid 2`] = `"Invalid"`;

exports[`index should run onSocketMessage.liveReload 1`] = `"Live Reloading enabled."`;

exports[`index should run onSocketMessage.ok 1`] = `"Ok"`;
Expand Down
9 changes: 0 additions & 9 deletions test/client/__snapshots__/overlay.test.js.snap.webpack4

This file was deleted.

9 changes: 0 additions & 9 deletions test/client/__snapshots__/overlay.test.js.snap.webpack5

This file was deleted.

0 comments on commit 307f2e7

Please sign in to comment.