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