Skip to content

Commit 05c2242

Browse files
committed
feat: support Trusted Types for client overlay
1 parent 7c1d680 commit 05c2242

File tree

12 files changed

+522
-117
lines changed

12 files changed

+522
-117
lines changed

client-src/index.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import createSocketURL from "./utils/createSocketURL.js";
1515
* @property {boolean} hot
1616
* @property {boolean} liveReload
1717
* @property {boolean} progress
18-
* @property {boolean | { warnings?: boolean, errors?: boolean }} overlay
18+
* @property {boolean | { warnings?: boolean, errors?: boolean, policyName?: string }} overlay
1919
* @property {string} [logging]
2020
* @property {number} [reconnect]
2121
*/
@@ -230,7 +230,9 @@ const onSocketMessage = {
230230
: options.overlay && options.overlay.warnings;
231231

232232
if (needShowOverlayForWarnings) {
233-
show("warning", warnings);
233+
const policyName =
234+
typeof options.overlay === "object" && options.overlay.policyName;
235+
show("warning", warnings, policyName || null);
234236
}
235237

236238
if (params && params.preventReloading) {
@@ -263,7 +265,9 @@ const onSocketMessage = {
263265
: options.overlay && options.overlay.errors;
264266

265267
if (needShowOverlayForErrors) {
266-
show("error", errors);
268+
const policyName =
269+
typeof options.overlay === "object" && options.overlay.policyName;
270+
show("error", errors, policyName || null);
267271
}
268272
},
269273
/**

client-src/overlay.js

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,26 @@ let iframeContainerElement;
2323
let containerElement;
2424
/** @type {Array<(element: HTMLDivElement) => void>} */
2525
let onLoadQueue = [];
26+
/** @type {any} */
27+
let overlayTrustedTypesPolicy;
28+
/** @type {any} */
2629

2730
ansiHTML.setColors(colors);
2831

29-
function createContainer() {
32+
/**
33+
* @param {string | null} policyName
34+
*/
35+
function createContainer(policyName) {
36+
// Enable Trusted Types if they are available in the current browser.
37+
if (window.trustedTypes) {
38+
overlayTrustedTypesPolicy = window.trustedTypes.createPolicy(
39+
policyName || "webpack-dev-server#overlay",
40+
{
41+
createHTML: (value) => value,
42+
}
43+
);
44+
}
45+
3046
iframeContainerElement = document.createElement("iframe");
3147
iframeContainerElement.id = "webpack-dev-server-client-overlay";
3248
iframeContainerElement.src = "about:blank";
@@ -109,8 +125,9 @@ function createContainer() {
109125

110126
/**
111127
* @param {(element: HTMLDivElement) => void} callback
128+
* @param {string | null} policyName
112129
*/
113-
function ensureOverlayExists(callback) {
130+
function ensureOverlayExists(callback, policyName) {
114131
if (containerElement) {
115132
// Everything is ready, call the callback right away.
116133
callback(containerElement);
@@ -124,7 +141,7 @@ function ensureOverlayExists(callback) {
124141
return;
125142
}
126143

127-
createContainer();
144+
createContainer(policyName);
128145
}
129146

130147
// Successful compilation.
@@ -178,8 +195,9 @@ function formatProblem(type, item) {
178195
/**
179196
* @param {string} type
180197
* @param {Array<string | { file?: string, moduleName?: string, loc?: string, message?: string }>} messages
198+
* @param {string | null} policyName
181199
*/
182-
function show(type, messages) {
200+
function show(type, messages, policyName) {
183201
ensureOverlayExists(() => {
184202
messages.forEach((message) => {
185203
const entryElement = document.createElement("div");
@@ -193,7 +211,9 @@ function show(type, messages) {
193211
const text = ansiHTML(encode(body));
194212
const messageTextNode = document.createElement("div");
195213

196-
messageTextNode.innerHTML = text;
214+
messageTextNode.innerHTML = overlayTrustedTypesPolicy
215+
? overlayTrustedTypesPolicy.createHTML(text)
216+
: text;
197217

198218
entryElement.appendChild(typeElement);
199219
entryElement.appendChild(document.createElement("br"));
@@ -205,7 +225,7 @@ function show(type, messages) {
205225
/** @type {HTMLDivElement} */
206226
(containerElement).appendChild(entryElement);
207227
});
208-
});
228+
}, policyName);
209229
}
210230

211231
export { formatProblem, show, hide };
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# client.overlay.policyName option
2+
3+
**webpack.config.js**
4+
5+
```js
6+
module.exports = {
7+
// ...
8+
output: {
9+
trustedTypes: { policyName: "webpack" },
10+
},
11+
devServer: {
12+
client: {
13+
overlay: {
14+
policyName: "overlay-policy",
15+
},
16+
},
17+
},
18+
};
19+
```
20+
21+
Usage via CLI:
22+
23+
```shell
24+
npx webpack serve --open
25+
```
26+
27+
## What Should Happen
28+
29+
1. The script should open `http://localhost:8080/` in your default browser.
30+
2. You should see an overlay in browser for compilation errors.
31+
3. Modify `devServer.client.overlay.policyName` in webpack.config.js to `disallowed-policy` and save.
32+
4. Restart the command and you should not see an overlay at all. In the console you should see the following error:
33+
34+
```
35+
Refused to create a TrustedTypePolicy named 'disallowed-policy' because it violates the following Content Security Policy directive: "trusted-types webpack overlay-policy".
36+
```
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"use strict";
2+
3+
const target = document.querySelector("#target");
4+
5+
target.classList.add("pass");
6+
target.textContent = "Success!";
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<!-- Originally copied from "../../.assets/layout.html" -->
2+
<!DOCTYPE html>
3+
<html>
4+
<head>
5+
<!-- Enable Trusted Types -->
6+
<meta
7+
http-equiv="Content-Security-Policy"
8+
content="require-trusted-types-for 'script'; trusted-types webpack overlay-policy;"
9+
/>
10+
11+
<title>WDS ▻ <%= htmlWebpackPlugin.options.title %></title>
12+
<meta charset="utf-8" />
13+
<meta name="viewport" content="width=device-width, initial-scale=1" />
14+
<link rel="shortcut icon" href=".assets/favicon.ico" />
15+
<link
16+
rel="stylesheet"
17+
href="https://fonts.googleapis.com/css?family=Source+Code+Pro:400,600|Source+Sans+Pro:400,400i,500,600"
18+
/>
19+
<link rel="stylesheet" href=".assets/style.css" />
20+
</head>
21+
<body>
22+
<main>
23+
<header>
24+
<h1>
25+
<img
26+
src=".assets/icon-square.svg"
27+
style="width: 35px; height: 35px"
28+
/>
29+
webpack-dev-server
30+
</h1>
31+
</header>
32+
<section>
33+
<h2><%= htmlWebpackPlugin.options.title %></h2>
34+
<div id="target"></div>
35+
</section>
36+
</main>
37+
</body>
38+
</html>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"use strict";
2+
3+
const path = require("path");
4+
const HtmlWebpackPlugin = require("html-webpack-plugin");
5+
// our setup function adds behind-the-scenes bits to the config that all of our
6+
// examples need
7+
const { setup } = require("../../util");
8+
9+
const config = setup({
10+
context: __dirname,
11+
// create error for overlay
12+
entry: "./invalid.js",
13+
output: {
14+
trustedTypes: { policyName: "webpack" },
15+
},
16+
devServer: {
17+
client: {
18+
overlay: {
19+
policyName: "overlay-policy",
20+
},
21+
},
22+
},
23+
});
24+
25+
// overwrite the index.html with our own to enable Trusted Types
26+
config.plugins[0] = new HtmlWebpackPlugin({
27+
filename: "index.html",
28+
template: path.join(__dirname, "./layout.html"),
29+
title: "trusted types overlay",
30+
});
31+
32+
module.exports = config;

lib/options.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@
110110
"cli": {
111111
"negatedDescription": "Disables the full-screen overlay in the browser when there are compiler warnings."
112112
}
113+
},
114+
"policyName": {
115+
"description": "The name of a Trusted Types policy for the overlay. Defaults to 'webpack-dev-server#overlay'.",
116+
"type": "string"
113117
}
114118
}
115119
}

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

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,14 @@ exports[`overlay should not show initially, then show on an error, then show oth
364364
"
365365
`;
366366

367+
exports[`overlay should not show overlay when Trusted Types are enabled, but policy is not allowed: page html 1`] = `
368+
"<body>
369+
<h1>webpack-dev-server is running...</h1>
370+
<script type=\\"text/javascript\\" charset=\\"utf-8\\" src=\\"/main.js\\"></script>
371+
</body>
372+
"
373+
`;
374+
367375
exports[`overlay should show a warning after invalidation: overlay html 1`] = `
368376
"<body>
369377
<div
@@ -1319,3 +1327,68 @@ exports[`overlay should show an error when "client.overlay.warnings" is "true":
13191327
</body>
13201328
"
13211329
`;
1330+
1331+
exports[`overlay should show overlay when Trusted Types are enabled: overlay html 1`] = `
1332+
"<body>
1333+
<div
1334+
id=\\"webpack-dev-server-client-overlay-div\\"
1335+
style=\\"
1336+
position: fixed;
1337+
box-sizing: border-box;
1338+
inset: 0px;
1339+
width: 100vw;
1340+
height: 100vh;
1341+
background-color: rgba(0, 0, 0, 0.85);
1342+
color: rgb(232, 232, 232);
1343+
font-family: Menlo, Consolas, monospace;
1344+
font-size: large;
1345+
padding: 2rem;
1346+
line-height: 1.2;
1347+
white-space: pre-wrap;
1348+
overflow: auto;
1349+
\\"
1350+
>
1351+
<span>Compiled with problems:</span
1352+
><button
1353+
style=\\"
1354+
background: transparent;
1355+
border: none;
1356+
font-size: 20px;
1357+
font-weight: bold;
1358+
color: white;
1359+
cursor: pointer;
1360+
float: right;
1361+
\\"
1362+
>
1363+
X</button
1364+
><br /><br />
1365+
<div>
1366+
<span style=\\"color: rgb(227, 96, 73)\\">ERROR</span><br /><br />
1367+
<div>Error from compilation. Can't find 'test' module.</div>
1368+
<br /><br />
1369+
</div>
1370+
</div>
1371+
</body>
1372+
"
1373+
`;
1374+
1375+
exports[`overlay should show overlay when Trusted Types are enabled: page html 1`] = `
1376+
"<body>
1377+
<h1>webpack-dev-server is running...</h1>
1378+
<script type=\\"text/javascript\\" charset=\\"utf-8\\" src=\\"/main.js\\"></script>
1379+
1380+
<iframe
1381+
id=\\"webpack-dev-server-client-overlay\\"
1382+
src=\\"about:blank\\"
1383+
style=\\"
1384+
position: fixed;
1385+
inset: 0px;
1386+
width: 100vw;
1387+
height: 100vh;
1388+
border: none;
1389+
z-index: 2147483647;
1390+
\\"
1391+
></iframe>
1392+
</body>
1393+
"
1394+
`;

0 commit comments

Comments
 (0)