Skip to content

Commit 4c351e1

Browse files
committed
feat: support universal platform as a web target (#5690)
* feat: support universal platform as a web target and update related tests * feat: enhance isWebTarget logic to support universal target and update tests * feat: update target determination logic to treat universal targets as web targets * test: add support for universal target in tests and HTML generation * feat: implement EventTarget-based emitter for universal targets in webpack * test: enhance ESM/universal target handling in snapshots for deterministic output * fix: update emitter resolution to use fileURLToPath for compatibility with newer webpack * fix: update emitter resolution to use cjsRequire for compatibility with older webpack versions
1 parent 2236aa4 commit 4c351e1

9 files changed

Lines changed: 291 additions & 139 deletions

File tree

.changeset/use-compiler-platform-target.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
"webpack-dev-server": minor
33
---
44

5-
Use `compiler.platform` to determine the target environment instead of inspecting the resolved `target` string.
5+
Use `compiler.platform` to determine the target environment instead of inspecting the resolved `target` string. Universal targets (`"universal"` or `["web", "node"]`, where `compiler.platform.universal` is `true` since webpack `5.108.0`) are treated as web targets so the client runtime is injected.

lib/Server.js

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -574,10 +574,20 @@ class Server {
574574
/**
575575
* @private
576576
* @param {Compiler} compiler compiler
577-
* @returns {boolean} true when target is `web`, otherwise false
577+
* @returns {boolean} true when target is `web` or `universal`, otherwise false
578578
*/
579579
static isWebTarget(compiler) {
580-
return compiler.platform.web || false;
580+
const { platform } = compiler;
581+
582+
// A `web` or universal target (`web` and `node` both `null`) injects the
583+
// client. `target: false` is `null` everywhere, so it is excluded.
584+
return Boolean(
585+
platform.web ||
586+
platform?.universal ||
587+
(compiler.options.target !== false &&
588+
platform.web === null &&
589+
platform.node === null),
590+
);
581591
}
582592

583593
/**
@@ -1683,6 +1693,38 @@ class Server {
16831693
__webpack_dev_server_client__: this.getClientTransport(),
16841694
}).apply(compiler);
16851695

1696+
// For universal targets `webpack/hot/emitter` uses Node's `events`,
1697+
// which breaks in the browser. Swap it for webpack's `EventTarget`
1698+
// emitter when available.
1699+
if (
1700+
compiler.options.output.module &&
1701+
compiler.platform.web === null &&
1702+
compiler.platform.node === null
1703+
) {
1704+
let emitter;
1705+
1706+
try {
1707+
emitter = cjsRequire.resolve("webpack/hot/emitter-event-target.js");
1708+
} catch {
1709+
// older webpack versions do not ship the `EventTarget` emitter
1710+
}
1711+
1712+
if (emitter) {
1713+
new webpack.NormalModuleReplacementPlugin(
1714+
/emitter(\.js)?$/,
1715+
(result) => {
1716+
if (
1717+
/webpack[/\\]hot|webpack-dev-server[/\\]client/.test(
1718+
result.context,
1719+
)
1720+
) {
1721+
result.request = emitter;
1722+
}
1723+
},
1724+
).apply(compiler);
1725+
}
1726+
}
1727+
16861728
if (this.options.hot) {
16871729
const HMRPluginExists = compiler.options.plugins.find(
16881730
(plugin) =>

0 commit comments

Comments
 (0)