@rspack/dev-server 0.0.21 → 0.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/LICENSE +1 -1
  3. package/dist/config.d.ts +19 -14
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +0 -52
  6. package/dist/config.js.map +1 -1
  7. package/dist/server.d.ts +14 -41
  8. package/dist/server.d.ts.map +1 -1
  9. package/dist/server.js +308 -257
  10. package/dist/server.js.map +1 -1
  11. package/jest.config.js +3 -1
  12. package/package.json +10 -9
  13. package/src/config.ts +21 -67
  14. package/src/server.ts +350 -311
  15. package/tests/__snapshots__/normalizeOptions.test.ts.snap +223 -24
  16. package/tests/e2e/hot-reaload.test.ts +35 -39
  17. package/tests/e2e-fixtures/react/node_modules/react/LICENSE +21 -0
  18. package/tests/e2e-fixtures/react/node_modules/react/README.md +37 -0
  19. package/tests/e2e-fixtures/react/node_modules/react/cjs/react-jsx-dev-runtime.development.js +1296 -0
  20. package/tests/e2e-fixtures/react/node_modules/react/cjs/react-jsx-dev-runtime.production.min.js +10 -0
  21. package/tests/e2e-fixtures/react/node_modules/react/cjs/react-jsx-dev-runtime.profiling.min.js +10 -0
  22. package/tests/e2e-fixtures/react/node_modules/react/cjs/react-jsx-runtime.development.js +1314 -0
  23. package/tests/e2e-fixtures/react/node_modules/react/cjs/react-jsx-runtime.production.min.js +11 -0
  24. package/tests/e2e-fixtures/react/node_modules/react/cjs/react-jsx-runtime.profiling.min.js +11 -0
  25. package/tests/e2e-fixtures/react/node_modules/react/cjs/react.development.js +2739 -0
  26. package/tests/e2e-fixtures/react/node_modules/react/cjs/react.production.min.js +26 -0
  27. package/tests/e2e-fixtures/react/node_modules/react/cjs/react.shared-subset.development.js +20 -0
  28. package/tests/e2e-fixtures/react/node_modules/react/cjs/react.shared-subset.production.min.js +10 -0
  29. package/tests/e2e-fixtures/react/node_modules/react/index.js +7 -0
  30. package/tests/e2e-fixtures/react/node_modules/react/jsx-dev-runtime.js +7 -0
  31. package/tests/e2e-fixtures/react/node_modules/react/jsx-runtime.js +7 -0
  32. package/tests/e2e-fixtures/react/node_modules/react/package.json +47 -0
  33. package/tests/e2e-fixtures/react/node_modules/react/react.shared-subset.js +7 -0
  34. package/tests/e2e-fixtures/react/node_modules/react/umd/react.development.js +3342 -0
  35. package/tests/e2e-fixtures/react/node_modules/react/umd/react.production.min.js +31 -0
  36. package/tests/e2e-fixtures/react/node_modules/react/umd/react.profiling.min.js +31 -0
  37. package/tests/e2e-fixtures/react/node_modules/react-dom/LICENSE +21 -0
  38. package/tests/e2e-fixtures/react/node_modules/react-dom/README.md +60 -0
  39. package/tests/e2e-fixtures/react/node_modules/react-dom/cjs/react-dom-server-legacy.browser.development.js +7018 -0
  40. package/tests/e2e-fixtures/react/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +93 -0
  41. package/tests/e2e-fixtures/react/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js +7078 -0
  42. package/tests/e2e-fixtures/react/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.min.js +101 -0
  43. package/tests/e2e-fixtures/react/node_modules/react-dom/cjs/react-dom-server.browser.development.js +7003 -0
  44. package/tests/e2e-fixtures/react/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js +96 -0
  45. package/tests/e2e-fixtures/react/node_modules/react-dom/cjs/react-dom-server.node.development.js +7059 -0
  46. package/tests/e2e-fixtures/react/node_modules/react-dom/cjs/react-dom-server.node.production.min.js +102 -0
  47. package/tests/e2e-fixtures/react/node_modules/react-dom/cjs/react-dom-test-utils.development.js +1741 -0
  48. package/tests/e2e-fixtures/react/node_modules/react-dom/cjs/react-dom-test-utils.production.min.js +40 -0
  49. package/tests/e2e-fixtures/react/node_modules/react-dom/cjs/react-dom.development.js +29868 -0
  50. package/tests/e2e-fixtures/react/node_modules/react-dom/cjs/react-dom.production.min.js +323 -0
  51. package/tests/e2e-fixtures/react/node_modules/react-dom/cjs/react-dom.profiling.min.js +367 -0
  52. package/tests/e2e-fixtures/react/node_modules/react-dom/client.js +25 -0
  53. package/tests/e2e-fixtures/react/node_modules/react-dom/index.js +38 -0
  54. package/tests/e2e-fixtures/react/node_modules/react-dom/package.json +62 -0
  55. package/tests/e2e-fixtures/react/node_modules/react-dom/profiling.js +38 -0
  56. package/tests/e2e-fixtures/react/node_modules/react-dom/server.browser.js +17 -0
  57. package/tests/e2e-fixtures/react/node_modules/react-dom/server.js +3 -0
  58. package/tests/e2e-fixtures/react/node_modules/react-dom/server.node.js +17 -0
  59. package/tests/e2e-fixtures/react/node_modules/react-dom/test-utils.js +7 -0
  60. package/tests/e2e-fixtures/react/node_modules/react-dom/umd/react-dom-server-legacy.browser.development.js +7015 -0
  61. package/tests/e2e-fixtures/react/node_modules/react-dom/umd/react-dom-server-legacy.browser.production.min.js +75 -0
  62. package/tests/e2e-fixtures/react/node_modules/react-dom/umd/react-dom-server.browser.development.js +7000 -0
  63. package/tests/e2e-fixtures/react/node_modules/react-dom/umd/react-dom-server.browser.production.min.js +76 -0
  64. package/tests/e2e-fixtures/react/node_modules/react-dom/umd/react-dom-test-utils.development.js +1737 -0
  65. package/tests/e2e-fixtures/react/node_modules/react-dom/umd/react-dom-test-utils.production.min.js +33 -0
  66. package/tests/e2e-fixtures/react/node_modules/react-dom/umd/react-dom.development.js +29869 -0
  67. package/tests/e2e-fixtures/react/node_modules/react-dom/umd/react-dom.production.min.js +267 -0
  68. package/tests/e2e-fixtures/react/node_modules/react-dom/umd/react-dom.profiling.min.js +285 -0
  69. package/tests/e2e-fixtures/react/node_modules/scheduler/LICENSE +21 -0
  70. package/tests/e2e-fixtures/react/node_modules/scheduler/README.md +9 -0
  71. package/tests/e2e-fixtures/react/node_modules/scheduler/cjs/scheduler-unstable_mock.development.js +700 -0
  72. package/tests/e2e-fixtures/react/node_modules/scheduler/cjs/scheduler-unstable_mock.production.min.js +20 -0
  73. package/tests/e2e-fixtures/react/node_modules/scheduler/cjs/scheduler-unstable_post_task.development.js +207 -0
  74. package/tests/e2e-fixtures/react/node_modules/scheduler/cjs/scheduler-unstable_post_task.production.min.js +14 -0
  75. package/tests/e2e-fixtures/react/node_modules/scheduler/cjs/scheduler.development.js +634 -0
  76. package/tests/e2e-fixtures/react/node_modules/scheduler/cjs/scheduler.production.min.js +19 -0
  77. package/tests/e2e-fixtures/react/node_modules/scheduler/index.js +7 -0
  78. package/tests/e2e-fixtures/react/node_modules/scheduler/package.json +36 -0
  79. package/tests/e2e-fixtures/react/node_modules/scheduler/umd/scheduler-unstable_mock.development.js +699 -0
  80. package/tests/e2e-fixtures/react/node_modules/scheduler/umd/scheduler-unstable_mock.production.min.js +19 -0
  81. package/tests/e2e-fixtures/react/node_modules/scheduler/umd/scheduler.development.js +152 -0
  82. package/tests/e2e-fixtures/react/node_modules/scheduler/umd/scheduler.production.min.js +146 -0
  83. package/tests/e2e-fixtures/react/node_modules/scheduler/umd/scheduler.profiling.min.js +146 -0
  84. package/tests/e2e-fixtures/react/node_modules/scheduler/unstable_mock.js +7 -0
  85. package/tests/e2e-fixtures/react/node_modules/scheduler/unstable_post_task.js +7 -0
  86. package/tests/e2e-fixtures/react/package.json +2 -0
  87. package/tests/e2e-fixtures/react/webpack.config.js +6 -2
  88. package/tests/helpers/emitFile.ts +58 -4
  89. package/tests/normalizeOptions.test.ts +58 -16
  90. package/tsconfig.tsbuildinfo +1 -1
  91. package/dist/logger.d.ts +0 -7
  92. package/dist/logger.d.ts.map +0 -1
  93. package/dist/logger.js +0 -28
  94. package/dist/logger.js.map +0 -1
  95. package/dist/ws.d.ts +0 -13
  96. package/dist/ws.d.ts.map +0 -1
  97. package/dist/ws.js +0 -12
  98. package/dist/ws.js.map +0 -1
  99. package/src/logger.ts +0 -30
  100. package/src/ws.ts +0 -18
