@rspack/dev-server 0.0.21 → 0.0.23

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 (48) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/LICENSE +2 -1
  3. package/dist/config.d.ts +20 -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 +330 -257
  10. package/dist/server.js.map +1 -1
  11. package/jest.config.js +3 -1
  12. package/package.json +14 -12
  13. package/src/config.ts +22 -67
  14. package/src/server.ts +374 -311
  15. package/tests/__snapshots__/normalizeOptions.test.ts.snap +164 -26
  16. package/tests/e2e/hot-reaload.test.ts +35 -39
  17. package/tests/e2e-fixtures/react/node_modules/scheduler/LICENSE +21 -0
  18. package/tests/e2e-fixtures/react/node_modules/scheduler/README.md +9 -0
  19. package/tests/e2e-fixtures/react/node_modules/scheduler/cjs/scheduler-unstable_mock.development.js +700 -0
  20. package/tests/e2e-fixtures/react/node_modules/scheduler/cjs/scheduler-unstable_mock.production.min.js +20 -0
  21. package/tests/e2e-fixtures/react/node_modules/scheduler/cjs/scheduler-unstable_post_task.development.js +207 -0
  22. package/tests/e2e-fixtures/react/node_modules/scheduler/cjs/scheduler-unstable_post_task.production.min.js +14 -0
  23. package/tests/e2e-fixtures/react/node_modules/scheduler/cjs/scheduler.development.js +634 -0
  24. package/tests/e2e-fixtures/react/node_modules/scheduler/cjs/scheduler.production.min.js +19 -0
  25. package/tests/e2e-fixtures/react/node_modules/scheduler/index.js +7 -0
  26. package/tests/e2e-fixtures/react/node_modules/scheduler/package.json +36 -0
  27. package/tests/e2e-fixtures/react/node_modules/scheduler/umd/scheduler-unstable_mock.development.js +699 -0
  28. package/tests/e2e-fixtures/react/node_modules/scheduler/umd/scheduler-unstable_mock.production.min.js +19 -0
  29. package/tests/e2e-fixtures/react/node_modules/scheduler/umd/scheduler.development.js +152 -0
  30. package/tests/e2e-fixtures/react/node_modules/scheduler/umd/scheduler.production.min.js +146 -0
  31. package/tests/e2e-fixtures/react/node_modules/scheduler/umd/scheduler.profiling.min.js +146 -0
  32. package/tests/e2e-fixtures/react/node_modules/scheduler/unstable_mock.js +7 -0
  33. package/tests/e2e-fixtures/react/node_modules/scheduler/unstable_post_task.js +7 -0
  34. package/tests/e2e-fixtures/react/package.json +2 -0
  35. package/tests/e2e-fixtures/react/webpack.config.js +6 -2
  36. package/tests/helpers/emitFile.ts +58 -4
  37. package/tests/normalizeOptions.test.ts +50 -16
  38. package/tsconfig.tsbuildinfo +1 -1
  39. package/dist/logger.d.ts +0 -7
  40. package/dist/logger.d.ts.map +0 -1
  41. package/dist/logger.js +0 -28
  42. package/dist/logger.js.map +0 -1
  43. package/dist/ws.d.ts +0 -13
  44. package/dist/ws.d.ts.map +0 -1
  45. package/dist/ws.js +0 -12
  46. package/dist/ws.js.map +0 -1
  47. package/src/logger.ts +0 -30
  48. package/src/ws.ts +0 -18
