Skip to content

Commit f7b1747

Browse files
committed
feat: allow filter overlay message with function
1 parent ee748a7 commit f7b1747

File tree

9 files changed

+408
-182
lines changed

9 files changed

+408
-182
lines changed

Diff for: client-src/index.js

+54-15
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,20 @@ import sendMessage from "./utils/sendMessage.js";
1010
import reloadApp from "./utils/reloadApp.js";
1111
import createSocketURL from "./utils/createSocketURL.js";
1212

13+
/**
14+
* @typedef {Object} OverlayOptions
15+
* @property {boolean | (error: Error) => boolean} [warnings]
16+
* @property {boolean | (error: Error) => boolean} [errors]
17+
* @property {boolean | (error: Error) => boolean} [runtimeErrors]
18+
* @property {string} [trustedTypesPolicyName]
19+
*/
20+
1321
/**
1422
* @typedef {Object} Options
1523
* @property {boolean} hot
1624
* @property {boolean} liveReload
1725
* @property {boolean} progress
18-
* @property {boolean | { warnings?: boolean, errors?: boolean, runtimeErrors?: boolean, trustedTypesPolicyName?: string }} overlay
26+
* @property {boolean | OverlayOptions} overlay
1927
* @property {string} [logging]
2028
* @property {number} [reconnect]
2129
*/
@@ -83,6 +91,23 @@ if (parsedResourceQuery.overlay) {
8391
runtimeErrors: true,
8492
...options.overlay,
8593
};
94+
95+
["errors", "warnings", "runtimeErrors"].forEach((property) => {
96+
if (typeof options.overlay[property] === "string") {
97+
const overlayFilterFunctionString = decodeURIComponent(
98+
options.overlay[property]
99+
);
100+
101+
// eslint-disable-next-line no-new-func
102+
const overlayFilterFunction = new Function(
103+
"message",
104+
`var callback = ${overlayFilterFunctionString}
105+
return callback(message)`
106+
);
107+
108+
options.overlay[property] = overlayFilterFunction;
109+
}
110+
});
86111
}
87112
enabledFeatures.Overlay = true;
88113
}
@@ -266,17 +291,24 @@ const onSocketMessage = {
266291
log.warn(printableWarnings[i]);
267292
}
268293

269-
const needShowOverlayForWarnings =
294+
const overlayWarningsSetting =
270295
typeof options.overlay === "boolean"
271296
? options.overlay
272297
: options.overlay && options.overlay.warnings;
273298

