webpack-dev-server 4.6.0 → 4.7.3
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 +26 -3
- package/client/clients/WebSocketClient.js +16 -1
- package/client/index.js +71 -2
- package/client/modules/logger/index.js +3 -0
- package/client/modules/sockjs-client/index.js +15 -4
- package/client/overlay.js +38 -3
- package/client/socket.js +17 -6
- package/client/utils/createSocketURL.js +71 -4
- package/client/utils/getCurrentScriptSource.js +3 -0
- package/client/utils/log.js +6 -1
- package/client/utils/parseURL.js +17 -19
- package/client/utils/reloadApp.js +15 -2
- package/client/utils/sendMessage.js +5 -0
- package/lib/Server.js +1673 -759
- 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 +24 -14
- 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;
|
|
267
|
+
}
|
|
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;
|
|
41
283
|
}
|
|
42
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") {
|
|
@@ -1038,13 +1478,7 @@ class Server {
|
|
|
1038
1478
|
} else if (typeof options.static === "string") {
|
|
1039
1479
|
options.static = [getStaticItem(options.static)];
|
|
1040
1480
|
} else if (Array.isArray(options.static)) {
|
|
1041
|
-
options.static = options.static.map((item) =>
|
|
1042
|
-
if (typeof item === "string") {
|
|
1043
|
-
return getStaticItem(item);
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
return getStaticItem(item);
|
|
1047
|
-
});
|
|
1481
|
+
options.static = options.static.map((item) => getStaticItem(item));
|
|
1048
1482
|
} else {
|
|
1049
1483
|
options.static = [getStaticItem(options.static)];
|
|
1050
1484
|
}
|
|
@@ -1102,38 +1536,61 @@ class Server {
|
|
|
1102
1536
|
};
|
|
1103
1537
|
} else {
|
|
1104
1538
|
options.webSocketServer = {
|
|
1105
|
-
type:
|
|
1539
|
+
type:
|
|
1540
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1541
|
+
(options.webSocketServer).type || defaultWebSocketServerType,
|
|
1106
1542
|
options: {
|
|
1107
1543
|
...defaultWebSocketServerOptions,
|
|
1108
|
-
|
|
1544
|
+
.../** @type {WebSocketServerConfiguration} */
|
|
1545
|
+
(options.webSocketServer).options,
|
|
1109
1546
|
},
|
|
1110
1547
|
};
|
|
1111
1548
|
|
|
1112
|
-
|
|
1113
|
-
options
|
|
1114
|
-
|
|
1115
|
-
|
|
1549
|
+
const webSocketServer =
|
|
1550
|
+
/** @type {{ type: WebSocketServerConfiguration["type"], options: NonNullable<WebSocketServerConfiguration["options"]> }} */
|
|
1551
|
+
(options.webSocketServer);
|
|
1552
|
+
|
|
1553
|
+
if (typeof webSocketServer.options.port === "string") {
|
|
1554
|
+
webSocketServer.options.port = Number(webSocketServer.options.port);
|
|
1116
1555
|
}
|
|
1117
1556
|
}
|
|
1118
1557
|
}
|
|
1119
1558
|
|
|
1559
|
+
/**
|
|
1560
|
+
* @private
|
|
1561
|
+
* @returns {string}
|
|
1562
|
+
*/
|
|
1120
1563
|
getClientTransport() {
|
|
1121
|
-
let
|
|
1564
|
+
let clientImplementation;
|
|
1122
1565
|
let clientImplementationFound = true;
|
|
1123
1566
|
|
|
1124
1567
|
const isKnownWebSocketServerImplementation =
|
|
1125
1568
|
this.options.webSocketServer &&
|
|
1126
|
-
typeof
|
|
1569
|
+
typeof (
|
|
1570
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1571
|
+
(this.options.webSocketServer).type
|
|
1572
|
+
) === "string" &&
|
|
1573
|
+
// @ts-ignore
|
|
1127
1574
|
(this.options.webSocketServer.type === "ws" ||
|
|
1128
|
-
|
|
1575
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1576
|
+
(this.options.webSocketServer).type === "sockjs");
|
|
1129
1577
|
|
|
1130
1578
|
let clientTransport;
|
|
1131
1579
|
|
|
1132
1580
|
if (this.options.client) {
|
|
1133
|
-
if (
|
|
1134
|
-
|
|
1581
|
+
if (
|
|
1582
|
+
typeof (
|
|
1583
|
+
/** @type {ClientConfiguration} */
|
|
1584
|
+
(this.options.client).webSocketTransport
|
|
1585
|
+
) !== "undefined"
|
|
1586
|
+
) {
|
|
1587
|
+
clientTransport =
|
|
1588
|
+
/** @type {ClientConfiguration} */
|
|
1589
|
+
(this.options.client).webSocketTransport;
|
|
1135
1590
|
} else if (isKnownWebSocketServerImplementation) {
|
|
1136
|
-
clientTransport =
|
|
1591
|
+
clientTransport =
|
|
1592
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1593
|
+
(this.options.webSocketServer).type;
|
|
1137
1594
|
} else {
|
|
1138
1595
|
clientTransport = "ws";
|
|
1139
1596
|
}
|
|
@@ -1145,17 +1602,16 @@ class Server {
|
|
|
1145
1602
|
case "string":
|
|
1146
1603
|
// could be 'sockjs', 'ws', or a path that should be required
|
|
1147
1604
|
if (clientTransport === "sockjs") {
|
|
1148
|
-
|
|
1605
|
+
clientImplementation = require.resolve(
|
|
1149
1606
|
"../client/clients/SockJSClient"
|
|
1150
1607
|
);
|
|
1151
1608
|
} else if (clientTransport === "ws") {
|
|
1152
|
-
|
|
1609
|
+
clientImplementation = require.resolve(
|
|
1153
1610
|
"../client/clients/WebSocketClient"
|
|
1154
1611
|
);
|
|
1155
1612
|
} else {
|
|
1156
1613
|
try {
|
|
1157
|
-
|
|
1158
|
-
ClientImplementation = require.resolve(clientTransport);
|
|
1614
|
+
clientImplementation = require.resolve(clientTransport);
|
|
1159
1615
|
} catch (e) {
|
|
1160
1616
|
clientImplementationFound = false;
|
|
1161
1617
|
}
|
|
@@ -1175,31 +1631,52 @@ class Server {
|
|
|
1175
1631
|
);
|
|
1176
1632
|
}
|
|
1177
1633
|
|
|
1178
|
-
return
|
|
1634
|
+
return /** @type {string} */ (clientImplementation);
|
|
1179
1635
|
}
|
|
1180
1636
|
|
|
1637
|
+
/**
|
|
1638
|
+
* @private
|
|
1639
|
+
* @returns {string}
|
|
1640
|
+
*/
|
|
1181
1641
|
getServerTransport() {
|
|
1182
1642
|
let implementation;
|
|
1183
1643
|
let implementationFound = true;
|
|
1184
1644
|
|
|
1185
|
-
switch (
|
|
1645
|
+
switch (
|
|
1646
|
+
typeof (
|
|
1647
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1648
|
+
(this.options.webSocketServer).type
|
|
1649
|
+
)
|
|
1650
|
+
) {
|
|
1186
1651
|
case "string":
|
|
1187
1652
|
// Could be 'sockjs', in the future 'ws', or a path that should be required
|
|
1188
|
-
if (
|
|
1653
|
+
if (
|
|
1654
|
+
/** @type {WebSocketServerConfiguration} */ (
|
|
1655
|
+
this.options.webSocketServer
|
|
1656
|
+
).type === "sockjs"
|
|
1657
|
+
) {
|
|
1189
1658
|
implementation = require("./servers/SockJSServer");
|
|
1190
|
-
} else if (
|
|
1659
|
+
} else if (
|
|
1660
|
+
/** @type {WebSocketServerConfiguration} */ (
|
|
1661
|
+
this.options.webSocketServer
|
|
1662
|
+
).type === "ws"
|
|
1663
|
+
) {
|
|
1191
1664
|
implementation = require("./servers/WebsocketServer");
|
|
1192
1665
|
} else {
|
|
1193
1666
|
try {
|
|
1194
1667
|
// eslint-disable-next-line import/no-dynamic-require
|
|
1195
|
-
implementation = require(
|
|
1668
|
+
implementation = require(/** @type {WebSocketServerConfiguration} */ (
|
|
1669
|
+
this.options.webSocketServer
|
|
1670
|
+
).type);
|
|
1196
1671
|
} catch (error) {
|
|
1197
1672
|
implementationFound = false;
|
|
1198
1673
|
}
|
|
1199
1674
|
}
|
|
1200
1675
|
break;
|
|
1201
1676
|
case "function":
|
|
1202
|
-
implementation =
|
|
1677
|
+
implementation = /** @type {WebSocketServerConfiguration} */ (
|
|
1678
|
+
this.options.webSocketServer
|
|
1679
|
+
).type;
|
|
1203
1680
|
break;
|
|
1204
1681
|
default:
|
|
1205
1682
|
implementationFound = false;
|
|
@@ -1216,37 +1693,61 @@ class Server {
|
|
|
1216
1693
|
return implementation;
|
|
1217
1694
|
}
|
|
1218
1695
|
|
|
1696
|
+
/**
|
|
1697
|
+
* @private
|
|
1698
|
+
* @returns {void}
|
|
1699
|
+
*/
|
|
1219
1700
|
setupProgressPlugin() {
|
|
1220
|
-
const { ProgressPlugin } =
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1701
|
+
const { ProgressPlugin } =
|
|
1702
|
+
/** @type {MultiCompiler}*/
|
|
1703
|
+
(this.compiler).compilers
|
|
1704
|
+
? /** @type {MultiCompiler}*/ (this.compiler).compilers[0].webpack
|
|
1705
|
+
: /** @type {Compiler}*/ (this.compiler).webpack ||
|
|
1706
|
+
// TODO remove me after drop webpack v4
|
|
1707
|
+
require("webpack");
|
|
1708
|
+
|
|
1709
|
+
new ProgressPlugin(
|
|
1710
|
+
/**
|
|
1711
|
+
* @param {number} percent
|
|
1712
|
+
* @param {string} msg
|
|
1713
|
+
* @param {string} addInfo
|
|
1714
|
+
* @param {string} pluginName
|
|
1715
|
+
*/
|
|
1716
|
+
(percent, msg, addInfo, pluginName) => {
|
|
1717
|
+
percent = Math.floor(percent * 100);
|
|
1224
1718
|
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1719
|
+
if (percent === 100) {
|
|
1720
|
+
msg = "Compilation completed";
|
|
1721
|
+
}
|
|
1228
1722
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1723
|
+
if (addInfo) {
|
|
1724
|
+
msg = `${msg} (${addInfo})`;
|
|
1725
|
+
}
|
|
1232
1726
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1727
|
+
if (this.webSocketServer) {
|
|
1728
|
+
this.sendMessage(this.webSocketServer.clients, "progress-update", {
|
|
1729
|
+
percent,
|
|
1730
|
+
msg,
|
|
1731
|
+
pluginName,
|
|
1732
|
+
});
|
|
1733
|
+
}
|
|
1240
1734
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1735
|
+
if (this.server) {
|
|
1736
|
+
this.server.emit("progress-update", { percent, msg, pluginName });
|
|
1737
|
+
}
|
|
1243
1738
|
}
|
|
1244
|
-
|
|
1739
|
+
).apply(this.compiler);
|
|
1245
1740
|
}
|
|
1246
1741
|
|
|
1742
|
+
/**
|
|
1743
|
+
* @private
|
|
1744
|
+
* @returns {Promise<void>}
|
|
1745
|
+
*/
|
|
1247
1746
|
async initialize() {
|
|
1248
1747
|
if (this.options.webSocketServer) {
|
|
1249
|
-
const compilers =
|
|
1748
|
+
const compilers =
|
|
1749
|
+
/** @type {MultiCompiler} */
|
|
1750
|
+
(this.compiler).compilers || [this.compiler];
|
|
1250
1751
|
|
|
1251
1752
|
compilers.forEach((compiler) => {
|
|
1252
1753
|
this.addAdditionalEntries(compiler);
|
|
@@ -1278,7 +1779,10 @@ class Server {
|
|
|
1278
1779
|
}
|
|
1279
1780
|
});
|
|
1280
1781
|
|
|
1281
|
-
if (
|
|
1782
|
+
if (
|
|
1783
|
+
this.options.client &&
|
|
1784
|
+
/** @type {ClientConfiguration} */ (this.options.client).progress
|
|
1785
|
+
) {
|
|
1282
1786
|
this.setupProgressPlugin();
|
|
1283
1787
|
}
|
|
1284
1788
|
}
|
|
@@ -1290,7 +1794,8 @@ class Server {
|
|
|
1290
1794
|
// Should be after `webpack-dev-middleware`, otherwise other middlewares might rewrite response
|
|
1291
1795
|
this.setupBuiltInRoutes();
|
|
1292
1796
|
this.setupWatchFiles();
|
|
1293
|
-
this.
|
|
1797
|
+
this.setupWatchStaticFiles();
|
|
1798
|
+
this.setupMiddlewares();
|
|
1294
1799
|
this.createServer();
|
|
1295
1800
|
|
|
1296
1801
|
if (this.options.setupExitSignals) {
|
|
@@ -1301,7 +1806,6 @@ class Server {
|
|
|
1301
1806
|
signals.forEach((signal) => {
|
|
1302
1807
|
const listener = () => {
|
|
1303
1808
|
if (needForceShutdown) {
|
|
1304
|
-
// eslint-disable-next-line no-process-exit
|
|
1305
1809
|
process.exit();
|
|
1306
1810
|
}
|
|
1307
1811
|
|
|
@@ -1314,11 +1818,9 @@ class Server {
|
|
|
1314
1818
|
this.stopCallback(() => {
|
|
1315
1819
|
if (typeof this.compiler.close === "function") {
|
|
1316
1820
|
this.compiler.close(() => {
|
|
1317
|
-
// eslint-disable-next-line no-process-exit
|
|
1318
1821
|
process.exit();
|
|
1319
1822
|
});
|
|
1320
1823
|
} else {
|
|
1321
|
-
// eslint-disable-next-line no-process-exit
|
|
1322
1824
|
process.exit();
|
|
1323
1825
|
}
|
|
1324
1826
|
});
|
|
@@ -1332,53 +1834,108 @@ class Server {
|
|
|
1332
1834
|
|
|
1333
1835
|
// Proxy WebSocket without the initial http request
|
|
1334
1836
|
// https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade
|
|
1335
|
-
|
|
1336
|
-
|
|
1837
|
+
/** @type {RequestHandler[]} */
|
|
1838
|
+
(this.webSocketProxies).forEach((webSocketProxy) => {
|
|
1839
|
+
/** @type {import("http").Server} */
|
|
1840
|
+
(this.server).on(
|
|
1841
|
+
"upgrade",
|
|
1842
|
+
/** @type {RequestHandler & { upgrade: NonNullable<RequestHandler["upgrade"]> }} */
|
|
1843
|
+
(webSocketProxy).upgrade
|
|
1844
|
+
);
|
|
1337
1845
|
}, this);
|
|
1338
1846
|
}
|
|
1339
1847
|
|
|
1848
|
+
/**
|
|
1849
|
+
* @private
|
|
1850
|
+
* @returns {void}
|
|
1851
|
+
*/
|
|
1340
1852
|
setupApp() {
|
|
1341
|
-
|
|
1853
|
+
/** @type {import("express").Application | undefined}*/
|
|
1342
1854
|
// eslint-disable-next-line new-cap
|
|
1343
|
-
this.app = new express();
|
|
1855
|
+
this.app = new /** @type {any} */ (express)();
|
|
1344
1856
|
}
|
|
1345
1857
|
|
|
1858
|
+
/**
|
|
1859
|
+
* @private
|
|
1860
|
+
* @param {Stats | MultiStats} statsObj
|
|
1861
|
+
* @returns {StatsCompilation}
|
|
1862
|
+
*/
|
|
1346
1863
|
getStats(statsObj) {
|
|
1347
1864
|
const stats = Server.DEFAULT_STATS;
|
|
1348
1865
|
const compilerOptions = this.getCompilerOptions();
|
|
1349
1866
|
|
|
1867
|
+
// @ts-ignore
|
|
1350
1868
|
if (compilerOptions.stats && compilerOptions.stats.warningsFilter) {
|
|
1869
|
+
// @ts-ignore
|
|
1351
1870
|
stats.warningsFilter = compilerOptions.stats.warningsFilter;
|
|
1352
1871
|
}
|
|
1353
1872
|
|
|
1354
1873
|
return statsObj.toJson(stats);
|
|
1355
1874
|
}
|
|
1356
1875
|
|
|
1876
|
+
/**
|
|
1877
|
+
* @private
|
|
1878
|
+
* @returns {void}
|
|
1879
|
+
*/
|
|
1357
1880
|
setupHooks() {
|
|
1358
1881
|
this.compiler.hooks.invalid.tap("webpack-dev-server", () => {
|
|
1359
1882
|
if (this.webSocketServer) {
|
|
1360
1883
|
this.sendMessage(this.webSocketServer.clients, "invalid");
|
|
1361
1884
|
}
|
|
1362
1885
|
});
|
|
1363
|
-
this.compiler.hooks.done.tap(
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1886
|
+
this.compiler.hooks.done.tap(
|
|
1887
|
+
"webpack-dev-server",
|
|
1888
|
+
/**
|
|
1889
|
+
* @param {Stats | MultiStats} stats
|
|
1890
|
+
*/
|
|
1891
|
+
(stats) => {
|
|
1892
|
+
if (this.webSocketServer) {
|
|
1893
|
+
this.sendStats(this.webSocketServer.clients, this.getStats(stats));
|
|
1894
|
+
}
|
|
1367
1895
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1896
|
+
/**
|
|
1897
|
+
* @private
|
|
1898
|
+
* @type {Stats | MultiStats}
|
|
1899
|
+
*/
|
|
1900
|
+
this.stats = stats;
|
|
1901
|
+
}
|
|
1902
|
+
);
|
|
1370
1903
|
}
|
|
1371
1904
|
|
|
1905
|
+
/**
|
|
1906
|
+
* @private
|
|
1907
|
+
* @returns {void}
|
|
1908
|
+
*/
|
|
1372
1909
|
setupHostHeaderCheck() {
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1910
|
+
/** @type {import("express").Application} */
|
|
1911
|
+
(this.app).all(
|
|
1912
|
+
"*",
|
|
1913
|
+
/**
|
|
1914
|
+
* @param {Request} req
|
|
1915
|
+
* @param {Response} res
|
|
1916
|
+
* @param {NextFunction} next
|
|
1917
|
+
* @returns {void}
|
|
1918
|
+
*/
|
|
1919
|
+
(req, res, next) => {
|
|
1920
|
+
if (
|
|
1921
|
+
this.checkHeader(
|
|
1922
|
+
/** @type {{ [key: string]: string | undefined }} */
|
|
1923
|
+
(req.headers),
|
|
1924
|
+
"host"
|
|
1925
|
+
)
|
|
1926
|
+
) {
|
|
1927
|
+
return next();
|
|
1928
|
+
}
|
|
1377
1929
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1930
|
+
res.send("Invalid Host header");
|
|
1931
|
+
}
|
|
1932
|
+
);
|
|
1380
1933
|
}
|
|
1381
1934
|
|
|
1935
|
+
/**
|
|
1936
|
+
* @private
|
|
1937
|
+
* @returns {void}
|
|
1938
|
+
*/
|
|
1382
1939
|
setupDevMiddleware() {
|
|
1383
1940
|
const webpackDevMiddleware = require("webpack-dev-middleware");
|
|
1384
1941
|
|
|
@@ -1389,435 +1946,616 @@ class Server {
|
|
|
1389
1946
|
);
|
|
1390
1947
|
}
|
|
1391
1948
|
|
|
1949
|
+
/**
|
|
1950
|
+
* @private
|
|
1951
|
+
* @returns {void}
|
|
1952
|
+
*/
|
|
1392
1953
|
setupBuiltInRoutes() {
|
|
1393
1954
|
const { app, middleware } = this;
|
|
1394
1955
|
|
|
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
|
-
});
|
|
1956
|
+
/** @type {import("express").Application} */
|
|
1957
|
+
(app).get(
|
|
1958
|
+
"/__webpack_dev_server__/sockjs.bundle.js",
|
|
1959
|
+
/**
|
|
1960
|
+
* @param {Request} req
|
|
1961
|
+
* @param {Response} res
|
|
1962
|
+
* @returns {void}
|
|
1963
|
+
*/
|
|
1964
|
+
(req, res) => {
|
|
1965
|
+
res.setHeader("Content-Type", "application/javascript");
|
|
1411
1966
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
res.setHeader("Content-Type", "text/html");
|
|
1415
|
-
res.write(
|
|
1416
|
-
'<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body>'
|
|
1417
|
-
);
|
|
1967
|
+
const { createReadStream } = fs;
|
|
1968
|
+
const clientPath = path.join(__dirname, "..", "client");
|
|
1418
1969
|
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1970
|
+
createReadStream(
|
|
1971
|
+
path.join(clientPath, "modules/sockjs-client/index.js")
|
|
1972
|
+
).pipe(res);
|
|
1973
|
+
}
|
|
1974
|
+
);
|
|
1423
1975
|
|
|
1424
|
-
|
|
1976
|
+
/** @type {import("express").Application} */
|
|
1977
|
+
(app).get(
|
|
1978
|
+
"/webpack-dev-server/invalidate",
|
|
1979
|
+
/**
|
|
1980
|
+
* @param {Request} _req
|
|
1981
|
+
* @param {Response} res
|
|
1982
|
+
* @returns {void}
|
|
1983
|
+
*/
|
|
1984
|
+
(_req, res) => {
|
|
1985
|
+
this.invalidate();
|
|
1425
1986
|
|
|
1426
|
-
|
|
1427
|
-
|
|
1987
|
+
res.end();
|
|
1988
|
+
}
|
|
1989
|
+
);
|
|
1428
1990
|
|
|
1429
|
-
|
|
1430
|
-
|
|
1991
|
+
/** @type {import("express").Application} */
|
|
1992
|
+
(app).get(
|
|
1993
|
+
"/webpack-dev-server",
|
|
1994
|
+
/**
|
|
1995
|
+
* @param {Request} req
|
|
1996
|
+
* @param {Response} res
|
|
1997
|
+
* @returns {void}
|
|
1998
|
+
*/
|
|
1999
|
+
(req, res) => {
|
|
2000
|
+
/** @type {import("webpack-dev-middleware").API<Request, Response>}*/
|
|
2001
|
+
(middleware).waitUntilValid((stats) => {
|
|
2002
|
+
res.setHeader("Content-Type", "text/html");
|
|
2003
|
+
res.write(
|
|
2004
|
+
'<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body>'
|
|
2005
|
+
);
|
|
1431
2006
|
|
|
1432
|
-
|
|
1433
|
-
|
|
2007
|
+
const statsForPrint =
|
|
2008
|
+
typeof (/** @type {MultiStats} */ (stats).stats) !== "undefined"
|
|
2009
|
+
? /** @type {MultiStats} */ (stats).toJson().children
|
|
2010
|
+
: [/** @type {Stats} */ (stats).toJson()];
|
|
1434
2011
|
|
|
1435
|
-
|
|
2012
|
+
res.write(`<h1>Assets Report:</h1>`);
|
|
1436
2013
|
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
2014
|
+
/**
|
|
2015
|
+
* @type {StatsCompilation[]}
|
|
2016
|
+
*/
|
|
2017
|
+
(statsForPrint).forEach((item, index) => {
|
|
2018
|
+
res.write("<div>");
|
|
1440
2019
|
|
|
1441
|
-
|
|
1442
|
-
|
|
2020
|
+
const name =
|
|
2021
|
+
// eslint-disable-next-line no-nested-ternary
|
|
2022
|
+
typeof item.name !== "undefined"
|
|
2023
|
+
? item.name
|
|
2024
|
+
: /** @type {MultiStats} */ (stats).stats
|
|
2025
|
+
? `unnamed[${index}]`
|
|
2026
|
+
: "unnamed";
|
|
2027
|
+
|
|
2028
|
+
res.write(`<h2>Compilation: ${name}</h2>`);
|
|
2029
|
+
res.write("<ul>");
|
|
2030
|
+
|
|
2031
|
+
const publicPath =
|
|
2032
|
+
item.publicPath === "auto" ? "" : item.publicPath;
|
|
2033
|
+
|
|
2034
|
+
for (const asset of /** @type {NonNullable<StatsCompilation["assets"]>} */ (
|
|
2035
|
+
item.assets
|
|
2036
|
+
)) {
|
|
2037
|
+
const assetName = asset.name;
|
|
2038
|
+
const assetURL = `${publicPath}${assetName}`;
|
|
2039
|
+
|
|
2040
|
+
res.write(
|
|
2041
|
+
`<li>
|
|
1443
2042
|
<strong><a href="${assetURL}" target="_blank">${assetName}</a></strong>
|
|
1444
2043
|
</li>`
|
|
1445
|
-
|
|
1446
|
-
|
|
2044
|
+
);
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
res.write("</ul>");
|
|
2048
|
+
res.write("</div>");
|
|
2049
|
+
});
|
|
1447
2050
|
|
|
1448
|
-
res.
|
|
1449
|
-
res.write("</div>");
|
|
2051
|
+
res.end("</body></html>");
|
|
1450
2052
|
});
|
|
2053
|
+
}
|
|
2054
|
+
);
|
|
2055
|
+
}
|
|
1451
2056
|
|
|
1452
|
-
|
|
2057
|
+
/**
|
|
2058
|
+
* @private
|
|
2059
|
+
* @returns {void}
|
|
2060
|
+
*/
|
|
2061
|
+
setupWatchStaticFiles() {
|
|
2062
|
+
if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) {
|
|
2063
|
+
/** @type {NormalizedStatic[]} */
|
|
2064
|
+
(this.options.static).forEach((staticOption) => {
|
|
2065
|
+
if (staticOption.watch) {
|
|
2066
|
+
this.watchFiles(staticOption.directory, staticOption.watch);
|
|
2067
|
+
}
|
|
1453
2068
|
});
|
|
1454
|
-
}
|
|
2069
|
+
}
|
|
1455
2070
|
}
|
|
1456
2071
|
|
|
1457
|
-
|
|
1458
|
-
|
|
2072
|
+
/**
|
|
2073
|
+
* @private
|
|
2074
|
+
* @returns {void}
|
|
2075
|
+
*/
|
|
2076
|
+
setupWatchFiles() {
|
|
2077
|
+
const { watchFiles } = this.options;
|
|
1459
2078
|
|
|
1460
|
-
|
|
2079
|
+
if (/** @type {WatchFiles[]} */ (watchFiles).length > 0) {
|
|
2080
|
+
/** @type {WatchFiles[]} */
|
|
2081
|
+
(watchFiles).forEach((item) => {
|
|
2082
|
+
this.watchFiles(item.paths, item.options);
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
1461
2085
|
}
|
|
1462
2086
|
|
|
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
|
-
};
|
|
2087
|
+
/**
|
|
2088
|
+
* @private
|
|
2089
|
+
* @returns {void}
|
|
2090
|
+
*/
|
|
2091
|
+
setupMiddlewares() {
|
|
1479
2092
|
/**
|
|
1480
|
-
*
|
|
1481
|
-
* proxy: [
|
|
1482
|
-
* {
|
|
1483
|
-
* context: "value",
|
|
1484
|
-
* ...options,
|
|
1485
|
-
* },
|
|
1486
|
-
* // or:
|
|
1487
|
-
* function() {
|
|
1488
|
-
* return {
|
|
1489
|
-
* context: "context",
|
|
1490
|
-
* ...options,
|
|
1491
|
-
* };
|
|
1492
|
-
* }
|
|
1493
|
-
* ]
|
|
2093
|
+
* @type {Array<Middleware>}
|
|
1494
2094
|
*/
|
|
1495
|
-
|
|
1496
|
-
let proxyMiddleware;
|
|
1497
|
-
|
|
1498
|
-
let proxyConfig =
|
|
1499
|
-
typeof proxyConfigOrCallback === "function"
|
|
1500
|
-
? proxyConfigOrCallback()
|
|
1501
|
-
: proxyConfigOrCallback;
|
|
2095
|
+
let middlewares = [];
|
|
1502
2096
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
this.webSocketProxies.push(proxyMiddleware);
|
|
1507
|
-
}
|
|
2097
|
+
// compress is placed last and uses unshift so that it will be the first middleware used
|
|
2098
|
+
if (this.options.compress) {
|
|
2099
|
+
const compression = require("compression");
|
|
1508
2100
|
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
const newProxyConfig = proxyConfigOrCallback(req, res, next);
|
|
2101
|
+
middlewares.push({ name: "compression", middleware: compression() });
|
|
2102
|
+
}
|
|
1512
2103
|
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
2104
|
+
if (typeof this.options.onBeforeSetupMiddleware === "function") {
|
|
2105
|
+
this.options.onBeforeSetupMiddleware(this);
|
|
2106
|
+
}
|
|
1518
2107
|
|
|
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
|
-
};
|
|
2108
|
+
if (typeof this.options.headers !== "undefined") {
|
|
2109
|
+
middlewares.push({
|
|
2110
|
+
name: "set-headers",
|
|
2111
|
+
path: "*",
|
|
2112
|
+
middleware: this.setHeaders.bind(this),
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
1542
2115
|
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
2116
|
+
middlewares.push({
|
|
2117
|
+
name: "webpack-dev-middleware",
|
|
2118
|
+
middleware:
|
|
2119
|
+
/** @type {import("webpack-dev-middleware").Middleware<Request, Response>}*/
|
|
2120
|
+
(this.middleware),
|
|
1546
2121
|
});
|
|
1547
|
-
}
|
|
1548
2122
|
|
|
1549
|
-
|
|
1550
|
-
|
|
2123
|
+
if (this.options.proxy) {
|
|
2124
|
+
const { createProxyMiddleware } = require("http-proxy-middleware");
|
|
1551
2125
|
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
2126
|
+
/**
|
|
2127
|
+
* @param {ProxyConfigArray} proxyConfig
|
|
2128
|
+
* @returns {RequestHandler | undefined}
|
|
2129
|
+
*/
|
|
2130
|
+
const getProxyMiddleware = (proxyConfig) => {
|
|
2131
|
+
// It is possible to use the `bypass` method without a `target` or `router`.
|
|
2132
|
+
// However, the proxy middleware has no use in this case, and will fail to instantiate.
|
|
2133
|
+
if (proxyConfig.target) {
|
|
2134
|
+
const context = proxyConfig.context || proxyConfig.path;
|
|
2135
|
+
|
|
2136
|
+
return createProxyMiddleware(
|
|
2137
|
+
/** @type {string} */ (context),
|
|
2138
|
+
proxyConfig
|
|
2139
|
+
);
|
|
2140
|
+
}
|
|
1561
2141
|
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
2142
|
+
if (proxyConfig.router) {
|
|
2143
|
+
return createProxyMiddleware(proxyConfig);
|
|
2144
|
+
}
|
|
2145
|
+
};
|
|
1565
2146
|
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
2147
|
+
/**
|
|
2148
|
+
* Assume a proxy configuration specified as:
|
|
2149
|
+
* proxy: [
|
|
2150
|
+
* {
|
|
2151
|
+
* context: "value",
|
|
2152
|
+
* ...options,
|
|
2153
|
+
* },
|
|
2154
|
+
* // or:
|
|
2155
|
+
* function() {
|
|
2156
|
+
* return {
|
|
2157
|
+
* context: "context",
|
|
2158
|
+
* ...options,
|
|
2159
|
+
* };
|
|
2160
|
+
* }
|
|
2161
|
+
* ]
|
|
2162
|
+
*/
|
|
2163
|
+
/** @type {ProxyArray} */
|
|
2164
|
+
(this.options.proxy).forEach(
|
|
2165
|
+
/**
|
|
2166
|
+
* @param {any} proxyConfigOrCallback
|
|
2167
|
+
*/
|
|
2168
|
+
(proxyConfigOrCallback) => {
|
|
2169
|
+
/**
|
|
2170
|
+
* @type {RequestHandler}
|
|
2171
|
+
*/
|
|
2172
|
+
let proxyMiddleware;
|
|
2173
|
+
|
|
2174
|
+
let proxyConfig =
|
|
2175
|
+
typeof proxyConfigOrCallback === "function"
|
|
2176
|
+
? proxyConfigOrCallback()
|
|
2177
|
+
: proxyConfigOrCallback;
|
|
2178
|
+
|
|
2179
|
+
proxyMiddleware =
|
|
2180
|
+
/** @type {RequestHandler} */
|
|
2181
|
+
(getProxyMiddleware(proxyConfig));
|
|
2182
|
+
|
|
2183
|
+
if (proxyConfig.ws) {
|
|
2184
|
+
this.webSocketProxies.push(proxyMiddleware);
|
|
2185
|
+
}
|
|
1576
2186
|
|
|
1577
|
-
|
|
1578
|
-
|
|
2187
|
+
/**
|
|
2188
|
+
* @param {Request} req
|
|
2189
|
+
* @param {Response} res
|
|
2190
|
+
* @param {NextFunction} next
|
|
2191
|
+
* @returns {Promise<void>}
|
|
2192
|
+
*/
|
|
2193
|
+
const handler = async (req, res, next) => {
|
|
2194
|
+
if (typeof proxyConfigOrCallback === "function") {
|
|
2195
|
+
const newProxyConfig = proxyConfigOrCallback(req, res, next);
|
|
2196
|
+
|
|
2197
|
+
if (newProxyConfig !== proxyConfig) {
|
|
2198
|
+
proxyConfig = newProxyConfig;
|
|
2199
|
+
proxyMiddleware =
|
|
2200
|
+
/** @type {RequestHandler} */
|
|
2201
|
+
(getProxyMiddleware(proxyConfig));
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
1579
2204
|
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
2205
|
+
// - Check if we have a bypass function defined
|
|
2206
|
+
// - In case the bypass function is defined we'll retrieve the
|
|
2207
|
+
// bypassUrl from it otherwise bypassUrl would be null
|
|
2208
|
+
// TODO remove in the next major in favor `context` and `router` options
|
|
2209
|
+
const isByPassFuncDefined =
|
|
2210
|
+
typeof proxyConfig.bypass === "function";
|
|
2211
|
+
const bypassUrl = isByPassFuncDefined
|
|
2212
|
+
? await proxyConfig.bypass(req, res, proxyConfig)
|
|
2213
|
+
: null;
|
|
2214
|
+
|
|
2215
|
+
if (typeof bypassUrl === "boolean") {
|
|
2216
|
+
// skip the proxy
|
|
2217
|
+
// @ts-ignore
|
|
2218
|
+
req.url = null;
|
|
2219
|
+
next();
|
|
2220
|
+
} else if (typeof bypassUrl === "string") {
|
|
2221
|
+
// byPass to that url
|
|
2222
|
+
req.url = bypassUrl;
|
|
2223
|
+
next();
|
|
2224
|
+
} else if (proxyMiddleware) {
|
|
2225
|
+
return proxyMiddleware(req, res, next);
|
|
2226
|
+
} else {
|
|
2227
|
+
next();
|
|
1587
2228
|
}
|
|
2229
|
+
};
|
|
1588
2230
|
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
2231
|
+
middlewares.push({
|
|
2232
|
+
name: "http-proxy-middleware",
|
|
2233
|
+
middleware: handler,
|
|
2234
|
+
});
|
|
2235
|
+
// Also forward error requests to the proxy so it can handle them.
|
|
2236
|
+
middlewares.push({
|
|
2237
|
+
name: "http-proxy-middleware-error-handler",
|
|
2238
|
+
middleware:
|
|
2239
|
+
/**
|
|
2240
|
+
* @param {Error} error
|
|
2241
|
+
* @param {Request} req
|
|
2242
|
+
* @param {Response} res
|
|
2243
|
+
* @param {NextFunction} next
|
|
2244
|
+
* @returns {any}
|
|
2245
|
+
*/
|
|
2246
|
+
(error, req, res, next) => handler(req, res, next),
|
|
1594
2247
|
});
|
|
1595
2248
|
}
|
|
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;
|
|
2249
|
+
);
|
|
1614
2250
|
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
2251
|
+
middlewares.push({
|
|
2252
|
+
name: "webpack-dev-middleware",
|
|
2253
|
+
middleware:
|
|
2254
|
+
/** @type {import("webpack-dev-middleware").Middleware<Request, Response>}*/
|
|
2255
|
+
(this.middleware),
|
|
1618
2256
|
});
|
|
1619
2257
|
}
|
|
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
2258
|
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
2259
|
+
if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) {
|
|
2260
|
+
/** @type {NormalizedStatic[]} */
|
|
2261
|
+
(this.options.static).forEach((staticOption) => {
|
|
2262
|
+
staticOption.publicPath.forEach((publicPath) => {
|
|
2263
|
+
middlewares.push({
|
|
2264
|
+
name: "express-static",
|
|
2265
|
+
path: publicPath,
|
|
2266
|
+
middleware: express.static(
|
|
2267
|
+
staticOption.directory,
|
|
2268
|
+
staticOption.staticOptions
|
|
2269
|
+
),
|
|
2270
|
+
});
|
|
2271
|
+
});
|
|
2272
|
+
});
|
|
1696
2273
|
}
|
|
1697
2274
|
|
|
1698
|
-
|
|
2275
|
+
if (this.options.historyApiFallback) {
|
|
2276
|
+
const connectHistoryApiFallback = require("connect-history-api-fallback");
|
|
2277
|
+
const { historyApiFallback } = this.options;
|
|
1699
2278
|
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
2279
|
+
if (
|
|
2280
|
+
typeof (
|
|
2281
|
+
/** @type {ConnectHistoryApiFallbackOptions} */
|
|
2282
|
+
(historyApiFallback).logger
|
|
2283
|
+
) === "undefined" &&
|
|
2284
|
+
!(
|
|
2285
|
+
/** @type {ConnectHistoryApiFallbackOptions} */
|
|
2286
|
+
(historyApiFallback).verbose
|
|
2287
|
+
)
|
|
2288
|
+
) {
|
|
2289
|
+
// @ts-ignore
|
|
2290
|
+
historyApiFallback.logger = this.logger.log.bind(
|
|
2291
|
+
this.logger,
|
|
2292
|
+
"[connect-history-api-fallback]"
|
|
2293
|
+
);
|
|
2294
|
+
}
|
|
1703
2295
|
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
2296
|
+
// Fall back to /index.html if nothing else matches.
|
|
2297
|
+
middlewares.push({
|
|
2298
|
+
name: "connect-history-api-fallback",
|
|
2299
|
+
middleware: connectHistoryApiFallback(
|
|
2300
|
+
/** @type {ConnectHistoryApiFallbackOptions} */
|
|
2301
|
+
(historyApiFallback)
|
|
2302
|
+
),
|
|
2303
|
+
});
|
|
1707
2304
|
|
|
1708
|
-
|
|
1709
|
-
|
|
2305
|
+
// include our middleware to ensure
|
|
2306
|
+
// it is able to handle '/index.html' request after redirect
|
|
2307
|
+
middlewares.push({
|
|
2308
|
+
name: "webpack-dev-middleware",
|
|
2309
|
+
middleware:
|
|
2310
|
+
/** @type {import("webpack-dev-middleware").Middleware<Request, Response>}*/
|
|
2311
|
+
(this.middleware),
|
|
2312
|
+
});
|
|
1710
2313
|
|
|
1711
|
-
if (this.options.static) {
|
|
1712
|
-
|
|
2314
|
+
if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) {
|
|
2315
|
+
/** @type {NormalizedStatic[]} */
|
|
2316
|
+
(this.options.static).forEach((staticOption) => {
|
|
2317
|
+
staticOption.publicPath.forEach((publicPath) => {
|
|
2318
|
+
middlewares.push({
|
|
2319
|
+
name: "express-static",
|
|
2320
|
+
path: publicPath,
|
|
2321
|
+
middleware: express.static(
|
|
2322
|
+
staticOption.directory,
|
|
2323
|
+
staticOption.staticOptions
|
|
2324
|
+
),
|
|
2325
|
+
});
|
|
2326
|
+
});
|
|
2327
|
+
});
|
|
1713
2328
|
}
|
|
1714
2329
|
}
|
|
1715
2330
|
|
|
1716
|
-
if (this.options.static) {
|
|
1717
|
-
|
|
2331
|
+
if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) {
|
|
2332
|
+
const serveIndex = require("serve-index");
|
|
2333
|
+
|
|
2334
|
+
/** @type {NormalizedStatic[]} */
|
|
2335
|
+
(this.options.static).forEach((staticOption) => {
|
|
2336
|
+
staticOption.publicPath.forEach((publicPath) => {
|
|
2337
|
+
if (staticOption.serveIndex) {
|
|
2338
|
+
middlewares.push({
|
|
2339
|
+
name: "serve-index",
|
|
2340
|
+
path: publicPath,
|
|
2341
|
+
/**
|
|
2342
|
+
* @param {Request} req
|
|
2343
|
+
* @param {Response} res
|
|
2344
|
+
* @param {NextFunction} next
|
|
2345
|
+
* @returns {void}
|
|
2346
|
+
*/
|
|
2347
|
+
middleware: (req, res, next) => {
|
|
2348
|
+
// serve-index doesn't fallthrough non-get/head request to next middleware
|
|
2349
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
2350
|
+
return next();
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
serveIndex(
|
|
2354
|
+
staticOption.directory,
|
|
2355
|
+
/** @type {ServeIndexOptions} */
|
|
2356
|
+
(staticOption.serveIndex)
|
|
2357
|
+
)(req, res, next);
|
|
2358
|
+
},
|
|
2359
|
+
});
|
|
2360
|
+
}
|
|
2361
|
+
});
|
|
2362
|
+
});
|
|
1718
2363
|
}
|
|
1719
2364
|
|
|
1720
2365
|
if (this.options.magicHtml) {
|
|
1721
|
-
|
|
2366
|
+
middlewares.push({
|
|
2367
|
+
name: "serve-magic-html",
|
|
2368
|
+
middleware: this.serveMagicHtml.bind(this),
|
|
2369
|
+
});
|
|
1722
2370
|
}
|
|
1723
2371
|
|
|
1724
|
-
if (this.options.
|
|
1725
|
-
|
|
2372
|
+
if (typeof this.options.setupMiddlewares === "function") {
|
|
2373
|
+
middlewares = this.options.setupMiddlewares(middlewares, this);
|
|
1726
2374
|
}
|
|
1727
2375
|
|
|
1728
|
-
|
|
1729
|
-
|
|
2376
|
+
middlewares.forEach((middleware) => {
|
|
2377
|
+
if (typeof middleware === "function") {
|
|
2378
|
+
/** @type {import("express").Application} */
|
|
2379
|
+
(this.app).use(middleware);
|
|
2380
|
+
} else if (typeof middleware.path !== "undefined") {
|
|
2381
|
+
/** @type {import("express").Application} */
|
|
2382
|
+
(this.app).use(middleware.path, middleware.middleware);
|
|
2383
|
+
} else {
|
|
2384
|
+
/** @type {import("express").Application} */
|
|
2385
|
+
(this.app).use(middleware.middleware);
|
|
2386
|
+
}
|
|
1730
2387
|
});
|
|
2388
|
+
|
|
2389
|
+
if (typeof this.options.onAfterSetupMiddleware === "function") {
|
|
2390
|
+
this.options.onAfterSetupMiddleware(this);
|
|
2391
|
+
}
|
|
1731
2392
|
}
|
|
1732
2393
|
|
|
2394
|
+
/**
|
|
2395
|
+
* @private
|
|
2396
|
+
* @returns {void}
|
|
2397
|
+
*/
|
|
1733
2398
|
createServer() {
|
|
2399
|
+
const { type, options } = /** @type {ServerConfiguration} */ (
|
|
2400
|
+
this.options.server
|
|
2401
|
+
);
|
|
2402
|
+
|
|
2403
|
+
/** @type {import("http").Server | undefined | null} */
|
|
1734
2404
|
// eslint-disable-next-line import/no-dynamic-require
|
|
1735
|
-
this.server = require(
|
|
1736
|
-
|
|
2405
|
+
this.server = require(/** @type {string} */ (type)).createServer(
|
|
2406
|
+
options,
|
|
1737
2407
|
this.app
|
|
1738
2408
|
);
|
|
1739
2409
|
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
2410
|
+
/** @type {import("http").Server} */
|
|
2411
|
+
(this.server).on(
|
|
2412
|
+
"connection",
|
|
2413
|
+
/**
|
|
2414
|
+
* @param {Socket} socket
|
|
2415
|
+
*/
|
|
2416
|
+
(socket) => {
|
|
2417
|
+
// Add socket to list
|
|
2418
|
+
this.sockets.push(socket);
|
|
1743
2419
|
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
2420
|
+
socket.once("close", () => {
|
|
2421
|
+
// Remove socket from list
|
|
2422
|
+
this.sockets.splice(this.sockets.indexOf(socket), 1);
|
|
2423
|
+
});
|
|
2424
|
+
}
|
|
2425
|
+
);
|
|
1749
2426
|
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
2427
|
+
/** @type {import("http").Server} */
|
|
2428
|
+
(this.server).on(
|
|
2429
|
+
"error",
|
|
2430
|
+
/**
|
|
2431
|
+
* @param {Error} error
|
|
2432
|
+
*/
|
|
2433
|
+
(error) => {
|
|
2434
|
+
throw error;
|
|
2435
|
+
}
|
|
2436
|
+
);
|
|
1753
2437
|
}
|
|
1754
2438
|
|
|
2439
|
+
/**
|
|
2440
|
+
* @private
|
|
2441
|
+
* @returns {void}
|
|
2442
|
+
*/
|
|
1755
2443
|
// TODO: remove `--web-socket-server` in favor of `--web-socket-server-type`
|
|
1756
2444
|
createWebSocketServer() {
|
|
1757
|
-
|
|
1758
|
-
this.webSocketServer
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
2445
|
+
/** @type {WebSocketServerImplementation | undefined | null} */
|
|
2446
|
+
this.webSocketServer = new /** @type {any} */ (this.getServerTransport())(
|
|
2447
|
+
this
|
|
2448
|
+
);
|
|
2449
|
+
/** @type {WebSocketServerImplementation} */
|
|
2450
|
+
(this.webSocketServer).implementation.on(
|
|
2451
|
+
"connection",
|
|
2452
|
+
/**
|
|
2453
|
+
* @param {ClientConnection} client
|
|
2454
|
+
* @param {IncomingMessage} request
|
|
2455
|
+
*/
|
|
2456
|
+
(client, request) => {
|
|
2457
|
+
/** @type {{ [key: string]: string | undefined } | undefined} */
|
|
2458
|
+
const headers =
|
|
2459
|
+
// eslint-disable-next-line no-nested-ternary
|
|
2460
|
+
typeof request !== "undefined"
|
|
2461
|
+
? /** @type {{ [key: string]: string | undefined }} */
|
|
2462
|
+
(request.headers)
|
|
2463
|
+
: typeof (
|
|
2464
|
+
/** @type {import("sockjs").Connection} */ (client).headers
|
|
2465
|
+
) !== "undefined"
|
|
2466
|
+
? /** @type {import("sockjs").Connection} */ (client).headers
|
|
2467
|
+
: // eslint-disable-next-line no-undefined
|
|
2468
|
+
undefined;
|
|
2469
|
+
|
|
2470
|
+
if (!headers) {
|
|
2471
|
+
this.logger.warn(
|
|
2472
|
+
'webSocketServer implementation must pass headers for the "connection" event'
|
|
2473
|
+
);
|
|
2474
|
+
}
|
|
1773
2475
|
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
2476
|
+
if (
|
|
2477
|
+
!headers ||
|
|
2478
|
+
!this.checkHeader(headers, "host") ||
|
|
2479
|
+
!this.checkHeader(headers, "origin")
|
|
2480
|
+
) {
|
|
2481
|
+
this.sendMessage([client], "error", "Invalid Host/Origin header");
|
|
1780
2482
|
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
2483
|
+
// With https enabled, the sendMessage above is encrypted asynchronously so not yet sent
|
|
2484
|
+
// Terminate would prevent it sending, so use close to allow it to be sent
|
|
2485
|
+
client.close();
|
|
1784
2486
|
|
|
1785
|
-
|
|
1786
|
-
|
|
2487
|
+
return;
|
|
2488
|
+
}
|
|
1787
2489
|
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
2490
|
+
if (this.options.hot === true || this.options.hot === "only") {
|
|
2491
|
+
this.sendMessage([client], "hot");
|
|
2492
|
+
}
|
|
1791
2493
|
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
2494
|
+
if (this.options.liveReload) {
|
|
2495
|
+
this.sendMessage([client], "liveReload");
|
|
2496
|
+
}
|
|
1795
2497
|
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
2498
|
+
if (
|
|
2499
|
+
this.options.client &&
|
|
2500
|
+
/** @type {ClientConfiguration} */
|
|
2501
|
+
(this.options.client).progress
|
|
2502
|
+
) {
|
|
2503
|
+
this.sendMessage(
|
|
2504
|
+
[client],
|
|
2505
|
+
"progress",
|
|
2506
|
+
/** @type {ClientConfiguration} */
|
|
2507
|
+
(this.options.client).progress
|
|
2508
|
+
);
|
|
2509
|
+
}
|
|
1799
2510
|
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
2511
|
+
if (
|
|
2512
|
+
this.options.client &&
|
|
2513
|
+
/** @type {ClientConfiguration} */ (this.options.client).reconnect
|
|
2514
|
+
) {
|
|
2515
|
+
this.sendMessage(
|
|
2516
|
+
[client],
|
|
2517
|
+
"reconnect",
|
|
2518
|
+
/** @type {ClientConfiguration} */
|
|
2519
|
+
(this.options.client).reconnect
|
|
2520
|
+
);
|
|
2521
|
+
}
|
|
1803
2522
|
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
2523
|
+
if (
|
|
2524
|
+
this.options.client &&
|
|
2525
|
+
/** @type {ClientConfiguration} */
|
|
2526
|
+
(this.options.client).overlay
|
|
2527
|
+
) {
|
|
2528
|
+
this.sendMessage(
|
|
2529
|
+
[client],
|
|
2530
|
+
"overlay",
|
|
2531
|
+
/** @type {ClientConfiguration} */
|
|
2532
|
+
(this.options.client).overlay
|
|
2533
|
+
);
|
|
2534
|
+
}
|
|
1807
2535
|
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
2536
|
+
if (!this.stats) {
|
|
2537
|
+
return;
|
|
2538
|
+
}
|
|
1811
2539
|
|
|
1812
|
-
|
|
1813
|
-
|
|
2540
|
+
this.sendStats([client], this.getStats(this.stats), true);
|
|
2541
|
+
}
|
|
2542
|
+
);
|
|
1814
2543
|
}
|
|
1815
2544
|
|
|
2545
|
+
/**
|
|
2546
|
+
* @private
|
|
2547
|
+
* @param {string} defaultOpenTarget
|
|
2548
|
+
* @returns {void}
|
|
2549
|
+
*/
|
|
1816
2550
|
openBrowser(defaultOpenTarget) {
|
|
1817
2551
|
const open = require("open");
|
|
1818
2552
|
|
|
1819
2553
|
Promise.all(
|
|
1820
|
-
|
|
2554
|
+
/** @type {NormalizedOpen[]} */
|
|
2555
|
+
(this.options.open).map((item) => {
|
|
2556
|
+
/**
|
|
2557
|
+
* @type {string}
|
|
2558
|
+
*/
|
|
1821
2559
|
let openTarget;
|
|
1822
2560
|
|
|
1823
2561
|
if (item.target === "<url>") {
|
|
@@ -1831,13 +2569,17 @@ class Server {
|
|
|
1831
2569
|
return open(openTarget, item.options).catch(() => {
|
|
1832
2570
|
this.logger.warn(
|
|
1833
2571
|
`Unable to open "${openTarget}" page${
|
|
1834
|
-
// eslint-disable-next-line no-nested-ternary
|
|
1835
2572
|
item.options.app
|
|
1836
|
-
? ` in "${
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
2573
|
+
? ` in "${
|
|
2574
|
+
/** @type {import("open").App} */
|
|
2575
|
+
(item.options.app).name
|
|
2576
|
+
}" app${
|
|
2577
|
+
/** @type {import("open").App} */
|
|
2578
|
+
(item.options.app).arguments
|
|
2579
|
+
? ` with "${
|
|
2580
|
+
/** @type {import("open").App} */
|
|
2581
|
+
(item.options.app).arguments.join(" ")
|
|
2582
|
+
}" arguments`
|
|
1841
2583
|
: ""
|
|
1842
2584
|
}`
|
|
1843
2585
|
: ""
|
|
@@ -1848,38 +2590,68 @@ class Server {
|
|
|
1848
2590
|
);
|
|
1849
2591
|
}
|
|
1850
2592
|
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
if (callback) {
|
|
1856
|
-
callback();
|
|
1857
|
-
}
|
|
1858
|
-
});
|
|
1859
|
-
}
|
|
1860
|
-
|
|
2593
|
+
/**
|
|
2594
|
+
* @private
|
|
2595
|
+
* @returns {void}
|
|
2596
|
+
*/
|
|
1861
2597
|
runBonjour() {
|
|
2598
|
+
/**
|
|
2599
|
+
* @private
|
|
2600
|
+
* @type {import("bonjour").Bonjour | undefined}
|
|
2601
|
+
*/
|
|
1862
2602
|
this.bonjour = require("bonjour")();
|
|
1863
2603
|
this.bonjour.publish({
|
|
1864
2604
|
name: `Webpack Dev Server ${os.hostname()}:${this.options.port}`,
|
|
1865
|
-
port: this.options.port,
|
|
1866
|
-
type:
|
|
2605
|
+
port: /** @type {number} */ (this.options.port),
|
|
2606
|
+
type:
|
|
2607
|
+
/** @type {ServerConfiguration} */
|
|
2608
|
+
(this.options.server).type === "http" ? "http" : "https",
|
|
1867
2609
|
subtypes: ["webpack"],
|
|
1868
|
-
|
|
2610
|
+
.../** @type {BonjourOptions} */ (this.options.bonjour),
|
|
2611
|
+
});
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
/**
|
|
2615
|
+
* @private
|
|
2616
|
+
* @returns {void}
|
|
2617
|
+
*/
|
|
2618
|
+
stopBonjour(callback = () => {}) {
|
|
2619
|
+
/** @type {Bonjour} */
|
|
2620
|
+
(this.bonjour).unpublishAll(() => {
|
|
2621
|
+
/** @type {Bonjour} */
|
|
2622
|
+
(this.bonjour).destroy();
|
|
2623
|
+
|
|
2624
|
+
if (callback) {
|
|
2625
|
+
callback();
|
|
2626
|
+
}
|
|
1869
2627
|
});
|
|
1870
2628
|
}
|
|
1871
2629
|
|
|
2630
|
+
/**
|
|
2631
|
+
* @private
|
|
2632
|
+
* @returns {void}
|
|
2633
|
+
*/
|
|
1872
2634
|
logStatus() {
|
|
1873
2635
|
const { isColorSupported, cyan, red } = require("colorette");
|
|
1874
2636
|
|
|
2637
|
+
/**
|
|
2638
|
+
* @param {Compiler["options"]} compilerOptions
|
|
2639
|
+
* @returns {boolean}
|
|
2640
|
+
*/
|
|
1875
2641
|
const getColorsOption = (compilerOptions) => {
|
|
2642
|
+
/**
|
|
2643
|
+
* @type {boolean}
|
|
2644
|
+
*/
|
|
1876
2645
|
let colorsEnabled;
|
|
1877
2646
|
|
|
1878
2647
|
if (
|
|
1879
2648
|
compilerOptions.stats &&
|
|
1880
|
-
typeof compilerOptions.stats.colors !==
|
|
2649
|
+
typeof (/** @type {StatsOptions} */ (compilerOptions.stats).colors) !==
|
|
2650
|
+
"undefined"
|
|
1881
2651
|
) {
|
|
1882
|
-
colorsEnabled =
|
|
2652
|
+
colorsEnabled =
|
|
2653
|
+
/** @type {boolean} */
|
|
2654
|
+
(/** @type {StatsOptions} */ (compilerOptions.stats).colors);
|
|
1883
2655
|
} else {
|
|
1884
2656
|
colorsEnabled = isColorSupported;
|
|
1885
2657
|
}
|
|
@@ -1888,6 +2660,11 @@ class Server {
|
|
|
1888
2660
|
};
|
|
1889
2661
|
|
|
1890
2662
|
const colors = {
|
|
2663
|
+
/**
|
|
2664
|
+
* @param {boolean} useColor
|
|
2665
|
+
* @param {string} msg
|
|
2666
|
+
* @returns {string}
|
|
2667
|
+
*/
|
|
1891
2668
|
info(useColor, msg) {
|
|
1892
2669
|
if (useColor) {
|
|
1893
2670
|
return cyan(msg);
|
|
@@ -1895,6 +2672,11 @@ class Server {
|
|
|
1895
2672
|
|
|
1896
2673
|
return msg;
|
|
1897
2674
|
},
|
|
2675
|
+
/**
|
|
2676
|
+
* @param {boolean} useColor
|
|
2677
|
+
* @param {string} msg
|
|
2678
|
+
* @returns {string}
|
|
2679
|
+
*/
|
|
1898
2680
|
error(useColor, msg) {
|
|
1899
2681
|
if (useColor) {
|
|
1900
2682
|
return red(msg);
|
|
@@ -1906,10 +2688,26 @@ class Server {
|
|
|
1906
2688
|
const useColor = getColorsOption(this.getCompilerOptions());
|
|
1907
2689
|
|
|
1908
2690
|
if (this.options.ipc) {
|
|
1909
|
-
this.logger.info(
|
|
2691
|
+
this.logger.info(
|
|
2692
|
+
`Project is running at: "${
|
|
2693
|
+
/** @type {import("http").Server} */
|
|
2694
|
+
(this.server).address()
|
|
2695
|
+
}"`
|
|
2696
|
+
);
|
|
1910
2697
|
} else {
|
|
1911
|
-
const protocol =
|
|
1912
|
-
|
|
2698
|
+
const protocol =
|
|
2699
|
+
/** @type {ServerConfiguration} */
|
|
2700
|
+
(this.options.server).type === "http" ? "http" : "https";
|
|
2701
|
+
const { address, port } =
|
|
2702
|
+
/** @type {import("net").AddressInfo} */
|
|
2703
|
+
(
|
|
2704
|
+
/** @type {import("http").Server} */
|
|
2705
|
+
(this.server).address()
|
|
2706
|
+
);
|
|
2707
|
+
/**
|
|
2708
|
+
* @param {string} newHostname
|
|
2709
|
+
* @returns {string}
|
|
2710
|
+
*/
|
|
1913
2711
|
const prettyPrintURL = (newHostname) =>
|
|
1914
2712
|
url.format({ protocol, hostname: newHostname, port, pathname: "/" });
|
|
1915
2713
|
|
|
@@ -1962,8 +2760,13 @@ class Server {
|
|
|
1962
2760
|
}
|
|
1963
2761
|
} else {
|
|
1964
2762
|
networkUrlIPv4 =
|
|
1965
|
-
parsedIP.kind() === "ipv6" &&
|
|
1966
|
-
|
|
2763
|
+
parsedIP.kind() === "ipv6" &&
|
|
2764
|
+
/** @type {IPv6} */
|
|
2765
|
+
(parsedIP).isIPv4MappedAddress()
|
|
2766
|
+
? prettyPrintURL(
|
|
2767
|
+
/** @type {IPv6} */
|
|
2768
|
+
(parsedIP).toIPv4Address().toString()
|
|
2769
|
+
)
|
|
1967
2770
|
: prettyPrintURL(address);
|
|
1968
2771
|
|
|
1969
2772
|
if (parsedIP.kind() === "ipv6") {
|
|
@@ -1978,10 +2781,19 @@ class Server {
|
|
|
1978
2781
|
}
|
|
1979
2782
|
|
|
1980
2783
|
if (localhost || loopbackIPv4 || loopbackIPv6) {
|
|
1981
|
-
const loopbacks = []
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
.
|
|
2784
|
+
const loopbacks = [];
|
|
2785
|
+
|
|
2786
|
+
if (localhost) {
|
|
2787
|
+
loopbacks.push([colors.info(useColor, localhost)]);
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
if (loopbackIPv4) {
|
|
2791
|
+
loopbacks.push([colors.info(useColor, loopbackIPv4)]);
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2794
|
+
if (loopbackIPv6) {
|
|
2795
|
+
loopbacks.push([colors.info(useColor, loopbackIPv6)]);
|
|
2796
|
+
}
|
|
1985
2797
|
|
|
1986
2798
|
this.logger.info(`Loopback: ${loopbacks.join(", ")}`);
|
|
1987
2799
|
}
|
|
@@ -1998,18 +2810,19 @@ class Server {
|
|
|
1998
2810
|
);
|
|
1999
2811
|
}
|
|
2000
2812
|
|
|
2001
|
-
if (this.options.open.length > 0) {
|
|
2813
|
+
if (/** @type {NormalizedOpen[]} */ (this.options.open).length > 0) {
|
|
2002
2814
|
const openTarget = prettyPrintURL(this.options.host || "localhost");
|
|
2003
2815
|
|
|
2004
2816
|
this.openBrowser(openTarget);
|
|
2005
2817
|
}
|
|
2006
2818
|
}
|
|
2007
2819
|
|
|
2008
|
-
if (
|
|
2820
|
+
if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) {
|
|
2009
2821
|
this.logger.info(
|
|
2010
2822
|
`Content not from webpack is served from '${colors.info(
|
|
2011
2823
|
useColor,
|
|
2012
|
-
|
|
2824
|
+
/** @type {NormalizedStatic[]} */
|
|
2825
|
+
(this.options.static)
|
|
2013
2826
|
.map((staticOption) => staticOption.directory)
|
|
2014
2827
|
.join(", ")
|
|
2015
2828
|
)}' directory`
|
|
@@ -2020,14 +2833,19 @@ class Server {
|
|
|
2020
2833
|
this.logger.info(
|
|
2021
2834
|
`404s will fallback to '${colors.info(
|
|
2022
2835
|
useColor,
|
|
2023
|
-
|
|
2836
|
+
/** @type {ConnectHistoryApiFallbackOptions} */ (
|
|
2837
|
+
this.options.historyApiFallback
|
|
2838
|
+
).index || "/index.html"
|
|
2024
2839
|
)}'`
|
|
2025
2840
|
);
|
|
2026
2841
|
}
|
|
2027
2842
|
|
|
2028
2843
|
if (this.options.bonjour) {
|
|
2029
2844
|
const bonjourProtocol =
|
|
2030
|
-
|
|
2845
|
+
/** @type {BonjourOptions} */
|
|
2846
|
+
(this.options.bonjour).type ||
|
|
2847
|
+
/** @type {ServerConfiguration} */
|
|
2848
|
+
(this.options.server).type === "http"
|
|
2031
2849
|
? "http"
|
|
2032
2850
|
: "https";
|
|
2033
2851
|
|
|
@@ -2037,32 +2855,59 @@ class Server {
|
|
|
2037
2855
|
}
|
|
2038
2856
|
}
|
|
2039
2857
|
|
|
2858
|
+
/**
|
|
2859
|
+
* @private
|
|
2860
|
+
* @param {Request} req
|
|
2861
|
+
* @param {Response} res
|
|
2862
|
+
* @param {NextFunction} next
|
|
2863
|
+
*/
|
|
2040
2864
|
setHeaders(req, res, next) {
|
|
2041
2865
|
let { headers } = this.options;
|
|
2042
2866
|
|
|
2043
2867
|
if (headers) {
|
|
2044
2868
|
if (typeof headers === "function") {
|
|
2045
|
-
headers = headers(
|
|
2869
|
+
headers = headers(
|
|
2870
|
+
req,
|
|
2871
|
+
res,
|
|
2872
|
+
/** @type {import("webpack-dev-middleware").API<Request, Response>}*/
|
|
2873
|
+
(this.middleware).context
|
|
2874
|
+
);
|
|
2046
2875
|
}
|
|
2047
2876
|
|
|
2877
|
+
/**
|
|
2878
|
+
* @type {{key: string, value: string}[]}
|
|
2879
|
+
*/
|
|
2048
2880
|
const allHeaders = [];
|
|
2049
2881
|
|
|
2050
2882
|
if (!Array.isArray(headers)) {
|
|
2051
2883
|
// eslint-disable-next-line guard-for-in
|
|
2052
2884
|
for (const name in headers) {
|
|
2885
|
+
// @ts-ignore
|
|
2053
2886
|
allHeaders.push({ key: name, value: headers[name] });
|
|
2054
2887
|
}
|
|
2888
|
+
|
|
2055
2889
|
headers = allHeaders;
|
|
2056
2890
|
}
|
|
2057
2891
|
|
|
2058
|
-
headers.forEach(
|
|
2059
|
-
|
|
2060
|
-
|
|
2892
|
+
headers.forEach(
|
|
2893
|
+
/**
|
|
2894
|
+
* @param {{key: string, value: any}} header
|
|
2895
|
+
*/
|
|
2896
|
+
(header) => {
|
|
2897
|
+
res.setHeader(header.key, header.value);
|
|
2898
|
+
}
|
|
2899
|
+
);
|
|
2061
2900
|
}
|
|
2062
2901
|
|
|
2063
2902
|
next();
|
|
2064
2903
|
}
|
|
2065
2904
|
|
|
2905
|
+
/**
|
|
2906
|
+
* @private
|
|
2907
|
+
* @param {{ [key: string]: string | undefined }} headers
|
|
2908
|
+
* @param {string} headerToCheck
|
|
2909
|
+
* @returns {boolean}
|
|
2910
|
+
*/
|
|
2066
2911
|
checkHeader(headers, headerToCheck) {
|
|
2067
2912
|
// allow user to opt out of this security check, at their own risk
|
|
2068
2913
|
// by explicitly enabling allowedHosts
|
|
@@ -2099,8 +2944,8 @@ class Server {
|
|
|
2099
2944
|
// always allow localhost host, for convenience (hostname === 'localhost')
|
|
2100
2945
|
// allow hostname of listening address (hostname === this.options.host)
|
|
2101
2946
|
const isValidHostname =
|
|
2102
|
-
ipaddr.IPv4.isValid(hostname) ||
|
|
2103
|
-
ipaddr.IPv6.isValid(hostname) ||
|
|
2947
|
+
(hostname !== null && ipaddr.IPv4.isValid(hostname)) ||
|
|
2948
|
+
(hostname !== null && ipaddr.IPv6.isValid(hostname)) ||
|
|
2104
2949
|
hostname === "localhost" ||
|
|
2105
2950
|
hostname === this.options.host;
|
|
2106
2951
|
|
|
@@ -2127,7 +2972,7 @@ class Server {
|
|
|
2127
2972
|
// "*.example.com" (hostname.endsWith(allowedHost))
|
|
2128
2973
|
if (
|
|
2129
2974
|
hostname === allowedHost.substring(1) ||
|
|
2130
|
-
hostname.endsWith(allowedHost)
|
|
2975
|
+
/** @type {string} */ (hostname).endsWith(allowedHost)
|
|
2131
2976
|
) {
|
|
2132
2977
|
return true;
|
|
2133
2978
|
}
|
|
@@ -2138,15 +2983,27 @@ class Server {
|
|
|
2138
2983
|
// Also allow if `client.webSocketURL.hostname` provided
|
|
2139
2984
|
if (
|
|
2140
2985
|
this.options.client &&
|
|
2141
|
-
typeof
|
|
2986
|
+
typeof (
|
|
2987
|
+
/** @type {ClientConfiguration} */ (this.options.client).webSocketURL
|
|
2988
|
+
) !== "undefined"
|
|
2142
2989
|
) {
|
|
2143
|
-
return
|
|
2990
|
+
return (
|
|
2991
|
+
/** @type {WebSocketURL} */
|
|
2992
|
+
(/** @type {ClientConfiguration} */ (this.options.client).webSocketURL)
|
|
2993
|
+
.hostname === hostname
|
|
2994
|
+
);
|
|
2144
2995
|
}
|
|
2145
2996
|
|
|
2146
2997
|
// disallow
|
|
2147
2998
|
return false;
|
|
2148
2999
|
}
|
|
2149
3000
|
|
|
3001
|
+
/**
|
|
3002
|
+
* @param {ClientConnection[]} clients
|
|
3003
|
+
* @param {string} type
|
|
3004
|
+
* @param {any} [data]
|
|
3005
|
+
* @param {any} [params]
|
|
3006
|
+
*/
|
|
2150
3007
|
// eslint-disable-next-line class-methods-use-this
|
|
2151
3008
|
sendMessage(clients, type, data, params) {
|
|
2152
3009
|
for (const client of clients) {
|
|
@@ -2158,21 +3015,41 @@ class Server {
|
|
|
2158
3015
|
}
|
|
2159
3016
|
}
|
|
2160
3017
|
|
|
3018
|
+
/**
|
|
3019
|
+
* @private
|
|
3020
|
+
* @param {Request} req
|
|
3021
|
+
* @param {Response} res
|
|
3022
|
+
* @param {NextFunction} next
|
|
3023
|
+
* @returns {void}
|
|
3024
|
+
*/
|
|
2161
3025
|
serveMagicHtml(req, res, next) {
|
|
2162
|
-
|
|
3026
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
3027
|
+
return next();
|
|
3028
|
+
}
|
|
3029
|
+
|
|
3030
|
+
/** @type {import("webpack-dev-middleware").API<Request, Response>}*/
|
|
3031
|
+
(this.middleware).waitUntilValid(() => {
|
|
2163
3032
|
const _path = req.path;
|
|
2164
3033
|
|
|
2165
3034
|
try {
|
|
2166
|
-
const filename =
|
|
2167
|
-
|
|
2168
|
-
.
|
|
2169
|
-
|
|
3035
|
+
const filename =
|
|
3036
|
+
/** @type {import("webpack-dev-middleware").API<Request, Response>}*/
|
|
3037
|
+
(this.middleware).getFilenameFromUrl(`${_path}.js`);
|
|
3038
|
+
const isFile =
|
|
3039
|
+
/** @type {Compiler["outputFileSystem"] & { statSync: import("fs").StatSyncFn }}*/
|
|
3040
|
+
(
|
|
3041
|
+
/** @type {import("webpack-dev-middleware").API<Request, Response>}*/
|
|
3042
|
+
(this.middleware).context.outputFileSystem
|
|
3043
|
+
)
|
|
3044
|
+
.statSync(/** @type {import("fs").PathLike} */ (filename))
|
|
3045
|
+
.isFile();
|
|
2170
3046
|
|
|
2171
3047
|
if (!isFile) {
|
|
2172
3048
|
return next();
|
|
2173
3049
|
}
|
|
2174
3050
|
|
|
2175
3051
|
// Serve a page that executes the javascript
|
|
3052
|
+
// @ts-ignore
|
|
2176
3053
|
const queries = req._parsedUrl.search || "";
|
|
2177
3054
|
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
3055
|
|
|
@@ -2184,6 +3061,12 @@ class Server {
|
|
|
2184
3061
|
}
|
|
2185
3062
|
|
|
2186
3063
|
// Send stats to a socket or multiple sockets
|
|
3064
|
+
/**
|
|
3065
|
+
* @private
|
|
3066
|
+
* @param {ClientConnection[]} clients
|
|
3067
|
+
* @param {StatsCompilation} stats
|
|
3068
|
+
* @param {boolean} [force]
|
|
3069
|
+
*/
|
|
2187
3070
|
sendStats(clients, stats, force) {
|
|
2188
3071
|
const shouldEmit =
|
|
2189
3072
|
!force &&
|
|
@@ -2201,10 +3084,20 @@ class Server {
|
|
|
2201
3084
|
this.currentHash = stats.hash;
|
|
2202
3085
|
this.sendMessage(clients, "hash", stats.hash);
|
|
2203
3086
|
|
|
2204
|
-
if (
|
|
2205
|
-
|
|
3087
|
+
if (
|
|
3088
|
+
/** @type {NonNullable<StatsCompilation["errors"]>} */
|
|
3089
|
+
(stats.errors).length > 0 ||
|
|
3090
|
+
/** @type {NonNullable<StatsCompilation["warnings"]>} */
|
|
3091
|
+
(stats.warnings).length > 0
|
|
3092
|
+
) {
|
|
3093
|
+
const hasErrors =
|
|
3094
|
+
/** @type {NonNullable<StatsCompilation["errors"]>} */
|
|
3095
|
+
(stats.errors).length > 0;
|
|
2206
3096
|
|
|
2207
|
-
if (
|
|
3097
|
+
if (
|
|
3098
|
+
/** @type {NonNullable<StatsCompilation["warnings"]>} */
|
|
3099
|
+
(stats.warnings).length > 0
|
|
3100
|
+
) {
|
|
2208
3101
|
let params;
|
|
2209
3102
|
|
|
2210
3103
|
if (hasErrors) {
|
|
@@ -2214,7 +3107,10 @@ class Server {
|
|
|
2214
3107
|
this.sendMessage(clients, "warnings", stats.warnings, params);
|
|
2215
3108
|
}
|
|
2216
3109
|
|
|
2217
|
-
if (
|
|
3110
|
+
if (
|
|
3111
|
+
/** @type {NonNullable<StatsCompilation["errors"]>} */ (stats.errors)
|
|
3112
|
+
.length > 0
|
|
3113
|
+
) {
|
|
2218
3114
|
this.sendMessage(clients, "errors", stats.errors);
|
|
2219
3115
|
}
|
|
2220
3116
|
} else {
|
|
@@ -2222,6 +3118,10 @@ class Server {
|
|
|
2222
3118
|
}
|
|
2223
3119
|
}
|
|
2224
3120
|
|
|
3121
|
+
/**
|
|
3122
|
+
* @param {string | string[]} watchPath
|
|
3123
|
+
* @param {WatchOptions} [watchOptions]
|
|
3124
|
+
*/
|
|
2225
3125
|
watchFiles(watchPath, watchOptions) {
|
|
2226
3126
|
const chokidar = require("chokidar");
|
|
2227
3127
|
const watcher = chokidar.watch(watchPath, watchOptions);
|
|
@@ -2242,44 +3142,65 @@ class Server {
|
|
|
2242
3142
|
this.staticWatchers.push(watcher);
|
|
2243
3143
|
}
|
|
2244
3144
|
|
|
2245
|
-
|
|
3145
|
+
/**
|
|
3146
|
+
* @param {import("webpack-dev-middleware").Callback} [callback]
|
|
3147
|
+
*/
|
|
3148
|
+
invalidate(callback = () => {}) {
|
|
2246
3149
|
if (this.middleware) {
|
|
2247
3150
|
this.middleware.invalidate(callback);
|
|
2248
3151
|
}
|
|
2249
3152
|
}
|
|
2250
3153
|
|
|
3154
|
+
/**
|
|
3155
|
+
* @returns {Promise<void>}
|
|
3156
|
+
*/
|
|
2251
3157
|
async start() {
|
|
2252
3158
|
await this.normalizeOptions();
|
|
2253
3159
|
|
|
2254
3160
|
if (this.options.ipc) {
|
|
2255
|
-
await
|
|
2256
|
-
|
|
2257
|
-
|
|
3161
|
+
await /** @type {Promise<void>} */ (
|
|
3162
|
+
new Promise((resolve, reject) => {
|
|
3163
|
+
const net = require("net");
|
|
3164
|
+
const socket = new net.Socket();
|
|
3165
|
+
|
|
3166
|
+
socket.on(
|
|
3167
|
+
"error",
|
|
3168
|
+
/**
|
|
3169
|
+
* @param {Error & { code?: string }} error
|
|
3170
|
+
*/
|
|
3171
|
+
(error) => {
|
|
3172
|
+
if (error.code === "ECONNREFUSED") {
|
|
3173
|
+
// No other server listening on this socket so it can be safely removed
|
|
3174
|
+
fs.unlinkSync(/** @type {string} */ (this.options.ipc));
|
|
3175
|
+
|
|
3176
|
+
resolve();
|
|
3177
|
+
|
|
3178
|
+
return;
|
|
3179
|
+
} else if (error.code === "ENOENT") {
|
|
3180
|
+
resolve();
|
|
3181
|
+
|
|
3182
|
+
return;
|
|
3183
|
+
}
|
|
2258
3184
|
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
fs.unlinkSync(this.options.ipc);
|
|
2263
|
-
|
|
2264
|
-
resolve();
|
|
2265
|
-
|
|
2266
|
-
return;
|
|
2267
|
-
} else if (error.code === "ENOENT") {
|
|
2268
|
-
resolve();
|
|
2269
|
-
|
|
2270
|
-
return;
|
|
2271
|
-
}
|
|
2272
|
-
|
|
2273
|
-
reject(error);
|
|
2274
|
-
});
|
|
3185
|
+
reject(error);
|
|
3186
|
+
}
|
|
3187
|
+
);
|
|
2275
3188
|
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
3189
|
+
socket.connect(
|
|
3190
|
+
{ path: /** @type {string} */ (this.options.ipc) },
|
|
3191
|
+
() => {
|
|
3192
|
+
throw new Error(`IPC "${this.options.ipc}" is already used`);
|
|
3193
|
+
}
|
|
3194
|
+
);
|
|
3195
|
+
})
|
|
3196
|
+
);
|
|
2280
3197
|
} else {
|
|
2281
|
-
this.options.host = await Server.getHostname(
|
|
2282
|
-
|
|
3198
|
+
this.options.host = await Server.getHostname(
|
|
3199
|
+
/** @type {Host} */ (this.options.host)
|
|
3200
|
+
);
|
|
3201
|
+
this.options.port = await Server.getFreePort(
|
|
3202
|
+
/** @type {Port} */ (this.options.port)
|
|
3203
|
+
);
|
|
2283
3204
|
}
|
|
2284
3205
|
|
|
2285
3206
|
await this.initialize();
|
|
@@ -2288,17 +3209,23 @@ class Server {
|
|
|
2288
3209
|
? { path: this.options.ipc }
|
|
2289
3210
|
: { host: this.options.host, port: this.options.port };
|
|
2290
3211
|
|
|
2291
|
-
await
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
3212
|
+
await /** @type {Promise<void>} */ (
|
|
3213
|
+
new Promise((resolve) => {
|
|
3214
|
+
/** @type {import("http").Server} */
|
|
3215
|
+
(this.server).listen(listenOptions, () => {
|
|
3216
|
+
resolve();
|
|
3217
|
+
});
|
|
3218
|
+
})
|
|
3219
|
+
);
|
|
2296
3220
|
|
|
2297
3221
|
if (this.options.ipc) {
|
|
2298
3222
|
// chmod 666 (rw rw rw)
|
|
2299
3223
|
const READ_WRITE = 438;
|
|
2300
3224
|
|
|
2301
|
-
await fs.promises.chmod(
|
|
3225
|
+
await fs.promises.chmod(
|
|
3226
|
+
/** @type {string} */ (this.options.ipc),
|
|
3227
|
+
READ_WRITE
|
|
3228
|
+
);
|
|
2302
3229
|
}
|
|
2303
3230
|
|
|
2304
3231
|
if (this.options.webSocketServer) {
|
|
@@ -2316,19 +3243,27 @@ class Server {
|
|
|
2316
3243
|
}
|
|
2317
3244
|
}
|
|
2318
3245
|
|
|
3246
|
+
/**
|
|
3247
|
+
* @param {(err?: Error) => void} [callback]
|
|
3248
|
+
*/
|
|
2319
3249
|
startCallback(callback = () => {}) {
|
|
2320
3250
|
this.start()
|
|
2321
|
-
.then(() => callback(
|
|
3251
|
+
.then(() => callback(), callback)
|
|
2322
3252
|
.catch(callback);
|
|
2323
3253
|
}
|
|
2324
3254
|
|
|
3255
|
+
/**
|
|
3256
|
+
* @returns {Promise<void>}
|
|
3257
|
+
*/
|
|
2325
3258
|
async stop() {
|
|
2326
3259
|
if (this.bonjour) {
|
|
2327
|
-
await
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
3260
|
+
await /** @type {Promise<void>} */ (
|
|
3261
|
+
new Promise((resolve) => {
|
|
3262
|
+
this.stopBonjour(() => {
|
|
3263
|
+
resolve();
|
|
3264
|
+
});
|
|
3265
|
+
})
|
|
3266
|
+
);
|
|
2332
3267
|
}
|
|
2333
3268
|
|
|
2334
3269
|
this.webSocketProxies = [];
|
|
@@ -2338,48 +3273,60 @@ class Server {
|
|
|
2338
3273
|
this.staticWatchers = [];
|
|
2339
3274
|
|
|
2340
3275
|
if (this.webSocketServer) {
|
|
2341
|
-
await
|
|
2342
|
-
|
|
2343
|
-
|
|
3276
|
+
await /** @type {Promise<void>} */ (
|
|
3277
|
+
new Promise((resolve) => {
|
|
3278
|
+
/** @type {WebSocketServerImplementation} */
|
|
3279
|
+
(this.webSocketServer).implementation.close(() => {
|
|
3280
|
+
this.webSocketServer = null;
|
|
2344
3281
|
|
|
2345
|
-
|
|
2346
|
-
|
|
3282
|
+
resolve();
|
|
3283
|
+
});
|
|
2347
3284
|
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
3285
|
+
for (const client of /** @type {WebSocketServerImplementation} */ (
|
|
3286
|
+
this.webSocketServer
|
|
3287
|
+
).clients) {
|
|
3288
|
+
client.terminate();
|
|
3289
|
+
}
|
|
2351
3290
|
|
|
2352
|
-
|
|
2353
|
-
|
|
3291
|
+
/** @type {WebSocketServerImplementation} */
|
|
3292
|
+
(this.webSocketServer).clients = [];
|
|
3293
|
+
})
|
|
3294
|
+
);
|
|
2354
3295
|
}
|
|
2355
3296
|
|
|
2356
3297
|
if (this.server) {
|
|
2357
|
-
await
|
|
2358
|
-
|
|
2359
|
-
|
|
3298
|
+
await /** @type {Promise<void>} */ (
|
|
3299
|
+
new Promise((resolve) => {
|
|
3300
|
+
/** @type {import("http").Server} */
|
|
3301
|
+
(this.server).close(() => {
|
|
3302
|
+
this.server = null;
|
|
2360
3303
|
|
|
2361
|
-
|
|
2362
|
-
|
|
3304
|
+
resolve();
|
|
3305
|
+
});
|
|
2363
3306
|
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
3307
|
+
for (const socket of this.sockets) {
|
|
3308
|
+
socket.destroy();
|
|
3309
|
+
}
|
|
2367
3310
|
|
|
2368
|
-
|
|
2369
|
-
|
|
3311
|
+
this.sockets = [];
|
|
3312
|
+
})
|
|
3313
|
+
);
|
|
2370
3314
|
|
|
2371
3315
|
if (this.middleware) {
|
|
2372
|
-
await
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
3316
|
+
await /** @type {Promise<void>} */ (
|
|
3317
|
+
new Promise((resolve, reject) => {
|
|
3318
|
+
/** @type {import("webpack-dev-middleware").API<Request, Response>}*/
|
|
3319
|
+
(this.middleware).close((error) => {
|
|
3320
|
+
if (error) {
|
|
3321
|
+
reject(error);
|
|
3322
|
+
|
|
3323
|
+
return;
|
|
3324
|
+
}
|
|
3325
|
+
|
|
3326
|
+
resolve();
|
|
3327
|
+
});
|
|
3328
|
+
})
|
|
3329
|
+
);
|
|
2383
3330
|
|
|
2384
3331
|
this.middleware = null;
|
|
2385
3332
|
}
|
|
@@ -2392,22 +3339,29 @@ class Server {
|
|
|
2392
3339
|
}
|
|
2393
3340
|
}
|
|
2394
3341
|
|
|
3342
|
+
/**
|
|
3343
|
+
* @param {(err?: Error) => void} [callback]
|
|
3344
|
+
*/
|
|
2395
3345
|
stopCallback(callback = () => {}) {
|
|
2396
3346
|
this.stop()
|
|
2397
|
-
.then(() => callback(
|
|
3347
|
+
.then(() => callback(), callback)
|
|
2398
3348
|
.catch(callback);
|
|
2399
3349
|
}
|
|
2400
3350
|
|
|
2401
3351
|
// TODO remove in the next major release
|
|
3352
|
+
/**
|
|
3353
|
+
* @param {Port} port
|
|
3354
|
+
* @param {Host} hostname
|
|
3355
|
+
* @param {(err?: Error) => void} fn
|
|
3356
|
+
* @returns {void}
|
|
3357
|
+
*/
|
|
2402
3358
|
listen(port, hostname, fn) {
|
|
2403
3359
|
util.deprecate(
|
|
2404
3360
|
() => {},
|
|
2405
|
-
"'listen' is deprecated. Please use async 'start' or 'startCallback'
|
|
3361
|
+
"'listen' is deprecated. Please use the async 'start' or 'startCallback' method.",
|
|
2406
3362
|
"DEP_WEBPACK_DEV_SERVER_LISTEN"
|
|
2407
3363
|
)();
|
|
2408
3364
|
|
|
2409
|
-
this.logger = this.compiler.getInfrastructureLogger("webpack-dev-server");
|
|
2410
|
-
|
|
2411
3365
|
if (typeof port === "function") {
|
|
2412
3366
|
fn = port;
|
|
2413
3367
|
}
|
|
@@ -2444,7 +3398,7 @@ class Server {
|
|
|
2444
3398
|
this.options.host = hostname;
|
|
2445
3399
|
}
|
|
2446
3400
|
|
|
2447
|
-
|
|
3401
|
+
this.start()
|
|
2448
3402
|
.then(() => {
|
|
2449
3403
|
if (fn) {
|
|
2450
3404
|
fn.call(this.server);
|
|
@@ -2458,18 +3412,22 @@ class Server {
|
|
|
2458
3412
|
});
|
|
2459
3413
|
}
|
|
2460
3414
|
|
|
3415
|
+
/**
|
|
3416
|
+
* @param {(err?: Error) => void} [callback]
|
|
3417
|
+
* @returns {void}
|
|
3418
|
+
*/
|
|
2461
3419
|
// TODO remove in the next major release
|
|
2462
3420
|
close(callback) {
|
|
2463
3421
|
util.deprecate(
|
|
2464
3422
|
() => {},
|
|
2465
|
-
"'close' is deprecated. Please use async 'stop' or 'stopCallback'
|
|
3423
|
+
"'close' is deprecated. Please use the async 'stop' or 'stopCallback' method.",
|
|
2466
3424
|
"DEP_WEBPACK_DEV_SERVER_CLOSE"
|
|
2467
3425
|
)();
|
|
2468
3426
|
|
|
2469
|
-
|
|
3427
|
+
this.stop()
|
|
2470
3428
|
.then(() => {
|
|
2471
3429
|
if (callback) {
|
|
2472
|
-
callback(
|
|
3430
|
+
callback();
|
|
2473
3431
|
}
|
|
2474
3432
|
})
|
|
2475
3433
|
.catch((error) => {
|
|
@@ -2480,48 +3438,4 @@ class Server {
|
|
|
2480
3438
|
}
|
|
2481
3439
|
}
|
|
2482
3440
|
|
|
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
|
-
});
|
|
3441
|
+
module.exports = Server;
|