@rspack/dev-server 0.2.0 → 0.2.1

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.
package/src/server.ts DELETED
@@ -1,476 +0,0 @@
1
- /**
2
- * The following code is modified based on
3
- * https://github.com/webpack/webpack-dev-server/blob/b0f15ace0123c125d5870609ef4691c141a6d187/lib/Server.js
4
- *
5
- * MIT Licensed
6
- * Author Tobias Koppers @sokra
7
- * Copyright (c) JS Foundation and other contributors
8
- * https://github.com/webpack/webpack-dev-server/blob/b0f15ace0123c125d5870609ef4691c141a6d187/LICENSE
9
- */
10
- import { Compiler, MultiCompiler } from "@rspack/core";
11
- import type { Socket } from "net";
12
- import type { FSWatcher } from "chokidar";
13
- import rdm from "@rspack/dev-middleware";
14
- import type { Server } from "http";
15
- import fs from "fs";
16
- import WebpackDevServer from "webpack-dev-server";
17
- import type { ResolvedDevServer, DevServer } from "./config";
18
- import { getRspackMemoryAssets } from "./middleware";
19
-
20
- export class RspackDevServer extends WebpackDevServer {
21
- /**
22
- * resolved after `normalizedOptions`
23
- */
24
- options: ResolvedDevServer;
25
- staticWatchers: FSWatcher[];
26
- sockets: Socket[];
27
- server: Server;
28
- // @ts-expect-error
29
- public compiler: Compiler | MultiCompiler;
30
- webSocketServer: WebpackDevServer.WebSocketServerImplementation | undefined;
31
-
32
- constructor(options: DevServer, compiler: Compiler | MultiCompiler) {
33
- super(
34
- {
35
- ...options,
36
- setupMiddlewares: (middlewares, devServer) => {
37
- const webpackDevMiddlewareIndex = middlewares.findIndex(
38
- mid => mid.name === "webpack-dev-middleware"
39
- );
40
- const compilers =
41
- compiler instanceof MultiCompiler ? compiler.compilers : [compiler];
42
- if (compilers[0].options.builtins.noEmitAssets) {
43
- if (Array.isArray(this.options.static)) {
44
- const memoryAssetsMiddlewares = this.options.static.flatMap(
45
- staticOptions => {
46
- return staticOptions.publicPath.flatMap(publicPath => {
47
- return compilers.map(compiler => {
48
- return {
49
- name: "rspack-memory-assets",
50
- path: publicPath,
51
- middleware: getRspackMemoryAssets(
52
- compiler,
53
- this.middleware
54
- )
55
- };
56
- });
57
- });
58
- }
59
- );
60
- middlewares.splice(
61
- webpackDevMiddlewareIndex,
62
- 0,
63
- ...memoryAssetsMiddlewares
64
- );
65
- }
66
- }
67
-
68
- options.setupMiddlewares?.call(this, middlewares, devServer);
69
- return middlewares;
70
- }
71
- },
72
- compiler as any
73
- );
74
- }
75
-
76
- addAdditionalEntries(compiler: Compiler) {
77
- const additionalEntries: string[] = [];
78
- // @ts-expect-error
79
- const isWebTarget = WebpackDevServer.isWebTarget(compiler);
80
- // inject runtime first, avoid other additional entry after transfrom depend on it
81
- const clientPath = require.resolve("webpack-dev-server/client/index.js");
82
- if (this.options.hot) {
83
- if (compiler.options.builtins.react?.refresh) {
84
- const reactRefreshEntryPath = require.resolve(
85
- "@rspack/dev-client/react-refresh"
86
- );
87
- additionalEntries.push(reactRefreshEntryPath);
88
- }
89
- // #3356 make sure resolve webpack/hot/dev-server from webpack-dev-server
90
- const hotUpdateEntryPath = require.resolve("webpack/hot/dev-server", {
91
- paths: [clientPath]
92
- });
93
- additionalEntries.push(hotUpdateEntryPath);
94
- }
95
- if (this.options.client && isWebTarget) {
96
- let webSocketURLStr = "";
97
-
98
- if (this.options.webSocketServer) {
99
- const webSocketURL = this.options.client
100
- .webSocketURL as WebpackDevServer.WebSocketURL;
101
- const webSocketServer = this.options.webSocketServer;
102
- const searchParams = new URLSearchParams();
103
-
104
- let protocol: string;
105
-
106
- // We are proxying dev server and need to specify custom `hostname`
107
- if (typeof webSocketURL.protocol !== "undefined") {
108
- protocol = webSocketURL.protocol;
109
- } else {
110
- protocol = this.options.server.type === "http" ? "ws:" : "wss:";
111
- }
112
-
113
- searchParams.set("protocol", protocol);
114
-
115
- if (typeof webSocketURL.username !== "undefined") {
116
- searchParams.set("username", webSocketURL.username);
117
- }
118
-
119
- if (typeof webSocketURL.password !== "undefined") {
120
- searchParams.set("password", webSocketURL.password);
121
- }
122
-
123
- let hostname: string;
124
-
125
- // SockJS is not supported server mode, so `hostname` and `port` can't specified, let's ignore them
126
- // TODO show warning about this
127
- const isSockJSType = webSocketServer.type === "sockjs";
128
-
129
- // We are proxying dev server and need to specify custom `hostname`
130
- if (typeof webSocketURL.hostname !== "undefined") {
131
- hostname = webSocketURL.hostname;
132
- }
133
- // Web socket server works on custom `hostname`, only for `ws` because `sock-js` is not support custom `hostname`
134
- else if (
135
- typeof webSocketServer.options.host !== "undefined" &&
136
- !isSockJSType
137
- ) {
138
- hostname = webSocketServer.options.host;
139
- }
140
- // The `host` option is specified
141
- else if (typeof this.options.host !== "undefined") {
142
- hostname = this.options.host;
143
- }
144
- // The `port` option is not specified
145
- else {
146
- hostname = "0.0.0.0";
147
- }
148
-
149
- searchParams.set("hostname", hostname);
150
-
151
- let port: number | string;
152
-
153
- // We are proxying dev server and need to specify custom `port`
154
- if (typeof webSocketURL.port !== "undefined") {
155
- port = webSocketURL.port;
156
- }
157
- // Web socket server works on custom `port`, only for `ws` because `sock-js` is not support custom `port`
158
- else if (
159
- typeof webSocketServer.options.port !== "undefined" &&
160
- !isSockJSType
161
- ) {
162
- port = webSocketServer.options.port;
163
- }
164
- // The `port` option is specified
165
- else if (typeof this.options.port === "number") {
166
- port = this.options.port;
167
- }
168
- // The `port` option is specified using `string`
169
- else if (
170
- typeof this.options.port === "string" &&
171
- this.options.port !== "auto"
172
- ) {
173
- port = Number(this.options.port);
174
- }
175
- // The `port` option is not specified or set to `auto`
176
- else {
177
- port = "0";
178
- }
179
-
180
- searchParams.set("port", String(port));
181
-
182
- let pathname = "";
183
-
184
- // We are proxying dev server and need to specify custom `pathname`
185
- if (typeof webSocketURL.pathname !== "undefined") {
186
- pathname = webSocketURL.pathname;
187
- }
188
- // Web socket server works on custom `path`
189
- else if (
190
- typeof webSocketServer.options.prefix !== "undefined" ||
191
- typeof webSocketServer.options.path !== "undefined"
192
- ) {
193
- pathname =
194
- webSocketServer.options.prefix || webSocketServer.options.path;
195
- }
196
-
197
- searchParams.set("pathname", pathname);
198
-
199
- const client = /** @type {ClientConfiguration} */ this.options.client;
200
-
201
- if (typeof client.logging !== "undefined") {
202
- searchParams.set("logging", client.logging);
203
- }
204
-
205
- if (typeof client.progress !== "undefined") {
206
- searchParams.set("progress", String(client.progress));
207
- }
208
-
209
- if (typeof client.overlay !== "undefined") {
210
- searchParams.set(
211
- "overlay",
212
- typeof client.overlay === "boolean"
213
- ? String(client.overlay)
214
- : JSON.stringify(client.overlay)
215
- );
216
- }
217
-
218
- if (typeof client.reconnect !== "undefined") {
219
- searchParams.set(
220
- "reconnect",
221
- typeof client.reconnect === "number"
222
- ? String(client.reconnect)
223
- : "10"
224
- );
225
- }
226
-
227
- if (typeof this.options.hot !== "undefined") {
228
- searchParams.set("hot", String(this.options.hot));
229
- }
230
-
231
- if (typeof this.options.liveReload !== "undefined") {
232
- searchParams.set("live-reload", String(this.options.liveReload));
233
- }
234
-
235
- webSocketURLStr = searchParams.toString();
236
- }
237
-
238
- additionalEntries.push(`${clientPath}?${webSocketURLStr}`);
239
- }
240
-
241
- for (const key in compiler.options.entry) {
242
- compiler.options.entry[key].import.unshift(...additionalEntries);
243
- }
244
- }
245
-
246
- getClientTransport(): string {
247
- // WARNING: we can't use `super.getClientTransport`,
248
- // because we doesn't had same directory structure.
249
- let clientImplementation: string | undefined;
250
- let clientImplementationFound = true;
251
- const isKnownWebSocketServerImplementation =
252
- this.options.webSocketServer &&
253
- typeof this.options.webSocketServer.type === "string" &&
254
- (this.options.webSocketServer.type === "ws" ||
255
- this.options.webSocketServer.type === "sockjs");
256
-
257
- let clientTransport: string | undefined;
258
-
259
- if (this.options.client) {
260
- if (typeof this.options.client.webSocketTransport !== "undefined") {
261
- clientTransport = this.options.client.webSocketTransport;
262
- } else if (isKnownWebSocketServerImplementation) {
263
- // @ts-expect-error: TS cannot infer webSocketServer is narrowed
264
- clientTransport = this.options.webSocketServer.type;
265
- } else {
266
- clientTransport = "ws";
267
- }
268
- } else {
269
- clientTransport = "ws";
270
- }
271
-
272
- switch (typeof clientTransport) {
273
- case "string":
274
- // could be 'sockjs', 'ws', or a path that should be required
275
- if (clientTransport === "sockjs") {
276
- clientImplementation = require.resolve(
277
- "webpack-dev-server/client/clients/SockJSClient"
278
- );
279
- } else if (clientTransport === "ws") {
280
- clientImplementation = require.resolve(
281
- "webpack-dev-server/client/clients/WebSocketClient"
282
- );
283
- } else {
284
- try {
285
- clientImplementation = require.resolve(clientTransport);
286
- throw Error("Do not support custom ws client now");
287
- } catch (e) {
288
- clientImplementationFound = false;
289
- }
290
- }
291
- break;
292
- default:
293
- clientImplementationFound = false;
294
- }
295
- if (!clientImplementationFound) {
296
- throw new Error(
297
- `${
298
- !isKnownWebSocketServerImplementation
299
- ? "When you use custom web socket implementation you must explicitly specify client.webSocketTransport. "
300
- : ""
301
- }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 `
302
- );
303
- }
304
-
305
- return clientImplementation;
306
- }
307
-
308
- async initialize() {
309
- const compilers =
310
- this.compiler instanceof MultiCompiler
311
- ? this.compiler.compilers
312
- : [this.compiler];
313
-
314
- compilers.forEach(compiler => {
315
- const mode = compiler.options.mode || process.env.NODE_ENV;
316
- if (this.options.hot) {
317
- if (mode === "production") {
318
- this.logger.warn(
319
- "Hot Module Replacement (HMR) is enabled for the production build. \n" +
320
- "Make sure to disable HMR for production by setting `devServer.hot` to `false` in the configuration."
321
- );
322
- }
323
- compiler.options.devServer ??= {};
324
- compiler.options.devServer.hot = true;
325
- compiler.options.builtins.react ??= {};
326
- compiler.options.builtins.react.refresh ??= true;
327
- compiler.options.builtins.react.development ??= true;
328
- } else if (compiler.options.builtins.react.refresh) {
329
- if (mode === "production") {
330
- this.logger.warn(
331
- "React Refresh runtime should not be included in the production bundle.\n" +
332
- "Make sure to disable React Refresh for production by setting `builtins.react.refresh` to `false` in the configuration."
333
- );
334
- } else {
335
- this.logger.warn(
336
- "The `builtins.react.refresh` needs `builtins.react.development` and `devServer.hot` enabled"
337
- );
338
- }
339
- }
340
- });
341
-
342
- if (this.options.webSocketServer) {
343
- compilers.forEach(compiler => {
344
- this.addAdditionalEntries(compiler);
345
-
346
- compiler.options.builtins.provide = {
347
- ...compiler.options.builtins.provide,
348
- __webpack_dev_server_client__: [this.getClientTransport()]
349
- };
350
- });
351
- }
352
-
353
- // @ts-expect-error: `setupHooks` is private function in base class.
354
- this.setupHooks();
355
- // @ts-expect-error: `setupApp` is private function in base class.
356
- this.setupApp();
357
- // @ts-expect-error: `setupHostHeaderCheck` is private function in base class.
358
- this.setupHostHeaderCheck();
359
- this.setupDevMiddleware();
360
- // @ts-expect-error: `setupBuiltInRoutes` is private function in base class.
361
- this.setupBuiltInRoutes();
362
- // @ts-expect-error: `setupWatchFiles` is private function in base class.
363
- this.setupWatchFiles();
364
- // @ts-expect-error: `setupWatchStaticFiles` is private function in base class.
365
- this.setupWatchStaticFiles();
366
- this.setupMiddlewares();
367
- // @ts-expect-error: `createServer` is private function in base class.
368
- this.createServer();
369
-
370
- if (this.options.setupExitSignals) {
371
- const signals = ["SIGINT", "SIGTERM"];
372
-
373
- let needForceShutdown = false;
374
-
375
- signals.forEach(signal => {
376
- const listener = () => {
377
- if (needForceShutdown) {
378
- process.exit();
379
- }
380
-
381
- this.logger.info(
382
- "Gracefully shutting down. To force exit, press ^C again. Please wait..."
383
- );
384
-
385
- needForceShutdown = true;
386
-
387
- this.stopCallback(() => {
388
- if (typeof this.compiler.close === "function") {
389
- this.compiler.close(() => {
390
- process.exit();
391
- });
392
- } else {
393
- process.exit();
394
- }
395
- });
396
- };
397
-
398
- // @ts-expect-error: `listeners` is private function in base class.
399
- this.listeners.push({ name: signal, listener });
400
-
401
- process.on(signal, listener);
402
- });
403
- }
404
-
405
- // Proxy WebSocket without the initial http request
406
- // https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade
407
- // @ts-expect-error: `webSocketProxies` is private function in base class.
408
- this.webSocketProxies.forEach(webSocketProxy => {
409
- this.server.on("upgrade", webSocketProxy.upgrade);
410
- }, this);
411
- }
412
-
413
- private setupDevMiddleware() {
414
- // @ts-expect-error
415
- this.middleware = rdm(this.compiler, this.options.devMiddleware);
416
- }
417
-
418
- private setupMiddlewares() {
419
- const middlewares: WebpackDevServer.Middleware[] = [];
420
- const compilers =
421
- this.compiler instanceof MultiCompiler
422
- ? this.compiler.compilers
423
- : [this.compiler];
424
-
425
- // if (Array.isArray(this.options.static)) {
426
- // this.options.static.forEach(staticOptions => {
427
- // staticOptions.publicPath.forEach(publicPath => {
428
- // compilers.forEach(compiler => {
429
- // if (compiler.options.builtins.noEmitAssets) {
430
- // middlewares.push({
431
- // name: "rspack-memory-assets",
432
- // path: publicPath,
433
- // middleware: getRspackMemoryAssets(compiler, this.middleware)
434
- // });
435
- // }
436
- // });
437
- // });
438
- // });
439
- // }
440
-
441
- compilers.forEach(compiler => {
442
- if (compiler.options.experiments.lazyCompilation) {
443
- middlewares.push({
444
- middleware: (req, res, next) => {
445
- if (req.url.indexOf("/lazy-compilation-web/") > -1) {
446
- const path = req.url.replace("/lazy-compilation-web/", "");
447
- if (fs.existsSync(path)) {
448
- compiler.rebuild(new Set([path]), new Set(), error => {
449
- if (error) {
450
- throw error;
451
- }
452
- res.write("");
453
- res.end();
454
- console.log("lazy compiler success");
455
- });
456
- }
457
- }
458
- }
459
- });
460
- }
461
- });
462
-
463
- middlewares.forEach(middleware => {
464
- if (typeof middleware === "function") {
465
- this.app.use(middleware);
466
- } else if (typeof middleware.path !== "undefined") {
467
- this.app.use(middleware.path, middleware.middleware);
468
- } else {
469
- this.app.use(middleware.middleware);
470
- }
471
- });
472
-
473
- // @ts-expect-error
474
- super.setupMiddlewares();
475
- }
476
- }
@@ -1,137 +0,0 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
2
-
3
- exports[`normalize options snapshot additional entires should added 1`] = `
4
- {
5
- "main": [
6
- "<prefix>/rspack-dev-client/dist/reactRefresh.js",
7
- "<prefix>/webpack/hot/dev-server.js",
8
- "<prefix>/webpack-dev-server/client/index.js?protocol=ws%3A&hostname=0.0.0.0&&pathname=%2Fws&logging=info&overlay=true&reconnect=10&hot=true&live-reload=true",
9
- "<prefix>/./placeholder.js",
10
- ],
11
- }
12
- `;
13
-
14
- exports[`normalize options snapshot no options 1`] = `
15
- {
16
- "allowedHosts": "auto",
17
- "bonjour": false,
18
- "client": {
19
- "logging": "info",
20
- "overlay": true,
21
- "reconnect": 10,
22
- "webSocketURL": {},
23
- },
24
- "compress": true,
25
- "devMiddleware": {},
26
- "historyApiFallback": false,
27
- "host": undefined,
28
- "hot": true,
29
- "liveReload": true,
30
- "magicHtml": true,
31
- "open": [],
32
- "server": {
33
- "options": {},
34
- "type": "http",
35
- },
36
- "setupExitSignals": true,
37
- "setupMiddlewares": [Function],
38
- "static": [
39
- {
40
- "directory": "<PROJECT_ROOT>/public",
41
- "publicPath": [
42
- "/",
43
- ],
44
- "serveIndex": {
45
- "icons": true,
46
- },
47
- "staticOptions": {},
48
- "watch": {
49
- "alwaysStat": true,
50
- "atomic": false,
51
- "followSymlinks": false,
52
- "ignoreInitial": true,
53
- "ignorePermissionErrors": true,
54
- "ignored": undefined,
55
- "interval": undefined,
56
- "persistent": true,
57
- "usePolling": false,
58
- },
59
- },
60
- ],
61
- "watchFiles": [],
62
- "webSocketServer": {
63
- "options": {
64
- "path": "/ws",
65
- },
66
- "type": "ws",
67
- },
68
- }
69
- `;
70
-
71
- exports[`normalize options snapshot port string 1`] = `
72
- {
73
- "allowedHosts": "auto",
74
- "bonjour": false,
75
- "client": {
76
- "logging": "info",
77
- "overlay": true,
78
- "reconnect": 10,
79
- "webSocketURL": {},
80
- },
81
- "compress": true,
82
- "devMiddleware": {},
83
- "historyApiFallback": false,
84
- "host": undefined,
85
- "hot": true,
86
- "liveReload": true,
87
- "magicHtml": true,
88
- "open": [],
89
- "server": {
90
- "options": {},
91
- "type": "http",
92
- },
93
- "setupExitSignals": true,
94
- "setupMiddlewares": [Function],
95
- "static": [
96
- {
97
- "directory": "<PROJECT_ROOT>/public",
98
- "publicPath": [
99
- "/",
100
- ],
101
- "serveIndex": {
102
- "icons": true,
103
- },
104
- "staticOptions": {},
105
- "watch": {
106
- "alwaysStat": true,
107
- "atomic": false,
108
- "followSymlinks": false,
109
- "ignoreInitial": true,
110
- "ignorePermissionErrors": true,
111
- "ignored": undefined,
112
- "interval": undefined,
113
- "persistent": true,
114
- "usePolling": false,
115
- },
116
- },
117
- ],
118
- "watchFiles": [],
119
- "webSocketServer": {
120
- "options": {
121
- "path": "/ws",
122
- },
123
- "type": "ws",
124
- },
125
- }
126
- `;
127
-
128
- exports[`normalize options snapshot react-refresh client added when react/refresh enabled 1`] = `
129
- {
130
- "main": [
131
- "<prefix>/rspack-dev-client/dist/reactRefresh.js",
132
- "<prefix>/webpack/hot/dev-server.js",
133
- "<prefix>/webpack-dev-server/client/index.js?protocol=ws%3A&hostname=0.0.0.0&&pathname=%2Fws&logging=info&overlay=true&reconnect=10&hot=true&live-reload=true",
134
- "<prefix>/./placeholder.js",
135
- ],
136
- }
137
- `;