webpack-dev-server 4.6.0 → 4.7.0
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/README.md +56 -66
- package/bin/cli-flags.js +265 -267
- package/bin/process-arguments.js +87 -7
- package/bin/webpack-dev-server.js +3 -0
- package/client/clients/SockJSClient.js +21 -2
- package/client/clients/WebSocketClient.js +16 -1
- package/client/index.js +21 -0
- package/client/modules/logger/index.js +3 -0
- package/client/socket.js +3 -4
- package/client/utils/log.js +6 -1
- package/client/utils/sendMessage.js +5 -0
- package/lib/Server.js +1672 -752
- package/lib/options.json +37 -18
- package/lib/servers/BaseServer.js +8 -0
- package/lib/servers/SockJSServer.js +42 -9
- package/lib/servers/WebsocketServer.js +66 -35
- package/package.json +20 -10
- package/types/bin/cli-flags.d.ts +934 -0
- package/types/bin/process-arguments.d.ts +50 -0
- package/types/bin/webpack-dev-server.d.ts +27 -0
- package/types/lib/Server.d.ts +3388 -0
- package/types/lib/servers/BaseServer.d.ts +15 -0
- package/types/lib/servers/SockJSServer.d.ts +12 -0
- package/types/lib/servers/WebsocketServer.d.ts +13 -0
package/lib/Server.js
CHANGED
|
@@ -11,14 +11,214 @@ const express = require("express");
|
|
|
11
11
|
const { validate } = require("schema-utils");
|
|
12
12
|
const schema = require("./options.json");
|
|
13
13
|
|
|
14
|
+
/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
|
|
15
|
+
/** @typedef {import("webpack").Compiler} Compiler */
|
|
16
|
+
/** @typedef {import("webpack").MultiCompiler} MultiCompiler */
|
|
17
|
+
/** @typedef {import("webpack").Configuration} WebpackConfiguration */
|
|
18
|
+
/** @typedef {import("webpack").StatsOptions} StatsOptions */
|
|
19
|
+
/** @typedef {import("webpack").StatsCompilation} StatsCompilation */
|
|
20
|
+
/** @typedef {import("webpack").Stats} Stats */
|
|
21
|
+
/** @typedef {import("webpack").MultiStats} MultiStats */
|
|
22
|
+
/** @typedef {import("os").NetworkInterfaceInfo} NetworkInterfaceInfo */
|
|
23
|
+
/** @typedef {import("express").Request} Request */
|
|
24
|
+
/** @typedef {import("express").Response} Response */
|
|
25
|
+
/** @typedef {import("express").NextFunction} NextFunction */
|
|
26
|
+
/** @typedef {import("express").RequestHandler} ExpressRequestHandler */
|
|
27
|
+
/** @typedef {import("express").ErrorRequestHandler} ExpressErrorRequestHandler */
|
|
28
|
+
/** @typedef {import("chokidar").WatchOptions} WatchOptions */
|
|
29
|
+
/** @typedef {import("chokidar").FSWatcher} FSWatcher */
|
|
30
|
+
/** @typedef {import("connect-history-api-fallback").Options} ConnectHistoryApiFallbackOptions */
|
|
31
|
+
/** @typedef {import("bonjour").Bonjour} Bonjour */
|
|
32
|
+
/** @typedef {import("bonjour").BonjourOptions} BonjourOptions */
|
|
33
|
+
/** @typedef {import("http-proxy-middleware").RequestHandler} RequestHandler */
|
|
34
|
+
/** @typedef {import("http-proxy-middleware").Options} HttpProxyMiddlewareOptions */
|
|
35
|
+
/** @typedef {import("http-proxy-middleware").Filter} HttpProxyMiddlewareOptionsFilter */
|
|
36
|
+
/** @typedef {import("serve-index").Options} ServeIndexOptions */
|
|
37
|
+
/** @typedef {import("serve-static").ServeStaticOptions} ServeStaticOptions */
|
|
38
|
+
/** @typedef {import("ipaddr.js").IPv4} IPv4 */
|
|
39
|
+
/** @typedef {import("ipaddr.js").IPv6} IPv6 */
|
|
40
|
+
/** @typedef {import("net").Socket} Socket */
|
|
41
|
+
/** @typedef {import("http").IncomingMessage} IncomingMessage */
|
|
42
|
+
/** @typedef {import("open").Options} OpenOptions */
|
|
43
|
+
|
|
44
|
+
/** @typedef {import("https").ServerOptions & { spdy?: { plain?: boolean | undefined, ssl?: boolean | undefined, 'x-forwarded-for'?: string | undefined, protocol?: string | undefined, protocols?: string[] | undefined }}} ServerOptions */
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @template Request, Response
|
|
48
|
+
* @typedef {import("webpack-dev-middleware").Options<Request, Response>} DevMiddlewareOptions
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @template Request, Response
|
|
53
|
+
* @typedef {import("webpack-dev-middleware").Context<Request, Response>} DevMiddlewareContext
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @typedef {"local-ip" | "local-ipv4" | "local-ipv6" | string} Host
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @typedef {number | string | "auto"} Port
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @typedef {Object} WatchFiles
|
|
66
|
+
* @property {string | string[]} paths
|
|
67
|
+
* @property {WatchOptions & { aggregateTimeout?: number, ignored?: string | RegExp | string[], poll?: number | boolean }} [options]
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @typedef {Object} Static
|
|
72
|
+
* @property {string} [directory]
|
|
73
|
+
* @property {string | string[]} [publicPath]
|
|
74
|
+
* @property {boolean | ServeIndexOptions} [serveIndex]
|
|
75
|
+
* @property {ServeStaticOptions} [staticOptions]
|
|
76
|
+
* @property {boolean | WatchOptions & { aggregateTimeout?: number, ignored?: string | RegExp | string[], poll?: number | boolean }} [watch]
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @typedef {Object} NormalizedStatic
|
|
81
|
+
* @property {string} directory
|
|
82
|
+
* @property {string[]} publicPath
|
|
83
|
+
* @property {false | ServeIndexOptions} serveIndex
|
|
84
|
+
* @property {ServeStaticOptions} staticOptions
|
|
85
|
+
* @property {false | WatchOptions} watch
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @typedef {Object} ServerConfiguration
|
|
90
|
+
* @property {"http" | "https" | "spdy" | string} [type]
|
|
91
|
+
* @property {ServerOptions} [options]
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @typedef {Object} WebSocketServerConfiguration
|
|
96
|
+
* @property {"sockjs" | "ws" | string | Function} [type]
|
|
97
|
+
* @property {Record<string, any>} [options]
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @typedef {(import("ws").WebSocket | import("sockjs").Connection & { send: import("ws").WebSocket["send"], terminate: import("ws").WebSocket["terminate"], ping: import("ws").WebSocket["ping"] }) & { isAlive?: boolean }} ClientConnection
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @typedef {import("ws").WebSocketServer | import("sockjs").Server & { close: import("ws").WebSocketServer["close"] }} WebSocketServer
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @typedef {{ implementation: WebSocketServer, clients: ClientConnection[] }} WebSocketServerImplementation
|
|
110
|
+
*/
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @typedef {{ [url: string]: string | HttpProxyMiddlewareOptions }} ProxyConfigMap
|
|
114
|
+
*/
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @typedef {HttpProxyMiddlewareOptions[]} ProxyArray
|
|
118
|
+
*/
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @callback ByPass
|
|
122
|
+
* @param {Request} req
|
|
123
|
+
* @param {Response} res
|
|
124
|
+
* @param {ProxyConfigArray} proxyConfig
|
|
125
|
+
*/
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @typedef {{ path?: string | string[] | undefined, context?: string | string[] | HttpProxyMiddlewareOptionsFilter | undefined } & HttpProxyMiddlewareOptions & ByPass} ProxyConfigArray
|
|
129
|
+
*/
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @typedef {Object} OpenApp
|
|
133
|
+
* @property {string} [name]
|
|
134
|
+
* @property {string[]} [arguments]
|
|
135
|
+
*/
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @typedef {Object} Open
|
|
139
|
+
* @property {string | string[] | OpenApp} [app]
|
|
140
|
+
* @property {string | string[]} [target]
|
|
141
|
+
*/
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @typedef {Object} NormalizedOpen
|
|
145
|
+
* @property {string} target
|
|
146
|
+
* @property {import("open").Options} options
|
|
147
|
+
*/
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @typedef {Object} WebSocketURL
|
|
151
|
+
* @property {string} [hostname]
|
|
152
|
+
* @property {string} [password]
|
|
153
|
+
* @property {string} [pathname]
|
|
154
|
+
* @property {number | string} [port]
|
|
155
|
+
* @property {string} [protocol]
|
|
156
|
+
* @property {string} [username]
|
|
157
|
+
*/
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @typedef {Object} ClientConfiguration
|
|
161
|
+
* @property {"log" | "info" | "warn" | "error" | "none" | "verbose"} [logging]
|
|
162
|
+
* @property {boolean | { warnings?: boolean, errors?: boolean }} [overlay]
|
|
163
|
+
* @property {boolean} [progress]
|
|
164
|
+
* @property {boolean | number} [reconnect]
|
|
165
|
+
* @property {"ws" | "sockjs" | string} [webSocketTransport]
|
|
166
|
+
* @property {string | WebSocketURL} [webSocketURL]
|
|
167
|
+
*/
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @typedef {Array<{ key: string; value: string }> | Record<string, string | string[]>} Headers
|
|
171
|
+
*/
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @typedef {{ name?: string, path?: string, middleware: ExpressRequestHandler | ExpressErrorRequestHandler } | ExpressRequestHandler | ExpressErrorRequestHandler} Middleware
|
|
175
|
+
*/
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* @typedef {Object} Configuration
|
|
179
|
+
* @property {boolean | string} [ipc]
|
|
180
|
+
* @property {Host} [host]
|
|
181
|
+
* @property {Port} [port]
|
|
182
|
+
* @property {boolean | "only"} [hot]
|
|
183
|
+
* @property {boolean} [liveReload]
|
|
184
|
+
* @property {DevMiddlewareOptions<Request, Response>} [devMiddleware]
|
|
185
|
+
* @property {boolean} [compress]
|
|
186
|
+
* @property {boolean} [magicHtml]
|
|
187
|
+
* @property {"auto" | "all" | string | string[]} [allowedHosts]
|
|
188
|
+
* @property {boolean | ConnectHistoryApiFallbackOptions} [historyApiFallback]
|
|
189
|
+
* @property {boolean} [setupExitSignals]
|
|
190
|
+
* @property {boolean | BonjourOptions} [bonjour]
|
|
191
|
+
* @property {string | string[] | WatchFiles | Array<string | WatchFiles>} [watchFiles]
|
|
192
|
+
* @property {boolean | string | Static | Array<string | Static>} [static]
|
|
193
|
+
* @property {boolean | ServerOptions} [https]
|
|
194
|
+
* @property {boolean} [http2]
|
|
195
|
+
* @property {"http" | "https" | "spdy" | string | ServerConfiguration} [server]
|
|
196
|
+
* @property {boolean | "sockjs" | "ws" | string | WebSocketServerConfiguration} [webSocketServer]
|
|
197
|
+
* @property {ProxyConfigMap | ProxyConfigArray | ProxyArray} [proxy]
|
|
198
|
+
* @property {boolean | string | Open | Array<string | Open>} [open]
|
|
199
|
+
* @property {boolean} [setupExitSignals]
|
|
200
|
+
* @property {boolean | ClientConfiguration} [client]
|
|
201
|
+
* @property {Headers | ((req: Request, res: Response, context: DevMiddlewareContext<Request, Response>) => Headers)} [headers]
|
|
202
|
+
* @property {(devServer: Server) => void} [onAfterSetupMiddleware]
|
|
203
|
+
* @property {(devServer: Server) => void} [onBeforeSetupMiddleware]
|
|
204
|
+
* @property {(devServer: Server) => void} [onListening]
|
|
205
|
+
* @property {(middlewares: Middleware[], devServer: Server) => Middleware[]} [setupMiddlewares]
|
|
206
|
+
*/
|
|
207
|
+
|
|
14
208
|
if (!process.env.WEBPACK_SERVE) {
|
|
209
|
+
// TODO fix me in the next major release
|
|
210
|
+
// @ts-ignore
|
|
15
211
|
process.env.WEBPACK_SERVE = true;
|
|
16
212
|
}
|
|
17
213
|
|
|
18
214
|
class Server {
|
|
215
|
+
/**
|
|
216
|
+
* @param {Configuration | Compiler | MultiCompiler} options
|
|
217
|
+
* @param {Compiler | MultiCompiler | Configuration} compiler
|
|
218
|
+
*/
|
|
19
219
|
constructor(options = {}, compiler) {
|
|
20
220
|
// TODO: remove this after plugin support is published
|
|
21
|
-
if (options.hooks) {
|
|
221
|
+
if (/** @type {Compiler | MultiCompiler} */ (options).hooks) {
|
|
22
222
|
util.deprecate(
|
|
23
223
|
() => {},
|
|
24
224
|
"Using 'compiler' as the first argument is deprecated. Please use 'options' as the first argument and 'compiler' as the second argument.",
|
|
@@ -28,18 +228,65 @@ class Server {
|
|
|
28
228
|
[options = {}, compiler] = [compiler, options];
|
|
29
229
|
}
|
|
30
230
|
|
|
31
|
-
validate(schema, options,
|
|
231
|
+
validate(/** @type {Schema} */ (schema), options, {
|
|
232
|
+
name: "Dev Server",
|
|
233
|
+
baseDataPath: "options",
|
|
234
|
+
});
|
|
32
235
|
|
|
33
|
-
this.
|
|
236
|
+
this.compiler = /** @type {Compiler | MultiCompiler} */ (compiler);
|
|
237
|
+
/**
|
|
238
|
+
* @type {ReturnType<Compiler["getInfrastructureLogger"]>}
|
|
239
|
+
* */
|
|
240
|
+
this.logger = this.compiler.getInfrastructureLogger("webpack-dev-server");
|
|
241
|
+
this.options = /** @type {Configuration} */ (options);
|
|
242
|
+
/**
|
|
243
|
+
* @type {FSWatcher[]}
|
|
244
|
+
*/
|
|
34
245
|
this.staticWatchers = [];
|
|
246
|
+
/**
|
|
247
|
+
* @private
|
|
248
|
+
* @type {{ name: string | symbol, listener: (...args: any[]) => void}[] }}
|
|
249
|
+
*/
|
|
35
250
|
this.listeners = [];
|
|
36
251
|
// Keep track of websocket proxies for external websocket upgrade.
|
|
252
|
+
/**
|
|
253
|
+
* @private
|
|
254
|
+
* @type {RequestHandler[]}
|
|
255
|
+
*/
|
|
37
256
|
this.webSocketProxies = [];
|
|
257
|
+
/**
|
|
258
|
+
* @type {Socket[]}
|
|
259
|
+
*/
|
|
38
260
|
this.sockets = [];
|
|
39
|
-
|
|
40
|
-
|
|
261
|
+
/**
|
|
262
|
+
* @private
|
|
263
|
+
* @type {string | undefined}
|
|
264
|
+
*/
|
|
265
|
+
// eslint-disable-next-line no-undefined
|
|
266
|
+
this.currentHash = undefined;
|
|
41
267
|
}
|
|
42
268
|
|
|
269
|
+
// TODO compatibility with webpack v4, remove it after drop
|
|
270
|
+
static get cli() {
|
|
271
|
+
return {
|
|
272
|
+
get getArguments() {
|
|
273
|
+
return () => require("../bin/cli-flags");
|
|
274
|
+
},
|
|
275
|
+
get processArguments() {
|
|
276
|
+
return require("../bin/process-arguments");
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
static get schema() {
|
|
282
|
+
return schema;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* @private
|
|
287
|
+
* @returns {StatsOptions}
|
|
288
|
+
* @constructor
|
|
289
|
+
*/
|
|
43
290
|
static get DEFAULT_STATS() {
|
|
44
291
|
return {
|
|
45
292
|
all: false,
|
|
@@ -50,6 +297,10 @@ class Server {
|
|
|
50
297
|
};
|
|
51
298
|
}
|
|
52
299
|
|
|
300
|
+
/**
|
|
301
|
+
* @param {string} URL
|
|
302
|
+
* @returns {boolean}
|
|
303
|
+
*/
|
|
53
304
|
static isAbsoluteURL(URL) {
|
|
54
305
|
// Don't match Windows paths `c:\`
|
|
55
306
|
if (/^[a-zA-Z]:\\/.test(URL)) {
|
|
@@ -61,13 +312,19 @@ class Server {
|
|
|
61
312
|
return /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(URL);
|
|
62
313
|
}
|
|
63
314
|
|
|
315
|
+
/**
|
|
316
|
+
* @param {string} gateway
|
|
317
|
+
* @returns {string | undefined}
|
|
318
|
+
*/
|
|
64
319
|
static findIp(gateway) {
|
|
65
320
|
const gatewayIp = ipaddr.parse(gateway);
|
|
66
321
|
|
|
67
322
|
// Look for the matching interface in all local interfaces.
|
|
68
323
|
for (const addresses of Object.values(os.networkInterfaces())) {
|
|
69
|
-
for (const { cidr } of
|
|
70
|
-
|
|
324
|
+
for (const { cidr } of /** @type {NetworkInterfaceInfo[]} */ (
|
|
325
|
+
addresses
|
|
326
|
+
)) {
|
|
327
|
+
const net = ipaddr.parseCIDR(/** @type {string} */ (cidr));
|
|
71
328
|
|
|
72
329
|
if (
|
|
73
330
|
net[0] &&
|
|
@@ -80,6 +337,10 @@ class Server {
|
|
|
80
337
|
}
|
|
81
338
|
}
|
|
82
339
|
|
|
340
|
+
/**
|
|
341
|
+
* @param {"v4" | "v6"} family
|
|
342
|
+
* @returns {Promise<string | undefined>}
|
|
343
|
+
*/
|
|
83
344
|
static async internalIP(family) {
|
|
84
345
|
try {
|
|
85
346
|
const { gateway } = await defaultGateway[family]();
|
|
@@ -89,6 +350,10 @@ class Server {
|
|
|
89
350
|
}
|
|
90
351
|
}
|
|
91
352
|
|
|
353
|
+
/**
|
|
354
|
+
* @param {"v4" | "v6"} family
|
|
355
|
+
* @returns {string | undefined}
|
|
356
|
+
*/
|
|
92
357
|
static internalIPSync(family) {
|
|
93
358
|
try {
|
|
94
359
|
const { gateway } = defaultGateway[family].sync();
|
|
@@ -98,6 +363,10 @@ class Server {
|
|
|
98
363
|
}
|
|
99
364
|
}
|
|
100
365
|
|
|
366
|
+
/**
|
|
367
|
+
* @param {Host} hostname
|
|
368
|
+
* @returns {Promise<string>}
|
|
369
|
+
*/
|
|
101
370
|
static async getHostname(hostname) {
|
|
102
371
|
if (hostname === "local-ip") {
|
|
103
372
|
return (
|
|
@@ -114,6 +383,10 @@ class Server {
|
|
|
114
383
|
return hostname;
|
|
115
384
|
}
|
|
116
385
|
|
|
386
|
+
/**
|
|
387
|
+
* @param {Port} port
|
|
388
|
+
* @returns {Promise<number | string>}
|
|
389
|
+
*/
|
|
117
390
|
static async getFreePort(port) {
|
|
118
391
|
if (typeof port !== "undefined" && port !== null && port !== "auto") {
|
|
119
392
|
return port;
|
|
@@ -122,21 +395,32 @@ class Server {
|
|
|
122
395
|
const pRetry = require("p-retry");
|
|
123
396
|
const portfinder = require("portfinder");
|
|
124
397
|
|
|
125
|
-
portfinder.basePort =
|
|
398
|
+
portfinder.basePort =
|
|
399
|
+
typeof process.env.WEBPACK_DEV_SERVER_BASE_PORT !== "undefined"
|
|
400
|
+
? parseInt(process.env.WEBPACK_DEV_SERVER_BASE_PORT, 10)
|
|
401
|
+
: 8080;
|
|
126
402
|
|
|
127
403
|
// Try to find unused port and listen on it for 3 times,
|
|
128
404
|
// if port is not specified in options.
|
|
129
405
|
const defaultPortRetry =
|
|
130
|
-
|
|
406
|
+
typeof process.env.WEBPACK_DEV_SERVER_PORT_RETRY !== "undefined"
|
|
407
|
+
? parseInt(process.env.WEBPACK_DEV_SERVER_PORT_RETRY, 10)
|
|
408
|
+
: 3;
|
|
131
409
|
|
|
132
410
|
return pRetry(() => portfinder.getPortPromise(), {
|
|
133
411
|
retries: defaultPortRetry,
|
|
134
412
|
});
|
|
135
413
|
}
|
|
136
414
|
|
|
415
|
+
/**
|
|
416
|
+
* @returns {string}
|
|
417
|
+
*/
|
|
137
418
|
static findCacheDir() {
|
|
138
419
|
const cwd = process.cwd();
|
|
139
420
|
|
|
421
|
+
/**
|
|
422
|
+
* @type {string | undefined}
|
|
423
|
+
*/
|
|
140
424
|
let dir = cwd;
|
|
141
425
|
|
|
142
426
|
for (;;) {
|
|
@@ -167,7 +451,14 @@ class Server {
|
|
|
167
451
|
return path.resolve(dir, "node_modules/.cache/webpack-dev-server");
|
|
168
452
|
}
|
|
169
453
|
|
|
454
|
+
/**
|
|
455
|
+
* @private
|
|
456
|
+
* @param {Compiler} compiler
|
|
457
|
+
*/
|
|
170
458
|
addAdditionalEntries(compiler) {
|
|
459
|
+
/**
|
|
460
|
+
* @type {string[]}
|
|
461
|
+
*/
|
|
171
462
|
const additionalEntries = [];
|
|
172
463
|
|
|
173
464
|
const isWebTarget = compiler.options.externalsPresets
|
|
@@ -181,38 +472,44 @@ class Server {
|
|
|
181
472
|
// eslint-disable-next-line no-undefined
|
|
182
473
|
undefined,
|
|
183
474
|
null,
|
|
184
|
-
].includes(compiler.options.target);
|
|
475
|
+
].includes(/** @type {string} */ (compiler.options.target));
|
|
185
476
|
|
|
186
477
|
// TODO maybe empty empty client
|
|
187
478
|
if (this.options.client && isWebTarget) {
|
|
188
|
-
let
|
|
479
|
+
let webSocketURLStr = "";
|
|
480
|
+
|
|
189
481
|
if (this.options.webSocketServer) {
|
|
482
|
+
const webSocketURL =
|
|
483
|
+
/** @type {WebSocketURL} */
|
|
484
|
+
(
|
|
485
|
+
/** @type {ClientConfiguration} */
|
|
486
|
+
(this.options.client).webSocketURL
|
|
487
|
+
);
|
|
488
|
+
const webSocketServer =
|
|
489
|
+
/** @type {{ type: WebSocketServerConfiguration["type"], options: NonNullable<WebSocketServerConfiguration["options"]> }} */
|
|
490
|
+
(this.options.webSocketServer);
|
|
190
491
|
const searchParams = new URLSearchParams();
|
|
191
492
|
|
|
192
|
-
/** @type {
|
|
493
|
+
/** @type {string} */
|
|
193
494
|
let protocol;
|
|
194
495
|
|
|
195
496
|
// We are proxying dev server and need to specify custom `hostname`
|
|
196
|
-
if (typeof
|
|
197
|
-
protocol =
|
|
497
|
+
if (typeof webSocketURL.protocol !== "undefined") {
|
|
498
|
+
protocol = webSocketURL.protocol;
|
|
198
499
|
} else {
|
|
199
|
-
protocol =
|
|
500
|
+
protocol =
|
|
501
|
+
/** @type {ServerConfiguration} */
|
|
502
|
+
(this.options.server).type === "http" ? "ws:" : "wss:";
|
|
200
503
|
}
|
|
201
504
|
|
|
202
505
|
searchParams.set("protocol", protocol);
|
|
203
506
|
|
|
204
|
-
if (typeof
|
|
205
|
-
searchParams.set(
|
|
206
|
-
"username",
|
|
207
|
-
this.options.client.webSocketURL.username
|
|
208
|
-
);
|
|
507
|
+
if (typeof webSocketURL.username !== "undefined") {
|
|
508
|
+
searchParams.set("username", webSocketURL.username);
|
|
209
509
|
}
|
|
210
510
|
|
|
211
|
-
if (typeof
|
|
212
|
-
searchParams.set(
|
|
213
|
-
"password",
|
|
214
|
-
this.options.client.webSocketURL.password
|
|
215
|
-
);
|
|
511
|
+
if (typeof webSocketURL.password !== "undefined") {
|
|
512
|
+
searchParams.set("password", webSocketURL.password);
|
|
216
513
|
}
|
|
217
514
|
|
|
218
515
|
/** @type {string} */
|
|
@@ -220,18 +517,18 @@ class Server {
|
|
|
220
517
|
|
|
221
518
|
// SockJS is not supported server mode, so `hostname` and `port` can't specified, let's ignore them
|
|
222
519
|
// TODO show warning about this
|
|
223
|
-
const isSockJSType =
|
|
520
|
+
const isSockJSType = webSocketServer.type === "sockjs";
|
|
224
521
|
|
|
225
522
|
// We are proxying dev server and need to specify custom `hostname`
|
|
226
|
-
if (typeof
|
|
227
|
-
hostname =
|
|
523
|
+
if (typeof webSocketURL.hostname !== "undefined") {
|
|
524
|
+
hostname = webSocketURL.hostname;
|
|
228
525
|
}
|
|
229
526
|
// Web socket server works on custom `hostname`, only for `ws` because `sock-js` is not support custom `hostname`
|
|
230
527
|
else if (
|
|
231
|
-
typeof
|
|
528
|
+
typeof webSocketServer.options.host !== "undefined" &&
|
|
232
529
|
!isSockJSType
|
|
233
530
|
) {
|
|
234
|
-
hostname =
|
|
531
|
+
hostname = webSocketServer.options.host;
|
|
235
532
|
}
|
|
236
533
|
// The `host` option is specified
|
|
237
534
|
else if (typeof this.options.host !== "undefined") {
|
|
@@ -248,15 +545,15 @@ class Server {
|
|
|
248
545
|
let port;
|
|
249
546
|
|
|
250
547
|
// We are proxying dev server and need to specify custom `port`
|
|
251
|
-
if (typeof
|
|
252
|
-
port =
|
|
548
|
+
if (typeof webSocketURL.port !== "undefined") {
|
|
549
|
+
port = webSocketURL.port;
|
|
253
550
|
}
|
|
254
551
|
// Web socket server works on custom `port`, only for `ws` because `sock-js` is not support custom `port`
|
|
255
552
|
else if (
|
|
256
|
-
typeof
|
|
553
|
+
typeof webSocketServer.options.port !== "undefined" &&
|
|
257
554
|
!isSockJSType
|
|
258
555
|
) {
|
|
259
|
-
port =
|
|
556
|
+
port = webSocketServer.options.port;
|
|
260
557
|
}
|
|
261
558
|
// The `port` option is specified
|
|
262
559
|
else if (typeof this.options.port === "number") {
|
|
@@ -280,47 +577,47 @@ class Server {
|
|
|
280
577
|
let pathname = "";
|
|
281
578
|
|
|
282
579
|
// We are proxying dev server and need to specify custom `pathname`
|
|
283
|
-
if (typeof
|
|
284
|
-
pathname =
|
|
580
|
+
if (typeof webSocketURL.pathname !== "undefined") {
|
|
581
|
+
pathname = webSocketURL.pathname;
|
|
285
582
|
}
|
|
286
583
|
// Web socket server works on custom `path`
|
|
287
584
|
else if (
|
|
288
|
-
typeof
|
|
289
|
-
typeof
|
|
585
|
+
typeof webSocketServer.options.prefix !== "undefined" ||
|
|
586
|
+
typeof webSocketServer.options.path !== "undefined"
|
|
290
587
|
) {
|
|
291
588
|
pathname =
|
|
292
|
-
|
|
293
|
-
this.options.webSocketServer.options.path;
|
|
589
|
+
webSocketServer.options.prefix || webSocketServer.options.path;
|
|
294
590
|
}
|
|
295
591
|
|
|
296
592
|
searchParams.set("pathname", pathname);
|
|
297
593
|
|
|
298
|
-
|
|
299
|
-
|
|
594
|
+
const client = /** @type {ClientConfiguration} */ (this.options.client);
|
|
595
|
+
|
|
596
|
+
if (typeof client.logging !== "undefined") {
|
|
597
|
+
searchParams.set("logging", client.logging);
|
|
300
598
|
}
|
|
301
599
|
|
|
302
|
-
if (typeof
|
|
303
|
-
searchParams.set(
|
|
600
|
+
if (typeof client.reconnect !== "undefined") {
|
|
601
|
+
searchParams.set(
|
|
602
|
+
"reconnect",
|
|
603
|
+
typeof client.reconnect === "number"
|
|
604
|
+
? String(client.reconnect)
|
|
605
|
+
: "10"
|
|
606
|
+
);
|
|
304
607
|
}
|
|
305
608
|
|
|
306
|
-
|
|
609
|
+
webSocketURLStr = searchParams.toString();
|
|
307
610
|
}
|
|
308
611
|
|
|
309
612
|
additionalEntries.push(
|
|
310
|
-
`${require.resolve("../client/index.js")}?${
|
|
613
|
+
`${require.resolve("../client/index.js")}?${webSocketURLStr}`
|
|
311
614
|
);
|
|
312
615
|
}
|
|
313
616
|
|
|
314
|
-
if (this.options.hot) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
hotEntry = require.resolve("webpack/hot/only-dev-server");
|
|
319
|
-
} else if (this.options.hot) {
|
|
320
|
-
hotEntry = require.resolve("webpack/hot/dev-server");
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
additionalEntries.push(hotEntry);
|
|
617
|
+
if (this.options.hot === "only") {
|
|
618
|
+
additionalEntries.push(require.resolve("webpack/hot/only-dev-server"));
|
|
619
|
+
} else if (this.options.hot) {
|
|
620
|
+
additionalEntries.push(require.resolve("webpack/hot/dev-server"));
|
|
324
621
|
}
|
|
325
622
|
|
|
326
623
|
const webpack = compiler.webpack || require("webpack");
|
|
@@ -338,9 +635,9 @@ class Server {
|
|
|
338
635
|
else {
|
|
339
636
|
/**
|
|
340
637
|
* prependEntry Method for webpack 4
|
|
341
|
-
* @param {
|
|
342
|
-
* @param {
|
|
343
|
-
* @returns {
|
|
638
|
+
* @param {any} originalEntry
|
|
639
|
+
* @param {any} newAdditionalEntries
|
|
640
|
+
* @returns {any}
|
|
344
641
|
*/
|
|
345
642
|
const prependEntry = (originalEntry, newAdditionalEntries) => {
|
|
346
643
|
if (typeof originalEntry === "function") {
|
|
@@ -369,7 +666,7 @@ class Server {
|
|
|
369
666
|
|
|
370
667
|
// in this case, entry is a string or an array.
|
|
371
668
|
// make sure that we do not add duplicates.
|
|
372
|
-
/** @type {
|
|
669
|
+
/** @type {any} */
|
|
373
670
|
const entriesClone = additionalEntries.slice(0);
|
|
374
671
|
|
|
375
672
|
[].concat(originalEntry).forEach((newEntry) => {
|
|
@@ -386,65 +683,80 @@ class Server {
|
|
|
386
683
|
additionalEntries
|
|
387
684
|
);
|
|
388
685
|
compiler.hooks.entryOption.call(
|
|
389
|
-
compiler.options.context,
|
|
686
|
+
/** @type {string} */ (compiler.options.context),
|
|
390
687
|
compiler.options.entry
|
|
391
688
|
);
|
|
392
689
|
}
|
|
393
690
|
}
|
|
394
691
|
|
|
692
|
+
/**
|
|
693
|
+
* @private
|
|
694
|
+
* @returns {Compiler["options"]}
|
|
695
|
+
*/
|
|
395
696
|
getCompilerOptions() {
|
|
396
|
-
if (
|
|
397
|
-
|
|
398
|
-
|
|
697
|
+
if (
|
|
698
|
+
typeof (/** @type {MultiCompiler} */ (this.compiler).compilers) !==
|
|
699
|
+
"undefined"
|
|
700
|
+
) {
|
|
701
|
+
if (/** @type {MultiCompiler} */ (this.compiler).compilers.length === 1) {
|
|
702
|
+
return (
|
|
703
|
+
/** @type {MultiCompiler} */
|
|
704
|
+
(this.compiler).compilers[0].options
|
|
705
|
+
);
|
|
399
706
|
}
|
|
400
707
|
|
|
401
708
|
// Configuration with the `devServer` options
|
|
402
|
-
const compilerWithDevServer =
|
|
403
|
-
|
|
404
|
-
|
|
709
|
+
const compilerWithDevServer =
|
|
710
|
+
/** @type {MultiCompiler} */
|
|
711
|
+
(this.compiler).compilers.find((config) => config.options.devServer);
|
|
405
712
|
|
|
406
713
|
if (compilerWithDevServer) {
|
|
407
714
|
return compilerWithDevServer.options;
|
|
408
715
|
}
|
|
409
716
|
|
|
410
717
|
// Configuration with `web` preset
|
|
411
|
-
const compilerWithWebPreset =
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
718
|
+
const compilerWithWebPreset =
|
|
719
|
+
/** @type {MultiCompiler} */
|
|
720
|
+
(this.compiler).compilers.find(
|
|
721
|
+
(config) =>
|
|
722
|
+
(config.options.externalsPresets &&
|
|
723
|
+
config.options.externalsPresets.web) ||
|
|
724
|
+
[
|
|
725
|
+
"web",
|
|
726
|
+
"webworker",
|
|
727
|
+
"electron-preload",
|
|
728
|
+
"electron-renderer",
|
|
729
|
+
"node-webkit",
|
|
730
|
+
// eslint-disable-next-line no-undefined
|
|
731
|
+
undefined,
|
|
732
|
+
null,
|
|
733
|
+
].includes(/** @type {string} */ (config.options.target))
|
|
734
|
+
);
|
|
426
735
|
|
|
427
736
|
if (compilerWithWebPreset) {
|
|
428
737
|
return compilerWithWebPreset.options;
|
|
429
738
|
}
|
|
430
739
|
|
|
431
740
|
// Fallback
|
|
432
|
-
return this.compiler.compilers[0].options;
|
|
741
|
+
return /** @type {MultiCompiler} */ (this.compiler).compilers[0].options;
|
|
433
742
|
}
|
|
434
743
|
|
|
435
|
-
return this.compiler.options;
|
|
744
|
+
return /** @type {Compiler} */ (this.compiler).options;
|
|
436
745
|
}
|
|
437
746
|
|
|
747
|
+
/**
|
|
748
|
+
* @private
|
|
749
|
+
* @returns {Promise<void>}
|
|
750
|
+
*/
|
|
438
751
|
async normalizeOptions() {
|
|
439
752
|
const { options } = this;
|
|
440
|
-
|
|
441
|
-
if (!this.logger) {
|
|
442
|
-
this.logger = this.compiler.getInfrastructureLogger("webpack-dev-server");
|
|
443
|
-
}
|
|
444
|
-
|
|
445
753
|
const compilerOptions = this.getCompilerOptions();
|
|
446
754
|
// TODO remove `{}` after drop webpack v4 support
|
|
447
755
|
const compilerWatchOptions = compilerOptions.watchOptions || {};
|
|
756
|
+
/**
|
|
757
|
+
* @param {WatchOptions & WebpackConfiguration["watchOptions"]} watchOptions
|
|
758
|
+
* @returns {WatchOptions}
|
|
759
|
+
*/
|
|
448
760
|
const getWatchOptions = (watchOptions = {}) => {
|
|
449
761
|
const getPolling = () => {
|
|
450
762
|
if (typeof watchOptions.usePolling !== "undefined") {
|
|
@@ -494,6 +806,10 @@ class Server {
|
|
|
494
806
|
...rest,
|
|
495
807
|
};
|
|
496
808
|
};
|
|
809
|
+
/**
|
|
810
|
+
* @param {string | Static | undefined} [optionsForStatic]
|
|
811
|
+
* @returns {NormalizedStatic}
|
|
812
|
+
*/
|
|
497
813
|
const getStaticItem = (optionsForStatic) => {
|
|
498
814
|
const getDefaultStaticOptions = () => {
|
|
499
815
|
return {
|
|
@@ -505,6 +821,7 @@ class Server {
|
|
|
505
821
|
};
|
|
506
822
|
};
|
|
507
823
|
|
|
824
|
+
/** @type {NormalizedStatic} */
|
|
508
825
|
let item;
|
|
509
826
|
|
|
510
827
|
if (typeof optionsForStatic === "undefined") {
|
|
@@ -528,8 +845,11 @@ class Server {
|
|
|
528
845
|
? optionsForStatic.staticOptions
|
|
529
846
|
: def.staticOptions,
|
|
530
847
|
publicPath:
|
|
848
|
+
// eslint-disable-next-line no-nested-ternary
|
|
531
849
|
typeof optionsForStatic.publicPath !== "undefined"
|
|
532
|
-
? optionsForStatic.publicPath
|
|
850
|
+
? Array.isArray(optionsForStatic.publicPath)
|
|
851
|
+
? optionsForStatic.publicPath
|
|
852
|
+
: [optionsForStatic.publicPath]
|
|
533
853
|
: def.publicPath,
|
|
534
854
|
// TODO: do merge in the next major release
|
|
535
855
|
serveIndex:
|
|
@@ -557,11 +877,6 @@ class Server {
|
|
|
557
877
|
throw new Error("Using a URL as static.directory is not supported");
|
|
558
878
|
}
|
|
559
879
|
|
|
560
|
-
// ensure that publicPath is an array
|
|
561
|
-
if (typeof item.publicPath === "string") {
|
|
562
|
-
item.publicPath = [item.publicPath];
|
|
563
|
-
}
|
|
564
|
-
|
|
565
880
|
return item;
|
|
566
881
|
};
|
|
567
882
|
|
|
@@ -674,14 +989,21 @@ class Server {
|
|
|
674
989
|
const isHTTPs = Boolean(options.https);
|
|
675
990
|
const isSPDY = Boolean(options.http2);
|
|
676
991
|
|
|
677
|
-
if (isHTTPs
|
|
992
|
+
if (isHTTPs) {
|
|
993
|
+
// TODO: remove in the next major release
|
|
994
|
+
util.deprecate(
|
|
995
|
+
() => {},
|
|
996
|
+
"'https' option is deprecated. Please use the 'server' option.",
|
|
997
|
+
"DEP_WEBPACK_DEV_SERVER_HTTPS"
|
|
998
|
+
)();
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
if (isSPDY) {
|
|
678
1002
|
// TODO: remove in the next major release
|
|
679
1003
|
util.deprecate(
|
|
680
1004
|
() => {},
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
}' option is deprecated. Please use the 'server' option.`,
|
|
684
|
-
`DEP_WEBPACK_DEV_SERVER_${isHTTPs ? "HTTPS" : "HTTP2"}`
|
|
1005
|
+
"'http2' option is deprecated. Please use the 'server' option.",
|
|
1006
|
+
"DEP_WEBPACK_DEV_SERVER_HTTP2"
|
|
685
1007
|
)();
|
|
686
1008
|
}
|
|
687
1009
|
|
|
@@ -692,7 +1014,7 @@ class Server {
|
|
|
692
1014
|
? options.server
|
|
693
1015
|
: // eslint-disable-next-line no-nested-ternary
|
|
694
1016
|
typeof (options.server || {}).type === "string"
|
|
695
|
-
? options.server.type
|
|
1017
|
+
? /** @type {ServerConfiguration} */ (options.server).type || "http"
|
|
696
1018
|
: // eslint-disable-next-line no-nested-ternary
|
|
697
1019
|
isSPDY
|
|
698
1020
|
? "spdy"
|
|
@@ -700,33 +1022,64 @@ class Server {
|
|
|
700
1022
|
? "https"
|
|
701
1023
|
: "http",
|
|
702
1024
|
options: {
|
|
703
|
-
|
|
704
|
-
|
|
1025
|
+
.../** @type {ServerOptions} */ (options.https),
|
|
1026
|
+
.../** @type {ServerConfiguration} */ (options.server || {}).options,
|
|
705
1027
|
},
|
|
706
1028
|
};
|
|
707
1029
|
|
|
708
1030
|
if (
|
|
709
1031
|
options.server.type === "spdy" &&
|
|
710
|
-
typeof options.server.options.spdy ===
|
|
1032
|
+
typeof (/** @type {ServerOptions} */ (options.server.options).spdy) ===
|
|
1033
|
+
"undefined"
|
|
711
1034
|
) {
|
|
712
|
-
|
|
1035
|
+
/** @type {ServerOptions} */
|
|
1036
|
+
(options.server.options).spdy = {
|
|
713
1037
|
protocols: ["h2", "http/1.1"],
|
|
714
1038
|
};
|
|
715
1039
|
}
|
|
716
1040
|
|
|
717
1041
|
if (options.server.type === "https" || options.server.type === "spdy") {
|
|
718
|
-
if (
|
|
719
|
-
|
|
1042
|
+
if (
|
|
1043
|
+
typeof (
|
|
1044
|
+
/** @type {ServerOptions} */ (options.server.options).requestCert
|
|
1045
|
+
) === "undefined"
|
|
1046
|
+
) {
|
|
1047
|
+
/** @type {ServerOptions} */
|
|
1048
|
+
(options.server.options).requestCert = false;
|
|
720
1049
|
}
|
|
721
1050
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
1051
|
+
const httpsProperties =
|
|
1052
|
+
/** @type {Array<keyof ServerOptions>} */
|
|
1053
|
+
(["cacert", "ca", "cert", "crl", "key", "pfx"]);
|
|
1054
|
+
|
|
1055
|
+
for (const property of httpsProperties) {
|
|
1056
|
+
if (
|
|
1057
|
+
typeof (
|
|
1058
|
+
/** @type {ServerOptions} */ (options.server.options)[property]
|
|
1059
|
+
) === "undefined"
|
|
1060
|
+
) {
|
|
725
1061
|
// eslint-disable-next-line no-continue
|
|
726
1062
|
continue;
|
|
727
1063
|
}
|
|
728
1064
|
|
|
729
|
-
|
|
1065
|
+
// @ts-ignore
|
|
1066
|
+
if (property === "cacert") {
|
|
1067
|
+
// TODO remove the `cacert` option in favor `ca` in the next major release
|
|
1068
|
+
util.deprecate(
|
|
1069
|
+
() => {},
|
|
1070
|
+
"The 'cacert' option is deprecated. Please use the 'ca' option.",
|
|
1071
|
+
"DEP_WEBPACK_DEV_SERVER_CACERT"
|
|
1072
|
+
)();
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/** @type {any} */
|
|
1076
|
+
const value =
|
|
1077
|
+
/** @type {ServerOptions} */
|
|
1078
|
+
(options.server.options)[property];
|
|
1079
|
+
/**
|
|
1080
|
+
* @param {string | Buffer | undefined} item
|
|
1081
|
+
* @returns {string | Buffer | undefined}
|
|
1082
|
+
*/
|
|
730
1083
|
const readFile = (item) => {
|
|
731
1084
|
if (
|
|
732
1085
|
Buffer.isBuffer(item) ||
|
|
@@ -749,14 +1102,18 @@ class Server {
|
|
|
749
1102
|
}
|
|
750
1103
|
};
|
|
751
1104
|
|
|
752
|
-
|
|
1105
|
+
/** @type {any} */
|
|
1106
|
+
(options.server.options)[property] = Array.isArray(value)
|
|
753
1107
|
? value.map((item) => readFile(item))
|
|
754
1108
|
: readFile(value);
|
|
755
1109
|
}
|
|
756
1110
|
|
|
757
1111
|
let fakeCert;
|
|
758
1112
|
|
|
759
|
-
if (
|
|
1113
|
+
if (
|
|
1114
|
+
!(/** @type {ServerOptions} */ (options.server.options).key) ||
|
|
1115
|
+
/** @type {ServerOptions} */ (!options.server.options).cert
|
|
1116
|
+
) {
|
|
760
1117
|
const certificateDir = Server.findCacheDir();
|
|
761
1118
|
const certificatePath = path.join(certificateDir, "server.pem");
|
|
762
1119
|
let certificateExists;
|
|
@@ -771,11 +1128,10 @@ class Server {
|
|
|
771
1128
|
if (certificateExists) {
|
|
772
1129
|
const certificateTtl = 1000 * 60 * 60 * 24;
|
|
773
1130
|
const certificateStat = await fs.promises.stat(certificatePath);
|
|
774
|
-
|
|
775
|
-
const now = new Date();
|
|
1131
|
+
const now = Number(new Date());
|
|
776
1132
|
|
|
777
1133
|
// cert is more than 30 days old, kill it with fire
|
|
778
|
-
if ((now - certificateStat.ctime) / certificateTtl > 30) {
|
|
1134
|
+
if ((now - Number(certificateStat.ctime)) / certificateTtl > 30) {
|
|
779
1135
|
const del = require("del");
|
|
780
1136
|
|
|
781
1137
|
this.logger.info(
|
|
@@ -791,6 +1147,7 @@ class Server {
|
|
|
791
1147
|
if (!certificateExists) {
|
|
792
1148
|
this.logger.info("Generating SSL certificate...");
|
|
793
1149
|
|
|
1150
|
+
// @ts-ignore
|
|
794
1151
|
const selfsigned = require("selfsigned");
|
|
795
1152
|
const attributes = [{ name: "commonName", value: "localhost" }];
|
|
796
1153
|
const pems = selfsigned.generate(attributes, {
|
|
@@ -871,20 +1228,37 @@ class Server {
|
|
|
871
1228
|
this.logger.info(`SSL certificate: ${certificatePath}`);
|
|
872
1229
|
}
|
|
873
1230
|
|
|
874
|
-
if (
|
|
875
|
-
|
|
1231
|
+
if (
|
|
1232
|
+
/** @type {ServerOptions & { cacert?: ServerOptions["ca"] }} */ (
|
|
1233
|
+
options.server.options
|
|
1234
|
+
).cacert
|
|
1235
|
+
) {
|
|
1236
|
+
if (/** @type {ServerOptions} */ (options.server.options).ca) {
|
|
876
1237
|
this.logger.warn(
|
|
877
1238
|
"Do not specify 'ca' and 'cacert' options together, the 'ca' option will be used."
|
|
878
1239
|
);
|
|
879
1240
|
} else {
|
|
880
|
-
|
|
1241
|
+
/** @type {ServerOptions} */
|
|
1242
|
+
(options.server.options).ca =
|
|
1243
|
+
/** @type {ServerOptions & { cacert?: ServerOptions["ca"] }} */
|
|
1244
|
+
(options.server.options).cacert;
|
|
881
1245
|
}
|
|
882
1246
|
|
|
883
|
-
delete
|
|
1247
|
+
delete (
|
|
1248
|
+
/** @type {ServerOptions & { cacert?: ServerOptions["ca"] }} */ (
|
|
1249
|
+
options.server.options
|
|
1250
|
+
).cacert
|
|
1251
|
+
);
|
|
884
1252
|
}
|
|
885
1253
|
|
|
886
|
-
|
|
887
|
-
options.server.options.
|
|
1254
|
+
/** @type {ServerOptions} */
|
|
1255
|
+
(options.server.options).key =
|
|
1256
|
+
/** @type {ServerOptions} */
|
|
1257
|
+
(options.server.options).key || fakeCert;
|
|
1258
|
+
/** @type {ServerOptions} */
|
|
1259
|
+
(options.server.options).cert =
|
|
1260
|
+
/** @type {ServerOptions} */
|
|
1261
|
+
(options.server.options).cert || fakeCert;
|
|
888
1262
|
}
|
|
889
1263
|
|
|
890
1264
|
if (typeof options.ipc === "boolean") {
|
|
@@ -903,6 +1277,11 @@ class Server {
|
|
|
903
1277
|
|
|
904
1278
|
// https://github.com/webpack/webpack-dev-server/issues/1990
|
|
905
1279
|
const defaultOpenOptions = { wait: false };
|
|
1280
|
+
/**
|
|
1281
|
+
* @param {any} target
|
|
1282
|
+
* @returns {NormalizedOpen[]}
|
|
1283
|
+
*/
|
|
1284
|
+
// TODO: remove --open-app in favor of --open-app-name
|
|
906
1285
|
const getOpenItemsFromObject = ({ target, ...rest }) => {
|
|
907
1286
|
const normalizedOptions = { ...defaultOpenOptions, ...rest };
|
|
908
1287
|
|
|
@@ -924,14 +1303,25 @@ class Server {
|
|
|
924
1303
|
};
|
|
925
1304
|
|
|
926
1305
|
if (typeof options.open === "undefined") {
|
|
927
|
-
|
|
1306
|
+
/** @type {NormalizedOpen[]} */
|
|
1307
|
+
(options.open) = [];
|
|
928
1308
|
} else if (typeof options.open === "boolean") {
|
|
929
|
-
|
|
930
|
-
|
|
1309
|
+
/** @type {NormalizedOpen[]} */
|
|
1310
|
+
(options.open) = options.open
|
|
1311
|
+
? [
|
|
1312
|
+
{
|
|
1313
|
+
target: "<url>",
|
|
1314
|
+
options: /** @type {OpenOptions} */ (defaultOpenOptions),
|
|
1315
|
+
},
|
|
1316
|
+
]
|
|
931
1317
|
: [];
|
|
932
1318
|
} else if (typeof options.open === "string") {
|
|
933
|
-
|
|
1319
|
+
/** @type {NormalizedOpen[]} */
|
|
1320
|
+
(options.open) = [{ target: options.open, options: defaultOpenOptions }];
|
|
934
1321
|
} else if (Array.isArray(options.open)) {
|
|
1322
|
+
/**
|
|
1323
|
+
* @type {NormalizedOpen[]}
|
|
1324
|
+
*/
|
|
935
1325
|
const result = [];
|
|
936
1326
|
|
|
937
1327
|
options.open.forEach((item) => {
|
|
@@ -944,9 +1334,29 @@ class Server {
|
|
|
944
1334
|
result.push(...getOpenItemsFromObject(item));
|
|
945
1335
|
});
|
|
946
1336
|
|
|
947
|
-
|
|
1337
|
+
/** @type {NormalizedOpen[]} */
|
|
1338
|
+
(options.open) = result;
|
|
948
1339
|
} else {
|
|
949
|
-
|
|
1340
|
+
/** @type {NormalizedOpen[]} */
|
|
1341
|
+
(options.open) = [...getOpenItemsFromObject(options.open)];
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
if (options.onAfterSetupMiddleware) {
|
|
1345
|
+
// TODO: remove in the next major release
|
|
1346
|
+
util.deprecate(
|
|
1347
|
+
() => {},
|
|
1348
|
+
"'onAfterSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.",
|
|
1349
|
+
`DEP_WEBPACK_DEV_SERVER_ON_AFTER_SETUP_MIDDLEWARE`
|
|
1350
|
+
)();
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
if (options.onBeforeSetupMiddleware) {
|
|
1354
|
+
// TODO: remove in the next major release
|
|
1355
|
+
util.deprecate(
|
|
1356
|
+
() => {},
|
|
1357
|
+
"'onBeforeSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.",
|
|
1358
|
+
`DEP_WEBPACK_DEV_SERVER_ON_BEFORE_SETUP_MIDDLEWARE`
|
|
1359
|
+
)();
|
|
950
1360
|
}
|
|
951
1361
|
|
|
952
1362
|
if (typeof options.port === "string" && options.port !== "auto") {
|
|
@@ -970,61 +1380,91 @@ class Server {
|
|
|
970
1380
|
Object.prototype.hasOwnProperty.call(options.proxy, "target") ||
|
|
971
1381
|
Object.prototype.hasOwnProperty.call(options.proxy, "router")
|
|
972
1382
|
) {
|
|
973
|
-
|
|
1383
|
+
/** @type {ProxyArray} */
|
|
1384
|
+
(options.proxy) = [/** @type {ProxyConfigMap} */ (options.proxy)];
|
|
974
1385
|
} else {
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1386
|
+
/** @type {ProxyArray} */
|
|
1387
|
+
(options.proxy) = Object.keys(options.proxy).map(
|
|
1388
|
+
/**
|
|
1389
|
+
* @param {string} context
|
|
1390
|
+
* @returns {HttpProxyMiddlewareOptions}
|
|
1391
|
+
*/
|
|
1392
|
+
(context) => {
|
|
1393
|
+
let proxyOptions;
|
|
1394
|
+
// For backwards compatibility reasons.
|
|
1395
|
+
const correctedContext = context
|
|
1396
|
+
.replace(/^\*$/, "**")
|
|
1397
|
+
.replace(/\/\*$/, "");
|
|
1398
|
+
|
|
1399
|
+
if (
|
|
1400
|
+
typeof (
|
|
1401
|
+
/** @type {ProxyConfigMap} */ (options.proxy)[context]
|
|
1402
|
+
) === "string"
|
|
1403
|
+
) {
|
|
1404
|
+
proxyOptions = {
|
|
1405
|
+
context: correctedContext,
|
|
1406
|
+
target:
|
|
1407
|
+
/** @type {ProxyConfigMap} */
|
|
1408
|
+
(options.proxy)[context],
|
|
1409
|
+
};
|
|
1410
|
+
} else {
|
|
1411
|
+
proxyOptions = {
|
|
1412
|
+
// @ts-ignore
|
|
1413
|
+
.../** @type {ProxyConfigMap} */ (options.proxy)[context],
|
|
1414
|
+
};
|
|
1415
|
+
proxyOptions.context = correctedContext;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
return proxyOptions;
|
|
990
1419
|
}
|
|
991
|
-
|
|
992
|
-
return proxyOptions;
|
|
993
|
-
});
|
|
1420
|
+
);
|
|
994
1421
|
}
|
|
995
1422
|
}
|
|
996
1423
|
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1424
|
+
/** @type {ProxyArray} */
|
|
1425
|
+
(options.proxy) =
|
|
1426
|
+
/** @type {ProxyArray} */
|
|
1427
|
+
(options.proxy).map(
|
|
1428
|
+
/**
|
|
1429
|
+
* @param {HttpProxyMiddlewareOptions} item
|
|
1430
|
+
* @returns {HttpProxyMiddlewareOptions}
|
|
1431
|
+
*/
|
|
1432
|
+
(item) => {
|
|
1433
|
+
/**
|
|
1434
|
+
* @param {"info" | "warn" | "error" | "debug" | "silent" | undefined | "none" | "log" | "verbose"} level
|
|
1435
|
+
* @returns {"info" | "warn" | "error" | "debug" | "silent" | undefined}
|
|
1436
|
+
*/
|
|
1437
|
+
const getLogLevelForProxy = (level) => {
|
|
1438
|
+
if (level === "none") {
|
|
1439
|
+
return "silent";
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
if (level === "log") {
|
|
1443
|
+
return "info";
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
if (level === "verbose") {
|
|
1447
|
+
return "debug";
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
return level;
|
|
1451
|
+
};
|
|
1452
|
+
|
|
1453
|
+
if (typeof item.logLevel === "undefined") {
|
|
1454
|
+
item.logLevel = getLogLevelForProxy(
|
|
1455
|
+
compilerOptions.infrastructureLogging
|
|
1456
|
+
? compilerOptions.infrastructureLogging.level
|
|
1457
|
+
: "info"
|
|
1458
|
+
);
|
|
1459
|
+
}
|
|
1002
1460
|
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1461
|
+
if (typeof item.logProvider === "undefined") {
|
|
1462
|
+
item.logProvider = () => this.logger;
|
|
1463
|
+
}
|
|
1006
1464
|
|
|
1007
|
-
|
|
1008
|
-
return "debug";
|
|
1465
|
+
return item;
|
|
1009
1466
|
}
|
|
1010
|
-
|
|
1011
|
-
return level;
|
|
1012
|
-
};
|
|
1013
|
-
|
|
1014
|
-
if (typeof item.logLevel === "undefined") {
|
|
1015
|
-
item.logLevel = getLogLevelForProxy(
|
|
1016
|
-
compilerOptions.infrastructureLogging
|
|
1017
|
-
? compilerOptions.infrastructureLogging.level
|
|
1018
|
-
: "info"
|
|
1019
|
-
);
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
if (typeof item.logProvider === "undefined") {
|
|
1023
|
-
item.logProvider = () => this.logger;
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
return item;
|
|
1027
|
-
});
|
|
1467
|
+
);
|
|
1028
1468
|
}
|
|
1029
1469
|
|
|
1030
1470
|
if (typeof options.setupExitSignals === "undefined") {
|
|
@@ -1102,38 +1542,61 @@ class Server {
|
|
|
1102
1542
|
};
|
|
1103
1543
|
} else {
|
|
1104
1544
|
options.webSocketServer = {
|
|
1105
|
-
type:
|
|
1545
|
+
type:
|
|
1546
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1547
|
+
(options.webSocketServer).type || defaultWebSocketServerType,
|
|
1106
1548
|
options: {
|
|
1107
1549
|
...defaultWebSocketServerOptions,
|
|
1108
|
-
|
|
1550
|
+
.../** @type {WebSocketServerConfiguration} */
|
|
1551
|
+
(options.webSocketServer).options,
|
|
1109
1552
|
},
|
|
1110
1553
|
};
|
|
1111
1554
|
|
|
1112
|
-
|
|
1113
|
-
options
|
|
1114
|
-
|
|
1115
|
-
|
|
1555
|
+
const webSocketServer =
|
|
1556
|
+
/** @type {{ type: WebSocketServerConfiguration["type"], options: NonNullable<WebSocketServerConfiguration["options"]> }} */
|
|
1557
|
+
(options.webSocketServer);
|
|
1558
|
+
|
|
1559
|
+
if (typeof webSocketServer.options.port === "string") {
|
|
1560
|
+
webSocketServer.options.port = Number(webSocketServer.options.port);
|
|
1116
1561
|
}
|
|
1117
1562
|
}
|
|
1118
1563
|
}
|
|
1119
1564
|
|
|
1565
|
+
/**
|
|
1566
|
+
* @private
|
|
1567
|
+
* @returns {string}
|
|
1568
|
+
*/
|
|
1120
1569
|
getClientTransport() {
|
|
1121
|
-
let
|
|
1570
|
+
let clientImplementation;
|
|
1122
1571
|
let clientImplementationFound = true;
|
|
1123
1572
|
|
|
1124
1573
|
const isKnownWebSocketServerImplementation =
|
|
1125
1574
|
this.options.webSocketServer &&
|
|
1126
|
-
typeof
|
|
1575
|
+
typeof (
|
|
1576
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1577
|
+
(this.options.webSocketServer).type
|
|
1578
|
+
) === "string" &&
|
|
1579
|
+
// @ts-ignore
|
|
1127
1580
|
(this.options.webSocketServer.type === "ws" ||
|
|
1128
|
-
|
|
1581
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1582
|
+
(this.options.webSocketServer).type === "sockjs");
|
|
1129
1583
|
|
|
1130
1584
|
let clientTransport;
|
|
1131
1585
|
|
|
1132
1586
|
if (this.options.client) {
|
|
1133
|
-
if (
|
|
1134
|
-
|
|
1587
|
+
if (
|
|
1588
|
+
typeof (
|
|
1589
|
+
/** @type {ClientConfiguration} */
|
|
1590
|
+
(this.options.client).webSocketTransport
|
|
1591
|
+
) !== "undefined"
|
|
1592
|
+
) {
|
|
1593
|
+
clientTransport =
|
|
1594
|
+
/** @type {ClientConfiguration} */
|
|
1595
|
+
(this.options.client).webSocketTransport;
|
|
1135
1596
|
} else if (isKnownWebSocketServerImplementation) {
|
|
1136
|
-
clientTransport =
|
|
1597
|
+
clientTransport =
|
|
1598
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1599
|
+
(this.options.webSocketServer).type;
|
|
1137
1600
|
} else {
|
|
1138
1601
|
clientTransport = "ws";
|
|
1139
1602
|
}
|
|
@@ -1145,17 +1608,16 @@ class Server {
|
|
|
1145
1608
|
case "string":
|
|
1146
1609
|
// could be 'sockjs', 'ws', or a path that should be required
|
|
1147
1610
|
if (clientTransport === "sockjs") {
|
|
1148
|
-
|
|
1611
|
+
clientImplementation = require.resolve(
|
|
1149
1612
|
"../client/clients/SockJSClient"
|
|
1150
1613
|
);
|
|
1151
1614
|
} else if (clientTransport === "ws") {
|
|
1152
|
-
|
|
1615
|
+
clientImplementation = require.resolve(
|
|
1153
1616
|
"../client/clients/WebSocketClient"
|
|
1154
1617
|
);
|
|
1155
1618
|
} else {
|
|
1156
1619
|
try {
|
|
1157
|
-
|
|
1158
|
-
ClientImplementation = require.resolve(clientTransport);
|
|
1620
|
+
clientImplementation = require.resolve(clientTransport);
|
|
1159
1621
|
} catch (e) {
|
|
1160
1622
|
clientImplementationFound = false;
|
|
1161
1623
|
}
|
|
@@ -1175,31 +1637,52 @@ class Server {
|
|
|
1175
1637
|
);
|
|
1176
1638
|
}
|
|
1177
1639
|
|
|
1178
|
-
return
|
|
1640
|
+
return /** @type {string} */ (clientImplementation);
|
|
1179
1641
|
}
|
|
1180
1642
|
|
|
1643
|
+
/**
|
|
1644
|
+
* @private
|
|
1645
|
+
* @returns {string}
|
|
1646
|
+
*/
|
|
1181
1647
|
getServerTransport() {
|
|
1182
1648
|
let implementation;
|
|
1183
1649
|
let implementationFound = true;
|
|
1184
1650
|
|
|
1185
|
-
switch (
|
|
1651
|
+
switch (
|
|
1652
|
+
typeof (
|
|
1653
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1654
|
+
(this.options.webSocketServer).type
|
|
1655
|
+
)
|
|
1656
|
+
) {
|
|
1186
1657
|
case "string":
|
|
1187
1658
|
// Could be 'sockjs', in the future 'ws', or a path that should be required
|
|
1188
|
-
if (
|
|
1659
|
+
if (
|
|
1660
|
+
/** @type {WebSocketServerConfiguration} */ (
|
|
1661
|
+
this.options.webSocketServer
|
|
1662
|
+
).type === "sockjs"
|
|
1663
|
+
) {
|
|
1189
1664
|
implementation = require("./servers/SockJSServer");
|
|
1190
|
-
} else if (
|
|
1665
|
+
} else if (
|
|
1666
|
+
/** @type {WebSocketServerConfiguration} */ (
|
|
1667
|
+
this.options.webSocketServer
|
|
1668
|
+
).type === "ws"
|
|
1669
|
+
) {
|
|
1191
1670
|
implementation = require("./servers/WebsocketServer");
|
|
1192
1671
|
} else {
|
|
1193
1672
|
try {
|
|
1194
1673
|
// eslint-disable-next-line import/no-dynamic-require
|
|
1195
|
-
implementation = require(
|
|
1674
|
+
implementation = require(/** @type {WebSocketServerConfiguration} */ (
|
|
1675
|
+
this.options.webSocketServer
|
|
1676
|
+
).type);
|
|
1196
1677
|
} catch (error) {
|
|
1197
1678
|
implementationFound = false;
|
|
1198
1679
|
}
|
|
1199
1680
|
}
|
|
1200
1681
|
break;
|
|
1201
1682
|
case "function":
|
|
1202
|
-
implementation =
|
|
1683
|
+
implementation = /** @type {WebSocketServerConfiguration} */ (
|
|
1684
|
+
this.options.webSocketServer
|
|
1685
|
+
).type;
|
|
1203
1686
|
break;
|
|
1204
1687
|
default:
|
|
1205
1688
|
implementationFound = false;
|
|
@@ -1216,37 +1699,61 @@ class Server {
|
|
|
1216
1699
|
return implementation;
|
|
1217
1700
|
}
|
|
1218
1701
|
|
|
1702
|
+
/**
|
|
1703
|
+
* @private
|
|
1704
|
+
* @returns {void}
|
|
1705
|
+
*/
|
|
1219
1706
|
setupProgressPlugin() {
|
|
1220
|
-
const { ProgressPlugin } =
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1707
|
+
const { ProgressPlugin } =
|
|
1708
|
+
/** @type {MultiCompiler}*/
|
|
1709
|
+
(this.compiler).compilers
|
|
1710
|
+
? /** @type {MultiCompiler}*/ (this.compiler).compilers[0].webpack
|
|
1711
|
+
: /** @type {Compiler}*/ (this.compiler).webpack ||
|
|
1712
|
+
// TODO remove me after drop webpack v4
|
|
1713
|
+
require("webpack");
|
|
1714
|
+
|
|
1715
|
+
new ProgressPlugin(
|
|
1716
|
+
/**
|
|
1717
|
+
* @param {number} percent
|
|
1718
|
+
* @param {string} msg
|
|
1719
|
+
* @param {string} addInfo
|
|
1720
|
+
* @param {string} pluginName
|
|
1721
|
+
*/
|
|
1722
|
+
(percent, msg, addInfo, pluginName) => {
|
|
1723
|
+
percent = Math.floor(percent * 100);
|
|
1224
1724
|
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1725
|
+
if (percent === 100) {
|
|
1726
|
+
msg = "Compilation completed";
|
|
1727
|
+
}
|
|
1228
1728
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1729
|
+
if (addInfo) {
|
|
1730
|
+
msg = `${msg} (${addInfo})`;
|
|
1731
|
+
}
|
|
1232
1732
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1733
|
+
if (this.webSocketServer) {
|
|
1734
|
+
this.sendMessage(this.webSocketServer.clients, "progress-update", {
|
|
1735
|
+
percent,
|
|
1736
|
+
msg,
|
|
1737
|
+
pluginName,
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1240
1740
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1741
|
+
if (this.server) {
|
|
1742
|
+
this.server.emit("progress-update", { percent, msg, pluginName });
|
|
1743
|
+
}
|
|
1243
1744
|
}
|
|
1244
|
-
|
|
1745
|
+
).apply(this.compiler);
|
|
1245
1746
|
}
|
|
1246
1747
|
|
|
1748
|
+
/**
|
|
1749
|
+
* @private
|
|
1750
|
+
* @returns {Promise<void>}
|
|
1751
|
+
*/
|
|
1247
1752
|
async initialize() {
|
|
1248
1753
|
if (this.options.webSocketServer) {
|
|
1249
|
-
const compilers =
|
|
1754
|
+
const compilers =
|
|
1755
|
+
/** @type {MultiCompiler} */
|
|
1756
|
+
(this.compiler).compilers || [this.compiler];
|
|
1250
1757
|
|
|
1251
1758
|
compilers.forEach((compiler) => {
|
|
1252
1759
|
this.addAdditionalEntries(compiler);
|
|
@@ -1278,7 +1785,10 @@ class Server {
|
|
|
1278
1785
|
}
|
|
1279
1786
|
});
|
|
1280
1787
|
|
|
1281
|
-
if (
|
|
1788
|
+
if (
|
|
1789
|
+
this.options.client &&
|
|
1790
|
+
/** @type {ClientConfiguration} */ (this.options.client).progress
|
|
1791
|
+
) {
|
|
1282
1792
|
this.setupProgressPlugin();
|
|
1283
1793
|
}
|
|
1284
1794
|
}
|
|
@@ -1290,7 +1800,8 @@ class Server {
|
|
|
1290
1800
|
// Should be after `webpack-dev-middleware`, otherwise other middlewares might rewrite response
|
|
1291
1801
|
this.setupBuiltInRoutes();
|
|
1292
1802
|
this.setupWatchFiles();
|
|
1293
|
-
this.
|
|
1803
|
+
this.setupWatchStaticFiles();
|
|
1804
|
+
this.setupMiddlewares();
|
|
1294
1805
|
this.createServer();
|
|
1295
1806
|
|
|
1296
1807
|
if (this.options.setupExitSignals) {
|
|
@@ -1301,7 +1812,6 @@ class Server {
|
|
|
1301
1812
|
signals.forEach((signal) => {
|
|
1302
1813
|
const listener = () => {
|
|
1303
1814
|
if (needForceShutdown) {
|
|
1304
|
-
// eslint-disable-next-line no-process-exit
|
|
1305
1815
|
process.exit();
|
|
1306
1816
|
}
|
|
1307
1817
|
|
|
@@ -1314,11 +1824,9 @@ class Server {
|
|
|
1314
1824
|
this.stopCallback(() => {
|
|
1315
1825
|
if (typeof this.compiler.close === "function") {
|
|
1316
1826
|
this.compiler.close(() => {
|
|
1317
|
-
// eslint-disable-next-line no-process-exit
|
|
1318
1827
|
process.exit();
|
|
1319
1828
|
});
|
|
1320
1829
|
} else {
|
|
1321
|
-
// eslint-disable-next-line no-process-exit
|
|
1322
1830
|
process.exit();
|
|
1323
1831
|
}
|
|
1324
1832
|
});
|
|
@@ -1332,53 +1840,108 @@ class Server {
|
|
|
1332
1840
|
|
|
1333
1841
|
// Proxy WebSocket without the initial http request
|
|
1334
1842
|
// https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade
|
|
1335
|
-
|
|
1336
|
-
|
|
1843
|
+
/** @type {RequestHandler[]} */
|
|
1844
|
+
(this.webSocketProxies).forEach((webSocketProxy) => {
|
|
1845
|
+
/** @type {import("http").Server} */
|
|
1846
|
+
(this.server).on(
|
|
1847
|
+
"upgrade",
|
|
1848
|
+
/** @type {RequestHandler & { upgrade: NonNullable<RequestHandler["upgrade"]> }} */
|
|
1849
|
+
(webSocketProxy).upgrade
|
|
1850
|
+
);
|
|
1337
1851
|
}, this);
|
|
1338
1852
|
}
|
|
1339
1853
|
|
|
1854
|
+
/**
|
|
1855
|
+
* @private
|
|
1856
|
+
* @returns {void}
|
|
1857
|
+
*/
|
|
1340
1858
|
setupApp() {
|
|
1341
|
-
|
|
1859
|
+
/** @type {import("express").Application | undefined}*/
|
|
1342
1860
|
// eslint-disable-next-line new-cap
|
|
1343
|
-
this.app = new express();
|
|
1861
|
+
this.app = new /** @type {any} */ (express)();
|
|
1344
1862
|
}
|
|
1345
1863
|
|
|
1864
|
+
/**
|
|
1865
|
+
* @private
|
|
1866
|
+
* @param {Stats | MultiStats} statsObj
|
|
1867
|
+
* @returns {StatsCompilation}
|
|
1868
|
+
*/
|
|
1346
1869
|
getStats(statsObj) {
|
|
1347
1870
|
const stats = Server.DEFAULT_STATS;
|
|
1348
1871
|
const compilerOptions = this.getCompilerOptions();
|
|
1349
1872
|
|
|
1873
|
+
// @ts-ignore
|
|
1350
1874
|
if (compilerOptions.stats && compilerOptions.stats.warningsFilter) {
|
|
1875
|
+
// @ts-ignore
|
|
1351
1876
|
stats.warningsFilter = compilerOptions.stats.warningsFilter;
|
|
1352
1877
|
}
|
|
1353
1878
|
|
|
1354
1879
|
return statsObj.toJson(stats);
|
|
1355
1880
|
}
|
|
1356
1881
|
|
|
1882
|
+
/**
|
|
1883
|
+
* @private
|
|
1884
|
+
* @returns {void}
|
|
1885
|
+
*/
|
|
1357
1886
|
setupHooks() {
|
|
1358
1887
|
this.compiler.hooks.invalid.tap("webpack-dev-server", () => {
|
|
1359
1888
|
if (this.webSocketServer) {
|
|
1360
1889
|
this.sendMessage(this.webSocketServer.clients, "invalid");
|
|
1361
1890
|
}
|
|
1362
1891
|
});
|
|
1363
|
-
this.compiler.hooks.done.tap(
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1892
|
+
this.compiler.hooks.done.tap(
|
|
1893
|
+
"webpack-dev-server",
|
|
1894
|
+
/**
|
|
1895
|
+
* @param {Stats | MultiStats} stats
|
|
1896
|
+
*/
|
|
1897
|
+
(stats) => {
|
|
1898
|
+
if (this.webSocketServer) {
|
|
1899
|
+
this.sendStats(this.webSocketServer.clients, this.getStats(stats));
|
|
1900
|
+
}
|
|
1367
1901
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1902
|
+
/**
|
|
1903
|
+
* @private
|
|
1904
|
+
* @type {Stats | MultiStats}
|
|
1905
|
+
*/
|
|
1906
|
+
this.stats = stats;
|
|
1907
|
+
}
|
|
1908
|
+
);
|
|
1370
1909
|
}
|
|
1371
1910
|
|
|
1911
|
+
/**
|
|
1912
|
+
* @private
|
|
1913
|
+
* @returns {void}
|
|
1914
|
+
*/
|
|
1372
1915
|
setupHostHeaderCheck() {
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1916
|
+
/** @type {import("express").Application} */
|
|
1917
|
+
(this.app).all(
|
|
1918
|
+
"*",
|
|
1919
|
+
/**
|
|
1920
|
+
* @param {Request} req
|
|
1921
|
+
* @param {Response} res
|
|
1922
|
+
* @param {NextFunction} next
|
|
1923
|
+
* @returns {void}
|
|
1924
|
+
*/
|
|
1925
|
+
(req, res, next) => {
|
|
1926
|
+
if (
|
|
1927
|
+
this.checkHeader(
|
|
1928
|
+
/** @type {{ [key: string]: string | undefined }} */
|
|
1929
|
+
(req.headers),
|
|
1930
|
+
"host"
|
|
1931
|
+
)
|
|
1932
|
+
) {
|
|
1933
|
+
return next();
|
|
1934
|
+
}
|
|
1377
1935
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1936
|
+
res.send("Invalid Host header");
|
|
1937
|
+
}
|
|
1938
|
+
);
|
|
1380
1939
|
}
|
|
1381
1940
|
|
|
1941
|
+
/**
|
|
1942
|
+
* @private
|
|
1943
|
+
* @returns {void}
|
|
1944
|
+
*/
|
|
1382
1945
|
setupDevMiddleware() {
|
|
1383
1946
|
const webpackDevMiddleware = require("webpack-dev-middleware");
|
|
1384
1947
|
|
|
@@ -1389,435 +1952,616 @@ class Server {
|
|
|
1389
1952
|
);
|
|
1390
1953
|
}
|
|
1391
1954
|
|
|
1955
|
+
/**
|
|
1956
|
+
* @private
|
|
1957
|
+
* @returns {void}
|
|
1958
|
+
*/
|
|
1392
1959
|
setupBuiltInRoutes() {
|
|
1393
1960
|
const { app, middleware } = this;
|
|
1394
1961
|
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
app.get("/webpack-dev-server/invalidate", (_req, res) => {
|
|
1407
|
-
this.invalidate();
|
|
1408
|
-
|
|
1409
|
-
res.end();
|
|
1410
|
-
});
|
|
1962
|
+
/** @type {import("express").Application} */
|
|
1963
|
+
(app).get(
|
|
1964
|
+
"/__webpack_dev_server__/sockjs.bundle.js",
|
|
1965
|
+
/**
|
|
1966
|
+
* @param {Request} req
|
|
1967
|
+
* @param {Response} res
|
|
1968
|
+
* @returns {void}
|
|
1969
|
+
*/
|
|
1970
|
+
(req, res) => {
|
|
1971
|
+
res.setHeader("Content-Type", "application/javascript");
|
|
1411
1972
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
res.setHeader("Content-Type", "text/html");
|
|
1415
|
-
res.write(
|
|
1416
|
-
'<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body>'
|
|
1417
|
-
);
|
|
1973
|
+
const { createReadStream } = fs;
|
|
1974
|
+
const clientPath = path.join(__dirname, "..", "client");
|
|
1418
1975
|
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1976
|
+
createReadStream(
|
|
1977
|
+
path.join(clientPath, "modules/sockjs-client/index.js")
|
|
1978
|
+
).pipe(res);
|
|
1979
|
+
}
|
|
1980
|
+
);
|
|
1423
1981
|
|
|
1424
|
-
|
|
1982
|
+
/** @type {import("express").Application} */
|
|
1983
|
+
(app).get(
|
|
1984
|
+
"/webpack-dev-server/invalidate",
|
|
1985
|
+
/**
|
|
1986
|
+
* @param {Request} _req
|
|
1987
|
+
* @param {Response} res
|
|
1988
|
+
* @returns {void}
|
|
1989
|
+
*/
|
|
1990
|
+
(_req, res) => {
|
|
1991
|
+
this.invalidate();
|
|
1425
1992
|
|
|
1426
|
-
|
|
1427
|
-
|
|
1993
|
+
res.end();
|
|
1994
|
+
}
|
|
1995
|
+
);
|
|
1428
1996
|
|
|
1429
|
-
|
|
1430
|
-
|
|
1997
|
+
/** @type {import("express").Application} */
|
|
1998
|
+
(app).get(
|
|
1999
|
+
"/webpack-dev-server",
|
|
2000
|
+
/**
|
|
2001
|
+
* @param {Request} req
|
|
2002
|
+
* @param {Response} res
|
|
2003
|
+
* @returns {void}
|
|
2004
|
+
*/
|
|
2005
|
+
(req, res) => {
|
|
2006
|
+
/** @type {import("webpack-dev-middleware").API<Request, Response>}*/
|
|
2007
|
+
(middleware).waitUntilValid((stats) => {
|
|
2008
|
+
res.setHeader("Content-Type", "text/html");
|
|
2009
|
+
res.write(
|
|
2010
|
+
'<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body>'
|
|
2011
|
+
);
|
|
1431
2012
|
|
|
1432
|
-
|
|
1433
|
-
|
|
2013
|
+
const statsForPrint =
|
|
2014
|
+
typeof (/** @type {MultiStats} */ (stats).stats) !== "undefined"
|
|
2015
|
+
? /** @type {MultiStats} */ (stats).toJson().children
|
|
2016
|
+
: [/** @type {Stats} */ (stats).toJson()];
|
|
1434
2017
|
|
|
1435
|
-
|
|
2018
|
+
res.write(`<h1>Assets Report:</h1>`);
|
|
1436
2019
|
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
2020
|
+
/**
|
|
2021
|
+
* @type {StatsCompilation[]}
|
|
2022
|
+
*/
|
|
2023
|
+
(statsForPrint).forEach((item, index) => {
|
|
2024
|
+
res.write("<div>");
|
|
1440
2025
|
|
|
1441
|
-
|
|
1442
|
-
|
|
2026
|
+
const name =
|
|
2027
|
+
// eslint-disable-next-line no-nested-ternary
|
|
2028
|
+
typeof item.name !== "undefined"
|
|
2029
|
+
? item.name
|
|
2030
|
+
: /** @type {MultiStats} */ (stats).stats
|
|
2031
|
+
? `unnamed[${index}]`
|
|
2032
|
+
: "unnamed";
|
|
2033
|
+
|
|
2034
|
+
res.write(`<h2>Compilation: ${name}</h2>`);
|
|
2035
|
+
res.write("<ul>");
|
|
2036
|
+
|
|
2037
|
+
const publicPath =
|
|
2038
|
+
item.publicPath === "auto" ? "" : item.publicPath;
|
|
2039
|
+
|
|
2040
|
+
for (const asset of /** @type {NonNullable<StatsCompilation["assets"]>} */ (
|
|
2041
|
+
item.assets
|
|
2042
|
+
)) {
|
|
2043
|
+
const assetName = asset.name;
|
|
2044
|
+
const assetURL = `${publicPath}${assetName}`;
|
|
2045
|
+
|
|
2046
|
+
res.write(
|
|
2047
|
+
`<li>
|
|
1443
2048
|
<strong><a href="${assetURL}" target="_blank">${assetName}</a></strong>
|
|
1444
2049
|
</li>`
|
|
1445
|
-
|
|
1446
|
-
|
|
2050
|
+
);
|
|
2051
|
+
}
|
|
1447
2052
|
|
|
1448
|
-
|
|
1449
|
-
|
|
2053
|
+
res.write("</ul>");
|
|
2054
|
+
res.write("</div>");
|
|
2055
|
+
});
|
|
2056
|
+
|
|
2057
|
+
res.end("</body></html>");
|
|
1450
2058
|
});
|
|
2059
|
+
}
|
|
2060
|
+
);
|
|
2061
|
+
}
|
|
1451
2062
|
|
|
1452
|
-
|
|
2063
|
+
/**
|
|
2064
|
+
* @private
|
|
2065
|
+
* @returns {void}
|
|
2066
|
+
*/
|
|
2067
|
+
setupWatchStaticFiles() {
|
|
2068
|
+
if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) {
|
|
2069
|
+
/** @type {NormalizedStatic[]} */
|
|
2070
|
+
(this.options.static).forEach((staticOption) => {
|
|
2071
|
+
if (staticOption.watch) {
|
|
2072
|
+
this.watchFiles(staticOption.directory, staticOption.watch);
|
|
2073
|
+
}
|
|
1453
2074
|
});
|
|
1454
|
-
}
|
|
2075
|
+
}
|
|
1455
2076
|
}
|
|
1456
2077
|
|
|
1457
|
-
|
|
1458
|
-
|
|
2078
|
+
/**
|
|
2079
|
+
* @private
|
|
2080
|
+
* @returns {void}
|
|
2081
|
+
*/
|
|
2082
|
+
setupWatchFiles() {
|
|
2083
|
+
const { watchFiles } = this.options;
|
|
1459
2084
|
|
|
1460
|
-
|
|
2085
|
+
if (/** @type {WatchFiles[]} */ (watchFiles).length > 0) {
|
|
2086
|
+
/** @type {WatchFiles[]} */
|
|
2087
|
+
(watchFiles).forEach((item) => {
|
|
2088
|
+
this.watchFiles(item.paths, item.options);
|
|
2089
|
+
});
|
|
2090
|
+
}
|
|
1461
2091
|
}
|
|
1462
2092
|
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
// However, the proxy middleware has no use in this case, and will fail to instantiate.
|
|
1469
|
-
if (proxyConfig.target) {
|
|
1470
|
-
const context = proxyConfig.context || proxyConfig.path;
|
|
1471
|
-
|
|
1472
|
-
return createProxyMiddleware(context, proxyConfig);
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
if (proxyConfig.router) {
|
|
1476
|
-
return createProxyMiddleware(proxyConfig);
|
|
1477
|
-
}
|
|
1478
|
-
};
|
|
2093
|
+
/**
|
|
2094
|
+
* @private
|
|
2095
|
+
* @returns {void}
|
|
2096
|
+
*/
|
|
2097
|
+
setupMiddlewares() {
|
|
1479
2098
|
/**
|
|
1480
|
-
*
|
|
1481
|
-
* proxy: [
|
|
1482
|
-
* {
|
|
1483
|
-
* context: "value",
|
|
1484
|
-
* ...options,
|
|
1485
|
-
* },
|
|
1486
|
-
* // or:
|
|
1487
|
-
* function() {
|
|
1488
|
-
* return {
|
|
1489
|
-
* context: "context",
|
|
1490
|
-
* ...options,
|
|
1491
|
-
* };
|
|
1492
|
-
* }
|
|
1493
|
-
* ]
|
|
2099
|
+
* @type {Array<Middleware>}
|
|
1494
2100
|
*/
|
|
1495
|
-
|
|
1496
|
-
let proxyMiddleware;
|
|
2101
|
+
let middlewares = [];
|
|
1497
2102
|
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
: proxyConfigOrCallback;
|
|
1502
|
-
|
|
1503
|
-
proxyMiddleware = getProxyMiddleware(proxyConfig);
|
|
1504
|
-
|
|
1505
|
-
if (proxyConfig.ws) {
|
|
1506
|
-
this.webSocketProxies.push(proxyMiddleware);
|
|
1507
|
-
}
|
|
2103
|
+
// compress is placed last and uses unshift so that it will be the first middleware used
|
|
2104
|
+
if (this.options.compress) {
|
|
2105
|
+
const compression = require("compression");
|
|
1508
2106
|
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
const newProxyConfig = proxyConfigOrCallback(req, res, next);
|
|
2107
|
+
middlewares.push({ name: "compression", middleware: compression() });
|
|
2108
|
+
}
|
|
1512
2109
|
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
2110
|
+
if (typeof this.options.onBeforeSetupMiddleware === "function") {
|
|
2111
|
+
this.options.onBeforeSetupMiddleware(this);
|
|
2112
|
+
}
|
|
1518
2113
|
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
: null;
|
|
1527
|
-
|
|
1528
|
-
if (typeof bypassUrl === "boolean") {
|
|
1529
|
-
// skip the proxy
|
|
1530
|
-
req.url = null;
|
|
1531
|
-
next();
|
|
1532
|
-
} else if (typeof bypassUrl === "string") {
|
|
1533
|
-
// byPass to that url
|
|
1534
|
-
req.url = bypassUrl;
|
|
1535
|
-
next();
|
|
1536
|
-
} else if (proxyMiddleware) {
|
|
1537
|
-
return proxyMiddleware(req, res, next);
|
|
1538
|
-
} else {
|
|
1539
|
-
next();
|
|
1540
|
-
}
|
|
1541
|
-
};
|
|
2114
|
+
if (typeof this.options.headers !== "undefined") {
|
|
2115
|
+
middlewares.push({
|
|
2116
|
+
name: "set-headers",
|
|
2117
|
+
path: "*",
|
|
2118
|
+
middleware: this.setHeaders.bind(this),
|
|
2119
|
+
});
|
|
2120
|
+
}
|
|
1542
2121
|
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
2122
|
+
middlewares.push({
|
|
2123
|
+
name: "webpack-dev-middleware",
|
|
2124
|
+
middleware:
|
|
2125
|
+
/** @type {import("webpack-dev-middleware").Middleware<Request, Response>}*/
|
|
2126
|
+
(this.middleware),
|
|
1546
2127
|
});
|
|
1547
|
-
}
|
|
1548
2128
|
|
|
1549
|
-
|
|
1550
|
-
|
|
2129
|
+
if (this.options.proxy) {
|
|
2130
|
+
const { createProxyMiddleware } = require("http-proxy-middleware");
|
|
1551
2131
|
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
2132
|
+
/**
|
|
2133
|
+
* @param {ProxyConfigArray} proxyConfig
|
|
2134
|
+
* @returns {RequestHandler | undefined}
|
|
2135
|
+
*/
|
|
2136
|
+
const getProxyMiddleware = (proxyConfig) => {
|
|
2137
|
+
// It is possible to use the `bypass` method without a `target` or `router`.
|
|
2138
|
+
// However, the proxy middleware has no use in this case, and will fail to instantiate.
|
|
2139
|
+
if (proxyConfig.target) {
|
|
2140
|
+
const context = proxyConfig.context || proxyConfig.path;
|
|
2141
|
+
|
|
2142
|
+
return createProxyMiddleware(
|
|
2143
|
+
/** @type {string} */ (context),
|
|
2144
|
+
proxyConfig
|
|
2145
|
+
);
|
|
2146
|
+
}
|
|
1561
2147
|
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
2148
|
+
if (proxyConfig.router) {
|
|
2149
|
+
return createProxyMiddleware(proxyConfig);
|
|
2150
|
+
}
|
|
2151
|
+
};
|
|
1565
2152
|
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
2153
|
+
/**
|
|
2154
|
+
* Assume a proxy configuration specified as:
|
|
2155
|
+
* proxy: [
|
|
2156
|
+
* {
|
|
2157
|
+
* context: "value",
|
|
2158
|
+
* ...options,
|
|
2159
|
+
* },
|
|
2160
|
+
* // or:
|
|
2161
|
+
* function() {
|
|
2162
|
+
* return {
|
|
2163
|
+
* context: "context",
|
|
2164
|
+
* ...options,
|
|
2165
|
+
* };
|
|
2166
|
+
* }
|
|
2167
|
+
* ]
|
|
2168
|
+
*/
|
|
2169
|
+
/** @type {ProxyArray} */
|
|
2170
|
+
(this.options.proxy).forEach(
|
|
2171
|
+
/**
|
|
2172
|
+
* @param {any} proxyConfigOrCallback
|
|
2173
|
+
*/
|
|
2174
|
+
(proxyConfigOrCallback) => {
|
|
2175
|
+
/**
|
|
2176
|
+
* @type {RequestHandler}
|
|
2177
|
+
*/
|
|
2178
|
+
let proxyMiddleware;
|
|
2179
|
+
|
|
2180
|
+
let proxyConfig =
|
|
2181
|
+
typeof proxyConfigOrCallback === "function"
|
|
2182
|
+
? proxyConfigOrCallback()
|
|
2183
|
+
: proxyConfigOrCallback;
|
|
2184
|
+
|
|
2185
|
+
proxyMiddleware =
|
|
2186
|
+
/** @type {RequestHandler} */
|
|
2187
|
+
(getProxyMiddleware(proxyConfig));
|
|
2188
|
+
|
|
2189
|
+
if (proxyConfig.ws) {
|
|
2190
|
+
this.webSocketProxies.push(proxyMiddleware);
|
|
2191
|
+
}
|
|
1576
2192
|
|
|
1577
|
-
|
|
1578
|
-
|
|
2193
|
+
/**
|
|
2194
|
+
* @param {Request} req
|
|
2195
|
+
* @param {Response} res
|
|
2196
|
+
* @param {NextFunction} next
|
|
2197
|
+
* @returns {Promise<void>}
|
|
2198
|
+
*/
|
|
2199
|
+
const handler = async (req, res, next) => {
|
|
2200
|
+
if (typeof proxyConfigOrCallback === "function") {
|
|
2201
|
+
const newProxyConfig = proxyConfigOrCallback(req, res, next);
|
|
2202
|
+
|
|
2203
|
+
if (newProxyConfig !== proxyConfig) {
|
|
2204
|
+
proxyConfig = newProxyConfig;
|
|
2205
|
+
proxyMiddleware =
|
|
2206
|
+
/** @type {RequestHandler} */
|
|
2207
|
+
(getProxyMiddleware(proxyConfig));
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
1579
2210
|
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
2211
|
+
// - Check if we have a bypass function defined
|
|
2212
|
+
// - In case the bypass function is defined we'll retrieve the
|
|
2213
|
+
// bypassUrl from it otherwise bypassUrl would be null
|
|
2214
|
+
// TODO remove in the next major in favor `context` and `router` options
|
|
2215
|
+
const isByPassFuncDefined =
|
|
2216
|
+
typeof proxyConfig.bypass === "function";
|
|
2217
|
+
const bypassUrl = isByPassFuncDefined
|
|
2218
|
+
? await proxyConfig.bypass(req, res, proxyConfig)
|
|
2219
|
+
: null;
|
|
2220
|
+
|
|
2221
|
+
if (typeof bypassUrl === "boolean") {
|
|
2222
|
+
// skip the proxy
|
|
2223
|
+
// @ts-ignore
|
|
2224
|
+
req.url = null;
|
|
2225
|
+
next();
|
|
2226
|
+
} else if (typeof bypassUrl === "string") {
|
|
2227
|
+
// byPass to that url
|
|
2228
|
+
req.url = bypassUrl;
|
|
2229
|
+
next();
|
|
2230
|
+
} else if (proxyMiddleware) {
|
|
2231
|
+
return proxyMiddleware(req, res, next);
|
|
2232
|
+
} else {
|
|
2233
|
+
next();
|
|
1587
2234
|
}
|
|
2235
|
+
};
|
|
1588
2236
|
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
2237
|
+
middlewares.push({
|
|
2238
|
+
name: "http-proxy-middleware",
|
|
2239
|
+
middleware: handler,
|
|
2240
|
+
});
|
|
2241
|
+
// Also forward error requests to the proxy so it can handle them.
|
|
2242
|
+
middlewares.push({
|
|
2243
|
+
name: "http-proxy-middleware-error-handler",
|
|
2244
|
+
middleware:
|
|
2245
|
+
/**
|
|
2246
|
+
* @param {Error} error
|
|
2247
|
+
* @param {Request} req
|
|
2248
|
+
* @param {Response} res
|
|
2249
|
+
* @param {NextFunction} next
|
|
2250
|
+
* @returns {any}
|
|
2251
|
+
*/
|
|
2252
|
+
(error, req, res, next) => handler(req, res, next),
|
|
1594
2253
|
});
|
|
1595
2254
|
}
|
|
1596
|
-
|
|
1597
|
-
});
|
|
1598
|
-
}
|
|
1599
|
-
|
|
1600
|
-
setupStaticWatchFeature() {
|
|
1601
|
-
this.options.static.forEach((staticOption) => {
|
|
1602
|
-
if (staticOption.watch) {
|
|
1603
|
-
this.watchFiles(staticOption.directory, staticOption.watch);
|
|
1604
|
-
}
|
|
1605
|
-
});
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
setupOnBeforeSetupMiddlewareFeature() {
|
|
1609
|
-
this.options.onBeforeSetupMiddleware(this);
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
|
-
setupWatchFiles() {
|
|
1613
|
-
const { watchFiles } = this.options;
|
|
2255
|
+
);
|
|
1614
2256
|
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
2257
|
+
middlewares.push({
|
|
2258
|
+
name: "webpack-dev-middleware",
|
|
2259
|
+
middleware:
|
|
2260
|
+
/** @type {import("webpack-dev-middleware").Middleware<Request, Response>}*/
|
|
2261
|
+
(this.middleware),
|
|
1618
2262
|
});
|
|
1619
2263
|
}
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
|
-
setupMiddleware() {
|
|
1623
|
-
this.app.use(this.middleware);
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
setupOnAfterSetupMiddlewareFeature() {
|
|
1627
|
-
this.options.onAfterSetupMiddleware(this);
|
|
1628
|
-
}
|
|
1629
|
-
|
|
1630
|
-
setupHeadersFeature() {
|
|
1631
|
-
this.app.all("*", this.setHeaders.bind(this));
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
setupMagicHtmlFeature() {
|
|
1635
|
-
this.app.get("*", this.serveMagicHtml.bind(this));
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
setupFeatures() {
|
|
1639
|
-
const features = {
|
|
1640
|
-
compress: () => {
|
|
1641
|
-
if (this.options.compress) {
|
|
1642
|
-
this.setupCompressFeature();
|
|
1643
|
-
}
|
|
1644
|
-
},
|
|
1645
|
-
proxy: () => {
|
|
1646
|
-
if (this.options.proxy) {
|
|
1647
|
-
this.setupProxyFeature();
|
|
1648
|
-
}
|
|
1649
|
-
},
|
|
1650
|
-
historyApiFallback: () => {
|
|
1651
|
-
if (this.options.historyApiFallback) {
|
|
1652
|
-
this.setupHistoryApiFallbackFeature();
|
|
1653
|
-
}
|
|
1654
|
-
},
|
|
1655
|
-
static: () => {
|
|
1656
|
-
this.setupStaticFeature();
|
|
1657
|
-
},
|
|
1658
|
-
staticServeIndex: () => {
|
|
1659
|
-
this.setupStaticServeIndexFeature();
|
|
1660
|
-
},
|
|
1661
|
-
staticWatch: () => {
|
|
1662
|
-
this.setupStaticWatchFeature();
|
|
1663
|
-
},
|
|
1664
|
-
onBeforeSetupMiddleware: () => {
|
|
1665
|
-
if (typeof this.options.onBeforeSetupMiddleware === "function") {
|
|
1666
|
-
this.setupOnBeforeSetupMiddlewareFeature();
|
|
1667
|
-
}
|
|
1668
|
-
},
|
|
1669
|
-
onAfterSetupMiddleware: () => {
|
|
1670
|
-
if (typeof this.options.onAfterSetupMiddleware === "function") {
|
|
1671
|
-
this.setupOnAfterSetupMiddlewareFeature();
|
|
1672
|
-
}
|
|
1673
|
-
},
|
|
1674
|
-
middleware: () => {
|
|
1675
|
-
// include our middleware to ensure
|
|
1676
|
-
// it is able to handle '/index.html' request after redirect
|
|
1677
|
-
this.setupMiddleware();
|
|
1678
|
-
},
|
|
1679
|
-
headers: () => {
|
|
1680
|
-
this.setupHeadersFeature();
|
|
1681
|
-
},
|
|
1682
|
-
magicHtml: () => {
|
|
1683
|
-
this.setupMagicHtmlFeature();
|
|
1684
|
-
},
|
|
1685
|
-
};
|
|
1686
|
-
|
|
1687
|
-
const runnableFeatures = [];
|
|
1688
|
-
|
|
1689
|
-
// compress is placed last and uses unshift so that it will be the first middleware used
|
|
1690
|
-
if (this.options.compress) {
|
|
1691
|
-
runnableFeatures.push("compress");
|
|
1692
|
-
}
|
|
1693
2264
|
|
|
1694
|
-
if (this.options.
|
|
1695
|
-
|
|
2265
|
+
if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) {
|
|
2266
|
+
/** @type {NormalizedStatic[]} */
|
|
2267
|
+
(this.options.static).forEach((staticOption) => {
|
|
2268
|
+
staticOption.publicPath.forEach((publicPath) => {
|
|
2269
|
+
middlewares.push({
|
|
2270
|
+
name: "express-static",
|
|
2271
|
+
path: publicPath,
|
|
2272
|
+
middleware: express.static(
|
|
2273
|
+
staticOption.directory,
|
|
2274
|
+
staticOption.staticOptions
|
|
2275
|
+
),
|
|
2276
|
+
});
|
|
2277
|
+
});
|
|
2278
|
+
});
|
|
1696
2279
|
}
|
|
1697
2280
|
|
|
1698
|
-
|
|
2281
|
+
if (this.options.historyApiFallback) {
|
|
2282
|
+
const connectHistoryApiFallback = require("connect-history-api-fallback");
|
|
2283
|
+
const { historyApiFallback } = this.options;
|
|
1699
2284
|
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
2285
|
+
if (
|
|
2286
|
+
typeof (
|
|
2287
|
+
/** @type {ConnectHistoryApiFallbackOptions} */
|
|
2288
|
+
(historyApiFallback).logger
|
|
2289
|
+
) === "undefined" &&
|
|
2290
|
+
!(
|
|
2291
|
+
/** @type {ConnectHistoryApiFallbackOptions} */
|
|
2292
|
+
(historyApiFallback).verbose
|
|
2293
|
+
)
|
|
2294
|
+
) {
|
|
2295
|
+
// @ts-ignore
|
|
2296
|
+
historyApiFallback.logger = this.logger.log.bind(
|
|
2297
|
+
this.logger,
|
|
2298
|
+
"[connect-history-api-fallback]"
|
|
2299
|
+
);
|
|
2300
|
+
}
|
|
1703
2301
|
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
2302
|
+
// Fall back to /index.html if nothing else matches.
|
|
2303
|
+
middlewares.push({
|
|
2304
|
+
name: "connect-history-api-fallback",
|
|
2305
|
+
middleware: connectHistoryApiFallback(
|
|
2306
|
+
/** @type {ConnectHistoryApiFallbackOptions} */
|
|
2307
|
+
(historyApiFallback)
|
|
2308
|
+
),
|
|
2309
|
+
});
|
|
1707
2310
|
|
|
1708
|
-
|
|
1709
|
-
|
|
2311
|
+
// include our middleware to ensure
|
|
2312
|
+
// it is able to handle '/index.html' request after redirect
|
|
2313
|
+
middlewares.push({
|
|
2314
|
+
name: "webpack-dev-middleware",
|
|
2315
|
+
middleware:
|
|
2316
|
+
/** @type {import("webpack-dev-middleware").Middleware<Request, Response>}*/
|
|
2317
|
+
(this.middleware),
|
|
2318
|
+
});
|
|
1710
2319
|
|
|
1711
|
-
if (this.options.static) {
|
|
1712
|
-
|
|
2320
|
+
if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) {
|
|
2321
|
+
/** @type {NormalizedStatic[]} */
|
|
2322
|
+
(this.options.static).forEach((staticOption) => {
|
|
2323
|
+
staticOption.publicPath.forEach((publicPath) => {
|
|
2324
|
+
middlewares.push({
|
|
2325
|
+
name: "express-static",
|
|
2326
|
+
path: publicPath,
|
|
2327
|
+
middleware: express.static(
|
|
2328
|
+
staticOption.directory,
|
|
2329
|
+
staticOption.staticOptions
|
|
2330
|
+
),
|
|
2331
|
+
});
|
|
2332
|
+
});
|
|
2333
|
+
});
|
|
1713
2334
|
}
|
|
1714
2335
|
}
|
|
1715
2336
|
|
|
1716
|
-
if (this.options.static) {
|
|
1717
|
-
|
|
2337
|
+
if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) {
|
|
2338
|
+
const serveIndex = require("serve-index");
|
|
2339
|
+
|
|
2340
|
+
/** @type {NormalizedStatic[]} */
|
|
2341
|
+
(this.options.static).forEach((staticOption) => {
|
|
2342
|
+
staticOption.publicPath.forEach((publicPath) => {
|
|
2343
|
+
if (staticOption.serveIndex) {
|
|
2344
|
+
middlewares.push({
|
|
2345
|
+
name: "serve-index",
|
|
2346
|
+
path: publicPath,
|
|
2347
|
+
/**
|
|
2348
|
+
* @param {Request} req
|
|
2349
|
+
* @param {Response} res
|
|
2350
|
+
* @param {NextFunction} next
|
|
2351
|
+
* @returns {void}
|
|
2352
|
+
*/
|
|
2353
|
+
middleware: (req, res, next) => {
|
|
2354
|
+
// serve-index doesn't fallthrough non-get/head request to next middleware
|
|
2355
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
2356
|
+
return next();
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
serveIndex(
|
|
2360
|
+
staticOption.directory,
|
|
2361
|
+
/** @type {ServeIndexOptions} */
|
|
2362
|
+
(staticOption.serveIndex)
|
|
2363
|
+
)(req, res, next);
|
|
2364
|
+
},
|
|
2365
|
+
});
|
|
2366
|
+
}
|
|
2367
|
+
});
|
|
2368
|
+
});
|
|
1718
2369
|
}
|
|
1719
2370
|
|
|
1720
2371
|
if (this.options.magicHtml) {
|
|
1721
|
-
|
|
2372
|
+
middlewares.push({
|
|
2373
|
+
name: "serve-magic-html",
|
|
2374
|
+
middleware: this.serveMagicHtml.bind(this),
|
|
2375
|
+
});
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
if (typeof this.options.onAfterSetupMiddleware === "function") {
|
|
2379
|
+
this.options.onAfterSetupMiddleware(this);
|
|
1722
2380
|
}
|
|
1723
2381
|
|
|
1724
|
-
if (this.options.
|
|
1725
|
-
|
|
2382
|
+
if (typeof this.options.setupMiddlewares === "function") {
|
|
2383
|
+
middlewares = this.options.setupMiddlewares(middlewares, this);
|
|
1726
2384
|
}
|
|
1727
2385
|
|
|
1728
|
-
|
|
1729
|
-
|
|
2386
|
+
middlewares.forEach((middleware) => {
|
|
2387
|
+
if (typeof middleware === "function") {
|
|
2388
|
+
/** @type {import("express").Application} */
|
|
2389
|
+
(this.app).use(middleware);
|
|
2390
|
+
} else if (typeof middleware.path !== "undefined") {
|
|
2391
|
+
/** @type {import("express").Application} */
|
|
2392
|
+
(this.app).use(middleware.path, middleware.middleware);
|
|
2393
|
+
} else {
|
|
2394
|
+
/** @type {import("express").Application} */
|
|
2395
|
+
(this.app).use(middleware.middleware);
|
|
2396
|
+
}
|
|
1730
2397
|
});
|
|
1731
2398
|
}
|
|
1732
2399
|
|
|
2400
|
+
/**
|
|
2401
|
+
* @private
|
|
2402
|
+
* @returns {void}
|
|
2403
|
+
*/
|
|
1733
2404
|
createServer() {
|
|
2405
|
+
const { type, options } = /** @type {ServerConfiguration} */ (
|
|
2406
|
+
this.options.server
|
|
2407
|
+
);
|
|
2408
|
+
|
|
2409
|
+
/** @type {import("http").Server | undefined | null} */
|
|
1734
2410
|
// eslint-disable-next-line import/no-dynamic-require
|
|
1735
|
-
this.server = require(
|
|
1736
|
-
|
|
2411
|
+
this.server = require(/** @type {string} */ (type)).createServer(
|
|
2412
|
+
options,
|
|
1737
2413
|
this.app
|
|
1738
2414
|
);
|
|
1739
2415
|
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
2416
|
+
/** @type {import("http").Server} */
|
|
2417
|
+
(this.server).on(
|
|
2418
|
+
"connection",
|
|
2419
|
+
/**
|
|
2420
|
+
* @param {Socket} socket
|
|
2421
|
+
*/
|
|
2422
|
+
(socket) => {
|
|
2423
|
+
// Add socket to list
|
|
2424
|
+
this.sockets.push(socket);
|
|
1743
2425
|
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
2426
|
+
socket.once("close", () => {
|
|
2427
|
+
// Remove socket from list
|
|
2428
|
+
this.sockets.splice(this.sockets.indexOf(socket), 1);
|
|
2429
|
+
});
|
|
2430
|
+
}
|
|
2431
|
+
);
|
|
1749
2432
|
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
2433
|
+
/** @type {import("http").Server} */
|
|
2434
|
+
(this.server).on(
|
|
2435
|
+
"error",
|
|
2436
|
+
/**
|
|
2437
|
+
* @param {Error} error
|
|
2438
|
+
*/
|
|
2439
|
+
(error) => {
|
|
2440
|
+
throw error;
|
|
2441
|
+
}
|
|
2442
|
+
);
|
|
1753
2443
|
}
|
|
1754
2444
|
|
|
2445
|
+
/**
|
|
2446
|
+
* @private
|
|
2447
|
+
* @returns {void}
|
|
2448
|
+
*/
|
|
1755
2449
|
// TODO: remove `--web-socket-server` in favor of `--web-socket-server-type`
|
|
1756
2450
|
createWebSocketServer() {
|
|
1757
|
-
|
|
1758
|
-
this.webSocketServer
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
2451
|
+
/** @type {WebSocketServerImplementation | undefined | null} */
|
|
2452
|
+
this.webSocketServer = new /** @type {any} */ (this.getServerTransport())(
|
|
2453
|
+
this
|
|
2454
|
+
);
|
|
2455
|
+
/** @type {WebSocketServerImplementation} */
|
|
2456
|
+
(this.webSocketServer).implementation.on(
|
|
2457
|
+
"connection",
|
|
2458
|
+
/**
|
|
2459
|
+
* @param {ClientConnection} client
|
|
2460
|
+
* @param {IncomingMessage} request
|
|
2461
|
+
*/
|
|
2462
|
+
(client, request) => {
|
|
2463
|
+
/** @type {{ [key: string]: string | undefined } | undefined} */
|
|
2464
|
+
const headers =
|
|
2465
|
+
// eslint-disable-next-line no-nested-ternary
|
|
2466
|
+
typeof request !== "undefined"
|
|
2467
|
+
? /** @type {{ [key: string]: string | undefined }} */
|
|
2468
|
+
(request.headers)
|
|
2469
|
+
: typeof (
|
|
2470
|
+
/** @type {import("sockjs").Connection} */ (client).headers
|
|
2471
|
+
) !== "undefined"
|
|
2472
|
+
? /** @type {import("sockjs").Connection} */ (client).headers
|
|
2473
|
+
: // eslint-disable-next-line no-undefined
|
|
2474
|
+
undefined;
|
|
2475
|
+
|
|
2476
|
+
if (!headers) {
|
|
2477
|
+
this.logger.warn(
|
|
2478
|
+
'webSocketServer implementation must pass headers for the "connection" event'
|
|
2479
|
+
);
|
|
2480
|
+
}
|
|
1773
2481
|
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
2482
|
+
if (
|
|
2483
|
+
!headers ||
|
|
2484
|
+
!this.checkHeader(headers, "host") ||
|
|
2485
|
+
!this.checkHeader(headers, "origin")
|
|
2486
|
+
) {
|
|
2487
|
+
this.sendMessage([client], "error", "Invalid Host/Origin header");
|
|
1780
2488
|
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
2489
|
+
// With https enabled, the sendMessage above is encrypted asynchronously so not yet sent
|
|
2490
|
+
// Terminate would prevent it sending, so use close to allow it to be sent
|
|
2491
|
+
client.close();
|
|
1784
2492
|
|
|
1785
|
-
|
|
1786
|
-
|
|
2493
|
+
return;
|
|
2494
|
+
}
|
|
1787
2495
|
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
2496
|
+
if (this.options.hot === true || this.options.hot === "only") {
|
|
2497
|
+
this.sendMessage([client], "hot");
|
|
2498
|
+
}
|
|
1791
2499
|
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
2500
|
+
if (this.options.liveReload) {
|
|
2501
|
+
this.sendMessage([client], "liveReload");
|
|
2502
|
+
}
|
|
1795
2503
|
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
2504
|
+
if (
|
|
2505
|
+
this.options.client &&
|
|
2506
|
+
/** @type {ClientConfiguration} */
|
|
2507
|
+
(this.options.client).progress
|
|
2508
|
+
) {
|
|
2509
|
+
this.sendMessage(
|
|
2510
|
+
[client],
|
|
2511
|
+
"progress",
|
|
2512
|
+
/** @type {ClientConfiguration} */
|
|
2513
|
+
(this.options.client).progress
|
|
2514
|
+
);
|
|
2515
|
+
}
|
|
1799
2516
|
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
2517
|
+
if (
|
|
2518
|
+
this.options.client &&
|
|
2519
|
+
/** @type {ClientConfiguration} */ (this.options.client).reconnect
|
|
2520
|
+
) {
|
|
2521
|
+
this.sendMessage(
|
|
2522
|
+
[client],
|
|
2523
|
+
"reconnect",
|
|
2524
|
+
/** @type {ClientConfiguration} */
|
|
2525
|
+
(this.options.client).reconnect
|
|
2526
|
+
);
|
|
2527
|
+
}
|
|
1803
2528
|
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
2529
|
+
if (
|
|
2530
|
+
this.options.client &&
|
|
2531
|
+
/** @type {ClientConfiguration} */
|
|
2532
|
+
(this.options.client).overlay
|
|
2533
|
+
) {
|
|
2534
|
+
this.sendMessage(
|
|
2535
|
+
[client],
|
|
2536
|
+
"overlay",
|
|
2537
|
+
/** @type {ClientConfiguration} */
|
|
2538
|
+
(this.options.client).overlay
|
|
2539
|
+
);
|
|
2540
|
+
}
|
|
1807
2541
|
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
2542
|
+
if (!this.stats) {
|
|
2543
|
+
return;
|
|
2544
|
+
}
|
|
1811
2545
|
|
|
1812
|
-
|
|
1813
|
-
|
|
2546
|
+
this.sendStats([client], this.getStats(this.stats), true);
|
|
2547
|
+
}
|
|
2548
|
+
);
|
|
1814
2549
|
}
|
|
1815
2550
|
|
|
2551
|
+
/**
|
|
2552
|
+
* @private
|
|
2553
|
+
* @param {string} defaultOpenTarget
|
|
2554
|
+
* @returns {void}
|
|
2555
|
+
*/
|
|
1816
2556
|
openBrowser(defaultOpenTarget) {
|
|
1817
2557
|
const open = require("open");
|
|
1818
2558
|
|
|
1819
2559
|
Promise.all(
|
|
1820
|
-
|
|
2560
|
+
/** @type {NormalizedOpen[]} */
|
|
2561
|
+
(this.options.open).map((item) => {
|
|
2562
|
+
/**
|
|
2563
|
+
* @type {string}
|
|
2564
|
+
*/
|
|
1821
2565
|
let openTarget;
|
|
1822
2566
|
|
|
1823
2567
|
if (item.target === "<url>") {
|
|
@@ -1831,13 +2575,17 @@ class Server {
|
|
|
1831
2575
|
return open(openTarget, item.options).catch(() => {
|
|
1832
2576
|
this.logger.warn(
|
|
1833
2577
|
`Unable to open "${openTarget}" page${
|
|
1834
|
-
// eslint-disable-next-line no-nested-ternary
|
|
1835
2578
|
item.options.app
|
|
1836
|
-
? ` in "${
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
2579
|
+
? ` in "${
|
|
2580
|
+
/** @type {import("open").App} */
|
|
2581
|
+
(item.options.app).name
|
|
2582
|
+
}" app${
|
|
2583
|
+
/** @type {import("open").App} */
|
|
2584
|
+
(item.options.app).arguments
|
|
2585
|
+
? ` with "${
|
|
2586
|
+
/** @type {import("open").App} */
|
|
2587
|
+
(item.options.app).arguments.join(" ")
|
|
2588
|
+
}" arguments`
|
|
1841
2589
|
: ""
|
|
1842
2590
|
}`
|
|
1843
2591
|
: ""
|
|
@@ -1848,38 +2596,68 @@ class Server {
|
|
|
1848
2596
|
);
|
|
1849
2597
|
}
|
|
1850
2598
|
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
if (callback) {
|
|
1856
|
-
callback();
|
|
1857
|
-
}
|
|
1858
|
-
});
|
|
1859
|
-
}
|
|
1860
|
-
|
|
2599
|
+
/**
|
|
2600
|
+
* @private
|
|
2601
|
+
* @returns {void}
|
|
2602
|
+
*/
|
|
1861
2603
|
runBonjour() {
|
|
2604
|
+
/**
|
|
2605
|
+
* @private
|
|
2606
|
+
* @type {import("bonjour").Bonjour | undefined}
|
|
2607
|
+
*/
|
|
1862
2608
|
this.bonjour = require("bonjour")();
|
|
1863
2609
|
this.bonjour.publish({
|
|
1864
2610
|
name: `Webpack Dev Server ${os.hostname()}:${this.options.port}`,
|
|
1865
|
-
port: this.options.port,
|
|
1866
|
-
type:
|
|
2611
|
+
port: /** @type {number} */ (this.options.port),
|
|
2612
|
+
type:
|
|
2613
|
+
/** @type {ServerConfiguration} */
|
|
2614
|
+
(this.options.server).type === "http" ? "http" : "https",
|
|
1867
2615
|
subtypes: ["webpack"],
|
|
1868
|
-
|
|
2616
|
+
.../** @type {BonjourOptions} */ (this.options.bonjour),
|
|
2617
|
+
});
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
/**
|
|
2621
|
+
* @private
|
|
2622
|
+
* @returns {void}
|
|
2623
|
+
*/
|
|
2624
|
+
stopBonjour(callback = () => {}) {
|
|
2625
|
+
/** @type {Bonjour} */
|
|
2626
|
+
(this.bonjour).unpublishAll(() => {
|
|
2627
|
+
/** @type {Bonjour} */
|
|
2628
|
+
(this.bonjour).destroy();
|
|
2629
|
+
|
|
2630
|
+
if (callback) {
|
|
2631
|
+
callback();
|
|
2632
|
+
}
|
|
1869
2633
|
});
|
|
1870
2634
|
}
|
|
1871
2635
|
|
|
2636
|
+
/**
|
|
2637
|
+
* @private
|
|
2638
|
+
* @returns {void}
|
|
2639
|
+
*/
|
|
1872
2640
|
logStatus() {
|
|
1873
2641
|
const { isColorSupported, cyan, red } = require("colorette");
|
|
1874
2642
|
|
|
2643
|
+
/**
|
|
2644
|
+
* @param {Compiler["options"]} compilerOptions
|
|
2645
|
+
* @returns {boolean}
|
|
2646
|
+
*/
|
|
1875
2647
|
const getColorsOption = (compilerOptions) => {
|
|
2648
|
+
/**
|
|
2649
|
+
* @type {boolean}
|
|
2650
|
+
*/
|
|
1876
2651
|
let colorsEnabled;
|
|
1877
2652
|
|
|
1878
2653
|
if (
|
|
1879
2654
|
compilerOptions.stats &&
|
|
1880
|
-
typeof compilerOptions.stats.colors !==
|
|
2655
|
+
typeof (/** @type {StatsOptions} */ (compilerOptions.stats).colors) !==
|
|
2656
|
+
"undefined"
|
|
1881
2657
|
) {
|
|
1882
|
-
colorsEnabled =
|
|
2658
|
+
colorsEnabled =
|
|
2659
|
+
/** @type {boolean} */
|
|
2660
|
+
(/** @type {StatsOptions} */ (compilerOptions.stats).colors);
|
|
1883
2661
|
} else {
|
|
1884
2662
|
colorsEnabled = isColorSupported;
|
|
1885
2663
|
}
|
|
@@ -1888,6 +2666,11 @@ class Server {
|
|
|
1888
2666
|
};
|
|
1889
2667
|
|
|
1890
2668
|
const colors = {
|
|
2669
|
+
/**
|
|
2670
|
+
* @param {boolean} useColor
|
|
2671
|
+
* @param {string} msg
|
|
2672
|
+
* @returns {string}
|
|
2673
|
+
*/
|
|
1891
2674
|
info(useColor, msg) {
|
|
1892
2675
|
if (useColor) {
|
|
1893
2676
|
return cyan(msg);
|
|
@@ -1895,6 +2678,11 @@ class Server {
|
|
|
1895
2678
|
|
|
1896
2679
|
return msg;
|
|
1897
2680
|
},
|
|
2681
|
+
/**
|
|
2682
|
+
* @param {boolean} useColor
|
|
2683
|
+
* @param {string} msg
|
|
2684
|
+
* @returns {string}
|
|
2685
|
+
*/
|
|
1898
2686
|
error(useColor, msg) {
|
|
1899
2687
|
if (useColor) {
|
|
1900
2688
|
return red(msg);
|
|
@@ -1906,10 +2694,26 @@ class Server {
|
|
|
1906
2694
|
const useColor = getColorsOption(this.getCompilerOptions());
|
|
1907
2695
|
|
|
1908
2696
|
if (this.options.ipc) {
|
|
1909
|
-
this.logger.info(
|
|
2697
|
+
this.logger.info(
|
|
2698
|
+
`Project is running at: "${
|
|
2699
|
+
/** @type {import("http").Server} */
|
|
2700
|
+
(this.server).address()
|
|
2701
|
+
}"`
|
|
2702
|
+
);
|
|
1910
2703
|
} else {
|
|
1911
|
-
const protocol =
|
|
1912
|
-
|
|
2704
|
+
const protocol =
|
|
2705
|
+
/** @type {ServerConfiguration} */
|
|
2706
|
+
(this.options.server).type === "http" ? "http" : "https";
|
|
2707
|
+
const { address, port } =
|
|
2708
|
+
/** @type {import("net").AddressInfo} */
|
|
2709
|
+
(
|
|
2710
|
+
/** @type {import("http").Server} */
|
|
2711
|
+
(this.server).address()
|
|
2712
|
+
);
|
|
2713
|
+
/**
|
|
2714
|
+
* @param {string} newHostname
|
|
2715
|
+
* @returns {string}
|
|
2716
|
+
*/
|
|
1913
2717
|
const prettyPrintURL = (newHostname) =>
|
|
1914
2718
|
url.format({ protocol, hostname: newHostname, port, pathname: "/" });
|
|
1915
2719
|
|
|
@@ -1962,8 +2766,13 @@ class Server {
|
|
|
1962
2766
|
}
|
|
1963
2767
|
} else {
|
|
1964
2768
|
networkUrlIPv4 =
|
|
1965
|
-
parsedIP.kind() === "ipv6" &&
|
|
1966
|
-
|
|
2769
|
+
parsedIP.kind() === "ipv6" &&
|
|
2770
|
+
/** @type {IPv6} */
|
|
2771
|
+
(parsedIP).isIPv4MappedAddress()
|
|
2772
|
+
? prettyPrintURL(
|
|
2773
|
+
/** @type {IPv6} */
|
|
2774
|
+
(parsedIP).toIPv4Address().toString()
|
|
2775
|
+
)
|
|
1967
2776
|
: prettyPrintURL(address);
|
|
1968
2777
|
|
|
1969
2778
|
if (parsedIP.kind() === "ipv6") {
|
|
@@ -1978,10 +2787,19 @@ class Server {
|
|
|
1978
2787
|
}
|
|
1979
2788
|
|
|
1980
2789
|
if (localhost || loopbackIPv4 || loopbackIPv6) {
|
|
1981
|
-
const loopbacks = []
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
.
|
|
2790
|
+
const loopbacks = [];
|
|
2791
|
+
|
|
2792
|
+
if (localhost) {
|
|
2793
|
+
loopbacks.push([colors.info(useColor, localhost)]);
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
if (loopbackIPv4) {
|
|
2797
|
+
loopbacks.push([colors.info(useColor, loopbackIPv4)]);
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
if (loopbackIPv6) {
|
|
2801
|
+
loopbacks.push([colors.info(useColor, loopbackIPv6)]);
|
|
2802
|
+
}
|
|
1985
2803
|
|
|
1986
2804
|
this.logger.info(`Loopback: ${loopbacks.join(", ")}`);
|
|
1987
2805
|
}
|
|
@@ -1998,18 +2816,19 @@ class Server {
|
|
|
1998
2816
|
);
|
|
1999
2817
|
}
|
|
2000
2818
|
|
|
2001
|
-
if (this.options.open.length > 0) {
|
|
2819
|
+
if (/** @type {NormalizedOpen[]} */ (this.options.open).length > 0) {
|
|
2002
2820
|
const openTarget = prettyPrintURL(this.options.host || "localhost");
|
|
2003
2821
|
|
|
2004
2822
|
this.openBrowser(openTarget);
|
|
2005
2823
|
}
|
|
2006
2824
|
}
|
|
2007
2825
|
|
|
2008
|
-
if (
|
|
2826
|
+
if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) {
|
|
2009
2827
|
this.logger.info(
|
|
2010
2828
|
`Content not from webpack is served from '${colors.info(
|
|
2011
2829
|
useColor,
|
|
2012
|
-
|
|
2830
|
+
/** @type {NormalizedStatic[]} */
|
|
2831
|
+
(this.options.static)
|
|
2013
2832
|
.map((staticOption) => staticOption.directory)
|
|
2014
2833
|
.join(", ")
|
|
2015
2834
|
)}' directory`
|
|
@@ -2020,14 +2839,19 @@ class Server {
|
|
|
2020
2839
|
this.logger.info(
|
|
2021
2840
|
`404s will fallback to '${colors.info(
|
|
2022
2841
|
useColor,
|
|
2023
|
-
|
|
2842
|
+
/** @type {ConnectHistoryApiFallbackOptions} */ (
|
|
2843
|
+
this.options.historyApiFallback
|
|
2844
|
+
).index || "/index.html"
|
|
2024
2845
|
)}'`
|
|
2025
2846
|
);
|
|
2026
2847
|
}
|
|
2027
2848
|
|
|
2028
2849
|
if (this.options.bonjour) {
|
|
2029
2850
|
const bonjourProtocol =
|
|
2030
|
-
|
|
2851
|
+
/** @type {BonjourOptions} */
|
|
2852
|
+
(this.options.bonjour).type ||
|
|
2853
|
+
/** @type {ServerConfiguration} */
|
|
2854
|
+
(this.options.server).type === "http"
|
|
2031
2855
|
? "http"
|
|
2032
2856
|
: "https";
|
|
2033
2857
|
|
|
@@ -2037,32 +2861,59 @@ class Server {
|
|
|
2037
2861
|
}
|
|
2038
2862
|
}
|
|
2039
2863
|
|
|
2864
|
+
/**
|
|
2865
|
+
* @private
|
|
2866
|
+
* @param {Request} req
|
|
2867
|
+
* @param {Response} res
|
|
2868
|
+
* @param {NextFunction} next
|
|
2869
|
+
*/
|
|
2040
2870
|
setHeaders(req, res, next) {
|
|
2041
2871
|
let { headers } = this.options;
|
|
2042
2872
|
|
|
2043
2873
|
if (headers) {
|
|
2044
2874
|
if (typeof headers === "function") {
|
|
2045
|
-
headers = headers(
|
|
2875
|
+
headers = headers(
|
|
2876
|
+
req,
|
|
2877
|
+
res,
|
|
2878
|
+
/** @type {import("webpack-dev-middleware").API<Request, Response>}*/
|
|
2879
|
+
(this.middleware).context
|
|
2880
|
+
);
|
|
2046
2881
|
}
|
|
2047
2882
|
|
|
2883
|
+
/**
|
|
2884
|
+
* @type {{key: string, value: string}[]}
|
|
2885
|
+
*/
|
|
2048
2886
|
const allHeaders = [];
|
|
2049
2887
|
|
|
2050
2888
|
if (!Array.isArray(headers)) {
|
|
2051
2889
|
// eslint-disable-next-line guard-for-in
|
|
2052
2890
|
for (const name in headers) {
|
|
2891
|
+
// @ts-ignore
|
|
2053
2892
|
allHeaders.push({ key: name, value: headers[name] });
|
|
2054
2893
|
}
|
|
2894
|
+
|
|
2055
2895
|
headers = allHeaders;
|
|
2056
2896
|
}
|
|
2057
2897
|
|
|
2058
|
-
headers.forEach(
|
|
2059
|
-
|
|
2060
|
-
|
|
2898
|
+
headers.forEach(
|
|
2899
|
+
/**
|
|
2900
|
+
* @param {{key: string, value: any}} header
|
|
2901
|
+
*/
|
|
2902
|
+
(header) => {
|
|
2903
|
+
res.setHeader(header.key, header.value);
|
|
2904
|
+
}
|
|
2905
|
+
);
|
|
2061
2906
|
}
|
|
2062
2907
|
|
|
2063
2908
|
next();
|
|
2064
2909
|
}
|
|
2065
2910
|
|
|
2911
|
+
/**
|
|
2912
|
+
* @private
|
|
2913
|
+
* @param {{ [key: string]: string | undefined }} headers
|
|
2914
|
+
* @param {string} headerToCheck
|
|
2915
|
+
* @returns {boolean}
|
|
2916
|
+
*/
|
|
2066
2917
|
checkHeader(headers, headerToCheck) {
|
|
2067
2918
|
// allow user to opt out of this security check, at their own risk
|
|
2068
2919
|
// by explicitly enabling allowedHosts
|
|
@@ -2099,8 +2950,8 @@ class Server {
|
|
|
2099
2950
|
// always allow localhost host, for convenience (hostname === 'localhost')
|
|
2100
2951
|
// allow hostname of listening address (hostname === this.options.host)
|
|
2101
2952
|
const isValidHostname =
|
|
2102
|
-
ipaddr.IPv4.isValid(hostname) ||
|
|
2103
|
-
ipaddr.IPv6.isValid(hostname) ||
|
|
2953
|
+
(hostname !== null && ipaddr.IPv4.isValid(hostname)) ||
|
|
2954
|
+
(hostname !== null && ipaddr.IPv6.isValid(hostname)) ||
|
|
2104
2955
|
hostname === "localhost" ||
|
|
2105
2956
|
hostname === this.options.host;
|
|
2106
2957
|
|
|
@@ -2127,7 +2978,7 @@ class Server {
|
|
|
2127
2978
|
// "*.example.com" (hostname.endsWith(allowedHost))
|
|
2128
2979
|
if (
|
|
2129
2980
|
hostname === allowedHost.substring(1) ||
|
|
2130
|
-
hostname.endsWith(allowedHost)
|
|
2981
|
+
/** @type {string} */ (hostname).endsWith(allowedHost)
|
|
2131
2982
|
) {
|
|
2132
2983
|
return true;
|
|
2133
2984
|
}
|
|
@@ -2138,15 +2989,27 @@ class Server {
|
|
|
2138
2989
|
// Also allow if `client.webSocketURL.hostname` provided
|
|
2139
2990
|
if (
|
|
2140
2991
|
this.options.client &&
|
|
2141
|
-
typeof
|
|
2992
|
+
typeof (
|
|
2993
|
+
/** @type {ClientConfiguration} */ (this.options.client).webSocketURL
|
|
2994
|
+
) !== "undefined"
|
|
2142
2995
|
) {
|
|
2143
|
-
return
|
|
2996
|
+
return (
|
|
2997
|
+
/** @type {WebSocketURL} */
|
|
2998
|
+
(/** @type {ClientConfiguration} */ (this.options.client).webSocketURL)
|
|
2999
|
+
.hostname === hostname
|
|
3000
|
+
);
|
|
2144
3001
|
}
|
|
2145
3002
|
|
|
2146
3003
|
// disallow
|
|
2147
3004
|
return false;
|
|
2148
3005
|
}
|
|
2149
3006
|
|
|
3007
|
+
/**
|
|
3008
|
+
* @param {ClientConnection[]} clients
|
|
3009
|
+
* @param {string} type
|
|
3010
|
+
* @param {any} [data]
|
|
3011
|
+
* @param {any} [params]
|
|
3012
|
+
*/
|
|
2150
3013
|
// eslint-disable-next-line class-methods-use-this
|
|
2151
3014
|
sendMessage(clients, type, data, params) {
|
|
2152
3015
|
for (const client of clients) {
|
|
@@ -2158,21 +3021,41 @@ class Server {
|
|
|
2158
3021
|
}
|
|
2159
3022
|
}
|
|
2160
3023
|
|
|
3024
|
+
/**
|
|
3025
|
+
* @private
|
|
3026
|
+
* @param {Request} req
|
|
3027
|
+
* @param {Response} res
|
|
3028
|
+
* @param {NextFunction} next
|
|
3029
|
+
* @returns {void}
|
|
3030
|
+
*/
|
|
2161
3031
|
serveMagicHtml(req, res, next) {
|
|
2162
|
-
|
|
3032
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
3033
|
+
return next();
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
/** @type {import("webpack-dev-middleware").API<Request, Response>}*/
|
|
3037
|
+
(this.middleware).waitUntilValid(() => {
|
|
2163
3038
|
const _path = req.path;
|
|
2164
3039
|
|
|
2165
3040
|
try {
|
|
2166
|
-
const filename =
|
|
2167
|
-
|
|
2168
|
-
.
|
|
2169
|
-
|
|
3041
|
+
const filename =
|
|
3042
|
+
/** @type {import("webpack-dev-middleware").API<Request, Response>}*/
|
|
3043
|
+
(this.middleware).getFilenameFromUrl(`${_path}.js`);
|
|
3044
|
+
const isFile =
|
|
3045
|
+
/** @type {Compiler["outputFileSystem"] & { statSync: import("fs").StatSyncFn }}*/
|
|
3046
|
+
(
|
|
3047
|
+
/** @type {import("webpack-dev-middleware").API<Request, Response>}*/
|
|
3048
|
+
(this.middleware).context.outputFileSystem
|
|
3049
|
+
)
|
|
3050
|
+
.statSync(/** @type {import("fs").PathLike} */ (filename))
|
|
3051
|
+
.isFile();
|
|
2170
3052
|
|
|
2171
3053
|
if (!isFile) {
|
|
2172
3054
|
return next();
|
|
2173
3055
|
}
|
|
2174
3056
|
|
|
2175
3057
|
// Serve a page that executes the javascript
|
|
3058
|
+
// @ts-ignore
|
|
2176
3059
|
const queries = req._parsedUrl.search || "";
|
|
2177
3060
|
const responsePage = `<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body><script type="text/javascript" charset="utf-8" src="${_path}.js${queries}"></script></body></html>`;
|
|
2178
3061
|
|
|
@@ -2184,6 +3067,12 @@ class Server {
|
|
|
2184
3067
|
}
|
|
2185
3068
|
|
|
2186
3069
|
// Send stats to a socket or multiple sockets
|
|
3070
|
+
/**
|
|
3071
|
+
* @private
|
|
3072
|
+
* @param {ClientConnection[]} clients
|
|
3073
|
+
* @param {StatsCompilation} stats
|
|
3074
|
+
* @param {boolean} [force]
|
|
3075
|
+
*/
|
|
2187
3076
|
sendStats(clients, stats, force) {
|
|
2188
3077
|
const shouldEmit =
|
|
2189
3078
|
!force &&
|
|
@@ -2201,10 +3090,20 @@ class Server {
|
|
|
2201
3090
|
this.currentHash = stats.hash;
|
|
2202
3091
|
this.sendMessage(clients, "hash", stats.hash);
|
|
2203
3092
|
|
|
2204
|
-
if (
|
|
2205
|
-
|
|
3093
|
+
if (
|
|
3094
|
+
/** @type {NonNullable<StatsCompilation["errors"]>} */
|
|
3095
|
+
(stats.errors).length > 0 ||
|
|
3096
|
+
/** @type {NonNullable<StatsCompilation["warnings"]>} */
|
|
3097
|
+
(stats.warnings).length > 0
|
|
3098
|
+
) {
|
|
3099
|
+
const hasErrors =
|
|
3100
|
+
/** @type {NonNullable<StatsCompilation["errors"]>} */
|
|
3101
|
+
(stats.errors).length > 0;
|
|
2206
3102
|
|
|
2207
|
-
if (
|
|
3103
|
+
if (
|
|
3104
|
+
/** @type {NonNullable<StatsCompilation["warnings"]>} */
|
|
3105
|
+
(stats.warnings).length > 0
|
|
3106
|
+
) {
|
|
2208
3107
|
let params;
|
|
2209
3108
|
|
|
2210
3109
|
if (hasErrors) {
|
|
@@ -2214,7 +3113,10 @@ class Server {
|
|
|
2214
3113
|
this.sendMessage(clients, "warnings", stats.warnings, params);
|
|
2215
3114
|
}
|
|
2216
3115
|
|
|
2217
|
-
if (
|
|
3116
|
+
if (
|
|
3117
|
+
/** @type {NonNullable<StatsCompilation["errors"]>} */ (stats.errors)
|
|
3118
|
+
.length > 0
|
|
3119
|
+
) {
|
|
2218
3120
|
this.sendMessage(clients, "errors", stats.errors);
|
|
2219
3121
|
}
|
|
2220
3122
|
} else {
|
|
@@ -2222,6 +3124,10 @@ class Server {
|
|
|
2222
3124
|
}
|
|
2223
3125
|
}
|
|
2224
3126
|
|
|
3127
|
+
/**
|
|
3128
|
+
* @param {string | string[]} watchPath
|
|
3129
|
+
* @param {WatchOptions} [watchOptions]
|
|
3130
|
+
*/
|
|
2225
3131
|
watchFiles(watchPath, watchOptions) {
|
|
2226
3132
|
const chokidar = require("chokidar");
|
|
2227
3133
|
const watcher = chokidar.watch(watchPath, watchOptions);
|
|
@@ -2242,44 +3148,65 @@ class Server {
|
|
|
2242
3148
|
this.staticWatchers.push(watcher);
|
|
2243
3149
|
}
|
|
2244
3150
|
|
|
2245
|
-
|
|
3151
|
+
/**
|
|
3152
|
+
* @param {import("webpack-dev-middleware").Callback} [callback]
|
|
3153
|
+
*/
|
|
3154
|
+
invalidate(callback = () => {}) {
|
|
2246
3155
|
if (this.middleware) {
|
|
2247
3156
|
this.middleware.invalidate(callback);
|
|
2248
3157
|
}
|
|
2249
3158
|
}
|
|
2250
3159
|
|
|
3160
|
+
/**
|
|
3161
|
+
* @returns {Promise<void>}
|
|
3162
|
+
*/
|
|
2251
3163
|
async start() {
|
|
2252
3164
|
await this.normalizeOptions();
|
|
2253
3165
|
|
|
2254
3166
|
if (this.options.ipc) {
|
|
2255
|
-
await
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
3167
|
+
await /** @type {Promise<void>} */ (
|
|
3168
|
+
new Promise((resolve, reject) => {
|
|
3169
|
+
const net = require("net");
|
|
3170
|
+
const socket = new net.Socket();
|
|
3171
|
+
|
|
3172
|
+
socket.on(
|
|
3173
|
+
"error",
|
|
3174
|
+
/**
|
|
3175
|
+
* @param {Error & { code?: string }} error
|
|
3176
|
+
*/
|
|
3177
|
+
(error) => {
|
|
3178
|
+
if (error.code === "ECONNREFUSED") {
|
|
3179
|
+
// No other server listening on this socket so it can be safely removed
|
|
3180
|
+
fs.unlinkSync(/** @type {string} */ (this.options.ipc));
|
|
3181
|
+
|
|
3182
|
+
resolve();
|
|
3183
|
+
|
|
3184
|
+
return;
|
|
3185
|
+
} else if (error.code === "ENOENT") {
|
|
3186
|
+
resolve();
|
|
3187
|
+
|
|
3188
|
+
return;
|
|
3189
|
+
}
|
|
2269
3190
|
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
reject(error);
|
|
2274
|
-
});
|
|
3191
|
+
reject(error);
|
|
3192
|
+
}
|
|
3193
|
+
);
|
|
2275
3194
|
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
3195
|
+
socket.connect(
|
|
3196
|
+
{ path: /** @type {string} */ (this.options.ipc) },
|
|
3197
|
+
() => {
|
|
3198
|
+
throw new Error(`IPC "${this.options.ipc}" is already used`);
|
|
3199
|
+
}
|
|
3200
|
+
);
|
|
3201
|
+
})
|
|
3202
|
+
);
|
|
2280
3203
|
} else {
|
|
2281
|
-
this.options.host = await Server.getHostname(
|
|
2282
|
-
|
|
3204
|
+
this.options.host = await Server.getHostname(
|
|
3205
|
+
/** @type {Host} */ (this.options.host)
|
|
3206
|
+
);
|
|
3207
|
+
this.options.port = await Server.getFreePort(
|
|
3208
|
+
/** @type {Port} */ (this.options.port)
|
|
3209
|
+
);
|
|
2283
3210
|
}
|
|
2284
3211
|
|
|
2285
3212
|
await this.initialize();
|
|
@@ -2288,17 +3215,23 @@ class Server {
|
|
|
2288
3215
|
? { path: this.options.ipc }
|
|
2289
3216
|
: { host: this.options.host, port: this.options.port };
|
|
2290
3217
|
|
|
2291
|
-
await
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
3218
|
+
await /** @type {Promise<void>} */ (
|
|
3219
|
+
new Promise((resolve) => {
|
|
3220
|
+
/** @type {import("http").Server} */
|
|
3221
|
+
(this.server).listen(listenOptions, () => {
|
|
3222
|
+
resolve();
|
|
3223
|
+
});
|
|
3224
|
+
})
|
|
3225
|
+
);
|
|
2296
3226
|
|
|
2297
3227
|
if (this.options.ipc) {
|
|
2298
3228
|
// chmod 666 (rw rw rw)
|
|
2299
3229
|
const READ_WRITE = 438;
|
|
2300
3230
|
|
|
2301
|
-
await fs.promises.chmod(
|
|
3231
|
+
await fs.promises.chmod(
|
|
3232
|
+
/** @type {string} */ (this.options.ipc),
|
|
3233
|
+
READ_WRITE
|
|
3234
|
+
);
|
|
2302
3235
|
}
|
|
2303
3236
|
|
|
2304
3237
|
if (this.options.webSocketServer) {
|
|
@@ -2316,19 +3249,27 @@ class Server {
|
|
|
2316
3249
|
}
|
|
2317
3250
|
}
|
|
2318
3251
|
|
|
3252
|
+
/**
|
|
3253
|
+
* @param {(err?: Error) => void} [callback]
|
|
3254
|
+
*/
|
|
2319
3255
|
startCallback(callback = () => {}) {
|
|
2320
3256
|
this.start()
|
|
2321
|
-
.then(() => callback(
|
|
3257
|
+
.then(() => callback(), callback)
|
|
2322
3258
|
.catch(callback);
|
|
2323
3259
|
}
|
|
2324
3260
|
|
|
3261
|
+
/**
|
|
3262
|
+
* @returns {Promise<void>}
|
|
3263
|
+
*/
|
|
2325
3264
|
async stop() {
|
|
2326
3265
|
if (this.bonjour) {
|
|
2327
|
-
await
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
3266
|
+
await /** @type {Promise<void>} */ (
|
|
3267
|
+
new Promise((resolve) => {
|
|
3268
|
+
this.stopBonjour(() => {
|
|
3269
|
+
resolve();
|
|
3270
|
+
});
|
|
3271
|
+
})
|
|
3272
|
+
);
|
|
2332
3273
|
}
|
|
2333
3274
|
|
|
2334
3275
|
this.webSocketProxies = [];
|
|
@@ -2338,48 +3279,60 @@ class Server {
|
|
|
2338
3279
|
this.staticWatchers = [];
|
|
2339
3280
|
|
|
2340
3281
|
if (this.webSocketServer) {
|
|
2341
|
-
await
|
|
2342
|
-
|
|
2343
|
-
|
|
3282
|
+
await /** @type {Promise<void>} */ (
|
|
3283
|
+
new Promise((resolve) => {
|
|
3284
|
+
/** @type {WebSocketServerImplementation} */
|
|
3285
|
+
(this.webSocketServer).implementation.close(() => {
|
|
3286
|
+
this.webSocketServer = null;
|
|
2344
3287
|
|
|
2345
|
-
|
|
2346
|
-
|
|
3288
|
+
resolve();
|
|
3289
|
+
});
|
|
2347
3290
|
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
3291
|
+
for (const client of /** @type {WebSocketServerImplementation} */ (
|
|
3292
|
+
this.webSocketServer
|
|
3293
|
+
).clients) {
|
|
3294
|
+
client.terminate();
|
|
3295
|
+
}
|
|
2351
3296
|
|
|
2352
|
-
|
|
2353
|
-
|
|
3297
|
+
/** @type {WebSocketServerImplementation} */
|
|
3298
|
+
(this.webSocketServer).clients = [];
|
|
3299
|
+
})
|
|
3300
|
+
);
|
|
2354
3301
|
}
|
|
2355
3302
|
|
|
2356
3303
|
if (this.server) {
|
|
2357
|
-
await
|
|
2358
|
-
|
|
2359
|
-
|
|
3304
|
+
await /** @type {Promise<void>} */ (
|
|
3305
|
+
new Promise((resolve) => {
|
|
3306
|
+
/** @type {import("http").Server} */
|
|
3307
|
+
(this.server).close(() => {
|
|
3308
|
+
this.server = null;
|
|
2360
3309
|
|
|
2361
|
-
|
|
2362
|
-
|
|
3310
|
+
resolve();
|
|
3311
|
+
});
|
|
2363
3312
|
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
3313
|
+
for (const socket of this.sockets) {
|
|
3314
|
+
socket.destroy();
|
|
3315
|
+
}
|
|
2367
3316
|
|
|
2368
|
-
|
|
2369
|
-
|
|
3317
|
+
this.sockets = [];
|
|
3318
|
+
})
|
|
3319
|
+
);
|
|
2370
3320
|
|
|
2371
3321
|
if (this.middleware) {
|
|
2372
|
-
await
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
3322
|
+
await /** @type {Promise<void>} */ (
|
|
3323
|
+
new Promise((resolve, reject) => {
|
|
3324
|
+
/** @type {import("webpack-dev-middleware").API<Request, Response>}*/
|
|
3325
|
+
(this.middleware).close((error) => {
|
|
3326
|
+
if (error) {
|
|
3327
|
+
reject(error);
|
|
3328
|
+
|
|
3329
|
+
return;
|
|
3330
|
+
}
|
|
3331
|
+
|
|
3332
|
+
resolve();
|
|
3333
|
+
});
|
|
3334
|
+
})
|
|
3335
|
+
);
|
|
2383
3336
|
|
|
2384
3337
|
this.middleware = null;
|
|
2385
3338
|
}
|
|
@@ -2392,22 +3345,29 @@ class Server {
|
|
|
2392
3345
|
}
|
|
2393
3346
|
}
|
|
2394
3347
|
|
|
3348
|
+
/**
|
|
3349
|
+
* @param {(err?: Error) => void} [callback]
|
|
3350
|
+
*/
|
|
2395
3351
|
stopCallback(callback = () => {}) {
|
|
2396
3352
|
this.stop()
|
|
2397
|
-
.then(() => callback(
|
|
3353
|
+
.then(() => callback(), callback)
|
|
2398
3354
|
.catch(callback);
|
|
2399
3355
|
}
|
|
2400
3356
|
|
|
2401
3357
|
// TODO remove in the next major release
|
|
3358
|
+
/**
|
|
3359
|
+
* @param {Port} port
|
|
3360
|
+
* @param {Host} hostname
|
|
3361
|
+
* @param {(err?: Error) => void} fn
|
|
3362
|
+
* @returns {void}
|
|
3363
|
+
*/
|
|
2402
3364
|
listen(port, hostname, fn) {
|
|
2403
3365
|
util.deprecate(
|
|
2404
3366
|
() => {},
|
|
2405
|
-
"'listen' is deprecated. Please use async 'start' or 'startCallback'
|
|
3367
|
+
"'listen' is deprecated. Please use the async 'start' or 'startCallback' method.",
|
|
2406
3368
|
"DEP_WEBPACK_DEV_SERVER_LISTEN"
|
|
2407
3369
|
)();
|
|
2408
3370
|
|
|
2409
|
-
this.logger = this.compiler.getInfrastructureLogger("webpack-dev-server");
|
|
2410
|
-
|
|
2411
3371
|
if (typeof port === "function") {
|
|
2412
3372
|
fn = port;
|
|
2413
3373
|
}
|
|
@@ -2444,7 +3404,7 @@ class Server {
|
|
|
2444
3404
|
this.options.host = hostname;
|
|
2445
3405
|
}
|
|
2446
3406
|
|
|
2447
|
-
|
|
3407
|
+
this.start()
|
|
2448
3408
|
.then(() => {
|
|
2449
3409
|
if (fn) {
|
|
2450
3410
|
fn.call(this.server);
|
|
@@ -2458,18 +3418,22 @@ class Server {
|
|
|
2458
3418
|
});
|
|
2459
3419
|
}
|
|
2460
3420
|
|
|
3421
|
+
/**
|
|
3422
|
+
* @param {(err?: Error) => void} [callback]
|
|
3423
|
+
* @returns {void}
|
|
3424
|
+
*/
|
|
2461
3425
|
// TODO remove in the next major release
|
|
2462
3426
|
close(callback) {
|
|
2463
3427
|
util.deprecate(
|
|
2464
3428
|
() => {},
|
|
2465
|
-
"'close' is deprecated. Please use async 'stop' or 'stopCallback'
|
|
3429
|
+
"'close' is deprecated. Please use the async 'stop' or 'stopCallback' method.",
|
|
2466
3430
|
"DEP_WEBPACK_DEV_SERVER_CLOSE"
|
|
2467
3431
|
)();
|
|
2468
3432
|
|
|
2469
|
-
|
|
3433
|
+
this.stop()
|
|
2470
3434
|
.then(() => {
|
|
2471
3435
|
if (callback) {
|
|
2472
|
-
callback(
|
|
3436
|
+
callback();
|
|
2473
3437
|
}
|
|
2474
3438
|
})
|
|
2475
3439
|
.catch((error) => {
|
|
@@ -2480,48 +3444,4 @@ class Server {
|
|
|
2480
3444
|
}
|
|
2481
3445
|
}
|
|
2482
3446
|
|
|
2483
|
-
|
|
2484
|
-
const descriptors = Object.getOwnPropertyDescriptors(exports);
|
|
2485
|
-
|
|
2486
|
-
for (const name of Object.keys(descriptors)) {
|
|
2487
|
-
const descriptor = descriptors[name];
|
|
2488
|
-
|
|
2489
|
-
if (descriptor.get) {
|
|
2490
|
-
const fn = descriptor.get;
|
|
2491
|
-
|
|
2492
|
-
Object.defineProperty(obj, name, {
|
|
2493
|
-
configurable: false,
|
|
2494
|
-
enumerable: true,
|
|
2495
|
-
get: fn,
|
|
2496
|
-
});
|
|
2497
|
-
} else if (typeof descriptor.value === "object") {
|
|
2498
|
-
Object.defineProperty(obj, name, {
|
|
2499
|
-
configurable: false,
|
|
2500
|
-
enumerable: true,
|
|
2501
|
-
writable: false,
|
|
2502
|
-
value: mergeExports({}, descriptor.value),
|
|
2503
|
-
});
|
|
2504
|
-
} else {
|
|
2505
|
-
throw new Error(
|
|
2506
|
-
"Exposed values must be either a getter or an nested object"
|
|
2507
|
-
);
|
|
2508
|
-
}
|
|
2509
|
-
}
|
|
2510
|
-
|
|
2511
|
-
return Object.freeze(obj);
|
|
2512
|
-
};
|
|
2513
|
-
|
|
2514
|
-
module.exports = mergeExports(Server, {
|
|
2515
|
-
get schema() {
|
|
2516
|
-
return schema;
|
|
2517
|
-
},
|
|
2518
|
-
// TODO compatibility with webpack v4, remove it after drop
|
|
2519
|
-
cli: {
|
|
2520
|
-
get getArguments() {
|
|
2521
|
-
return () => require("../bin/cli-flags");
|
|
2522
|
-
},
|
|
2523
|
-
get processArguments() {
|
|
2524
|
-
return require("../bin/process-arguments");
|
|
2525
|
-
},
|
|
2526
|
-
},
|
|
2527
|
-
});
|
|
3447
|
+
module.exports = Server;
|