package/src/server.ts CHANGED
@@ -1,154 +1,204 @@
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
+ const isWebTarget = isWebTarget2(compiler);
30
+ if (this.options.client && isWebTarget) {
31
+ let webSocketURLStr = "";
78
32
 
79
- addAdditionEntires() {
80
- const entries: string[] = [];
33
+ if (this.options.webSocketServer) {
34
+ const webSocketURL = this.options.client
35
+ .webSocketURL as WebpackDevServer.WebSocketURL;
36
+ const webSocketServer = this.options.webSocketServer;
37
+ const searchParams = new URLSearchParams();
81
38
 
82
- if (this.options.hot) {
83
- const hotUpdateEntryPath = require.resolve(
84
- "@rspack/dev-client/devServer"
85
- );
86
- entries.push(hotUpdateEntryPath);
39
+ let protocol: string;
87
40
 
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
- }
41
+ // We are proxying dev server and need to specify custom `hostname`
42
+ if (typeof webSocketURL.protocol !== "undefined") {
43
+ protocol = webSocketURL.protocol;
44
+ } else {
45
+ protocol = this.options.server.type === "http" ? "ws:" : "wss:";
46
+ }
95
47
 
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
- }
48
+ searchParams.set("protocol", protocol);
102
49
 
103
- static isAbsoluteURL(URL: string): boolean {
104
- return WebpackDevServer.isAbsoluteURL(URL);
105
- }
50
+ if (typeof webSocketURL.username !== "undefined") {
51
+ searchParams.set("username", webSocketURL.username);
52
+ }
106
53
 
107
- static findIp(gateway: string): string | undefined {
108
- return WebpackDevServer.findIp(gateway);
109
- }
54
+ if (typeof webSocketURL.password !== "undefined") {
55
+ searchParams.set("password", webSocketURL.password);
56
+ }
110
57
 
111
- static async internalIP(family: "v6" | "v4"): Promise<string | undefined> {
112
- return WebpackDevServer.internalIP(family);
113
- }
58
+ let hostname: string;
114
59
 
115
- static async internalIPSync(
116
- family: "v6" | "v4"
117
- ): Promise<string | undefined> {
118
- return WebpackDevServer.internalIPSync(family);
119
- }
60
+ // SockJS is not supported server mode, so `hostname` and `port` can't specified, let's ignore them
61
+ // TODO show warning about this
62
+ const isSockJSType = webSocketServer.type === "sockjs";
120
63
 
121
- static async getHostname(hostname?: Host): Promise<string> {
122
- return WebpackDevServer.getHostname(hostname);
123
- }
64
+ // We are proxying dev server and need to specify custom `hostname`
65
+ if (typeof webSocketURL.hostname !== "undefined") {
66
+ hostname = webSocketURL.hostname;
67
+ }
68
+ // Web socket server works on custom `hostname`, only for `ws` because `sock-js` is not support custom `hostname`
69
+ else if (
70
+ typeof webSocketServer.options.host !== "undefined" &&
71
+ !isSockJSType
72
+ ) {
73
+ hostname = webSocketServer.options.host;
74
+ }
75
+ // The `host` option is specified
76
+ else if (typeof this.options.host !== "undefined") {
77
+ hostname = this.options.host;
78
+ }
79
+ // The `port` option is not specified
80
+ else {
81
+ hostname = "0.0.0.0";
82
+ }
124
83
 
125
- static async getFreePort(port: Port, host: string): Promise<string | number> {
126
- return WebpackDevServer.getFreePort(port, host);
127
- }
84
+ searchParams.set("hostname", hostname);
128
85
 
129
- static findCacheDir(): string {
130
- // TODO: we need remove the `webpack-dev-server` tag in WebpackDevServer;
131
- return "";
132
- }
86
+ let port: number | string;
133
87
 
134
- private getCompilerOptions(): RspackOptionsNormalized {
135
- return this.compiler.options;
136
- }
88
+ // We are proxying dev server and need to specify custom `port`
89
+ if (typeof webSocketURL.port !== "undefined") {
90
+ port = webSocketURL.port;
91
+ }
92
+ // Web socket server works on custom `port`, only for `ws` because `sock-js` is not support custom `port`
93
+ else if (
94
+ typeof webSocketServer.options.port !== "undefined" &&
95
+ !isSockJSType
96
+ ) {
97
+ port = webSocketServer.options.port;
98
+ }
99
+ // The `port` option is specified
100
+ else if (typeof this.options.port === "number") {
101
+ port = this.options.port;
102
+ }
103
+ // The `port` option is specified using `string`
104
+ else if (
105
+ typeof this.options.port === "string" &&
106
+ this.options.port !== "auto"
107
+ ) {
108
+ port = Number(this.options.port);
109
+ }
110
+ // The `port` option is not specified or set to `auto`
111
+ else {
112
+ port = "0";
113
+ }
137
114
 
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 }));
115
+ searchParams.set("port", String(port));
116
+
117
+ let pathname = "";
118
+
119
+ // We are proxying dev server and need to specify custom `pathname`
120
+ if (typeof webSocketURL.pathname !== "undefined") {
121
+ pathname = webSocketURL.pathname;
122
+ }
123
+ // Web socket server works on custom `path`
124
+ else if (
125
+ typeof webSocketServer.options.prefix !== "undefined" ||
126
+ typeof webSocketServer.options.path !== "undefined"
127
+ ) {
128
+ pathname =
129
+ webSocketServer.options.prefix || webSocketServer.options.path;
130
+ }
131
+
132
+ searchParams.set("pathname", pathname);
133
+
134
+ const client = /** @type {ClientConfiguration} */ this.options.client;
135
+
136
+ if (typeof client.logging !== "undefined") {
137
+ searchParams.set("logging", client.logging);
138
+ }
139
+
140
+ if (typeof client.progress !== "undefined") {
141
+ searchParams.set("progress", String(client.progress));
142
+ }
143
+
144
+ if (typeof client.overlay !== "undefined") {
145
+ searchParams.set(
146
+ "overlay",
147
+ typeof client.overlay === "boolean"
148
+ ? String(client.overlay)
149
+ : JSON.stringify(client.overlay)
150
+ );
151
+ }
152
+
153
+ if (typeof client.reconnect !== "undefined") {
154
+ searchParams.set(
155
+ "reconnect",
156
+ typeof client.reconnect === "number"
157
+ ? String(client.reconnect)
158
+ : "10"
159
+ );
160
+ }
161
+
162
+ if (typeof this.options.hot !== "undefined") {
163
+ searchParams.set("hot", String(this.options.hot));
164
+ }
165
+
166
+ if (typeof this.options.liveReload !== "undefined") {
167
+ searchParams.set("live-reload", String(this.options.liveReload));
168
+ }
169
+
170
+ webSocketURLStr = searchParams.toString();
147
171
  }
