Skip to content

Commit 3e86c54

Browse files
bjohansebasclaude
andcommitted
fix(client): keep HMR alive when Trusted Types blocks the logging runtime
Under a require-trusted-types-for 'script' CSP, tapable — bundled through webpack's logging runtime — cannot compile its hooks: the first log call throws an EvalError from new Function, killing whatever listener happened to log (and with it the update flow). The logger methods are now guarded, so webpack's logging runtime stays the engine and enforcement just turns logging off. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent bb135d3 commit 3e86c54

1 file changed

Lines changed: 30 additions & 68 deletions

File tree

client-src/utils/log.js

Lines changed: 30 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,46 @@
1-
/* eslint-disable no-console -- this module is the console logger */
2-
// A self-contained level-gated console logger. webpack's logging runtime is
3-
// deliberately not used: it pulls tapable into the bundle, whose hook
4-
// compiler calls `new Function` — which throws under a
5-
// `require-trusted-types-for 'script'` Content Security Policy.
6-
7-
const LOG_PREFIX = "[webpack-dev-middleware]";
8-
9-
/** @typedef {false | true | "none" | "error" | "warn" | "info" | "log" | "verbose"} LogLevel */
10-
11-
const LEVELS = ["none", "error", "warn", "info", "log", "verbose"];
1+
// @ts-expect-error -- no published types for this entry point
2+
import logger from "webpack/lib/logging/runtime.js";
123

4+
const LOGGER_NAME = "webpack-dev-middleware";
135
const DEFAULT_LEVEL = "info";
146

15-
let currentLevel = DEFAULT_LEVEL;
7+
/** @typedef {false | true | "none" | "error" | "warn" | "info" | "log" | "verbose"} LogLevel */
168

179
/**
18-
* @param {LogLevel} level log level (or `false` for off, `true` for "log")
10+
* @param {LogLevel} level log level (or `false` for off, `true` for default)
1911
*/
2012
export function setLogLevel(level) {
21-
if (level === false) {
22-
currentLevel = "none";
23-
} else if (level === true) {
24-
currentLevel = "log";
25-
} else if (LEVELS.includes(level)) {
26-
currentLevel = level;
27-
}
13+
logger.configureDefaultLogger({ level });
2814
}
2915

30-
/**
31-
* @param {string} level level of the message
32-
* @returns {boolean} true when the current level shows the message
33-
*/
34-
function enabled(level) {
35-
return LEVELS.indexOf(currentLevel) >= LEVELS.indexOf(level);
36-
}
16+
setLogLevel(DEFAULT_LEVEL);
17+
18+
const rawLog = logger.getLogger(LOGGER_NAME);
3719

3820
/**
39-
* Prefix like webpack's logger: merged into the first argument when it is a
40-
* string, passed separately otherwise (e.g. Error objects).
41-
* @param {unknown[]} args arguments
42-
* @returns {unknown[]} prefixed arguments
21+
* Guard a logger method: under a `require-trusted-types-for 'script'`
22+
* Content Security Policy, tapable (bundled through webpack's logging
23+
* runtime) cannot compile its hooks — `new Function` throws an EvalError on
24+
* the first log call. Swallowing it keeps HMR fully functional with logging
25+
* off instead of breaking whatever listener happened to log.
26+
* @param {string} method logger method name
27+
* @returns {(...args: unknown[]) => void} guarded method
4328
*/
44-
function prefixed(args) {
45-
return typeof args[0] === "string"
46-
? [`${LOG_PREFIX} ${args[0]}`, ...args.slice(1)]
47-
: [LOG_PREFIX, ...args];
29+
function guarded(method) {
30+
return (...args) => {
31+
try {
32+
rawLog[method](...args);
33+
} catch {
34+
// Logging is unavailable (e.g. Trusted Types enforcement).
35+
}
36+
};
4837
}
4938

5039
export const log = {
51-
/**
52-
* @param {...unknown} args arguments
53-
*/
54-
error(...args) {
55-
if (enabled("error")) console.error(...prefixed(args));
56-
},
57-
/**
58-
* @param {...unknown} args arguments
59-
*/
60-
warn(...args) {
61-
if (enabled("warn")) console.warn(...prefixed(args));
62-
},
63-
/**
64-
* @param {...unknown} args arguments
65-
*/
66-
info(...args) {
67-
if (enabled("info")) console.info(...prefixed(args));
68-
},
69-
/**
70-
* @param {...unknown} args arguments
71-
*/
72-
log(...args) {
73-
if (enabled("log")) console.log(...prefixed(args));
74-
},
75-
/**
76-
* @param {...unknown} args arguments
77-
*/
78-
groupCollapsed(...args) {
79-
if (enabled("log")) console.groupCollapsed(...prefixed(args));
80-
},
81-
groupEnd() {
82-
if (enabled("log")) console.groupEnd();
83-
},
40+
error: guarded("error"),
41+
warn: guarded("warn"),
42+
info: guarded("info"),
43+
log: guarded("log"),
44+
groupCollapsed: guarded("groupCollapsed"),
45+
groupEnd: guarded("groupEnd"),
8446
};

0 commit comments

Comments
 (0)