Skip to content

Commit 24bd59e

Browse files
committed
feat: open editor when clicking error on overlay
1 parent de25774 commit 24bd59e

File tree

9 files changed

+139
-6
lines changed

9 files changed

+139
-6
lines changed

client-src/overlay.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ function formatProblem(type, item) {
184184
// Compilation with errors (e.g. syntax error or missing modules).
185185
/**
186186
* @param {string} type
187-
* @param {Array<string | { file?: string, moduleName?: string, loc?: string, message?: string }>} messages
187+
* @param {Array<string | { moduleIdentifier?: string, moduleName?: string, loc?: string, message?: string }>} messages
188188
* @param {string | null} trustedTypesPolicyName
189189
*/
190190
function show(type, messages, trustedTypesPolicyName) {
@@ -203,6 +203,16 @@ function show(type, messages, trustedTypesPolicyName) {
203203
typeElement.innerText = header;
204204
applyStyle(typeElement, msgTypeStyle);
205205

206+
if (message.moduleIdentifier) {
207+
applyStyle(typeElement, { cursor: "pointer" });
208+
typeElement.dataset.canOpen = true;
209+
typeElement.addEventListener("click", () => {
210+
fetch(
211+
`/webpack-dev-server/open-editor?fileName=${message.moduleIdentifier}`
212+
);
213+
});
214+
}
215+
206216
// Make it look similar to our terminal.
207217
const text = ansiHTML(encode(body));
208218
const messageTextNode = document.createElement("div");

examples/client/overlay/app.js

+4
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,9 @@
22

33
const target = document.querySelector("#target");
44

5+
// eslint-disable-next-line import/no-unresolved, import/extensions
6+
const invalid = require("./invalid.js");
7+
8+
console.log(invalid);
59
target.classList.add("pass");
610
target.innerHTML = "Success!";

examples/client/overlay/webpack.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const { setup } = require("../../util");
77
module.exports = setup({
88
context: __dirname,
99
// create error for overlay
10-
entry: "./invalid.js",
10+
entry: "./app.js",
1111
devServer: {
1212
client: {
1313
overlay: true,

lib/Server.js

+13
Original file line numberDiff line numberDiff line change
@@ -2033,6 +2033,19 @@ class Server {
20332033
}
20342034
);
20352035

2036+
/** @type {import("express").Application} */
2037+
(app).get("/webpack-dev-server/open-editor", (req, res) => {
2038+
const fileName = req.query.fileName;
2039+
2040+
if (typeof fileName === "string") {
2041+
// @ts-ignore
2042+
const launchEditor = require("launch-editor");
2043+
launchEditor(fileName);
2044+
}
2045+
2046+
res.end();
2047+
});
2048+
20362049
/** @type {import("express").Application} */
20372050
(app).get(
20382051
"/webpack-dev-server",

package-lock.json

+34-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"html-entities": "^2.3.2",
6464
"http-proxy-middleware": "^2.0.3",
6565
"ipaddr.js": "^2.0.1",
66+
"launch-editor": "^2.6.0",
6667
"open": "^8.0.9",
6768
"p-retry": "^4.5.0",
6869
"rimraf": "^3.0.2",
@@ -126,6 +127,7 @@
126127
"tcp-port-used": "^1.0.2",
127128
"typescript": "^4.7.2",
128129
"url-loader": "^4.1.1",
130+
"wait-for-expect": "^3.0.2",
129131
"webpack": "^5.71.0",
130132
"webpack-cli": "^4.7.2",
131133
"webpack-merge": "^5.8.0"

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

+8
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,13 @@ exports[`overlay should not show initially, then show on an error and allow to c
9090
\\"
9191
>
9292
<div
93+
data-can-open=\\"true\\"
9394
style=\\"
9495
color: rgb(232, 59, 70);
9596
font-size: 1.2em;
9697
margin-bottom: 1rem;
9798
font-family: sans-serif;
99+
cursor: pointer;
98100
\\"
99101
>
100102
ERROR in ./foo.js 1:1
@@ -212,11 +214,13 @@ exports[`overlay should not show initially, then show on an error, then hide on
212214
\\"
213215
>
214216
<div
217+
data-can-open=\\"true\\"
215218
style=\\"
216219
color: rgb(232, 59, 70);
217220
font-size: 1.2em;
218221
margin-bottom: 1rem;
219222
font-family: sans-serif;
223+
cursor: pointer;
220224
\\"
221225
>
222226
ERROR in ./foo.js 1:1
@@ -334,11 +338,13 @@ exports[`overlay should not show initially, then show on an error, then show oth
334338
\\"
335339
>
336340
<div
341+
data-can-open=\\"true\\"
337342
style=\\"
338343
color: rgb(232, 59, 70);
339344
font-size: 1.2em;
340345
margin-bottom: 1rem;
341346
font-family: sans-serif;
347+
cursor: pointer;
342348
\\"
343349
>
344350
ERROR in ./foo.js 1:1
@@ -419,11 +425,13 @@ exports[`overlay should not show initially, then show on an error, then show oth
419425
\\"
420426
>
421427
<div
428+
data-can-open=\\"true\\"
422429
style=\\"
423430
color: rgb(232, 59, 70);
424431
font-size: 1.2em;
425432
margin-bottom: 1rem;
426433
font-family: sans-serif;
434+
cursor: pointer;
427435
\\"
428436
>
429437
ERROR in ./foo.js 1:1

test/e2e/overlay.test.js

+50
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const path = require("path");
44
const fs = require("graceful-fs");
55
const prettier = require("prettier");
66
const webpack = require("webpack");
7+
const waitForExpect = require("wait-for-expect");
78
const Server = require("../../lib/Server");
89
const config = require("../fixtures/overlay-config/webpack.config");
910
const trustedTypesConfig = require("../fixtures/overlay-config/trusted-types.webpack.config");
@@ -478,6 +479,55 @@ describe("overlay", () => {
478479
await server.stop();
479480
});
480481

482+
(isWebpack5 ? it : it.skip)(
483+
"should open editor when error with file info is clicked",
484+
async () => {
485+
const mockLaunchEditorCb = jest.fn();
486+
jest.mock("launch-editor", () => mockLaunchEditorCb);
487+
488+
const compiler = webpack(config);
489+
const devServerOptions = {
490+
port,
491+
};
492+
const server = new Server(devServerOptions, compiler);
493+
494+
await server.start();
495+
496+
const { page, browser } = await runBrowser();
497+
498+
await page.goto(`http://localhost:${port}/`, {
499+
waitUntil: "networkidle0",
500+
});
501+
502+
const pathToFile = path.resolve(
503+
__dirname,
504+
"../fixtures/overlay-config/foo.js"
505+
);
506+
const originalCode = fs.readFileSync(pathToFile);
507+
508+
fs.writeFileSync(pathToFile, "`;");
509+
510+
await page.waitForSelector("#webpack-dev-server-client-overlay");
511+
512+
const frame = page
513+
.frames()
514+
.find((item) => item.name() === "webpack-dev-server-client-overlay");
515+
516+
const errorHandle = await frame.$("[data-can-open]");
517+
518+
await errorHandle.click();
519+
520+
await waitForExpect(() => {
521+
expect(mockLaunchEditorCb).toHaveBeenCalledTimes(1);
522+
});
523+
524+
fs.writeFileSync(pathToFile, originalCode);
525+
526+
await browser.close();
527+
await server.stop();
528+
}
529+
);
530+
481531
it('should not show a warning when "client.overlay" is "false"', async () => {
482532
const compiler = webpack(config);
483533

test/helpers/run-browser.js

+16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
33
const puppeteer = require("puppeteer");
44
const { puppeteerArgs } = require("./puppeteer-constants");
55

6+
/**
7+
* @typedef {Object} RunBrowserResult
8+
* @property {import('puppeteer').Page} page
9+
* @property {import('puppeteer').Browser} browser
10+
*/
11+
12+
/**
13+
* @param {Parameters<import('puppeteer').Page['emulate']>[0]} config
14+
* @returns {Promise<RunBrowserResult>}
15+
*/
616
function runBrowser(config) {
717
const options = {
818
viewport: {
@@ -14,7 +24,13 @@ function runBrowser(config) {
1424
};
1525

1626
return new Promise((resolve, reject) => {
27+
/**
28+
* @type {import('puppeteer').Page}
29+
*/
1730
let page;
31+
/**
32+
* @type {import('puppeteer').Browser}
33+
*/
1834
let browser;
1935

2036
puppeteer

0 commit comments

Comments
 (0)