172
+
173
+ // TODO: should use providerPlugin
174
+ additionalEntries.push(this.getClientTransport());
175
+
176
+ additionalEntries.push(
177
+ `${require.resolve("@rspack/dev-client")}?${webSocketURLStr}`
178
+ );
179
+ }
180
+
181
+ if (this.options.hot) {
182
+ const hotUpdateEntryPath = require.resolve(
183
+ "@rspack/dev-client/devServer"
184
+ );
185
+ additionalEntries.push(hotUpdateEntryPath);
186
+
187
+ if (compiler.options.builtins.react?.refresh) {
188
+ const reactRefreshEntryPath = require.resolve(
189
+ "@rspack/dev-client/react-refresh"
190
+ );
191
+ additionalEntries.push(reactRefreshEntryPath);
192
+ }
193
+ }
194
+
195
+ for (const key in compiler.options.entry) {
196
+ compiler.options.entry[key].import.unshift(...additionalEntries);
148
197
  }
149
198
  }
150
199
 
151
200
  watchFiles(watchPath: string | string[], watchOptions?: WatchOptions): void {
201
+ const chokidar = require("chokidar");
152
202
  const watcher = chokidar.watch(watchPath, watchOptions);
153
203
 
154
204
  // disabling refreshing on changing the content
@@ -172,80 +222,154 @@ export class RspackDevServer {
172
222
  this.staticWatchers.push(watcher);
173
223
  }
174
224
 
175
- invalidate(callback = () => {}): void {
176
- if (this.middleware) {
177
- this.middleware.invalidate(callback);
225
+ getClientTransport(): string {
226
+ // WARNING: we can't use `super.getClientTransport`,
227
+ // because we doesn't had same directory structure.
228
+ // and TODO: we need impelement `webpack.providerPlugin`
229
+ let clientImplementation: string | undefined;
230
+ let clientImplementationFound = true;
231
+ const isKnownWebSocketServerImplementation =
232
+ this.options.webSocketServer &&
233
+ typeof this.options.webSocketServer.type === "string" &&
234
+ (this.options.webSocketServer.type === "ws" ||
235
+ this.options.webSocketServer.type === "sockjs");
236
+
237
+ let clientTransport: string | undefined;
238
+
239
+ if (this.options.client) {
240
+ if (
241
+ // @ts-ignore
242
+ typeof this.options.client.webSocketTransport !== "undefined"
243
+ ) {
244
+ // @ts-ignore
245
+ clientTransport = this.options.client.webSocketTransport;
246
+ } else if (isKnownWebSocketServerImplementation) {
247
+ // @ts-ignore
248
+ clientTransport = this.options.webSocketServer.type;
249
+ } else {
250
+ clientTransport = "ws";
251
+ }
252
+ } else {
253
+ clientTransport = "ws";
178
254
  }
255
+
256
+ switch (typeof clientTransport) {
257
+ case "string":
258
+ // could be 'sockjs', 'ws', or a path that should be required
259
+ if (clientTransport === "sockjs") {
260
+ clientImplementation = require.resolve(
261
+ "@rspack/dev-client/clients/SockJSClient"
262
+ );
263
+ } else if (clientTransport === "ws") {
264
+ clientImplementation = require.resolve(
265
+ "@rspack/dev-client/clients/WebSocketClient"
266
+ );
267
+ } else {
268
+ try {
269
+ clientImplementation = require.resolve(clientTransport);
270
+ throw Error("Do not support custom ws client now");
271
+ } catch (e) {
272
+ clientImplementationFound = false;
273
+ }
274
+ }
275
+ break;
276
+ default:
277
+ clientImplementationFound = false;
278
+ }
279
+ if (!clientImplementationFound) {
280
+ throw new Error(
281
+ `${
282
+ !isKnownWebSocketServerImplementation
283
+ ? "When you use custom web socket implementation you must explicitly specify client.webSocketTransport. "
284
+ : ""
285
+ }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 `
286
+ );
287
+ }
288
+
289
+ return clientImplementation;
179
290
  }
180
291
 
181
- async start(): Promise<void> {
292
+ async initialize() {
293
+ const compilers =
294
+ this.compiler instanceof MultiCompiler
295
+ ? this.compiler.compilers
296
+ : [this.compiler];
297
+
298
+ compilers.forEach(compiler => {
299
+ compiler.options.builtins.react ??= {};
300
+ if (this.options.hot) {
301
+ compiler.options.builtins.react.refresh ??= true;
302
+ compiler.options.builtins.react.development ??= true;
303
+ } else if (compiler.options.builtins.react.refresh) {
304
+ this.logger.warn(
305
+ "builtins.react.refresh needs builtins.react.development and devServer.hot enabled"
306
+ );
307
+ }
308
+ });
309
+
310
+ if (this.options.webSocketServer) {
311
+ compilers.forEach(compiler => {
312
+ this.addAdditionEntires(compiler);
313
+ });
314
+ }
315
+
182
316
  this.setupHooks();
317
+ // @ts-expect-error: `setupApp` is private function in base class.
183
318
  this.setupApp();
184
- this.createServer();
185
- this.setupWatchStaticFiles();
186
- this.createWebsocketServer();
319
+ // @ts-expect-error: `setupHostHeaderCheck` is private function in base class.
320
+ this.setupHostHeaderCheck();
187
321
  this.setupDevMiddleware();
322
+ // @ts-expect-error: `setupBuiltInRoutes` is private function in base class.
323
+ this.setupBuiltInRoutes();
324
+ // @ts-expect-error: `setupWatchFiles` is private function in base class.
325
+ this.setupWatchFiles();
326
+ // @ts-expect-error: `setupWatchStaticFiles` is private function in base class.
327
+ this.setupWatchStaticFiles();
188
328
  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");
329
+ // @ts-expect-error: `createServer` is private function in base class.
330
+ this.createServer();
331
+
332
+ if (this.options.setupExitSignals) {
333
+ const signals = ["SIGINT", "SIGTERM"];
334
+
335
+ let needForceShutdown = false;
336
+
337
+ signals.forEach(signal => {
338
+ const listener = () => {
339
+ if (needForceShutdown) {
340
+ process.exit();
341
+ }
342
+
201
343
  this.logger.info(
202
- `Your Network (IPV4) http://${internalIPv4}:${port}`
344
+ "Gracefully shutting down. To force exit, press ^C again. Please wait..."
203
345
  );
204
- resolve({});
205
- }
206
- )
207
- );
208
- }
209
346
 
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
- }
347
+ needForceShutdown = true;
222
348
 
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
- }
349
+ this.stopCallback(() => {
350
+ if (typeof this.compiler.close === "function") {
351
+ this.compiler.close(() => {
352
+ process.exit();
353
+ });
354
+ } else {
355
+ process.exit();
356
+ }
357
+ });
358
+ };
239
359
 
240
- private setupApp() {
241
- this.app = express();
242
- }
360
+ // @ts-expect-error: `listeners` is private function in base class.
361
+ this.listeners.push({ name: signal, listener });
243
362
 
244
- private setupWatchStaticFiles() {
245
- if (this.options.static.watch === false) {
246
- return;
363
+ process.on(signal, listener);
364
+ });
247
365
  }
248
- this.watchFiles(this.options.static.directory, this.options.static.watch);
366
+
367
+ // Proxy WebSocket without the initial http request
368
+ // https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade
369
+ // @ts-expect-error: `webSocketProxies` is private function in base class.
370
+ this.webSocketProxies.forEach(webSocketProxy => {
371
+ this.server.on("upgrade", webSocketProxy.upgrade);
372
+ }, this);
249
373
  }