274-
if (needShowOverlayForWarnings) {
275-
overlay.send({
276-
type: "BUILD_ERROR",
277-
level: "warning",
278-
messages: warnings,
279-
});
299+
if (overlayWarningsSetting) {
300+
const warningsToDisplay =
301+
typeof overlayWarningsSetting === "function"
302+
? warnings.filter(overlayWarningsSetting)
303+
: warnings;
304+
305+
if (warningsToDisplay.length) {
306+
overlay.send({
307+
type: "BUILD_ERROR",
308+
level: "warning",
309+
messages: warnings,
310+
});
311+
}
280312
}
281313

282314
if (params && params.preventReloading) {
@@ -303,17 +335,24 @@ const onSocketMessage = {
303335
log.error(printableErrors[i]);
304336
}
305337

306-
const needShowOverlayForErrors =
338+
const overlayErrorsSettings =
307339
typeof options.overlay === "boolean"
308340
? options.overlay
309341
: options.overlay && options.overlay.errors;
310342

311-
if (needShowOverlayForErrors) {
312-
overlay.send({
313-
type: "BUILD_ERROR",
314-
level: "error",
315-
messages: errors,
316-
});
343+
if (overlayErrorsSettings) {
344+
const errorsToDisplay =
345+
typeof overlayErrorsSettings === "function"
346+
? errors.filter(overlayErrorsSettings)
347+
: errors;
348+
349+
if (errorsToDisplay.length) {
350+
overlay.send({
351+
type: "BUILD_ERROR",
352+
level: "error",
353+
messages: errors,
354+
});
355+
}
317356
}
318357
},
319358
/**

Diff for: client-src/overlay.js

+30-14
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ function formatProblem(type, item) {
7878
/**
7979
* @typedef {Object} CreateOverlayOptions
8080
* @property {string | null} trustedTypesPolicyName
81-
* @property {boolean} [catchRuntimeError]
81+
* @property {boolean | (error: Error) => void} [catchRuntimeError]
8282
*/
8383

8484
/**
@@ -90,6 +90,8 @@ const createOverlay = (options) => {
9090
let iframeContainerElement;
9191
/** @type {HTMLDivElement | null | undefined} */
9292
let containerElement;
93+
/** @type {HTMLDivElement | null | undefined} */
94+
let headerElement;
9395
/** @type {Array<(element: HTMLDivElement) => void>} */
9496
let onLoadQueue = [];
9597
/** @type {TrustedTypePolicy | undefined} */
@@ -124,6 +126,7 @@ const createOverlay = (options) => {
124126
iframeContainerElement.id = "webpack-dev-server-client-overlay";
125127
iframeContainerElement.src = "about:blank";
126128
applyStyle(iframeContainerElement, iframeStyle);
129+
127130
iframeContainerElement.onload = () => {
128131
const contentElement =
129132
/** @type {Document} */
@@ -141,7 +144,7 @@ const createOverlay = (options) => {
141144
contentElement.id = "webpack-dev-server-client-overlay-div";
142145
applyStyle(contentElement, containerStyle);
143146

144-
const headerElement = document.createElement("div");
147+
headerElement = document.createElement("div");
145148

146149
headerElement.innerText = "Compiled with problems:";
147150
applyStyle(headerElement, headerStyle);
@@ -219,9 +222,15 @@ const createOverlay = (options) => {
219222
* @param {string} type
220223
* @param {Array<string | { moduleIdentifier?: string, moduleName?: string, loc?: string, message?: string }>} messages
221224
* @param {string | null} trustedTypesPolicyName
225+
* @param {'build' | 'runtime'} messageSource
222226
*/
223-
function show(type, messages, trustedTypesPolicyName) {
227+
function show(type, messages, trustedTypesPolicyName, messageSource) {
224228
ensureOverlayExists(() => {
229+
headerElement.innerText =
230+
messageSource === "runtime"
231+
? "Uncaught runtime errors:"
232+
: "Compiled with problems:";
233+
225234
messages.forEach((message) => {
226235
const entryElement = document.createElement("div");
227236
const msgStyle =
@@ -267,8 +276,8 @@ const createOverlay = (options) => {
267276
}
268277

269278
const overlayService = createOverlayMachine({
270-
showOverlay: ({ level = "error", messages }) =>
271-
show(level, messages, options.trustedTypesPolicyName),
279+
showOverlay: ({ level = "error", messages, messageSource }) =>
280+
show(level, messages, options.trustedTypesPolicyName, messageSource),
272281
hideOverlay: hide,
273282
});
274283

@@ -284,15 +293,22 @@ const createOverlay = (options) => {
284293
const errorObject =
285294
error instanceof Error ? error : new Error(error || message);
286295

287-
overlayService.send({
288-
type: "RUNTIME_ERROR",
289-
messages: [
290-
{
291-
message: errorObject.message,
292-
stack: parseErrorToStacks(errorObject),
293-
},
294-
],
295-
});
296+
const shouldDisplay =
297+
typeof options.catchRuntimeError === "function"
298+
? options.catchRuntimeError(errorObject)
299+
: true;
300+
301+
if (shouldDisplay) {
302+
overlayService.send({
303+
type: "RUNTIME_ERROR",
304+
messages: [
305+
{
306+
message: errorObject.message,
307+
stack: parseErrorToStacks(errorObject),
308+
},
309+
],
310+
});
311+
}
296312
});
297313
}
298314

Diff for: client-src/overlay/state-machine.js

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import createMachine from "./fsm.js";
44
* @typedef {Object} ShowOverlayData
55
* @property {'warning' | 'error'} level
66
* @property {Array<string | { moduleIdentifier?: string, moduleName?: string, loc?: string, message?: string }>} messages
7+
* @property {'build' | 'runtime'} messageSource
78
*/
89

910
/**
@@ -23,6 +24,7 @@ const createOverlayMachine = (options) => {
2324
context: {
2425
level: "error",
2526
messages: [],
27+
messageSource: "build",
2628
},
2729
states: {
2830
hidden: {
@@ -73,18 +75,21 @@ const createOverlayMachine = (options) => {
7375
return {
7476
messages: [],
7577
level: "error",
78+
messageSource: "build",
7679
};
7780
},
7881
appendMessages: (context, event) => {
7982
return {
8083
messages: context.messages.concat(event.messages),
8184
level: event.level || context.level,
85+
messageSource: event.type === "RUNTIME_ERROR" ? "runtime" : "build",
8286
};
8387
},
8488
setMessages: (context, event) => {
8589
return {
8690
messages: event.messages,
8791
level: event.level || context.level,
92+
messageSource: event.type === "RUNTIME_ERROR" ? "runtime" : "build",
8893
};
8994
},
9095
hideOverlay,

Diff for: examples/client/overlay/app.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,15 @@ const createErrorBtn = require("./error-button");
55

66
const target = document.querySelector("#target");
77

8-
target.insertAdjacentElement("afterend", createErrorBtn());
8+
target.insertAdjacentElement(
9+
"afterend",
10+
createErrorBtn("Click to throw error", "Error message thrown from JS")
11+
);
12+
13+
target.insertAdjacentElement(
14+
"afterend",
15+
createErrorBtn("Click to throw ignored error", "something something")
16+
);
917

1018
// eslint-disable-next-line import/no-unresolved, import/extensions
1119
const invalid = require("./invalid.js");

Diff for: examples/client/overlay/error-button.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
"use strict";
22

3-
function unsafeOperation() {
4-
throw new Error("Error message thrown from JS");
5-
}
3+
/**
4+
*
5+
* @param {string} label
6+
* @param {string} errorMessage
7+
* @returns HTMLButtonElement
8+
*/
9+
module.exports = function createErrorButton(label, errorMessage) {
10+
function unsafeOperation() {
11+
throw new Error(errorMessage);
12+
}
613

7-
function handleButtonClick() {
8-
unsafeOperation();
9-
}
14+
function handleButtonClick() {
15+
unsafeOperation();
16+
}
1017

11-
module.exports = function createErrorButton() {
1218
const errorBtn = document.createElement("button");
1319

1420
errorBtn.addEventListener("click", handleButtonClick);
15-
errorBtn.innerHTML = "Click to throw error";
21+
errorBtn.innerHTML = label;
1622

1723
return errorBtn;
1824
};

Diff for: examples/client/overlay/webpack.config.js

+20-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,26 @@ module.exports = setup({
1010
entry: "./app.js",
1111
devServer: {
1212
client: {
13-
overlay: true,
13+
overlay: {
14+
warnings: false,
15+
runtimeErrors: (msg) => {
16+
if (msg) {
17+
let msgString;
18+
19+
if (msg instanceof Error) {
20+
msgString = msg.message;
21+
} else if (typeof msg === "string") {
22+
msgString = msg;
23+
}
24+
25+
if (msgString) {
26+
return !/something/i.test(msgString);
27+
}
28+
}
29+
30+
return true;
31+
},
32+
},
1433
},
1534
},
1635
// uncomment to test for IE

Diff for: lib/Server.js

+25-5
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,14 @@ const schema = require("./options.json");
154154
* @property {string} [username]
155155
*/
156156

157+
/**
158+
* @typedef {boolean | ((error: Error) => void)} OverlayMessageOptions
159+
*/
160+
157161
/**
158162
* @typedef {Object} ClientConfiguration
159163
* @property {"log" | "info" | "warn" | "error" | "none" | "verbose"} [logging]
160-
* @property {boolean | { warnings?: boolean, errors?: boolean, runtimeErrors?: boolean }} [overlay]
164+
* @property {boolean | { warnings?: OverlayMessageOptions, errors?: OverlayMessageOptions, runtimeErrors?: OverlayMessageOptions }} [overlay]
161165
* @property {boolean} [progress]
162166
* @property {boolean | number} [reconnect]
163167
* @property {"ws" | "sockjs" | string} [webSocketTransport]
@@ -654,12 +658,28 @@ class Server {
654658
}
655659

656660
if (typeof client.overlay !== "undefined") {
657-
searchParams.set(
658-
"overlay",
661+
/**
662+
*
663+
* @param {OverlayMessageOptions} [setting]
664+
* @returns
665+
*/
666+
const encodeOverlaySettings = (setting) =>
667+
typeof setting === "function"
668+
? encodeURIComponent(setting.toString())
669+
: setting;
670+
671+
const overlayString =
659672
typeof client.overlay === "boolean"
660673
? String(client.overlay)
661-
: JSON.stringify(client.overlay)
662-
);
674+
: JSON.stringify({
675+
errors: encodeOverlaySettings(client.overlay.errors),
676+
warnings: encodeOverlaySettings(client.overlay.warnings),
677+
runtimeErrors: encodeOverlaySettings(
678+
client.overlay.runtimeErrors
679+
),
680+
});
681+
682+
searchParams.set("overlay", overlayString);
663683
}
664684

665685
if (typeof client.reconnect !== "undefined") {

0 commit comments

Comments
 (0)