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

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