250
374
 
251
375
  private setupDevMiddleware() {
@@ -253,151 +377,63 @@ export class RspackDevServer {
253
377
  this.middleware = rdm(this.compiler, this.options.devMiddleware);
254
378
  }
255
379
 
256
- private createWebsocketServer() {
257
- if (this.options.webSocketServer !== false) {
258
- this.webSocketServer = createWebsocketServer(this);
259
- }
260
- }
261
-
262
380
  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");
381
+ const middlewares: WebpackDevServer.Middleware[] = [];
382
+ const compilers =
383
+ this.compiler instanceof MultiCompiler
384
+ ? this.compiler.compilers
385
+ : [this.compiler];
386
+
387
+ if (Array.isArray(this.options.static)) {
388
+ this.options.static.forEach(staticOptions => {
389
+ staticOptions.publicPath.forEach(publicPath => {
390
+ compilers.forEach(compiler => {
391
+ if (compiler.options.builtins.noEmitAssets) {
392
+ middlewares.push({
393
+ name: "rspack-memory-assets",
394
+ path: publicPath,
395
+ middleware: getRspackMemoryAssets(compiler, this.middleware)
283
396
  });
284
397
  }
285
- }
286
- }
398
+ });
399
+ });
287
400
  });
288
401
  }
289
402
 
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;
403
+ compilers.forEach(compiler => {
404
+ if (compiler.options.experiments.lazyCompilation) {
405
+ middlewares.push({
406
+ middleware: (req, res, next) => {
407
+ if (req.url.indexOf("/lazy-compilation-web/") > -1) {
408
+ const path = req.url.replace("/lazy-compilation-web/", "");
409
+ if (fs.existsSync(path)) {
410
+ compiler.rebuild(new Set([path]), new Set(), error => {
411
+ if (error) {
412
+ throw error;
413
+ }
414
+ res.write("");
415
+ res.end();
416
+ console.log("lazy compiler success");
417
+ });
418
+ }
346
419
  }
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
420
  }
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
421
  });
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)
422
+ }
388
423
  });
