Skip to content

Commit 307f2e7

Browse files
authored
feat: add option to close overlay (#3433)
1 parent 639cd75 commit 307f2e7

23 files changed

+3019
-257
lines changed

client-src/index.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ const onSocketMessage = {
8787

8888
// Fixes #1042. overlay doesn't clear if errors are fixed but warnings remain.
8989
if (options.overlay) {
90-
overlay.clear();
90+
overlay.hide();
9191
}
9292

9393
sendMessage('Invalid');
@@ -121,7 +121,7 @@ const onSocketMessage = {
121121
log.info('Nothing changed.');
122122

123123
if (options.overlay) {
124-
overlay.clear();
124+
overlay.hide();
125125
}
126126

127127
sendMessage('StillOk');
@@ -130,7 +130,7 @@ const onSocketMessage = {
130130
sendMessage('Ok');
131131

132132
if (options.overlay) {
133-
overlay.clear();
133+
overlay.hide();
134134
}
135135

136136
if (options.initial) {
@@ -177,7 +177,7 @@ const onSocketMessage = {
177177
: options.overlay && options.overlay.warnings;
178178

179179
if (needShowOverlay) {
180-
overlay.showMessage(warnings);
180+
overlay.show(warnings, 'warnings');
181181
}
182182

183183
if (options.initial) {
@@ -205,7 +205,7 @@ const onSocketMessage = {
205205
: options.overlay && options.overlay.errors;
206206

207207
if (needShowOverlay) {
208-
overlay.showMessage(errors);
208+
overlay.show(errors, 'errors');
209209
}
210210

211211
options.initial = false;

client-src/overlay.js

+109-83
Original file line numberDiff line numberDiff line change
@@ -19,113 +19,139 @@ const colors = {
1919
darkgrey: '6D7891',
2020
};
2121

22-
let overlayIframe = null;
23-
let overlayDiv = null;
24-
let lastOnOverlayDivReady = null;
22+
let iframeContainerElement;
23+
let containerElement;
24+
let onLoadQueue = [];
2525

2626
ansiHTML.setColors(colors);
2727

28-
function createOverlayIframe(onIframeLoad) {
29-
const iframe = document.createElement('iframe');
30-
31-
iframe.id = 'webpack-dev-server-client-overlay';
32-
iframe.src = 'about:blank';
33-
iframe.style.position = 'fixed';
34-
iframe.style.left = 0;
35-
iframe.style.top = 0;
36-
iframe.style.right = 0;
37-
iframe.style.bottom = 0;
38-
iframe.style.width = '100vw';
39-
iframe.style.height = '100vh';
40-
iframe.style.border = 'none';
41-
iframe.style.zIndex = 9999999999;
42-
iframe.onload = onIframeLoad;
43-
44-
return iframe;
28+
function createContainer() {
29+
iframeContainerElement = document.createElement('iframe');
30+
iframeContainerElement.id = 'webpack-dev-server-client-overlay';
31+
iframeContainerElement.src = 'about:blank';
32+
iframeContainerElement.style.position = 'fixed';
33+
iframeContainerElement.style.left = 0;
34+
iframeContainerElement.style.top = 0;
35+
iframeContainerElement.style.right = 0;
36+
iframeContainerElement.style.bottom = 0;
37+
iframeContainerElement.style.width = '100vw';
38+
iframeContainerElement.style.height = '100vh';
39+
iframeContainerElement.style.border = 'none';
40+
iframeContainerElement.style.zIndex = 9999999999;
41+
iframeContainerElement.onload = () => {
42+
containerElement =
43+
iframeContainerElement.contentDocument.createElement('div');
44+
containerElement.id = 'webpack-dev-server-client-overlay-div';
45+
containerElement.style.position = 'fixed';
46+
containerElement.style.boxSizing = 'border-box';
47+
containerElement.style.left = 0;
48+
containerElement.style.top = 0;
49+
containerElement.style.right = 0;
50+
containerElement.style.bottom = 0;
51+
containerElement.style.width = '100vw';
52+
containerElement.style.height = '100vh';
53+
containerElement.style.backgroundColor = 'rgba(0, 0, 0, 0.85)';
54+
containerElement.style.color = '#E8E8E8';
55+
containerElement.style.fontFamily = 'Menlo, Consolas, monospace';
56+
containerElement.style.fontSize = 'large';
57+
containerElement.style.padding = '2rem';
58+
containerElement.style.lineHeight = '1.2';
59+
containerElement.style.whiteSpace = 'pre-wrap';
60+
containerElement.style.overflow = 'auto';
61+
62+
const headerElement = document.createElement('span');
63+
64+
headerElement.innerText = 'Compiled with problems:';
65+
66+
const closeButtonElement = document.createElement('button');
67+
68+
closeButtonElement.innerText = 'X';
69+
closeButtonElement.style.background = 'transparent';
70+
closeButtonElement.style.border = 'none';
71+
closeButtonElement.style.fontSize = '20px';
72+
closeButtonElement.style.fontWeight = 'bold';
73+
closeButtonElement.style.color = 'white';
74+
closeButtonElement.style.cursor = 'pointer';
75+
closeButtonElement.style.cssFloat = 'right';
76+
closeButtonElement.style.styleFloat = 'right';
77+
closeButtonElement.addEventListener('click', () => {
78+
hide();
79+
});
80+
81+
containerElement.appendChild(headerElement);
82+
containerElement.appendChild(closeButtonElement);
83+
containerElement.appendChild(document.createElement('br'));
84+
containerElement.appendChild(document.createElement('br'));
85+
86+
iframeContainerElement.contentDocument.body.appendChild(containerElement);
87+
88+
onLoadQueue.forEach((onLoad) => {
89+
onLoad(containerElement);
90+
});
91+
onLoadQueue = [];
92+
93+
iframeContainerElement.onload = null;
94+
};
95+
96+
document.body.appendChild(iframeContainerElement);
4597
}
4698

47-
function addOverlayDivTo(iframe) {
48-
const div = iframe.contentDocument.createElement('div');
49-
50-
div.id = 'webpack-dev-server-client-overlay-div';
51-
div.style.position = 'fixed';
52-
div.style.boxSizing = 'border-box';
53-
div.style.left = 0;
54-
div.style.top = 0;
55-
div.style.right = 0;
56-
div.style.bottom = 0;
57-
div.style.width = '100vw';
58-
div.style.height = '100vh';
59-
div.style.backgroundColor = 'rgba(0, 0, 0, 0.85)';
60-
div.style.color = '#E8E8E8';
61-
div.style.fontFamily = 'Menlo, Consolas, monospace';
62-
div.style.fontSize = 'large';
63-
div.style.padding = '2rem';
64-
div.style.lineHeight = '1.2';
65-
div.style.whiteSpace = 'pre-wrap';
66-
div.style.overflow = 'auto';
67-
68-
iframe.contentDocument.body.appendChild(div);
69-
70-
return div;
71-
}
72-
73-
function ensureOverlayDivExists(onOverlayDivReady) {
74-
if (overlayDiv) {
99+
function ensureOverlayExists(callback) {
100+
if (containerElement) {
75101
// Everything is ready, call the callback right away.
76-
onOverlayDivReady(overlayDiv);
102+
callback(containerElement);
103+
77104
return;
78105
}
79106

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

84-
if (overlayIframe) {
85-
// We've already created it.
109+
if (iframeContainerElement) {
86110
return;
87111
}
88112

89-
// Create iframe and, when it is ready, a div inside it.
90-
overlayIframe = createOverlayIframe(() => {
91-
overlayDiv = addOverlayDivTo(overlayIframe);
92-
// Now we can talk!
93-
lastOnOverlayDivReady(overlayDiv);
94-
});
95-
96-
// Zalgo alert: onIframeLoad() will be called either synchronously
97-
// or asynchronously depending on the browser.
98-
// We delay adding it so `overlayIframe` is set when `onIframeLoad` fires.
99-
document.body.appendChild(overlayIframe);
113+
createContainer();
100114
}
101115

102116
// Successful compilation.
103-
function clear() {
104-
if (!overlayDiv) {
105-
// It is not there in the first place.
117+
function hide() {
118+
if (!iframeContainerElement) {
106119
return;
107120
}
108121

109122
// Clean up and reset internal state.
110-
document.body.removeChild(overlayIframe);
123+
document.body.removeChild(iframeContainerElement);
111124

112-
overlayDiv = null;
113-
overlayIframe = null;
114-
lastOnOverlayDivReady = null;
125+
iframeContainerElement = null;
126+
containerElement = null;
115127
}
116128

117129
// Compilation with errors (e.g. syntax error or missing modules).
118-
function showMessage(messages) {
119-
ensureOverlayDivExists((div) => {
120-
// Make it look similar to our terminal.
121-
const errorMessage = messages[0].message || messages[0];
122-
const text = ansiHTML(encode(errorMessage));
123-
124-
div.innerHTML = `<span style="color: #${colors.red}">Failed to compile.</span><br><br>${text}`;
130+
function show(messages, type) {
131+
ensureOverlayExists(() => {
132+
messages.forEach((message) => {
133+
const entryElement = document.createElement('div');
134+
const typeElement = document.createElement('span');
135+
136+
typeElement.innerText = type === 'warnings' ? 'Warning:' : 'Error:';
137+
typeElement.style.color = `#${colors.red}`;
138+
139+
// Make it look similar to our terminal.
140+
const errorMessage = message.message || messages[0];
141+
const text = ansiHTML(encode(errorMessage));
142+
const messageTextNode = document.createTextNode(text);
143+
144+
entryElement.appendChild(typeElement);
145+
entryElement.appendChild(document.createElement('br'));
146+
entryElement.appendChild(document.createElement('br'));
147+
entryElement.appendChild(messageTextNode);
148+
entryElement.appendChild(document.createElement('br'));
149+
entryElement.appendChild(document.createElement('br'));
150+
entryElement.appendChild(document.createElement('br'));
151+
152+
containerElement.appendChild(entryElement);
153+
});
125154
});
126155
}
127156

128-
module.exports = {
129-
clear,
130-
showMessage,
131-
};
157+
module.exports = { show, hide };

lib/Server.js

+5-9
Original file line numberDiff line numberDiff line change
@@ -982,13 +982,13 @@ class Server {
982982
this.sendMessage(webSocketConnections, 'hash', stats.hash);
983983

984984
if (stats.errors.length > 0 || stats.warnings.length > 0) {
985-
if (stats.errors.length > 0) {
986-
this.sendMessage(webSocketConnections, 'errors', stats.errors);
987-
}
988-
989985
if (stats.warnings.length > 0) {
990986
this.sendMessage(webSocketConnections, 'warnings', stats.warnings);
991987
}
988+
989+
if (stats.errors.length > 0) {
990+
this.sendMessage(webSocketConnections, 'errors', stats.errors);
991+
}
992992
} else {
993993
this.sendMessage(webSocketConnections, 'ok');
994994
}
@@ -1031,11 +1031,7 @@ class Server {
10311031
// disabling refreshing on changing the content
10321032
if (this.options.liveReload) {
10331033
watcher.on('change', (item) => {
1034-
this.sendMessage(
1035-
this.webSocketConnections,
1036-
'static-changed',
1037-
item
1038-
);
1034+
this.sendMessage(this.webSocketConnections, 'static-changed', item);
10391035
});
10401036
}
10411037

lib/utils/normalizeOptions.js

+6
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ function normalizeOptions(compiler, options, logger) {
132132
// Enable client overlay by default
133133
if (typeof options.client.overlay === 'undefined') {
134134
options.client.overlay = true;
135+
} else if (typeof options.client.overlay !== 'boolean') {
136+
options.client.overlay = {
137+
errors: true,
138+
warnings: true,
139+
...options.client.overlay,
140+
};
135141
}
136142

137143
// client.hotEntry

test/client/__snapshots__/index.test.js.snap.webpack4

-22
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,8 @@ exports[`index should run onSocketMessage.close 2`] = `"Close"`;
1414

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

17-
exports[`index should run onSocketMessage.errors 1`] = `"Errors while compiling. Reload prevented."`;
18-
19-
exports[`index should run onSocketMessage.errors 2`] = `"Errors"`;
20-
21-
exports[`index should run onSocketMessage.errors 3`] = `
22-
Array [
23-
Array [
24-
"error1",
25-
],
26-
Array [
27-
"error2",
28-
],
29-
Array [
30-
"error3",
31-
],
32-
]
33-
`;
34-
3517
exports[`index should run onSocketMessage.hot 1`] = `"Hot Module Replacement enabled."`;
3618

37-
exports[`index should run onSocketMessage.invalid 1`] = `"App updated. Recompiling..."`;
38-
39-
exports[`index should run onSocketMessage.invalid 2`] = `"Invalid"`;
40-
4119
exports[`index should run onSocketMessage.liveReload 1`] = `"Live Reloading enabled."`;
4220

4321
exports[`index should run onSocketMessage.ok 1`] = `"Ok"`;

test/client/__snapshots__/index.test.js.snap.webpack5

-22
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,8 @@ exports[`index should run onSocketMessage.close 2`] = `"Close"`;
1414

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

17-
exports[`index should run onSocketMessage.errors 1`] = `"Errors while compiling. Reload prevented."`;
18-
19-
exports[`index should run onSocketMessage.errors 2`] = `"Errors"`;
20-
21-
exports[`index should run onSocketMessage.errors 3`] = `
22-
Array [
23-
Array [
24-
"error1",
25-
],
26-
Array [
27-
"error2",
28-
],
29-
Array [
30-
"error3",
31-
],
32-
]
33-
`;
34-
3517
exports[`index should run onSocketMessage.hot 1`] = `"Hot Module Replacement enabled."`;
3618

37-
exports[`index should run onSocketMessage.invalid 1`] = `"App updated. Recompiling..."`;
38-
39-
exports[`index should run onSocketMessage.invalid 2`] = `"Invalid"`;
40-
4119
exports[`index should run onSocketMessage.liveReload 1`] = `"Live Reloading enabled."`;
4220

4321
exports[`index should run onSocketMessage.ok 1`] = `"Ok"`;

test/client/__snapshots__/overlay.test.js.snap.webpack4

-9
This file was deleted.

test/client/__snapshots__/overlay.test.js.snap.webpack5

-9
This file was deleted.

0 commit comments

Comments
 (0)