@rspack/dev-server 1.2.1 → 2.0.0-beta.2

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