389
424
 
390
- middlewares.forEach(m => {
391
- if (m.path) {
392
- this.app.use(m.path, m.middleware);
425
+ middlewares.forEach(middleware => {
426
+ if (typeof middleware === "function") {
427
+ this.app.use(middleware);
428
+ } else if (typeof middleware.path !== "undefined") {
429
+ this.app.use(middleware.path, middleware.middleware);
393
430
  } else {
394
- this.app.use(m.middleware);
431
+ this.app.use(middleware.middleware);
395
432
  }
396
433
  });
397
- }
398
434
 
399
- private createServer() {
400
- this.server = http.createServer(this.app);
435
+ // @ts-expect-error
436
+ super.setupMiddlewares();
401
437
  }
402
438
 
403
439
  private setupHooks() {
@@ -409,3 +445,30 @@ export class RspackDevServer {
409
445
  });
410
446
  }
411
447
  }
448
+
449
+ // TODO: use WebpackDevServer.isWebTarget instead of this once we have a new webpack-dev-server version
450
+ function isWebTarget2(compiler: Compiler): boolean {
451
+ if (
452
+ compiler.options.resolve.conditionNames &&
453
+ compiler.options.resolve.conditionNames.includes("browser")
454
+ ) {
455
+ return true;
456
+ }
457
+ const target = compiler.options.target;
458
+ const webTargets = [
459
+ "web",
460
+ "webworker",
461
+ "electron-preload",
462
+ "electron-renderer",
463
+ "node-webkit",
464
+ undefined,
465
+ null
466
+ ];
467
+ if (Array.isArray(target)) {
468
+ return target.some(r => webTargets.includes(r));
469
+ }
470
+ if (typeof target === "string") {
471
+ return webTargets.includes(target);
472
+ }
473
+ return false;
474
+ }