package/src/server.ts CHANGED
@@ -1,154 +1,207 @@
1
- import type { Compiler, Dev, RspackOptionsNormalized } from "@rspack/core";
2
- import type { Logger } from "./logger";
1
+ import { Compiler, MultiCompiler } from "@rspack/core";
3
2
  import type { Socket } from "net";
4
3
  import type { FSWatcher, WatchOptions } from "chokidar";
5
- import type { WebSocketServer, ClientConnection } from "./ws";
6
- import type {
7
- Application,
8
- RequestHandler as ExpressRequestHandler,
9
- ErrorRequestHandler as ExpressErrorRequestHandler
10
- } from "express";
11
- import type { DevMiddleware } from "@rspack/dev-middleware";
4
+ import rdm, { getRspackMemoryAssets } from "@rspack/dev-middleware";
12
5
  import type { Server } from "http";
13
- import type { ResolvedDev } from "./config";
14
6
  import fs from "fs";
15
- import chokidar from "chokidar";
16
- import http from "http";
17
- import { createLogger } from "./logger";
18
7
  import WebpackDevServer from "webpack-dev-server";
19
- import express from "express";
8
+ import type { ResolvedDevServer, DevServer } from "./config";
20
9
 
21
- import rdm from "@rspack/dev-middleware";
22
- import { createWebsocketServer } from "./ws";
23
- import { resolveDevOptions } from "./config";
24
-
25
- interface Middleware {
26
- name?: string;
27
- path?: string;
28
- middleware: ExpressErrorRequestHandler | ExpressRequestHandler;
29
- }
30
- interface Listener {
31
- name: string | Symbol;
32
- listener: (...args: any) => void;
33
- }
34
- type Host = "local-ip" | "local-ipv4" | "local-ipv6" | string;
35
- type Port = number | string | "auto";
36
-
37
- // copy from webpack-dev-server
38
- export class RspackDevServer {
39
- options: ResolvedDev;
40
- logger: Logger;
10
+ export class RspackDevServer extends WebpackDevServer {
11
+ /**
12
+ * resolved after `normalizedOptions`
13
+ */
14
+ options: ResolvedDevServer;
41
15
  staticWatchers: FSWatcher[];
42
16
  sockets: Socket[];
43
- app: Application;
44
17
  server: Server;
45
- private listeners: Listener[];
46
- private currentHash: string;
47
- private middleware: DevMiddleware | undefined;
48
- // TODO: now only support 'ws'
49
- webSocketServer: WebSocketServer | undefined;
50
-
51
- constructor(public compiler: Compiler) {
52
- this.logger = createLogger("rspack-dev-server");
53
- this.staticWatchers = [];
54
- this.listeners = [];
55
- this.sockets = [];
56
- this.currentHash = "";
57
- this.options = this.normalizeOptions(compiler.options.devServer);
58
- this.rewriteCompilerOptions();
59
- this.addAdditionEntires();
60
- }
18
+ // @ts-expect-error
19
+ public compiler: Compiler | MultiCompiler;
20
+ webSocketServer: WebpackDevServer.WebSocketServerImplementation | undefined;
61
21
 
62
- normalizeOptions(dev: Dev = {}) {
63
- return resolveDevOptions(dev, this.compiler.options);
22
+ constructor(options: DevServer, compiler: Compiler | MultiCompiler) {
23
+ // @ts-expect-error
24
+ super(options, compiler);
64
25
  }
65
26
 
66
- rewriteCompilerOptions() {
67
- this.compiler.options.devServer = this.options;
68
- if (!this.compiler.options.builtins.react) {
69
- this.compiler.options.builtins.react = {};
70
- }
71
- this.compiler.options.builtins.react.development =
72
- this.compiler.options.builtins.react.development ?? true;
73
- if (this.options.hot) {
74
- this.compiler.options.builtins.react.refresh =
75
- this.compiler.options.builtins.react.refresh ?? true;
76
- }
77
- }
27
+ addAdditionEntires(compiler: Compiler) {
28
+ const additionalEntries: string[] = [];
29
+ // TODO: align with webpack-dev-server after options.target is aligned
30
+ const isWebTarget =
31
+ compiler.options.target.includes("web") ||
32
+ compiler.options.target.includes("webworker");
33
+ if (this.options.client && isWebTarget) {
34
+ let webSocketURLStr = "";
35
+
36
+ if (this.options.webSocketServer) {
37
+ const webSocketURL = this.options.client
38
+ .webSocketURL as WebpackDevServer.WebSocketURL;
39
+ const webSocketServer = this.options.webSocketServer;
40
+ const searchParams = new URLSearchParams();
41
+
42
+ let protocol: string;
43
+
44
+ // We are proxying dev server and need to specify custom `hostname`
45
+ if (typeof webSocketURL.protocol !== "undefined") {
46
+ protocol = webSocketURL.protocol;
47
+ } else {
48
+ protocol = this.options.server.type === "http" ? "ws:" : "wss:";
49
+ }
78
50
 
79
- addAdditionEntires() {
80
- const entries: string[] = [];
51
+ searchParams.set("protocol", protocol);
81
52
 
82
- if (this.options.hot) {
83
- const hotUpdateEntryPath = require.resolve(
84
- "@rspack/dev-client/devServer"
85
- );
86
- entries.push(hotUpdateEntryPath);
53
+ if (typeof webSocketURL.username !== "undefined") {
54
+ searchParams.set("username", webSocketURL.username);
55
+ }
87
56
 
88
- if (this.compiler.options.builtins.react?.refresh) {
89
- const reactRefreshEntryPath = require.resolve(
90
- "@rspack/dev-client/react-refresh"
91
- );
92
- entries.push(reactRefreshEntryPath);
93
- }
94
- }
57
+ if (typeof webSocketURL.password !== "undefined") {
58
+ searchParams.set("password", webSocketURL.password);
59
+ }
95
60
 
96
- const devClientEntryPath = require.resolve("@rspack/dev-client");
97
- entries.push(devClientEntryPath);
98
- for (const key in this.compiler.options.entry) {
99
- this.compiler.options.entry[key].import.unshift(...entries);
100
- }
101
- }
61
+ let hostname: string;
102
62
 
103
- static isAbsoluteURL(URL: string): boolean {
104
- return WebpackDevServer.isAbsoluteURL(URL);
105
- }
63
+ // SockJS is not supported server mode, so `hostname` and `port` can't specified, let's ignore them
64
+ // TODO show warning about this
65
+ const isSockJSType = webSocketServer.type === "sockjs";
106
66
 
107
- static findIp(gateway: string): string | undefined {
108
- return WebpackDevServer.findIp(gateway);
109
- }
67
+ // We are proxying dev server and need to specify custom `hostname`
68
+ if (typeof webSocketURL.hostname !== "undefined") {
69
+ hostname = webSocketURL.hostname;
70
+ }
71
+ // Web socket server works on custom `hostname`, only for `ws` because `sock-js` is not support custom `hostname`
72
+ else if (
73
+ typeof webSocketServer.options.host !== "undefined" &&
74
+ !isSockJSType
75
+ ) {
76
+ hostname = webSocketServer.options.host;
77
+ }
78
+ // The `host` option is specified
79
+ else if (typeof this.options.host !== "undefined") {
80
+ hostname = this.options.host;
81
+ }
82
+ // The `port` option is not specified
83
+ else {
84
+ hostname = "0.0.0.0";
85
+ }
110
86
 
111
- static async internalIP(family: "v6" | "v4"): Promise<string | undefined> {
112
- return WebpackDevServer.internalIP(family);
113
- }
87
+ searchParams.set("hostname", hostname);
114
88
 
115
- static async internalIPSync(
116
- family: "v6" | "v4"
117
- ): Promise<string | undefined> {
118
- return WebpackDevServer.internalIPSync(family);
119
- }
89
+ let port: number | string;
120
90
 
121
- static async getHostname(hostname?: Host): Promise<string> {
122
- return WebpackDevServer.getHostname(hostname);
123
- }
91
+ // We are proxying dev server and need to specify custom `port`
92
+ if (typeof webSocketURL.port !== "undefined") {
93
+ port = webSocketURL.port;
94
+ }
95
+ // Web socket server works on custom `port`, only for `ws` because `sock-js` is not support custom `port`
96
+ else if (
97
+ typeof webSocketServer.options.port !== "undefined" &&
98
+ !isSockJSType
99
+ ) {
100
+ port = webSocketServer.options.port;
101
+ }
102
+ // The `port` option is specified
103
+ else if (typeof this.options.port === "number") {
104
+ port = this.options.port;
105
+ }
106
+ // The `port` option is specified using `string`
107
+ else if (
108
+ typeof this.options.port === "string" &&
109
+ this.options.port !== "auto"
110
+ ) {
111
+ port = Number(this.options.port);
112
+ }
113
+ // The `port` option is not specified or set to `auto`
114
+ else {
115
+ port = "0";
116
+ }
124
117
 
125
- static async getFreePort(port: Port, host: string): Promise<string | number> {
126
- return WebpackDevServer.getFreePort(port, host);
127
- }
118
+ searchParams.set("port", String(port));
128
119
 
129
- static findCacheDir(): string {
130
- // TODO: we need remove the `webpack-dev-server` tag in WebpackDevServer;
131
- return "";
132
- }
120
+ let pathname = "";
133
121
 
134
- private getCompilerOptions(): RspackOptionsNormalized {
135
- return this.compiler.options;
136
- }
122
+ // We are proxying dev server and need to specify custom `pathname`
123
+ if (typeof webSocketURL.pathname !== "undefined") {
124
+ pathname = webSocketURL.pathname;
125
+ }
126
+ // Web socket server works on custom `path`
127
+ else if (
128
+ typeof webSocketServer.options.prefix !== "undefined" ||
129
+ typeof webSocketServer.options.path !== "undefined"
130
+ ) {
131
+ pathname =
132
+ webSocketServer.options.prefix || webSocketServer.options.path;
133
+ }
134
+
135
+ searchParams.set("pathname", pathname);
136
+
137
+ const client = /** @type {ClientConfiguration} */ this.options.client;
138
+
139
+ if (typeof client.logging !== "undefined") {
140
+ searchParams.set("logging", client.logging);
141
+ }
142
+
143
+ if (typeof client.progress !== "undefined") {
144
+ searchParams.set("progress", String(client.progress));
145
+ }
146
+
147
+ if (typeof client.overlay !== "undefined") {
148
+ searchParams.set(
149
+ "overlay",
150
+ typeof client.overlay === "boolean"
151
+ ? String(client.overlay)
152
+ : JSON.stringify(client.overlay)
153
+ );
154
+ }
137
155
 
138
- sendMessage(
139
- clients: ClientConnection[],
140
- type: string,
141
- data?: any,
142
- params?: any
143
- ) {
144
- for (const client of clients) {
145
- if (client.readyState === 1) {
146
- client.send(JSON.stringify({ type, data, params }));
156
+ if (typeof client.reconnect !== "undefined") {
157
+ searchParams.set(
158
+ "reconnect",
159
+ typeof client.reconnect === "number"
160
+ ? String(client.reconnect)
161
+ : "10"
162
+ );
163
+ }
164
+
165
+ if (typeof this.options.hot !== "undefined") {
166
+ searchParams.set("hot", String(this.options.hot));
167
+ }
168
+
169
+ if (typeof this.options.liveReload !== "undefined") {
170
+ searchParams.set("live-reload", String(this.options.liveReload));
171
+ }
172
+
173
+ webSocketURLStr = searchParams.toString();
147
174
  }
175
+
176
+ // TODO: should use providerPlugin
177
+ additionalEntries.push(this.getClientTransport());
178
+
179
+ additionalEntries.push(
180
+ `${require.resolve("@rspack/dev-client")}?${webSocketURLStr}`
181
+ );
182
+ }
183
+
184
+ if (this.options.hot) {
185
+ const hotUpdateEntryPath = require.resolve(
186
+ "@rspack/dev-client/devServer"
187
+ );
188
+ additionalEntries.push(hotUpdateEntryPath);
189
+
190
+ if (compiler.options.builtins.react?.refresh) {
191
+ const reactRefreshEntryPath = require.resolve(
192
+ "@rspack/dev-client/react-refresh"
193
+ );
194
+ additionalEntries.push(reactRefreshEntryPath);
195
+ }
196
+ }
197
+
198
+ for (const key in compiler.options.entry) {
199
+ compiler.options.entry[key].import.unshift(...additionalEntries);
148
200
  }
149
201
  }
150
202
 
151
203
  watchFiles(watchPath: string | string[], watchOptions?: WatchOptions): void {
204
+ const chokidar = require("chokidar");
152
205
  const watcher = chokidar.watch(watchPath, watchOptions);
153
206
 
154
207
  // disabling refreshing on changing the content
@@ -172,80 +225,154 @@ export class RspackDevServer {
172
225
  this.staticWatchers.push(watcher);
173
226
  }
174
227
 
175
- invalidate(callback = () => {}): void {
176
- if (this.middleware) {
177
- this.middleware.invalidate(callback);
228
+ getClientTransport(): string {
229
+ // WARNING: we can't use `super.getClientTransport`,
230
+ // because we doesn't had same directory structure.
231
+ // and TODO: we need impelement `webpack.providerPlugin`
232
+ let clientImplementation: string | undefined;
233
+ let clientImplementationFound = true;
234
+ const isKnownWebSocketServerImplementation =
235
+ this.options.webSocketServer &&
236
+ typeof this.options.webSocketServer.type === "string" &&
237
+ (this.options.webSocketServer.type === "ws" ||
238
+ this.options.webSocketServer.type === "sockjs");
239
+
240
+ let clientTransport: string | undefined;
241
+
242
+ if (this.options.client) {
243
+ if (
244
+ // @ts-ignore
245
+ typeof this.options.client.webSocketTransport !== "undefined"
246
+ ) {
247
+ // @ts-ignore
248
+ clientTransport = this.options.client.webSocketTransport;
249
+ } else if (isKnownWebSocketServerImplementation) {
250
+ // @ts-ignore
251
+ clientTransport = this.options.webSocketServer.type;
252
+ } else {
253
+ clientTransport = "ws";
254
+ }
255
+ } else {
256
+ clientTransport = "ws";
257
+ }
258
+
259
+ switch (typeof clientTransport) {
260
+ case "string":
261
+ // could be 'sockjs', 'ws', or a path that should be required
262
+ if (clientTransport === "sockjs") {
263
+ clientImplementation = require.resolve(
264
+ "@rspack/dev-client/clients/SockJSClient"
265
+ );
266
+ } else if (clientTransport === "ws") {
267
+ clientImplementation = require.resolve(
268
+ "@rspack/dev-client/clients/WebSocketClient"
269
+ );
270
+ } else {
271
+ try {
272
+ clientImplementation = require.resolve(clientTransport);
273
+ throw Error("Do not support custom ws client now");
274
+ } catch (e) {
275
+ clientImplementationFound = false;
276
+ }
277
+ }
278
+ break;
279
+ default:
280
+ clientImplementationFound = false;
281
+ }
282
+ if (!clientImplementationFound) {
283
+ throw new Error(
284
+ `${
285
+ !isKnownWebSocketServerImplementation
286
+ ? "When you use custom web socket implementation you must explicitly specify client.webSocketTransport. "
287
+ : ""
288
+ }client.webSocketTransport must be a string denoting a default implementation (e.g. 'sockjs', 'ws') or a full path to a JS file via require.resolve(...) which exports a class `
289
+ );
178
290
  }
291
+
292
+ return clientImplementation;
179
293
  }
180
294
 
181
- async start(): Promise<void> {
295
+ async initialize() {
296
+ const compilers =
297
+ this.compiler instanceof MultiCompiler
298
+ ? this.compiler.compilers
299
+ : [this.compiler];
300
+
301
+ compilers.forEach(compiler => {
302
+ compiler.options.builtins.react ??= {};
303
+ if (this.options.hot) {
304
+ compiler.options.builtins.react.refresh ??= true;
305
+ compiler.options.builtins.react.development ??= true;
306
+ } else if (compiler.options.builtins.react.refresh) {
307
+ this.logger.warn(
308
+ "builtins.react.refresh needs builtins.react.development and devServer.hot enabled"
309
+ );
310
+ }
311
+ });
312
+
313
+ if (this.options.webSocketServer) {
314
+ compilers.forEach(compiler => {
315
+ this.addAdditionEntires(compiler);
316
+ });
317
+ }
318
+
182
319
  this.setupHooks();
320
+ // @ts-expect-error: `setupApp` is private function in base class.
183
321
  this.setupApp();
184
- this.createServer();
185
- this.setupWatchStaticFiles();
186
- this.createWebsocketServer();
322
+ // @ts-expect-error: `setupHostHeaderCheck` is private function in base class.
323
+ this.setupHostHeaderCheck();
187
324
  this.setupDevMiddleware();
325
+ // @ts-expect-error: `setupBuiltInRoutes` is private function in base class.
326
+ this.setupBuiltInRoutes();
327
+ // @ts-expect-error: `setupWatchFiles` is private function in base class.
328
+ this.setupWatchFiles();
329
+ // @ts-expect-error: `setupWatchStaticFiles` is private function in base class.
330
+ this.setupWatchStaticFiles();
188
331
  this.setupMiddlewares();
189
- const host = await RspackDevServer.getHostname(this.options.host);
190
- const port = await RspackDevServer.getFreePort(this.options.port, host);
191
- this.options.port = port;
192
- await new Promise(resolve =>
193
- this.server.listen(
194
- {
195
- port,
196
- host
197
- },
198
- () => {
199
- this.logger.info(`Loopback: http://localhost:${port}`);
200
- let internalIPv4 = WebpackDevServer.internalIPSync("v4");
332
+ // @ts-expect-error: `createServer` is private function in base class.
333
+ this.createServer();
334
+
335
+ if (this.options.setupExitSignals) {
336
+ const signals = ["SIGINT", "SIGTERM"];
337
+
338
+ let needForceShutdown = false;
339
+
340
+ signals.forEach(signal => {
341
+ const listener = () => {
342
+ if (needForceShutdown) {
343
+ process.exit();
344
+ }
345
+
201
346
  this.logger.info(
202
- `Your Network (IPV4) http://${internalIPv4}:${port}`
347
+ "Gracefully shutting down. To force exit, press ^C again. Please wait..."
203
348
  );
204
- resolve({});
205
- }
206
- )
207
- );
208
- }
209
349
 
210
- startCallback(callback?: (err?: Error) => void): void {
211
- throw new Error("Method not implemented.");
212
- }
213
- stopCallback(callback?: (err?: Error) => void): void {
214
- throw new Error("Method not implemented.");
215
- }
216
- listen(port: Port, hostname: string, fn: (err?: Error) => void): void {
217
- throw new Error("Method not implemented.");
218
- }
219
- close(callback?: (err?: Error) => void): void {
220
- throw new Error("Method not implemented.");
221
- }
350
+ needForceShutdown = true;
222
351
 
223
- async stop(): Promise<void> {
224
- await Promise.all(this.staticWatchers.map(watcher => watcher.close()));
225
- this.middleware = null;
226
- this.staticWatchers = [];
227
- if (this.server) {
228
- this.server.close();
229
- }
230
- if (this.webSocketServer) {
231
- await new Promise(resolve => {
232
- this.webSocketServer.implementation.close(() => {
233
- resolve(void 0);
234
- });
235
- for (const client of this.webSocketServer.clients) client.terminate();
236
- });
237
- }
238
- }
352
+ this.stopCallback(() => {
353
+ if (typeof this.compiler.close === "function") {
354
+ this.compiler.close(() => {
355
+ process.exit();
356
+ });
357
+ } else {
358
+ process.exit();
359
+ }
360
+ });
361
+ };
239
362
 
240
- private setupApp() {
241
- this.app = express();
242
- }
363
+ // @ts-expect-error: `listeners` is private function in base class.
364
+ this.listeners.push({ name: signal, listener });
243
365
 
244
- private setupWatchStaticFiles() {
245
- if (this.options.static.watch === false) {
246
- return;
366
+ process.on(signal, listener);
367
+ });
247
368
  }
248
- this.watchFiles(this.options.static.directory, this.options.static.watch);
369
+
370
+ // Proxy WebSocket without the initial http request
371
+ // https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade
372
+ // @ts-expect-error: `webSocketProxies` is private function in base class.
373
+ this.webSocketProxies.forEach(webSocketProxy => {
374
+ this.server.on("upgrade", webSocketProxy.upgrade);
375
+ }, this);
249
376
  }
250
377
 
251
378
  private setupDevMiddleware() {
@@ -253,151 +380,63 @@ export class RspackDevServer {
253
380
  this.middleware = rdm(this.compiler, this.options.devMiddleware);
254
381
  }
255
382
 
256
- private createWebsocketServer() {
257
- if (this.options.webSocketServer !== false) {
258
- this.webSocketServer = createWebsocketServer(this);
259
- }
260
- }
261
-
262
383
  private setupMiddlewares() {
263
- const options = this.options;
264
- const middlewares: Middleware[] = [];
265
- middlewares.push({
266
- name: "rdm",
267
- middleware: this.middleware
268
- });
269
-
270
- if (this.compiler.options.experiments.lazyCompilation) {
271
- middlewares.push({
272
- middleware: (req, res, next) => {
273
- if (req.url.indexOf("/lazy-compilation-web/") > -1) {
274
- const path = req.url.replace("/lazy-compilation-web/", "");
275
- if (fs.existsSync(path)) {
276
- this.compiler.rebuild([path], (error, stats) => {
277
- if (error) {
278
- throw error;
279
- }
280
- res.write("");
281
- res.end();
282
- console.log("lazy compiler success");
384
+ const middlewares: WebpackDevServer.Middleware[] = [];
385
+ const compilers =
386
+ this.compiler instanceof MultiCompiler
387
+ ? this.compiler.compilers
388
+ : [this.compiler];
389
+
390
+ if (Array.isArray(this.options.static)) {
391
+ this.options.static.forEach(staticOptions => {
392
+ staticOptions.publicPath.forEach(publicPath => {
393
+ compilers.forEach(compiler => {
394
+ if (compiler.options.builtins.noEmitAssets) {
395
+ middlewares.push({
396
+ name: "rspack-memory-assets",
397
+ path: publicPath,
398
+ middleware: getRspackMemoryAssets(compiler, this.middleware)
283
399
  });
284
400
  }
285
- }
286
- }
401
+ });
402
+ });
287
403
  });
288
404
  }
289
405
 
290
- // Todo Add options
291
- const connectHistoryApiFallback = require("connect-history-api-fallback");
292
- middlewares.push({
293
- name: "[connect-history-api-fallback]",
294
- middleware: connectHistoryApiFallback({
295
- verbose: true,
296
- logger: console.log.bind(console)
297
- })
298
- });
299
- /**
300
- * supports three kinds of proxy configuration
301
- * {context: 'xxxx', target: 'yyy}
302
- * {['xxx']: { target: 'yyy}}
303
- * [{context: 'xxx',target:'yyy'}, {context: 'aaa', target: 'zzzz'}]
304
- */
305
- if (typeof options.proxy !== "undefined") {
306
- const { createProxyMiddleware } = require("http-proxy-middleware");
307
- function getProxyMiddleware(proxyConfig) {
308
- if (proxyConfig.target) {
309
- const context = proxyConfig.context || proxyConfig.path;
310
- return createProxyMiddleware(context, proxyConfig);
311
- }
312
- if (proxyConfig.router) {
313
- return createProxyMiddleware(proxyConfig);
314
- }
315
- }
316
- if (!Array.isArray(options.proxy)) {
317
- if (
318
- Object.prototype.hasOwnProperty.call(options.proxy, "target") ||
319
- Object.prototype.hasOwnProperty.call(options.proxy, "router")
320
- ) {
321
- options.proxy = [options.proxy];
322
- } else {
323
- options.proxy = Object.keys(options.proxy).map(context => {
324
- let proxyOptions;
325
- // For backwards compatibility reasons.
326
- const correctedContext = context
327
- .replace(/^\*$/, "**")
328
- .replace(/\/\*$/, "");
329
-
330
- if (
331
- typeof (/** @type {ProxyConfigMap} */ options.proxy[context]) ===
332
- "string"
333
- ) {
334
- proxyOptions = {
335
- context: correctedContext,
336
- target:
337
- /** @type {ProxyConfigMap} */
338
- options.proxy[context]
339
- };
340
- } else {
341
- proxyOptions = {
342
- // @ts-ignore
343
- .../** @type {ProxyConfigMap} */ options.proxy[context]
344
- };
345
- proxyOptions.context = correctedContext;
406
+ compilers.forEach(compiler => {
407
+ if (compiler.options.experiments.lazyCompilation) {
408
+ middlewares.push({
409
+ middleware: (req, res, next) => {
410
+ if (req.url.indexOf("/lazy-compilation-web/") > -1) {
411
+ const path = req.url.replace("/lazy-compilation-web/", "");
412
+ if (fs.existsSync(path)) {
413
+ compiler.rebuild(new Set([path]), new Set(), error => {
414
+ if (error) {
415
+ throw error;
416
+ }
417
+ res.write("");
418
+ res.end();
419
+ console.log("lazy compiler success");
420
+ });
421
+ }
346
422
  }
347
-
348
- return proxyOptions;
349
- });
350
- }
351
- }
352
- options.proxy.forEach(proxyConfig => {
353
- const handler = async (req, res, next) => {
354
- let proxyMiddleware = getProxyMiddleware(proxyConfig);
355
- const isByPassFuncDefined = typeof proxyConfig.bypass === "function";
356
- const bypassUrl = isByPassFuncDefined
357
- ? await proxyConfig.bypass(req, res, proxyConfig)
358
- : null;
359
- if (typeof bypassUrl === "boolean") {
360
- req.url = null;
361
- next();
362
- } else if (typeof bypassUrl === "string") {
363
- req.url = bypassUrl;
364
- } else if (proxyMiddleware) {
365
- return proxyMiddleware(req, res, next);
366
- } else {
367
- next();
368
423
  }
369
- };
370
- middlewares.push({
371
- name: "http-proxy-middleware",
372
- middleware: handler
373
- });
374
- middlewares.push({
375
- name: "http-proxy-middleware-error-handler",
376
- middleware: (error, req, res, next) => handler(req, res, next)
377
424
  });
378
- });
379
- }
380
- const publicPath =
381
- this.compiler.options.output.publicPath === "auto"
382
- ? ""
383
- : this.compiler.options.output.publicPath;
384
- middlewares.push({
385
- name: "express-static",
386
- path: publicPath,
387
- middleware: express.static(this.options.static.directory)
425
+ }
388
426
  });
389
427
 
390
- middlewares.forEach(m => {
391
- if (m.path) {
392
- this.app.use(m.path, m.middleware);
428
+ middlewares.forEach(middleware => {
429
+ if (typeof middleware === "function") {
430
+ this.app.use(middleware);
431
+ } else if (typeof middleware.path !== "undefined") {
432
+ this.app.use(middleware.path, middleware.middleware);
393
433
  } else {
394
- this.app.use(m.middleware);
434
+ this.app.use(middleware.middleware);
395
435
  }
396
436
  });
397
- }
398
437
 
399
- private createServer() {
400
- this.server = http.createServer(this.app);
438
+ // @ts-expect-error
439
+ super.setupMiddlewares();
401
440
  }
402
441
 
403
442
  private setupHooks() {