@rspack/dev-server 2.0.0-beta.5 → 2.0.0-beta.7

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/dist/index.js CHANGED
@@ -1 +1,1467 @@
1
- export { RspackDevServer } from "./131.js";
1
+ import * as __rspack_external_zlib from "zlib";
2
+ import { __webpack_require__ } from "./rslib-runtime.js";
3
+ import { lstatSync, promises, readFileSync, realpathSync, statSync, unlinkSync } from "node:fs";
4
+ import { createRequire } from "node:module";
5
+ import { networkInterfaces, tmpdir } from "node:os";
6
+ import { dirname, join, resolve as external_node_path_resolve } from "node:path";
7
+ import { format } from "node:url";
8
+ import { deprecate, styleText } from "node:util";
9
+ import { devMiddleware } from "@rspack/dev-middleware";
10
+ import ipaddr from "ipaddr.js";
11
+ import { createServer } from "node:net";
12
+ import { WebSocketServer } from "ws";
13
+ __webpack_require__.add({
14
+ "./node_modules/.pnpm/http-compression@1.1.2/node_modules/http-compression/src/index.js" (module, __unused_rspack_exports, __webpack_require__) {
15
+ const zlib = __webpack_require__("zlib");
16
+ const MIMES = /text|javascript|\/json|xml/i;
17
+ const noop = ()=>{};
18
+ const getChunkSize = (chunk, enc)=>chunk ? Buffer.byteLength(chunk, enc) : 0;
19
+ module.exports = ({ threshold = 1024, level = {
20
+ brotli: 0,
21
+ gzip: 1
22
+ }, brotli = true, gzip = true, mimes = MIMES } = {})=>{
23
+ const brotliOpts = 'object' == typeof brotli && brotli || {};
24
+ const gzipOpts = 'object' == typeof gzip && gzip || {};
25
+ if (brotli && !zlib.createBrotliCompress) brotli = false;
26
+ return (req, res, next = noop)=>{
27
+ const accept = req.headers['accept-encoding'];
28
+ const encoding = accept && (brotli && accept.match(/\bbr\b/) || gzip && accept.match(/\bgzip\b/) || [])[0];
29
+ if ('HEAD' === req.method || !encoding) return next();
30
+ let compress;
31
+ let pendingStatus;
32
+ let pendingListeners = [];
33
+ let started = false;
34
+ let size = 0;
35
+ function start() {
36
+ started = true;
37
+ size = 0 | res.getHeader('Content-Length') || size;
38
+ const compressible = mimes.test(String(res.getHeader('Content-Type') || 'text/plain'));
39
+ const cleartext = !res.getHeader('Content-Encoding');
40
+ const listeners = pendingListeners || [];
41
+ if (compressible && cleartext && size >= threshold) {
42
+ res.setHeader('Content-Encoding', encoding);
43
+ res.removeHeader('Content-Length');
44
+ compress = 'br' === encoding ? zlib.createBrotliCompress({
45
+ params: Object.assign({
46
+ [zlib.constants.BROTLI_PARAM_QUALITY]: level.brotli,
47
+ [zlib.constants.BROTLI_PARAM_SIZE_HINT]: size
48
+ }, brotliOpts)
49
+ }) : zlib.createGzip(Object.assign({
50
+ level: level.gzip
51
+ }, gzipOpts));
52
+ compress.on('data', (chunk)=>false === write.call(res, chunk) && compress.pause());
53
+ on.call(res, 'drain', ()=>compress.resume());
54
+ compress.on('end', ()=>end.call(res));
55
+ listeners.forEach((p)=>compress.on.apply(compress, p));
56
+ } else {
57
+ pendingListeners = null;
58
+ listeners.forEach((p)=>on.apply(res, p));
59
+ }
60
+ writeHead.call(res, pendingStatus || res.statusCode);
61
+ }
62
+ const { end, write, on, writeHead } = res;
63
+ res.writeHead = function(status, reason, headers) {
64
+ if ('string' != typeof reason) [headers, reason] = [
65
+ reason,
66
+ headers
67
+ ];
68
+ if (headers) for(const i in headers)res.setHeader(i, headers[i]);
69
+ pendingStatus = status;
70
+ return this;
71
+ };
72
+ res.write = function(chunk, enc) {
73
+ size += getChunkSize(chunk, enc);
74
+ if (!started) start();
75
+ if (!compress) return write.apply(this, arguments);
76
+ return compress.write.apply(compress, arguments);
77
+ };
78
+ res.end = function(chunk, enc) {
79
+ if (arguments.length > 0 && 'function' != typeof chunk) size += getChunkSize(chunk, enc);
80
+ if (!started) start();
81
+ if (!compress) return end.apply(this, arguments);
82
+ return compress.end.apply(compress, arguments);
83
+ };
84
+ res.on = function(type, listener) {
85
+ if (pendingListeners && 'drain' === type) if (compress) compress.on(type, listener);
86
+ else pendingListeners.push([
87
+ type,
88
+ listener
89
+ ]);
90
+ else on.call(this, type, listener);
91
+ return this;
92
+ };
93
+ next();
94
+ };
95
+ };
96
+ },
97
+ zlib (module) {
98
+ module.exports = __rspack_external_zlib;
99
+ }
100
+ });
101
+ var external_node_util_namespaceObject = {};
102
+ __webpack_require__.r(external_node_util_namespaceObject);
103
+ __webpack_require__.d(external_node_util_namespaceObject, {
104
+ deprecate: ()=>deprecate,
105
+ styleText: ()=>styleText
106
+ });
107
+ const minPort = 1024;
108
+ const maxPort = 65535;
109
+ const getLocalHosts = ()=>{
110
+ const interfaces = networkInterfaces();
111
+ const results = new Set([
112
+ void 0,
113
+ '0.0.0.0'
114
+ ]);
115
+ for (const _interface of Object.values(interfaces))if (_interface) for (const config of _interface)results.add(config.address);
116
+ return results;
117
+ };
118
+ const checkAvailablePort = (basePort, host)=>new Promise((resolve, reject)=>{
119
+ const server = createServer();
120
+ server.unref();
121
+ server.on('error', reject);
122
+ server.listen(basePort, host, ()=>{
123
+ const { port } = server.address();
124
+ server.close(()=>{
125
+ resolve(port);
126
+ });
127
+ });
128
+ });
129
+ const getAvailablePort = async (port, hosts)=>{
130
+ const nonExistentInterfaceErrors = new Set([
131
+ 'EADDRNOTAVAIL',
132
+ 'EINVAL'
133
+ ]);
134
+ for (const host of hosts)try {
135
+ await checkAvailablePort(port, host);
136
+ } catch (error) {
137
+ if (!nonExistentInterfaceErrors.has(error.code || '')) throw error;
138
+ }
139
+ return port;
140
+ };
141
+ async function getPort(basePort, host) {
142
+ if (basePort < minPort || basePort > maxPort) throw new Error(`Port number must lie between ${minPort} and ${maxPort}`);
143
+ let port = basePort;
144
+ const localhosts = getLocalHosts();
145
+ const hosts = host && !localhosts.has(host) ? new Set([
146
+ host
147
+ ]) : localhosts;
148
+ const portUnavailableErrors = new Set([
149
+ 'EADDRINUSE',
150
+ 'EACCES'
151
+ ]);
152
+ while(port <= maxPort)try {
153
+ const availablePort = await getAvailablePort(port, hosts);
154
+ return availablePort;
155
+ } catch (error) {
156
+ if (!portUnavailableErrors.has(error.code || '')) throw error;
157
+ port += 1;
158
+ }
159
+ throw new Error('No available ports found');
160
+ }
161
+ class BaseServer {
162
+ server;
163
+ clients;
164
+ constructor(server){
165
+ this.server = server;
166
+ this.clients = [];
167
+ }
168
+ }
169
+ const servers_BaseServer = BaseServer;
170
+ class WebsocketServer extends servers_BaseServer {
171
+ static heartbeatInterval = 1000;
172
+ implementation;
173
+ constructor(server){
174
+ super(server);
175
+ const options = {
176
+ ...this.server.options.webSocketServer.options,
177
+ clientTracking: false
178
+ };
179
+ const isNoServerMode = void 0 === options.port && void 0 === options.server;
180
+ if (isNoServerMode) options.noServer = true;
181
+ this.implementation = new WebSocketServer(options);
182
+ this.server.server.on('upgrade', (req, sock, head)=>{
183
+ if (!this.implementation.shouldHandle(req)) return;
184
+ this.implementation.handleUpgrade(req, sock, head, (connection)=>{
185
+ this.implementation.emit('connection', connection, req);
186
+ });
187
+ });
188
+ this.implementation.on('error', (err)=>{
189
+ this.server.logger.error(err.message);
190
+ });
191
+ const interval = setInterval(()=>{
192
+ for (const client of this.clients){
193
+ if (false === client.isAlive) {
194
+ client.terminate();
195
+ continue;
196
+ }
197
+ client.isAlive = false;
198
+ client.ping(()=>{});
199
+ }
200
+ }, WebsocketServer.heartbeatInterval);
201
+ this.implementation.on('connection', (client)=>{
202
+ this.clients.push(client);
203
+ client.isAlive = true;
204
+ client.on('pong', ()=>{
205
+ client.isAlive = true;
206
+ });
207
+ client.on('close', ()=>{
208
+ this.clients.splice(this.clients.indexOf(client), 1);
209
+ });
210
+ client.on('error', (err)=>{
211
+ this.server.logger.error(err.message);
212
+ });
213
+ });
214
+ this.implementation.on('close', ()=>{
215
+ clearInterval(interval);
216
+ });
217
+ }
218
+ }
219
+ const src = __webpack_require__("./node_modules/.pnpm/http-compression@1.1.2/node_modules/http-compression/src/index.js");
220
+ var src_default = /*#__PURE__*/ __webpack_require__.n(src);
221
+ const { styleText: server_styleText } = external_node_util_namespaceObject;
222
+ const server_require = createRequire(import.meta.url);
223
+ if (!process.env.WEBPACK_SERVE) process.env.WEBPACK_SERVE = 'true';
224
+ const memoize = (fn)=>{
225
+ let cache = false;
226
+ let result;
227
+ let fnRef = fn;
228
+ return ()=>{
229
+ if (cache) return result;
230
+ result = fnRef();
231
+ cache = true;
232
+ fnRef = void 0;
233
+ return result;
234
+ };
235
+ };
236
+ const getConnect = async ()=>{
237
+ const { connect } = await import("connect-next");
238
+ return connect;
239
+ };
240
+ const getChokidar = memoize(()=>import("chokidar"));
241
+ const getServeStatic = memoize(()=>server_require('serve-static'));
242
+ const encodeOverlaySettings = (setting)=>'function' == typeof setting ? encodeURIComponent(setting.toString()) : setting;
243
+ const DEFAULT_ALLOWED_PROTOCOLS = /^(file|.+-extension):/i;
244
+ function parseHostnameFromHeader(header) {
245
+ try {
246
+ const parsedUrl = new URL(/^(.+:)?\/\//.test(header) ? header : `//${header}`, 'http://localhost/');
247
+ let { hostname } = parsedUrl;
248
+ if (hostname.startsWith('[') && hostname.endsWith(']')) hostname = hostname.slice(1, -1);
249
+ return hostname;
250
+ } catch {
251
+ return null;
252
+ }
253
+ }
254
+ function isMultiCompiler(compiler) {
255
+ return Array.isArray(compiler.compilers);
256
+ }
257
+ class Server {
258
+ compiler;
259
+ logger;
260
+ options;
261
+ staticWatchers;
262
+ listeners;
263
+ webSocketProxies;
264
+ sockets;
265
+ currentHash;
266
+ isTlsServer = false;
267
+ webSocketServer;
268
+ middleware;
269
+ server;
270
+ app;
271
+ stats;
272
+ constructor(options, compiler){
273
+ this.compiler = compiler;
274
+ this.logger = this.compiler.getInfrastructureLogger('rspack-dev-server');
275
+ this.options = options;
276
+ this.staticWatchers = [];
277
+ this.listeners = [];
278
+ this.webSocketProxies = [];
279
+ this.sockets = [];
280
+ this.currentHash = void 0;
281
+ }
282
+ static get DEFAULT_STATS() {
283
+ return {
284
+ all: false,
285
+ hash: true,
286
+ warnings: true,
287
+ errors: true,
288
+ errorDetails: false
289
+ };
290
+ }
291
+ static isAbsoluteURL(URL1) {
292
+ if (/^[a-zA-Z]:\\/.test(URL1)) return false;
293
+ return /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(URL1);
294
+ }
295
+ static findIp(gatewayOrFamily, isInternal) {
296
+ if ('v4' === gatewayOrFamily || 'v6' === gatewayOrFamily) {
297
+ let host;
298
+ const networks = Object.values(networkInterfaces()).flatMap((networks)=>networks ?? []).filter((network)=>{
299
+ if (!network || !network.address) return false;
300
+ if (network.family !== `IP${gatewayOrFamily}`) return false;
301
+ if (void 0 !== isInternal && network.internal !== isInternal) return false;
302
+ if ('v6' === gatewayOrFamily) {
303
+ const range = ipaddr.parse(network.address).range();
304
+ if ('ipv4Mapped' !== range && 'uniqueLocal' !== range && 'loopback' !== range) return false;
305
+ }
306
+ return network.address;
307
+ });
308
+ if (networks.length > 0) {
309
+ host = networks[0].address;
310
+ if (host.includes(':')) host = `[${host}]`;
311
+ }
312
+ return host;
313
+ }
314
+ const gatewayIp = ipaddr.parse(gatewayOrFamily);
315
+ for (const addresses of Object.values(networkInterfaces()))for (const { cidr } of addresses){
316
+ const net = ipaddr.parseCIDR(cidr);
317
+ if (net[0] && net[0].kind() === gatewayIp.kind() && gatewayIp.match(net)) return net[0].toString();
318
+ }
319
+ }
320
+ static async getHostname(hostname) {
321
+ if ('local-ip' === hostname) return Server.findIp('v4', false) || Server.findIp('v6', false) || '0.0.0.0';
322
+ if ('local-ipv4' === hostname) return Server.findIp('v4', false) || '0.0.0.0';
323
+ if ('local-ipv6' === hostname) return Server.findIp('v6', false) || '::';
324
+ return hostname;
325
+ }
326
+ static async getFreePort(port, host) {
327
+ if (null != port && 'auto' !== port) return port;
328
+ const { default: pRetry } = await import("./0~p-retry.js");
329
+ const basePort = void 0 !== process.env.RSPACK_DEV_SERVER_BASE_PORT ? Number.parseInt(process.env.RSPACK_DEV_SERVER_BASE_PORT, 10) : 8080;
330
+ const defaultPortRetry = void 0 !== process.env.RSPACK_DEV_SERVER_PORT_RETRY ? Number.parseInt(process.env.RSPACK_DEV_SERVER_PORT_RETRY, 10) : 3;
331
+ return pRetry(()=>getPort(basePort, host), {
332
+ retries: defaultPortRetry
333
+ });
334
+ }
335
+ static findCacheDir() {
336
+ const cwd = process.cwd();
337
+ let dir = cwd;
338
+ for(;;){
339
+ try {
340
+ if (statSync(join(dir, 'package.json')).isFile()) break;
341
+ } catch {}
342
+ const parent = dirname(dir);
343
+ if (dir === parent) {
344
+ dir = void 0;
345
+ break;
346
+ }
347
+ dir = parent;
348
+ }
349
+ if (!dir) return external_node_path_resolve(cwd, '.cache/rspack-dev-server');
350
+ if ('1' === process.versions.pnp) return external_node_path_resolve(dir, '.pnp/.cache/rspack-dev-server');
351
+ if ('3' === process.versions.pnp) return external_node_path_resolve(dir, '.yarn/.cache/rspack-dev-server');
352
+ return external_node_path_resolve(dir, 'node_modules/.cache/rspack-dev-server');
353
+ }
354
+ #addAdditionalEntries(compiler) {
355
+ const additionalEntries = [];
356
+ const isWebTarget = Boolean(compiler.platform.web);
357
+ if (this.options.client && isWebTarget) {
358
+ let webSocketURLStr = '';
359
+ if (this.options.webSocketServer) {
360
+ const webSocketURL = this.options.client.webSocketURL;
361
+ const webSocketServer = this.options.webSocketServer;
362
+ const searchParams = new URLSearchParams();
363
+ let protocol;
364
+ protocol = void 0 !== webSocketURL.protocol ? webSocketURL.protocol : this.isTlsServer ? 'wss:' : 'ws:';
365
+ searchParams.set('protocol', protocol);
366
+ if (void 0 !== webSocketURL.username) searchParams.set('username', webSocketURL.username);
367
+ if (void 0 !== webSocketURL.password) searchParams.set('password', webSocketURL.password);
368
+ let hostname;
369
+ const isWebSocketServerHostDefined = void 0 !== webSocketServer.options.host;
370
+ const isWebSocketServerPortDefined = void 0 !== webSocketServer.options.port;
371
+ hostname = void 0 !== webSocketURL.hostname ? webSocketURL.hostname : isWebSocketServerHostDefined ? webSocketServer.options.host : void 0 !== this.options.host ? this.options.host : '0.0.0.0';
372
+ searchParams.set('hostname', hostname);
373
+ let port;
374
+ port = void 0 !== webSocketURL.port ? webSocketURL.port : isWebSocketServerPortDefined ? webSocketServer.options.port : 'number' == typeof this.options.port ? this.options.port : 'string' == typeof this.options.port && 'auto' !== this.options.port ? Number(this.options.port) : '0';
375
+ searchParams.set('port', String(port));
376
+ let pathname = '';
377
+ if (void 0 !== webSocketURL.pathname) pathname = webSocketURL.pathname;
378
+ else if (void 0 !== webSocketServer.options.path) pathname = webSocketServer.options.path;
379
+ searchParams.set('pathname', pathname);
380
+ const client = this.options.client;
381
+ if (void 0 !== client.logging) searchParams.set('logging', client.logging);
382
+ if (void 0 !== client.progress) searchParams.set('progress', String(client.progress));
383
+ if (void 0 !== client.overlay) {
384
+ const overlayString = 'boolean' == typeof client.overlay ? String(client.overlay) : JSON.stringify({
385
+ ...client.overlay,
386
+ errors: encodeOverlaySettings(client.overlay.errors),
387
+ warnings: encodeOverlaySettings(client.overlay.warnings),
388
+ runtimeErrors: encodeOverlaySettings(client.overlay.runtimeErrors)
389
+ });
390
+ searchParams.set('overlay', overlayString);
391
+ }
392
+ if (void 0 !== client.reconnect) searchParams.set('reconnect', 'number' == typeof client.reconnect ? String(client.reconnect) : '10');
393
+ if (void 0 !== this.options.hot) searchParams.set('hot', String(this.options.hot));
394
+ if (void 0 !== this.options.liveReload) searchParams.set('live-reload', String(this.options.liveReload));
395
+ webSocketURLStr = searchParams.toString();
396
+ }
397
+ additionalEntries.push(`${this.getClientEntry()}?${webSocketURLStr}`);
398
+ }
399
+ const clientHotEntry = this.getClientHotEntry();
400
+ if (clientHotEntry) additionalEntries.push(clientHotEntry);
401
+ for (const additionalEntry of additionalEntries)new compiler.rspack.EntryPlugin(compiler.context, additionalEntry, {
402
+ name: void 0
403
+ }).apply(compiler);
404
+ }
405
+ #getCompilerOptions() {
406
+ if (void 0 !== this.compiler.compilers) {
407
+ if (1 === this.compiler.compilers.length) return this.compiler.compilers[0].options;
408
+ const compilerWithDevServer = this.compiler.compilers.find((config)=>config.options.devServer);
409
+ if (compilerWithDevServer) return compilerWithDevServer.options;
410
+ const compilerWithWebTarget = this.compiler.compilers.find((compiler)=>Boolean(compiler.platform.web));
411
+ if (compilerWithWebTarget) return compilerWithWebTarget.options;
412
+ return this.compiler.compilers[0].options;
413
+ }
414
+ return this.compiler.options;
415
+ }
416
+ #shouldLogInfrastructureInfo() {
417
+ const compilerOptions = this.#getCompilerOptions();
418
+ const { level = 'info' } = compilerOptions.infrastructureLogging || {};
419
+ return 'info' === level || 'log' === level || 'verbose' === level;
420
+ }
421
+ async #normalizeOptions() {
422
+ const { options } = this;
423
+ const compilerOptions = this.#getCompilerOptions();
424
+ const compilerWatchOptions = compilerOptions.watchOptions;
425
+ const getWatchOptions = (watchOptions = {})=>{
426
+ const getPolling = ()=>{
427
+ if (void 0 !== watchOptions.usePolling) return watchOptions.usePolling;
428
+ if (void 0 !== watchOptions.poll) return Boolean(watchOptions.poll);
429
+ if (void 0 !== compilerWatchOptions.poll) return Boolean(compilerWatchOptions.poll);
430
+ return false;
431
+ };
432
+ const getInterval = ()=>{
433
+ if (void 0 !== watchOptions.interval) return watchOptions.interval;
434
+ if ('number' == typeof watchOptions.poll) return watchOptions.poll;
435
+ if ('number' == typeof compilerWatchOptions.poll) return compilerWatchOptions.poll;
436
+ };
437
+ const usePolling = getPolling();
438
+ const interval = getInterval();
439
+ const { poll: _poll, ...rest } = watchOptions;
440
+ return {
441
+ ignoreInitial: true,
442
+ persistent: true,
443
+ followSymlinks: false,
444
+ atomic: false,
445
+ alwaysStat: true,
446
+ ignorePermissionErrors: true,
447
+ usePolling,
448
+ ...void 0 !== interval ? {
449
+ interval
450
+ } : {},
451
+ ...rest
452
+ };
453
+ };
454
+ const getStaticItem = (optionsForStatic)=>{
455
+ const getDefaultStaticOptions = ()=>({
456
+ directory: join(process.cwd(), 'public'),
457
+ staticOptions: {},
458
+ publicPath: [
459
+ '/'
460
+ ],
461
+ watch: getWatchOptions()
462
+ });
463
+ let item;
464
+ if (void 0 === optionsForStatic) item = getDefaultStaticOptions();
465
+ else if ('string' == typeof optionsForStatic) item = {
466
+ ...getDefaultStaticOptions(),
467
+ directory: optionsForStatic
468
+ };
469
+ else {
470
+ const def = getDefaultStaticOptions();
471
+ item = {
472
+ directory: void 0 !== optionsForStatic.directory ? optionsForStatic.directory : def.directory,
473
+ staticOptions: void 0 !== optionsForStatic.staticOptions ? {
474
+ ...def.staticOptions,
475
+ ...optionsForStatic.staticOptions
476
+ } : def.staticOptions,
477
+ publicPath: void 0 !== optionsForStatic.publicPath ? Array.isArray(optionsForStatic.publicPath) ? optionsForStatic.publicPath : [
478
+ optionsForStatic.publicPath
479
+ ] : def.publicPath,
480
+ watch: void 0 !== optionsForStatic.watch ? 'boolean' == typeof optionsForStatic.watch ? optionsForStatic.watch ? def.watch : false : getWatchOptions(optionsForStatic.watch) : def.watch
481
+ };
482
+ }
483
+ if (Server.isAbsoluteURL(item.directory)) throw new Error('Using a URL as static.directory is not supported');
484
+ return item;
485
+ };
486
+ if (void 0 === options.allowedHosts) options.allowedHosts = 'auto';
487
+ else if ('string' == typeof options.allowedHosts && 'auto' !== options.allowedHosts && 'all' !== options.allowedHosts) options.allowedHosts = [
488
+ options.allowedHosts
489
+ ];
490
+ else if (Array.isArray(options.allowedHosts) && options.allowedHosts.includes('all')) options.allowedHosts = 'all';
491
+ if (void 0 === options.client || 'object' == typeof options.client && null !== options.client) {
492
+ if (!options.client) options.client = {};
493
+ if (void 0 === options.client.webSocketURL) options.client.webSocketURL = {};
494
+ else if ('string' == typeof options.client.webSocketURL) {
495
+ const parsedURL = new URL(options.client.webSocketURL);
496
+ options.client.webSocketURL = {
497
+ protocol: parsedURL.protocol,
498
+ hostname: parsedURL.hostname,
499
+ port: parsedURL.port.length > 0 ? Number(parsedURL.port) : '',
500
+ pathname: parsedURL.pathname,
501
+ username: parsedURL.username,
502
+ password: parsedURL.password
503
+ };
504
+ } else if ('string' == typeof options.client.webSocketURL.port) options.client.webSocketURL.port = Number(options.client.webSocketURL.port);
505
+ if (void 0 === options.client.overlay) options.client.overlay = true;
506
+ else if ('boolean' != typeof options.client.overlay) options.client.overlay = {
507
+ errors: true,
508
+ warnings: true,
509
+ ...options.client.overlay
510
+ };
511
+ if (void 0 === options.client.reconnect) options.client.reconnect = 10;
512
+ else if (true === options.client.reconnect) options.client.reconnect = 1 / 0;
513
+ else if (false === options.client.reconnect) options.client.reconnect = 0;
514
+ if (void 0 === options.client.logging) options.client.logging = compilerOptions.infrastructureLogging ? compilerOptions.infrastructureLogging.level : 'info';
515
+ }
516
+ if (void 0 === options.compress) options.compress = true;
517
+ if (void 0 === options.devMiddleware) options.devMiddleware = {};
518
+ if (void 0 === options.historyApiFallback) options.historyApiFallback = false;
519
+ else if ('boolean' == typeof options.historyApiFallback && options.historyApiFallback) options.historyApiFallback = {};
520
+ options.hot = 'boolean' == typeof options.hot || 'only' === options.hot ? options.hot : true;
521
+ if ('function' == typeof options.server || 'string' == typeof options.server) options.server = {
522
+ type: options.server,
523
+ options: {}
524
+ };
525
+ else {
526
+ const serverOptions = options.server || {};
527
+ options.server = {
528
+ type: serverOptions.type || 'http',
529
+ options: {
530
+ ...serverOptions.options
531
+ }
532
+ };
533
+ }
534
+ const serverOptions = options.server.options;
535
+ if ('https' === options.server.type || 'http2' === options.server.type) {
536
+ if (void 0 === serverOptions.requestCert) serverOptions.requestCert = false;
537
+ const httpsProperties = [
538
+ 'ca',
539
+ 'cert',
540
+ 'crl',
541
+ 'key',
542
+ 'pfx'
543
+ ];
544
+ for (const property of httpsProperties){
545
+ if (void 0 === serverOptions[property]) continue;
546
+ const value = serverOptions[property];
547
+ const readFile = (item)=>{
548
+ if (Buffer.isBuffer(item) || 'object' == typeof item && null !== item && !Array.isArray(item)) return item;
549
+ if (item) {
550
+ let stats = null;
551
+ try {
552
+ stats = lstatSync(realpathSync(item)).isFile();
553
+ } catch (error) {}
554
+ return stats ? readFileSync(item) : item;
555
+ }
556
+ };
557
+ serverOptions[property] = Array.isArray(value) ? value.map((item)=>readFile(item)) : readFile(value);
558
+ }
559
+ let fakeCert;
560
+ if (!serverOptions.key || !serverOptions.cert) {
561
+ const certificateDir = Server.findCacheDir();
562
+ const certificatePath = join(certificateDir, 'server.pem');
563
+ let certificateExists;
564
+ try {
565
+ const certificate = await promises.stat(certificatePath);
566
+ certificateExists = certificate.isFile();
567
+ } catch {
568
+ certificateExists = false;
569
+ }
570
+ if (certificateExists) {
571
+ const certificateTtl = 86400000;
572
+ const certificateStat = await promises.stat(certificatePath);
573
+ const now = Number(new Date());
574
+ if ((now - Number(certificateStat.ctime)) / certificateTtl > 30) {
575
+ this.logger.info('SSL certificate is more than 30 days old. Removing...');
576
+ await promises.rm(certificatePath, {
577
+ recursive: true
578
+ });
579
+ certificateExists = false;
580
+ }
581
+ }
582
+ if (!certificateExists) {
583
+ this.logger.info('Generating SSL certificate...');
584
+ let selfsigned;
585
+ try {
586
+ selfsigned = server_require('selfsigned');
587
+ } catch (error) {
588
+ if (error instanceof Error && 'MODULE_NOT_FOUND' === error.code) throw new Error('Cannot generate a self-signed certificate because optional peer dependency `selfsigned@^5.0.0` is not installed. Please install it and try again.');
589
+ throw error;
590
+ }
591
+ const attributes = [
592
+ {
593
+ name: 'commonName',
594
+ value: 'localhost'
595
+ }
596
+ ];
597
+ const notBeforeDate = new Date();
598
+ const notAfterDate = new Date(notBeforeDate);
599
+ notAfterDate.setDate(notAfterDate.getDate() + 30);
600
+ const pems = await selfsigned.generate(attributes, {
601
+ algorithm: 'sha256',
602
+ keySize: 2048,
603
+ notBeforeDate,
604
+ notAfterDate,
605
+ extensions: [
606
+ {
607
+ name: 'basicConstraints',
608
+ cA: true
609
+ },
610
+ {
611
+ name: 'keyUsage',
612
+ keyCertSign: true,
613
+ digitalSignature: true,
614
+ nonRepudiation: true,
615
+ keyEncipherment: true,
616
+ dataEncipherment: true
617
+ },
618
+ {
619
+ name: 'extKeyUsage',
620
+ serverAuth: true,
621
+ clientAuth: true,
622
+ codeSigning: true,
623
+ timeStamping: true
624
+ },
625
+ {
626
+ name: 'subjectAltName',
627
+ altNames: [
628
+ {
629
+ type: 2,
630
+ value: 'localhost'
631
+ },
632
+ {
633
+ type: 2,
634
+ value: 'localhost.localdomain'
635
+ },
636
+ {
637
+ type: 2,
638
+ value: 'lvh.me'
639
+ },
640
+ {
641
+ type: 2,
642
+ value: '*.lvh.me'
643
+ },
644
+ {
645
+ type: 2,
646
+ value: '[::1]'
647
+ },
648
+ {
649
+ type: 7,
650
+ ip: '127.0.0.1'
651
+ },
652
+ {
653
+ type: 7,
654
+ ip: 'fe80::1'
655
+ }
656
+ ]
657
+ }
658
+ ]
659
+ });
660
+ await promises.mkdir(certificateDir, {
661
+ recursive: true
662
+ });
663
+ await promises.writeFile(certificatePath, pems.private + pems.cert, {
664
+ encoding: 'utf8'
665
+ });
666
+ }
667
+ fakeCert = await promises.readFile(certificatePath);
668
+ this.logger.info(`SSL certificate: ${certificatePath}`);
669
+ }
670
+ serverOptions.key = serverOptions.key || fakeCert;
671
+ serverOptions.cert = serverOptions.cert || fakeCert;
672
+ }
673
+ if ('boolean' == typeof options.ipc) {
674
+ const isWindows = 'win32' === process.platform;
675
+ const pipePrefix = isWindows ? '\\\\.\\pipe\\' : tmpdir();
676
+ const pipeName = 'webpack-dev-server.sock';
677
+ options.ipc = join(pipePrefix, pipeName);
678
+ }
679
+ options.liveReload = void 0 !== options.liveReload ? options.liveReload : true;
680
+ const defaultOpenOptions = {
681
+ wait: false
682
+ };
683
+ const getOpenItemsFromObject = ({ target, ...rest })=>{
684
+ const normalizedOptions = {
685
+ ...defaultOpenOptions,
686
+ ...rest
687
+ };
688
+ if ('string' == typeof normalizedOptions.app) normalizedOptions.app = {
689
+ name: normalizedOptions.app
690
+ };
691
+ const normalizedTarget = void 0 === target ? '<url>' : target;
692
+ if (Array.isArray(normalizedTarget)) return normalizedTarget.map((singleTarget)=>({
693
+ target: singleTarget,
694
+ options: normalizedOptions
695
+ }));
696
+ return [
697
+ {
698
+ target: normalizedTarget,
699
+ options: normalizedOptions
700
+ }
701
+ ];
702
+ };
703
+ let normalizedOpens = [];
704
+ if (void 0 === options.open) ;
705
+ else if ('boolean' == typeof options.open) {
706
+ if (options.open) normalizedOpens = [
707
+ {
708
+ target: '<url>',
709
+ options: defaultOpenOptions
710
+ }
711
+ ];
712
+ } else if ('string' == typeof options.open) normalizedOpens = [
713
+ {
714
+ target: options.open,
715
+ options: defaultOpenOptions
716
+ }
717
+ ];
718
+ else if (Array.isArray(options.open)) for (const item of options.open){
719
+ if ('string' == typeof item) {
720
+ normalizedOpens.push({
721
+ target: item,
722
+ options: defaultOpenOptions
723
+ });
724
+ continue;
725
+ }
726
+ normalizedOpens.push(...getOpenItemsFromObject(item));
727
+ }
728
+ else normalizedOpens = [
729
+ ...getOpenItemsFromObject(options.open)
730
+ ];
731
+ options.open = normalizedOpens;
732
+ if ('string' == typeof options.port && 'auto' !== options.port) options.port = Number(options.port);
733
+ if (void 0 === options.setupExitSignals) options.setupExitSignals = true;
734
+ if (void 0 === options.static) options.static = [
735
+ getStaticItem()
736
+ ];
737
+ else if ('boolean' == typeof options.static) options.static = options.static ? [
738
+ getStaticItem()
739
+ ] : false;
740
+ else if ('string' == typeof options.static) options.static = [
741
+ getStaticItem(options.static)
742
+ ];
743
+ else if (Array.isArray(options.static)) options.static = options.static.map((item)=>getStaticItem(item));
744
+ else options.static = [
745
+ getStaticItem(options.static)
746
+ ];
747
+ if ('string' == typeof options.watchFiles) options.watchFiles = [
748
+ {
749
+ paths: options.watchFiles,
750
+ options: getWatchOptions()
751
+ }
752
+ ];
753
+ else if ('object' != typeof options.watchFiles || null === options.watchFiles || Array.isArray(options.watchFiles)) if (Array.isArray(options.watchFiles)) options.watchFiles = options.watchFiles.map((item)=>{
754
+ if ('string' == typeof item) return {
755
+ paths: item,
756
+ options: getWatchOptions()
757
+ };
758
+ return {
759
+ paths: item.paths,
760
+ options: getWatchOptions(item.options || {})
761
+ };
762
+ });
763
+ else options.watchFiles = [];
764
+ else options.watchFiles = [
765
+ {
766
+ paths: options.watchFiles.paths,
767
+ options: getWatchOptions(options.watchFiles.options || {})
768
+ }
769
+ ];
770
+ const defaultWebSocketServerType = 'ws';
771
+ const defaultWebSocketServerOptions = {
772
+ path: '/ws'
773
+ };
774
+ if (void 0 === options.webSocketServer) options.webSocketServer = {
775
+ type: defaultWebSocketServerType,
776
+ options: defaultWebSocketServerOptions
777
+ };
778
+ else if ('boolean' != typeof options.webSocketServer || options.webSocketServer) if ('string' == typeof options.webSocketServer || 'function' == typeof options.webSocketServer) options.webSocketServer = {
779
+ type: options.webSocketServer,
780
+ options: defaultWebSocketServerOptions
781
+ };
782
+ else {
783
+ options.webSocketServer = {
784
+ type: options.webSocketServer.type || defaultWebSocketServerType,
785
+ options: {
786
+ ...defaultWebSocketServerOptions,
787
+ ...options.webSocketServer.options
788
+ }
789
+ };
790
+ const webSocketServer = options.webSocketServer;
791
+ if ('string' == typeof webSocketServer.options.port) webSocketServer.options.port = Number(webSocketServer.options.port);
792
+ }
793
+ else options.webSocketServer = false;
794
+ }
795
+ #getClientTransport() {
796
+ let clientImplementation;
797
+ let clientImplementationFound = true;
798
+ const isKnownWebSocketServerImplementation = this.options.webSocketServer && 'string' == typeof this.options.webSocketServer.type && 'ws' === this.options.webSocketServer.type;
799
+ let clientTransport;
800
+ clientTransport = this.options.client ? void 0 !== this.options.client.webSocketTransport ? this.options.client.webSocketTransport : isKnownWebSocketServerImplementation ? this.options.webSocketServer.type : 'ws' : 'ws';
801
+ switch(typeof clientTransport){
802
+ case 'string':
803
+ if ('sockjs' === clientTransport) throw new Error("SockJS support has been removed. Please set client.webSocketTransport to 'ws' or provide a custom transport implementation path.");
804
+ if ('ws' === clientTransport) clientImplementation = server_require.resolve('../client/clients/WebSocketClient');
805
+ else try {
806
+ clientImplementation = server_require.resolve(clientTransport);
807
+ } catch {
808
+ clientImplementationFound = false;
809
+ }
810
+ break;
811
+ default:
812
+ clientImplementationFound = false;
813
+ }
814
+ if (!clientImplementationFound) throw new Error(`${!isKnownWebSocketServerImplementation ? 'When you use custom web socket implementation you must explicitly specify client.webSocketTransport. ' : ''}client.webSocketTransport must be a string denoting a default implementation (e.g. 'ws') or a full path to a JS file via require.resolve(...) which exports a class `);
815
+ return clientImplementation;
816
+ }
817
+ #getServerTransport() {
818
+ let implementation;
819
+ let implementationFound = true;
820
+ switch(typeof this.options.webSocketServer.type){
821
+ case 'string':
822
+ if ('sockjs' === this.options.webSocketServer.type) throw new Error("SockJS support has been removed. Please set webSocketServer to 'ws' or provide a custom WebSocket server implementation.");
823
+ if ('ws' === this.options.webSocketServer.type) implementation = WebsocketServer;
824
+ else try {
825
+ implementation = server_require(this.options.webSocketServer.type);
826
+ } catch {
827
+ implementationFound = false;
828
+ }
829
+ break;
830
+ case 'function':
831
+ implementation = this.options.webSocketServer.type;
832
+ break;
833
+ default:
834
+ implementationFound = false;
835
+ }
836
+ if (!implementationFound) throw new Error("webSocketServer (webSocketServer.type) must be a string denoting a default implementation (e.g. 'ws'), a full path to a JS file which exports a class extending BaseServer (webpack-dev-server/lib/servers/BaseServer.js) via require.resolve(...), or the class itself which extends BaseServer");
837
+ return implementation;
838
+ }
839
+ getClientEntry() {
840
+ return server_require.resolve('@rspack/dev-server/client/index');
841
+ }
842
+ getClientHotEntry() {
843
+ if ('only' === this.options.hot) return server_require.resolve('@rspack/core/hot/only-dev-server');
844
+ if (this.options.hot) return server_require.resolve('@rspack/core/hot/dev-server');
845
+ }
846
+ #setupProgressPlugin() {
847
+ const { ProgressPlugin } = this.compiler.compilers ? this.compiler.compilers[0].webpack : this.compiler.webpack;
848
+ new ProgressPlugin((percent, msg)=>{
849
+ const percentValue = Math.floor(100 * percent);
850
+ let msgValue = msg;
851
+ if (100 === percentValue) msgValue = 'Compilation completed';
852
+ const payload = {
853
+ percent: percentValue,
854
+ msg: msgValue
855
+ };
856
+ if (this.webSocketServer) this.sendMessage(this.webSocketServer.clients, 'progress-update', payload);
857
+ if (this.server) this.server.emit('progress-update', payload);
858
+ }).apply(this.compiler);
859
+ }
860
+ async #initialize() {
861
+ const compilers = isMultiCompiler(this.compiler) ? this.compiler.compilers : [
862
+ this.compiler
863
+ ];
864
+ for (const compiler of compilers){
865
+ const mode = compiler.options.mode || process.env.NODE_ENV;
866
+ if (this.options.hot) {
867
+ if ('production' === mode) this.logger.warn("Hot Module Replacement (HMR) is enabled for the production build. \nMake sure to disable HMR for production by setting `devServer.hot` to `false` in the configuration.");
868
+ compiler.options.resolve.alias = {
869
+ 'ansi-html-community': server_require.resolve('@rspack/dev-server/client/utils/ansiHTML'),
870
+ ...compiler.options.resolve.alias
871
+ };
872
+ }
873
+ }
874
+ this.#setupHooks();
875
+ await this.#setupApp();
876
+ await this.#createServer();
877
+ if (this.options.webSocketServer) {
878
+ const compilers = this.compiler.compilers || [
879
+ this.compiler
880
+ ];
881
+ for (const compiler of compilers){
882
+ if (false === compiler.options.devServer) continue;
883
+ this.#addAdditionalEntries(compiler);
884
+ const { ProvidePlugin, HotModuleReplacementPlugin } = compiler.rspack;
885
+ new ProvidePlugin({
886
+ __rspack_dev_server_client__: this.#getClientTransport()
887
+ }).apply(compiler);
888
+ if (this.options.hot) {
889
+ const HMRPluginExists = compiler.options.plugins.find((plugin)=>plugin && plugin.constructor === HotModuleReplacementPlugin);
890
+ if (HMRPluginExists) this.logger.warn('"hot: true" automatically applies HMR plugin, you don\'t have to add it manually to your webpack configuration.');
891
+ else {
892
+ const plugin = new HotModuleReplacementPlugin();
893
+ plugin.apply(compiler);
894
+ }
895
+ }
896
+ }
897
+ if (this.options.client && this.options.client.progress) this.#setupProgressPlugin();
898
+ }
899
+ await this.#setupWatchFiles();
900
+ await this.#setupWatchStaticFiles();
901
+ await this.#setupMiddlewares();
902
+ if (this.options.setupExitSignals) {
903
+ const signals = [
904
+ 'SIGINT',
905
+ 'SIGTERM'
906
+ ];
907
+ let needForceShutdown = false;
908
+ for (const signal of signals){
909
+ const listener = ()=>{
910
+ if (needForceShutdown) process.exit();
911
+ this.logger.info('Gracefully shutting down. Press ^C again to force exit...');
912
+ needForceShutdown = true;
913
+ this.stopCallback(()=>{
914
+ if ('function' == typeof this.compiler.close) this.compiler.close(()=>{
915
+ process.exit();
916
+ });
917
+ else process.exit();
918
+ });
919
+ };
920
+ this.listeners.push({
921
+ name: signal,
922
+ listener
923
+ });
924
+ process.on(signal, listener);
925
+ }
926
+ }
927
+ const webSocketProxies = this.webSocketProxies;
928
+ for (const webSocketProxy of webSocketProxies)this.server.on('upgrade', webSocketProxy.upgrade);
929
+ }
930
+ async #setupApp() {
931
+ this.app = 'function' == typeof this.options.app ? await this.options.app() : (await getConnect())();
932
+ }
933
+ #getStats(statsObj) {
934
+ const stats = Server.DEFAULT_STATS;
935
+ const compilerOptions = this.#getCompilerOptions();
936
+ if (compilerOptions.stats && compilerOptions.stats.warningsFilter) stats.warningsFilter = compilerOptions.stats.warningsFilter;
937
+ return statsObj.toJson(stats);
938
+ }
939
+ #setupHooks() {
940
+ this.compiler.hooks.invalid.tap('rspack-dev-server', ()=>{
941
+ if (this.webSocketServer) this.sendMessage(this.webSocketServer.clients, 'invalid');
942
+ });
943
+ this.compiler.hooks.done.tap('rspack-dev-server', (stats)=>{
944
+ if (this.webSocketServer) this.#sendStats(this.webSocketServer.clients, this.#getStats(stats));
945
+ this.stats = stats;
946
+ });
947
+ }
948
+ async #setupWatchStaticFiles() {
949
+ const watchFiles = this.options.static;
950
+ if (watchFiles.length > 0) {
951
+ for (const item of watchFiles)if (item.watch) await this.watchFiles(item.directory, item.watch);
952
+ }
953
+ }
954
+ async #setupWatchFiles() {
955
+ const watchFiles = this.options.watchFiles;
956
+ if (watchFiles.length > 0) for (const item of watchFiles)await this.watchFiles(item.paths, item.options);
957
+ }
958
+ async #setupMiddlewares() {
959
+ let middlewares = [];
960
+ middlewares.push({
961
+ name: 'host-header-check',
962
+ middleware: (req, res, next)=>{
963
+ const headers = req.headers;
964
+ const headerName = headers[':authority'] ? ':authority' : 'host';
965
+ if (this.isValidHost(headers, headerName)) return void next();
966
+ res.statusCode = 403;
967
+ res.end('Invalid Host header');
968
+ }
969
+ });
970
+ middlewares.push({
971
+ name: 'cross-origin-header-check',
972
+ middleware: (req, res, next)=>{
973
+ const headers = req.headers;
974
+ const headerName = headers[':authority'] ? ':authority' : 'host';
975
+ if (this.isValidHost(headers, headerName, false)) return void next();
976
+ if ('no-cors' === headers['sec-fetch-mode'] && 'cross-site' === headers['sec-fetch-site']) {
977
+ res.statusCode = 403;
978
+ res.end('Cross-Origin request blocked');
979
+ return;
980
+ }
981
+ next();
982
+ }
983
+ });
984
+ const isHTTP2 = 'http2' === this.options.server.type;
985
+ if (isHTTP2) middlewares.push({
986
+ name: 'http2-status-message-patch',
987
+ middleware: (_req, res, next)=>{
988
+ Object.defineProperty(res, 'statusMessage', {
989
+ get () {
990
+ return '';
991
+ },
992
+ set () {}
993
+ });
994
+ next();
995
+ }
996
+ });
997
+ if (this.options.compress) middlewares.push({
998
+ name: 'compression',
999
+ middleware: src_default()()
1000
+ });
1001
+ if (void 0 !== this.options.headers) middlewares.push({
1002
+ name: 'set-headers',
1003
+ middleware: this.#setHeaders.bind(this)
1004
+ });
1005
+ middlewares.push({
1006
+ name: '@rspack/dev-middleware',
1007
+ middleware: this.middleware
1008
+ });
1009
+ middlewares.push({
1010
+ name: 'rspack-dev-server-invalidate',
1011
+ path: '/rspack-dev-server/invalidate',
1012
+ middleware: (req, res, next)=>{
1013
+ if ('GET' !== req.method && 'HEAD' !== req.method) return void next();
1014
+ this.invalidate();
1015
+ res.end();
1016
+ }
1017
+ });
1018
+ middlewares.push({
1019
+ name: 'rspack-dev-server-open-editor',
1020
+ path: '/rspack-dev-server/open-editor',
1021
+ middleware: async (req, res, next)=>{
1022
+ if ('GET' !== req.method && 'HEAD' !== req.method) return void next();
1023
+ if (!req.url) return void next();
1024
+ const resolveUrl = new URL(req.url, `http://${req.headers.host}`);
1025
+ const params = new URLSearchParams(resolveUrl.search);
1026
+ const fileName = params.get('fileName');
1027
+ if ('string' == typeof fileName) {
1028
+ const { default: launchEditor } = await import("./0~launch-editor.js").then(__webpack_require__.t.bind(__webpack_require__, "./node_modules/.pnpm/launch-editor@2.13.1/node_modules/launch-editor/index.js", 23));
1029
+ launchEditor(fileName);
1030
+ }
1031
+ res.end();
1032
+ }
1033
+ });
1034
+ middlewares.push({
1035
+ name: 'rspack-dev-server-assets',
1036
+ path: '/rspack-dev-server',
1037
+ middleware: (req, res, next)=>{
1038
+ if ('GET' !== req.method && 'HEAD' !== req.method) return void next();
1039
+ if (!this.middleware) return void next();
1040
+ this.middleware.waitUntilValid((stats)=>{
1041
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
1042
+ if ('HEAD' === req.method) return void res.end();
1043
+ res.write('<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body>');
1044
+ const statsForPrint = void 0 !== stats.stats ? stats.toJson({
1045
+ assets: true
1046
+ }).children : [
1047
+ stats.toJson({
1048
+ assets: true
1049
+ })
1050
+ ];
1051
+ res.write('<h1>Assets Report:</h1>');
1052
+ for (const [index, item] of statsForPrint?.entries() ?? []){
1053
+ res.write('<div>');
1054
+ const name = void 0 !== item.name ? item.name : stats.stats ? `unnamed[${index}]` : 'unnamed';
1055
+ res.write(`<h2>Compilation: ${name}</h2>`);
1056
+ res.write('<ul>');
1057
+ const publicPath = 'auto' === item.publicPath ? '' : item.publicPath;
1058
+ const assets = item.assets;
1059
+ for (const asset of assets ?? []){
1060
+ const assetName = asset.name;
1061
+ const assetURL = `${publicPath}${assetName}`;
1062
+ res.write(`<li>
1063
+ <strong><a href="${assetURL}" target="_blank">${assetName}</a></strong>
1064
+ </li>`);
1065
+ }
1066
+ res.write('</ul>');
1067
+ res.write('</div>');
1068
+ }
1069
+ res.end('</body></html>');
1070
+ });
1071
+ }
1072
+ });
1073
+ if (this.options.proxy) {
1074
+ const { createProxyMiddleware } = server_require('http-proxy-middleware');
1075
+ const getProxyMiddleware = (proxyConfig)=>{
1076
+ const { context, ...proxyOptions } = proxyConfig;
1077
+ const pathFilter = proxyOptions.pathFilter ?? context;
1078
+ if (void 0 !== pathFilter) proxyOptions.pathFilter = pathFilter;
1079
+ if (void 0 === proxyOptions.logger) proxyOptions.logger = this.logger;
1080
+ if (proxyOptions.target || proxyOptions.router) return createProxyMiddleware(proxyOptions);
1081
+ deprecate(()=>{}, `Invalid proxy configuration:\n\n${JSON.stringify(proxyConfig, null, 2)}\n\nThe use of proxy object notation as proxy routes has been removed.\nPlease use the 'router' or 'context' options. Read more at https://github.com/chimurai/http-proxy-middleware`, 'DEP_WEBPACK_DEV_SERVER_PROXY_ROUTES_ARGUMENT')();
1082
+ };
1083
+ for (const proxyConfigOrCallback of this.options.proxy){
1084
+ let proxyMiddleware;
1085
+ let proxyConfig = 'function' == typeof proxyConfigOrCallback ? proxyConfigOrCallback() : proxyConfigOrCallback;
1086
+ proxyMiddleware = getProxyMiddleware(proxyConfig);
1087
+ if (proxyConfig.ws && proxyMiddleware) this.webSocketProxies.push(proxyMiddleware);
1088
+ const handler = async (req, res, next)=>{
1089
+ if ('function' == typeof proxyConfigOrCallback) {
1090
+ const newProxyConfig = proxyConfigOrCallback(req, res, next);
1091
+ if (newProxyConfig !== proxyConfig) {
1092
+ proxyConfig = newProxyConfig;
1093
+ const socket = req.socket || req.connection;
1094
+ const server = socket ? socket.server : null;
1095
+ if (server) server.removeAllListeners('close');
1096
+ proxyMiddleware = getProxyMiddleware(proxyConfig);
1097
+ }
1098
+ }
1099
+ if (proxyMiddleware) return proxyMiddleware(req, res, next);
1100
+ next();
1101
+ };
1102
+ middlewares.push({
1103
+ name: 'http-proxy-middleware',
1104
+ middleware: handler
1105
+ });
1106
+ middlewares.push({
1107
+ name: 'http-proxy-middleware-error-handler',
1108
+ middleware: (error, req, res, next)=>handler(req, res, next)
1109
+ });
1110
+ }
1111
+ middlewares.push({
1112
+ name: '@rspack/dev-middleware',
1113
+ middleware: this.middleware
1114
+ });
1115
+ }
1116
+ const staticOptions = this.options.static;
1117
+ if (staticOptions.length > 0) for (const staticOption of staticOptions)for (const publicPath of staticOption.publicPath)middlewares.push({
1118
+ name: 'serve-static',
1119
+ path: publicPath,
1120
+ middleware: getServeStatic()(staticOption.directory, staticOption.staticOptions)
1121
+ });
1122
+ if (this.options.historyApiFallback) {
1123
+ const connectHistoryApiFallback = server_require('connect-history-api-fallback');
1124
+ const { historyApiFallback } = this.options;
1125
+ if (void 0 === historyApiFallback && !historyApiFallback.verbose) historyApiFallback.logger = this.logger.log.bind(this.logger, '[connect-history-api-fallback]');
1126
+ middlewares.push({
1127
+ name: 'connect-history-api-fallback',
1128
+ middleware: connectHistoryApiFallback(historyApiFallback)
1129
+ });
1130
+ middlewares.push({
1131
+ name: '@rspack/dev-middleware',
1132
+ middleware: this.middleware
1133
+ });
1134
+ if (staticOptions.length > 0) for (const staticOption of staticOptions)for (const publicPath of staticOption.publicPath)middlewares.push({
1135
+ name: 'serve-static',
1136
+ path: publicPath,
1137
+ middleware: getServeStatic()(staticOption.directory, staticOption.staticOptions)
1138
+ });
1139
+ }
1140
+ middlewares.push({
1141
+ name: 'options-middleware',
1142
+ middleware: (req, res, next)=>{
1143
+ if ('OPTIONS' === req.method) {
1144
+ res.statusCode = 204;
1145
+ res.setHeader('Content-Length', '0');
1146
+ res.end();
1147
+ return;
1148
+ }
1149
+ next();
1150
+ }
1151
+ });
1152
+ if ('function' == typeof this.options.setupMiddlewares) middlewares = this.options.setupMiddlewares(middlewares, this);
1153
+ const lazyInitDevMiddleware = ()=>{
1154
+ if (!this.middleware) this.middleware = devMiddleware(this.compiler, this.options.devMiddleware);
1155
+ return this.middleware;
1156
+ };
1157
+ for (const i of middlewares)if ('@rspack/dev-middleware' === i.name) {
1158
+ const item = i;
1159
+ if (void 0 === item.middleware) item.middleware = lazyInitDevMiddleware();
1160
+ }
1161
+ for (const middleware of middlewares)if ('function' == typeof middleware) this.app.use(middleware);
1162
+ else if (void 0 !== middleware.path) this.app.use(middleware.path, middleware.middleware);
1163
+ else this.app.use(middleware.middleware);
1164
+ }
1165
+ async #createServer() {
1166
+ const { type, options } = this.options.server;
1167
+ if ('function' == typeof type) this.server = await type(options, this.app);
1168
+ else {
1169
+ const serverType = server_require(type);
1170
+ this.server = 'http2' === type ? serverType.createSecureServer({
1171
+ ...options,
1172
+ allowHTTP1: true
1173
+ }, this.app) : serverType.createServer(options, this.app);
1174
+ }
1175
+ this.isTlsServer = void 0 !== this.server.setSecureContext;
1176
+ this.server.on('connection', (socket)=>{
1177
+ this.sockets.push(socket);
1178
+ socket.once('close', ()=>{
1179
+ this.sockets.splice(this.sockets.indexOf(socket), 1);
1180
+ });
1181
+ });
1182
+ this.server.on('error', (error)=>{
1183
+ throw error;
1184
+ });
1185
+ }
1186
+ #createWebSocketServer() {
1187
+ this.webSocketServer = new (this.#getServerTransport())(this);
1188
+ (this.webSocketServer?.implementation).on('connection', (client, request)=>{
1189
+ const headers = void 0 !== request ? request.headers : void 0;
1190
+ if (!headers) this.logger.warn('webSocketServer implementation must pass headers for the "connection" event');
1191
+ if (!headers || !this.isValidHost(headers, 'host') || !this.isValidHost(headers, 'origin') || !this.#isSameOrigin(headers)) {
1192
+ this.sendMessage([
1193
+ client
1194
+ ], 'error', 'Invalid Host/Origin header');
1195
+ client.close();
1196
+ return;
1197
+ }
1198
+ if (true === this.options.hot || 'only' === this.options.hot) this.sendMessage([
1199
+ client
1200
+ ], 'hot');
1201
+ if (this.options.liveReload) this.sendMessage([
1202
+ client
1203
+ ], 'liveReload');
1204
+ if (this.options.client && this.options.client.progress) this.sendMessage([
1205
+ client
1206
+ ], 'progress', this.options.client.progress);
1207
+ if (this.options.client && this.options.client.reconnect) this.sendMessage([
1208
+ client
1209
+ ], 'reconnect', this.options.client.reconnect);
1210
+ if (this.options.client && this.options.client.overlay) {
1211
+ const overlayConfig = this.options.client.overlay;
1212
+ this.sendMessage([
1213
+ client
1214
+ ], 'overlay', 'object' == typeof overlayConfig ? {
1215
+ ...overlayConfig,
1216
+ errors: overlayConfig.errors && encodeOverlaySettings(overlayConfig.errors),
1217
+ warnings: overlayConfig.warnings && encodeOverlaySettings(overlayConfig.warnings),
1218
+ runtimeErrors: overlayConfig.runtimeErrors && encodeOverlaySettings(overlayConfig.runtimeErrors)
1219
+ } : overlayConfig);
1220
+ }
1221
+ if (!this.stats) return;
1222
+ this.#sendStats([
1223
+ client
1224
+ ], this.#getStats(this.stats), true);
1225
+ });
1226
+ }
1227
+ async #openBrowser(defaultOpenTarget) {
1228
+ const { default: open } = await import("./0~open.js");
1229
+ Promise.all(this.options.open.map((item)=>{
1230
+ let openTarget;
1231
+ openTarget = '<url>' === item.target ? defaultOpenTarget : Server.isAbsoluteURL(item.target) ? item.target : new URL(item.target, defaultOpenTarget).toString();
1232
+ return open(openTarget, item.options).catch(()=>{
1233
+ const app = item.options.app;
1234
+ this.logger.warn(`Unable to open "${openTarget}" page${app ? ` in "${app.name}" app${app.arguments ? ` with "${app.arguments.join(' ')}" arguments` : ''}` : ''}. If you are running in a headless environment, please do not use the "open" option or related flags like "--open", "--open-target", and "--open-app-name".`);
1235
+ });
1236
+ }));
1237
+ }
1238
+ async #logStatus() {
1239
+ const server = this.server;
1240
+ if (this.options.ipc) this.logger.info(`Project is running at: "${server?.address()}"`);
1241
+ else {
1242
+ const protocol = this.isTlsServer ? 'https' : 'http';
1243
+ const addressInfo = server?.address();
1244
+ if (!addressInfo) return;
1245
+ const { address, port } = addressInfo;
1246
+ const prettyPrintURL = (newHostname)=>format({
1247
+ protocol,
1248
+ hostname: newHostname,
1249
+ port,
1250
+ pathname: '/'
1251
+ });
1252
+ let localhost;
1253
+ let loopbackIPv4;
1254
+ let loopbackIPv6;
1255
+ let networkUrlIPv4;
1256
+ let networkUrlIPv6;
1257
+ if ('localhost' === this.options.host) localhost = prettyPrintURL('localhost');
1258
+ const parsedIP = ipaddr.parse(address);
1259
+ if ('unspecified' === parsedIP.range()) {
1260
+ localhost = prettyPrintURL('localhost');
1261
+ loopbackIPv6 = prettyPrintURL('::1');
1262
+ const networkIPv4 = Server.findIp('v4', false);
1263
+ if (networkIPv4) networkUrlIPv4 = prettyPrintURL(networkIPv4);
1264
+ const networkIPv6 = Server.findIp('v6', false);
1265
+ if (networkIPv6) networkUrlIPv6 = prettyPrintURL(networkIPv6);
1266
+ } else if ('loopback' === parsedIP.range()) {
1267
+ if ('ipv4' === parsedIP.kind()) loopbackIPv4 = prettyPrintURL(parsedIP.toString());
1268
+ else if ('ipv6' === parsedIP.kind()) loopbackIPv6 = prettyPrintURL(parsedIP.toString());
1269
+ } else {
1270
+ networkUrlIPv4 = 'ipv6' === parsedIP.kind() && parsedIP.isIPv4MappedAddress() ? prettyPrintURL(parsedIP.toIPv4Address().toString()) : prettyPrintURL(address);
1271
+ if ('ipv6' === parsedIP.kind()) networkUrlIPv6 = prettyPrintURL(address);
1272
+ }
1273
+ const urlLogs = [];
1274
+ const local = localhost || loopbackIPv4 || loopbackIPv6;
1275
+ if (local) urlLogs.push(` ${server_styleText('white', '➜')} ${server_styleText([
1276
+ 'white',
1277
+ 'dim'
1278
+ ], 'Local:')} ${server_styleText('cyan', local)}`);
1279
+ if (networkUrlIPv4) urlLogs.push(` ${server_styleText('white', '➜')} ${server_styleText([
1280
+ 'white',
1281
+ 'dim'
1282
+ ], 'Network:')} ${server_styleText('cyan', networkUrlIPv4)}`);
1283
+ else if (networkUrlIPv6) urlLogs.push(` ${server_styleText('white', '➜')} ${server_styleText([
1284
+ 'white',
1285
+ 'dim'
1286
+ ], 'Network:')} ${server_styleText('cyan', networkUrlIPv6)}`);
1287
+ if (urlLogs.length && this.#shouldLogInfrastructureInfo()) console.log(`${urlLogs.join('\n')}\n`);
1288
+ if (this.options.open?.length > 0) {
1289
+ const openTarget = prettyPrintURL(this.options.host && '0.0.0.0' !== this.options.host && '::' !== this.options.host ? this.options.host : 'localhost');
1290
+ await this.#openBrowser(openTarget);
1291
+ }
1292
+ }
1293
+ }
1294
+ #setHeaders(req, res, next) {
1295
+ let { headers } = this.options;
1296
+ if (headers) {
1297
+ if ('function' == typeof headers) headers = headers(req, res, this.middleware ? this.middleware.context : void 0);
1298
+ const allHeaders = [];
1299
+ if (!Array.isArray(headers)) {
1300
+ for(const name in headers)allHeaders.push({
1301
+ key: name,
1302
+ value: headers[name]
1303
+ });
1304
+ headers = allHeaders;
1305
+ }
1306
+ for (const { key, value } of headers)res.setHeader(key, value);
1307
+ }
1308
+ next();
1309
+ }
1310
+ #isHostAllowed(value) {
1311
+ const { allowedHosts } = this.options;
1312
+ if ('all' === allowedHosts) return true;
1313
+ if (Array.isArray(allowedHosts) && allowedHosts.length > 0) for (const allowedHost of allowedHosts){
1314
+ if (allowedHost === value) return true;
1315
+ if (allowedHost.startsWith('.') && (value === allowedHost.slice(1) || value.endsWith(allowedHost))) return true;
1316
+ }
1317
+ if (this.options.client && void 0 !== this.options.client.webSocketURL) return this.options.client.webSocketURL === value;
1318
+ return false;
1319
+ }
1320
+ isValidHost(headers, headerToCheck, validateHost = true) {
1321
+ if ('all' === this.options.allowedHosts) return true;
1322
+ const header = headers[headerToCheck];
1323
+ if (!header) return false;
1324
+ if (DEFAULT_ALLOWED_PROTOCOLS.test(header)) return true;
1325
+ const hostname = parseHostnameFromHeader(header);
1326
+ if (null === hostname) return false;
1327
+ if (this.#isHostAllowed(hostname)) return true;
1328
+ const isValidHostname = validateHost ? ipaddr.IPv4.isValid(hostname) || ipaddr.IPv6.isValid(hostname) || 'localhost' === hostname || hostname.endsWith('.localhost') || hostname === this.options.host : false;
1329
+ return isValidHostname;
1330
+ }
1331
+ #isSameOrigin(headers) {
1332
+ if ('all' === this.options.allowedHosts) return true;
1333
+ const originHeader = headers.origin;
1334
+ if (!originHeader) return 'all' === this.options.allowedHosts;
1335
+ if (DEFAULT_ALLOWED_PROTOCOLS.test(originHeader)) return true;
1336
+ const origin = parseHostnameFromHeader(originHeader);
1337
+ if (null === origin) return false;
1338
+ if (this.#isHostAllowed(origin)) return true;
1339
+ const hostHeader = headers.host;
1340
+ if (!hostHeader) return 'all' === this.options.allowedHosts;
1341
+ if (DEFAULT_ALLOWED_PROTOCOLS.test(hostHeader)) return true;
1342
+ const host = parseHostnameFromHeader(hostHeader);
1343
+ if (null === host) return false;
1344
+ if (this.#isHostAllowed(host)) return true;
1345
+ return origin === host;
1346
+ }
1347
+ sendMessage(clients, type, data, params) {
1348
+ for (const client of clients)if (1 === client.readyState) client.send(JSON.stringify({
1349
+ type,
1350
+ data,
1351
+ params
1352
+ }));
1353
+ }
1354
+ #sendStats(clients, stats, force) {
1355
+ if (!stats) return;
1356
+ const shouldEmit = !force && stats && (!stats.errors || 0 === stats.errors.length) && (!stats.warnings || 0 === stats.warnings.length) && this.currentHash === stats.hash;
1357
+ if (shouldEmit) return void this.sendMessage(clients, 'still-ok');
1358
+ this.currentHash = stats.hash;
1359
+ this.sendMessage(clients, 'hash', stats.hash);
1360
+ if (stats.errors?.length > 0 || stats.warnings?.length > 0) {
1361
+ const hasErrors = stats.errors?.length > 0;
1362
+ if (stats.warnings?.length > 0) {
1363
+ let params;
1364
+ if (hasErrors) params = {
1365
+ preventReloading: true
1366
+ };
1367
+ this.sendMessage(clients, 'warnings', stats.warnings, params);
1368
+ }
1369
+ if (stats.errors?.length > 0) this.sendMessage(clients, 'errors', stats.errors);
1370
+ } else this.sendMessage(clients, 'ok');
1371
+ }
1372
+ async watchFiles(watchPath, watchOptions) {
1373
+ const { watch } = await getChokidar();
1374
+ const watcher = watch(watchPath, watchOptions);
1375
+ if (this.options.liveReload) watcher.on('change', (item)=>{
1376
+ if (this.webSocketServer) this.sendMessage(this.webSocketServer.clients, 'static-changed', item);
1377
+ });
1378
+ this.staticWatchers.push(watcher);
1379
+ }
1380
+ invalidate(callback = ()=>{}) {
1381
+ if (this.middleware) this.middleware.invalidate(callback);
1382
+ }
1383
+ async start() {
1384
+ await this.#normalizeOptions();
1385
+ if (this.options.ipc) await new Promise((resolve, reject)=>{
1386
+ const net = server_require('node:net');
1387
+ const socket = new net.Socket();
1388
+ socket.on('error', (error)=>{
1389
+ if ('ECONNREFUSED' === error.code) {
1390
+ unlinkSync(this.options.ipc);
1391
+ resolve();
1392
+ return;
1393
+ }
1394
+ if ('ENOENT' === error.code) return void resolve();
1395
+ reject(error);
1396
+ });
1397
+ socket.connect({
1398
+ path: this.options.ipc
1399
+ }, ()=>{
1400
+ throw new Error(`IPC "${this.options.ipc}" is already used`);
1401
+ });
1402
+ });
1403
+ else {
1404
+ this.options.host = await Server.getHostname(this.options.host);
1405
+ this.options.port = await Server.getFreePort(this.options.port, this.options.host);
1406
+ }
1407
+ await this.#initialize();
1408
+ const listenOptions = this.options.ipc ? {
1409
+ path: this.options.ipc
1410
+ } : {
1411
+ host: this.options.host,
1412
+ port: this.options.port
1413
+ };
1414
+ await new Promise((resolve)=>{
1415
+ this.server.listen(listenOptions, ()=>{
1416
+ resolve();
1417
+ });
1418
+ });
1419
+ if (this.options.ipc) {
1420
+ const READ_WRITE = 438;
1421
+ await promises.chmod(this.options.ipc, READ_WRITE);
1422
+ }
1423
+ if (this.options.webSocketServer) this.#createWebSocketServer();
1424
+ await this.#logStatus();
1425
+ if ('function' == typeof this.options.onListening) this.options.onListening(this);
1426
+ }
1427
+ startCallback(callback = ()=>{}) {
1428
+ this.start().then(()=>callback(), callback).catch(callback);
1429
+ }
1430
+ async stop() {
1431
+ this.webSocketProxies = [];
1432
+ await Promise.all(this.staticWatchers.map((watcher)=>watcher.close()));
1433
+ this.staticWatchers = [];
1434
+ if (this.webSocketServer) await new Promise((resolve)=>{
1435
+ this.webSocketServer.implementation.close(()=>{
1436
+ this.webSocketServer = null;
1437
+ resolve();
1438
+ });
1439
+ for (const client of this.webSocketServer.clients)client.terminate();
1440
+ this.webSocketServer.clients = [];
1441
+ });
1442
+ if (this.server) {
1443
+ await new Promise((resolve)=>{
1444
+ this.server.close(()=>{
1445
+ this.server = void 0;
1446
+ resolve();
1447
+ });
1448
+ for (const socket of this.sockets)socket.destroy();
1449
+ this.sockets = [];
1450
+ });
1451
+ if (this.middleware) {
1452
+ await new Promise((resolve, reject)=>{
1453
+ this.middleware.close((error)=>{
1454
+ if (error) return void reject(error);
1455
+ resolve();
1456
+ });
1457
+ });
1458
+ this.middleware = void 0;
1459
+ }
1460
+ }
1461
+ for (const item of this.listeners)process.removeListener(item.name, item.listener);
1462
+ }
1463
+ stopCallback(callback = ()=>{}) {
1464
+ this.stop().then(()=>callback(), callback).catch(callback);
1465
+ }
1466
+ }
1467
+ export { Server as RspackDevServer };