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