webpack-dev-server 4.4.0 → 4.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -65
- package/bin/cli-flags.js +268 -244
- package/bin/process-arguments.js +87 -7
- package/bin/webpack-dev-server.js +3 -0
- package/client/clients/SockJSClient.js +26 -3
- package/client/clients/WebSocketClient.js +16 -1
- package/client/index.js +76 -3
- package/client/modules/logger/index.js +3 -0
- package/client/modules/sockjs-client/index.js +18 -16
- package/client/overlay.js +38 -3
- package/client/socket.js +19 -8
- package/client/utils/createSocketURL.js +71 -4
- package/client/utils/getCurrentScriptSource.js +3 -0
- package/client/utils/log.js +6 -1
- package/client/utils/parseURL.js +17 -19
- package/client/utils/reloadApp.js +15 -2
- package/client/utils/sendMessage.js +5 -0
- package/lib/Server.js +1887 -824
- package/lib/options.json +42 -22
- package/lib/servers/BaseServer.js +8 -0
- package/lib/servers/SockJSServer.js +42 -9
- package/lib/servers/WebsocketServer.js +66 -35
- package/package.json +27 -17
- 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,47 +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);
|
|
259
598
|
}
|
|
260
599
|
|
|
261
|
-
if (typeof
|
|
262
|
-
searchParams.set(
|
|
600
|
+
if (typeof client.reconnect !== "undefined") {
|
|
601
|
+
searchParams.set(
|
|
602
|
+
"reconnect",
|
|
603
|
+
typeof client.reconnect === "number"
|
|
604
|
+
? String(client.reconnect)
|
|
605
|
+
: "10"
|
|
606
|
+
);
|
|
263
607
|
}
|
|
264
608
|
|
|
265
|
-
|
|
609
|
+
webSocketURLStr = searchParams.toString();
|
|
266
610
|
}
|
|
267
611
|
|
|
268
612
|
additionalEntries.push(
|
|
269
|
-
`${require.resolve("../client/index.js")}?${
|
|
613
|
+
`${require.resolve("../client/index.js")}?${webSocketURLStr}`
|
|
270
614
|
);
|
|
271
615
|
}
|
|
272
616
|
|
|
273
|
-
if (this.options.hot) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
hotEntry = require.resolve("webpack/hot/only-dev-server");
|
|
278
|
-
} else if (this.options.hot) {
|
|
279
|
-
hotEntry = require.resolve("webpack/hot/dev-server");
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
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"));
|
|
283
621
|
}
|
|
284
622
|
|
|
285
623
|
const webpack = compiler.webpack || require("webpack");
|
|
@@ -297,9 +635,9 @@ class Server {
|
|
|
297
635
|
else {
|
|
298
636
|
/**
|
|
299
637
|
* prependEntry Method for webpack 4
|
|
300
|
-
* @param {
|
|
301
|
-
* @param {
|
|
302
|
-
* @returns {
|
|
638
|
+
* @param {any} originalEntry
|
|
639
|
+
* @param {any} newAdditionalEntries
|
|
640
|
+
* @returns {any}
|
|
303
641
|
*/
|
|
304
642
|
const prependEntry = (originalEntry, newAdditionalEntries) => {
|
|
305
643
|
if (typeof originalEntry === "function") {
|
|
@@ -328,7 +666,7 @@ class Server {
|
|
|
328
666
|
|
|
329
667
|
// in this case, entry is a string or an array.
|
|
330
668
|
// make sure that we do not add duplicates.
|
|
331
|
-
/** @type {
|
|
669
|
+
/** @type {any} */
|
|
332
670
|
const entriesClone = additionalEntries.slice(0);
|
|
333
671
|
|
|
334
672
|
[].concat(originalEntry).forEach((newEntry) => {
|
|
@@ -345,73 +683,201 @@ class Server {
|
|
|
345
683
|
additionalEntries
|
|
346
684
|
);
|
|
347
685
|
compiler.hooks.entryOption.call(
|
|
348
|
-
compiler.options.context,
|
|
686
|
+
/** @type {string} */ (compiler.options.context),
|
|
349
687
|
compiler.options.entry
|
|
350
688
|
);
|
|
351
689
|
}
|
|
352
690
|
}
|
|
353
691
|
|
|
692
|
+
/**
|
|
693
|
+
* @private
|
|
694
|
+
* @returns {Compiler["options"]}
|
|
695
|
+
*/
|
|
354
696
|
getCompilerOptions() {
|
|
355
|
-
if (
|
|
356
|
-
|
|
357
|
-
|
|
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
|
+
);
|
|
358
706
|
}
|
|
359
707
|
|
|
360
708
|
// Configuration with the `devServer` options
|
|
361
|
-
const compilerWithDevServer =
|
|
362
|
-
|
|
363
|
-
|
|
709
|
+
const compilerWithDevServer =
|
|
710
|
+
/** @type {MultiCompiler} */
|
|
711
|
+
(this.compiler).compilers.find((config) => config.options.devServer);
|
|
364
712
|
|
|
365
713
|
if (compilerWithDevServer) {
|
|
366
714
|
return compilerWithDevServer.options;
|
|
367
715
|
}
|
|
368
716
|
|
|
369
717
|
// Configuration with `web` preset
|
|
370
|
-
const compilerWithWebPreset =
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
+
);
|
|
385
735
|
|
|
386
736
|
if (compilerWithWebPreset) {
|
|
387
737
|
return compilerWithWebPreset.options;
|
|
388
738
|
}
|
|
389
739
|
|
|
390
740
|
// Fallback
|
|
391
|
-
return this.compiler.compilers[0].options;
|
|
741
|
+
return /** @type {MultiCompiler} */ (this.compiler).compilers[0].options;
|
|
392
742
|
}
|
|
393
743
|
|
|
394
|
-
return this.compiler.options;
|
|
744
|
+
return /** @type {Compiler} */ (this.compiler).options;
|
|
395
745
|
}
|
|
396
746
|
|
|
397
|
-
|
|
747
|
+
/**
|
|
748
|
+
* @private
|
|
749
|
+
* @returns {Promise<void>}
|
|
750
|
+
*/
|
|
398
751
|
async normalizeOptions() {
|
|
399
752
|
const { options } = this;
|
|
400
|
-
|
|
401
|
-
if (!this.logger) {
|
|
402
|
-
this.logger = this.compiler.getInfrastructureLogger("webpack-dev-server");
|
|
403
|
-
}
|
|
404
|
-
|
|
405
753
|
const compilerOptions = this.getCompilerOptions();
|
|
406
754
|
// TODO remove `{}` after drop webpack v4 support
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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;
|
|
415
881
|
};
|
|
416
882
|
|
|
417
883
|
if (typeof options.allowedHosts === "undefined") {
|
|
@@ -523,6 +989,24 @@ class Server {
|
|
|
523
989
|
const isHTTPs = Boolean(options.https);
|
|
524
990
|
const isSPDY = Boolean(options.http2);
|
|
525
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
|
+
|
|
526
1010
|
options.server = {
|
|
527
1011
|
type:
|
|
528
1012
|
// eslint-disable-next-line no-nested-ternary
|
|
@@ -530,7 +1014,7 @@ class Server {
|
|
|
530
1014
|
? options.server
|
|
531
1015
|
: // eslint-disable-next-line no-nested-ternary
|
|
532
1016
|
typeof (options.server || {}).type === "string"
|
|
533
|
-
? options.server.type
|
|
1017
|
+
? /** @type {ServerConfiguration} */ (options.server).type || "http"
|
|
534
1018
|
: // eslint-disable-next-line no-nested-ternary
|
|
535
1019
|
isSPDY
|
|
536
1020
|
? "spdy"
|
|
@@ -538,33 +1022,64 @@ class Server {
|
|
|
538
1022
|
? "https"
|
|
539
1023
|
: "http",
|
|
540
1024
|
options: {
|
|
541
|
-
|
|
542
|
-
|
|
1025
|
+
.../** @type {ServerOptions} */ (options.https),
|
|
1026
|
+
.../** @type {ServerConfiguration} */ (options.server || {}).options,
|
|
543
1027
|
},
|
|
544
1028
|
};
|
|
545
1029
|
|
|
546
1030
|
if (
|
|
547
1031
|
options.server.type === "spdy" &&
|
|
548
|
-
typeof options.server.options.spdy ===
|
|
1032
|
+
typeof (/** @type {ServerOptions} */ (options.server.options).spdy) ===
|
|
1033
|
+
"undefined"
|
|
549
1034
|
) {
|
|
550
|
-
|
|
1035
|
+
/** @type {ServerOptions} */
|
|
1036
|
+
(options.server.options).spdy = {
|
|
551
1037
|
protocols: ["h2", "http/1.1"],
|
|
552
1038
|
};
|
|
553
1039
|
}
|
|
554
1040
|
|
|
555
1041
|
if (options.server.type === "https" || options.server.type === "spdy") {
|
|
556
|
-
if (
|
|
557
|
-
|
|
1042
|
+
if (
|
|
1043
|
+
typeof (
|
|
1044
|
+
/** @type {ServerOptions} */ (options.server.options).requestCert
|
|
1045
|
+
) === "undefined"
|
|
1046
|
+
) {
|
|
1047
|
+
/** @type {ServerOptions} */
|
|
1048
|
+
(options.server.options).requestCert = false;
|
|
558
1049
|
}
|
|
559
1050
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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
|
+
) {
|
|
563
1061
|
// eslint-disable-next-line no-continue
|
|
564
1062
|
continue;
|
|
565
1063
|
}
|
|
566
1064
|
|
|
567
|
-
|
|
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
|
+
*/
|
|
568
1083
|
const readFile = (item) => {
|
|
569
1084
|
if (
|
|
570
1085
|
Buffer.isBuffer(item) ||
|
|
@@ -587,14 +1102,18 @@ class Server {
|
|
|
587
1102
|
}
|
|
588
1103
|
};
|
|
589
1104
|
|
|
590
|
-
|
|
1105
|
+
/** @type {any} */
|
|
1106
|
+
(options.server.options)[property] = Array.isArray(value)
|
|
591
1107
|
? value.map((item) => readFile(item))
|
|
592
1108
|
: readFile(value);
|
|
593
1109
|
}
|
|
594
1110
|
|
|
595
1111
|
let fakeCert;
|
|
596
1112
|
|
|
597
|
-
if (
|
|
1113
|
+
if (
|
|
1114
|
+
!(/** @type {ServerOptions} */ (options.server.options).key) ||
|
|
1115
|
+
/** @type {ServerOptions} */ (!options.server.options).cert
|
|
1116
|
+
) {
|
|
598
1117
|
const certificateDir = Server.findCacheDir();
|
|
599
1118
|
const certificatePath = path.join(certificateDir, "server.pem");
|
|
600
1119
|
let certificateExists;
|
|
@@ -609,11 +1128,10 @@ class Server {
|
|
|
609
1128
|
if (certificateExists) {
|
|
610
1129
|
const certificateTtl = 1000 * 60 * 60 * 24;
|
|
611
1130
|
const certificateStat = await fs.promises.stat(certificatePath);
|
|
612
|
-
|
|
613
|
-
const now = new Date();
|
|
1131
|
+
const now = Number(new Date());
|
|
614
1132
|
|
|
615
1133
|
// cert is more than 30 days old, kill it with fire
|
|
616
|
-
if ((now - certificateStat.ctime) / certificateTtl > 30) {
|
|
1134
|
+
if ((now - Number(certificateStat.ctime)) / certificateTtl > 30) {
|
|
617
1135
|
const del = require("del");
|
|
618
1136
|
|
|
619
1137
|
this.logger.info(
|
|
@@ -629,6 +1147,7 @@ class Server {
|
|
|
629
1147
|
if (!certificateExists) {
|
|
630
1148
|
this.logger.info("Generating SSL certificate...");
|
|
631
1149
|
|
|
1150
|
+
// @ts-ignore
|
|
632
1151
|
const selfsigned = require("selfsigned");
|
|
633
1152
|
const attributes = [{ name: "commonName", value: "localhost" }];
|
|
634
1153
|
const pems = selfsigned.generate(attributes, {
|
|
@@ -709,20 +1228,37 @@ class Server {
|
|
|
709
1228
|
this.logger.info(`SSL certificate: ${certificatePath}`);
|
|
710
1229
|
}
|
|
711
1230
|
|
|
712
|
-
if (
|
|
713
|
-
|
|
1231
|
+
if (
|
|
1232
|
+
/** @type {ServerOptions & { cacert?: ServerOptions["ca"] }} */ (
|
|
1233
|
+
options.server.options
|
|
1234
|
+
).cacert
|
|
1235
|
+
) {
|
|
1236
|
+
if (/** @type {ServerOptions} */ (options.server.options).ca) {
|
|
714
1237
|
this.logger.warn(
|
|
715
1238
|
"Do not specify 'ca' and 'cacert' options together, the 'ca' option will be used."
|
|
716
1239
|
);
|
|
717
1240
|
} else {
|
|
718
|
-
|
|
1241
|
+
/** @type {ServerOptions} */
|
|
1242
|
+
(options.server.options).ca =
|
|
1243
|
+
/** @type {ServerOptions & { cacert?: ServerOptions["ca"] }} */
|
|
1244
|
+
(options.server.options).cacert;
|
|
719
1245
|
}
|
|
720
1246
|
|
|
721
|
-
delete
|
|
1247
|
+
delete (
|
|
1248
|
+
/** @type {ServerOptions & { cacert?: ServerOptions["ca"] }} */ (
|
|
1249
|
+
options.server.options
|
|
1250
|
+
).cacert
|
|
1251
|
+
);
|
|
722
1252
|
}
|
|
723
1253
|
|
|
724
|
-
|
|
725
|
-
options.server.options.
|
|
1254
|
+
/** @type {ServerOptions} */
|
|
1255
|
+
(options.server.options).key =
|
|
1256
|
+
/** @type {ServerOptions} */
|
|
1257
|
+
(options.server.options).key || fakeCert;
|
|
1258
|
+
/** @type {ServerOptions} */
|
|
1259
|
+
(options.server.options).cert =
|
|
1260
|
+
/** @type {ServerOptions} */
|
|
1261
|
+
(options.server.options).cert || fakeCert;
|
|
726
1262
|
}
|
|
727
1263
|
|
|
728
1264
|
if (typeof options.ipc === "boolean") {
|
|
@@ -741,6 +1277,11 @@ class Server {
|
|
|
741
1277
|
|
|
742
1278
|
// https://github.com/webpack/webpack-dev-server/issues/1990
|
|
743
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
|
|
744
1285
|
const getOpenItemsFromObject = ({ target, ...rest }) => {
|
|
745
1286
|
const normalizedOptions = { ...defaultOpenOptions, ...rest };
|
|
746
1287
|
|
|
@@ -762,14 +1303,25 @@ class Server {
|
|
|
762
1303
|
};
|
|
763
1304
|
|
|
764
1305
|
if (typeof options.open === "undefined") {
|
|
765
|
-
|
|
1306
|
+
/** @type {NormalizedOpen[]} */
|
|
1307
|
+
(options.open) = [];
|
|
766
1308
|
} else if (typeof options.open === "boolean") {
|
|
767
|
-
|
|
768
|
-
|
|
1309
|
+
/** @type {NormalizedOpen[]} */
|
|
1310
|
+
(options.open) = options.open
|
|
1311
|
+
? [
|
|
1312
|
+
{
|
|
1313
|
+
target: "<url>",
|
|
1314
|
+
options: /** @type {OpenOptions} */ (defaultOpenOptions),
|
|
1315
|
+
},
|
|
1316
|
+
]
|
|
769
1317
|
: [];
|
|
770
1318
|
} else if (typeof options.open === "string") {
|
|
771
|
-
|
|
1319
|
+
/** @type {NormalizedOpen[]} */
|
|
1320
|
+
(options.open) = [{ target: options.open, options: defaultOpenOptions }];
|
|
772
1321
|
} else if (Array.isArray(options.open)) {
|
|
1322
|
+
/**
|
|
1323
|
+
* @type {NormalizedOpen[]}
|
|
1324
|
+
*/
|
|
773
1325
|
const result = [];
|
|
774
1326
|
|
|
775
1327
|
options.open.forEach((item) => {
|
|
@@ -782,9 +1334,29 @@ class Server {
|
|
|
782
1334
|
result.push(...getOpenItemsFromObject(item));
|
|
783
1335
|
});
|
|
784
1336
|
|
|
785
|
-
|
|
1337
|
+
/** @type {NormalizedOpen[]} */
|
|
1338
|
+
(options.open) = result;
|
|
786
1339
|
} else {
|
|
787
|
-
|
|
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
|
+
)();
|
|
788
1360
|
}
|
|
789
1361
|
|
|
790
1362
|
if (typeof options.port === "string" && options.port !== "auto") {
|
|
@@ -808,61 +1380,91 @@ class Server {
|
|
|
808
1380
|
Object.prototype.hasOwnProperty.call(options.proxy, "target") ||
|
|
809
1381
|
Object.prototype.hasOwnProperty.call(options.proxy, "router")
|
|
810
1382
|
) {
|
|
811
|
-
|
|
1383
|
+
/** @type {ProxyArray} */
|
|
1384
|
+
(options.proxy) = [/** @type {ProxyConfigMap} */ (options.proxy)];
|
|
812
1385
|
} else {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
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;
|
|
828
1419
|
}
|
|
829
|
-
|
|
830
|
-
return proxyOptions;
|
|
831
|
-
});
|
|
1420
|
+
);
|
|
832
1421
|
}
|
|
833
1422
|
}
|
|
834
1423
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
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
|
+
}
|
|
840
1460
|
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
1461
|
+
if (typeof item.logProvider === "undefined") {
|
|
1462
|
+
item.logProvider = () => this.logger;
|
|
1463
|
+
}
|
|
844
1464
|
|
|
845
|
-
|
|
846
|
-
return "debug";
|
|
1465
|
+
return item;
|
|
847
1466
|
}
|
|
848
|
-
|
|
849
|
-
return level;
|
|
850
|
-
};
|
|
851
|
-
|
|
852
|
-
if (typeof item.logLevel === "undefined") {
|
|
853
|
-
item.logLevel = getLogLevelForProxy(
|
|
854
|
-
compilerOptions.infrastructureLogging
|
|
855
|
-
? compilerOptions.infrastructureLogging.level
|
|
856
|
-
: "info"
|
|
857
|
-
);
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
if (typeof item.logProvider === "undefined") {
|
|
861
|
-
item.logProvider = () => this.logger;
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
return item;
|
|
865
|
-
});
|
|
1467
|
+
);
|
|
866
1468
|
}
|
|
867
1469
|
|
|
868
1470
|
if (typeof options.setupExitSignals === "undefined") {
|
|
@@ -870,50 +1472,27 @@ class Server {
|
|
|
870
1472
|
}
|
|
871
1473
|
|
|
872
1474
|
if (typeof options.static === "undefined") {
|
|
873
|
-
options.static = [
|
|
1475
|
+
options.static = [getStaticItem()];
|
|
874
1476
|
} else if (typeof options.static === "boolean") {
|
|
875
|
-
options.static = options.static ? [
|
|
1477
|
+
options.static = options.static ? [getStaticItem()] : false;
|
|
876
1478
|
} else if (typeof options.static === "string") {
|
|
877
|
-
options.static = [
|
|
878
|
-
{ ...defaultOptionsForStatic, directory: options.static },
|
|
879
|
-
];
|
|
1479
|
+
options.static = [getStaticItem(options.static)];
|
|
880
1480
|
} else if (Array.isArray(options.static)) {
|
|
881
1481
|
options.static = options.static.map((item) => {
|
|
882
1482
|
if (typeof item === "string") {
|
|
883
|
-
return
|
|
1483
|
+
return getStaticItem(item);
|
|
884
1484
|
}
|
|
885
1485
|
|
|
886
|
-
return
|
|
1486
|
+
return getStaticItem(item);
|
|
887
1487
|
});
|
|
888
1488
|
} else {
|
|
889
|
-
options.static = [
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
if (options.static) {
|
|
893
|
-
options.static.forEach((staticOption) => {
|
|
894
|
-
if (Server.isAbsoluteURL(staticOption.directory)) {
|
|
895
|
-
throw new Error("Using a URL as static.directory is not supported");
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
// ensure that publicPath is an array
|
|
899
|
-
if (typeof staticOption.publicPath === "string") {
|
|
900
|
-
staticOption.publicPath = [staticOption.publicPath];
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
// ensure that watch is an object if true
|
|
904
|
-
if (staticOption.watch === true) {
|
|
905
|
-
staticOption.watch = defaultOptionsForStatic.watch;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
// ensure that serveIndex is an object if true
|
|
909
|
-
if (staticOption.serveIndex === true) {
|
|
910
|
-
staticOption.serveIndex = defaultOptionsForStatic.serveIndex;
|
|
911
|
-
}
|
|
912
|
-
});
|
|
1489
|
+
options.static = [getStaticItem(options.static)];
|
|
913
1490
|
}
|
|
914
1491
|
|
|
915
1492
|
if (typeof options.watchFiles === "string") {
|
|
916
|
-
options.watchFiles = [
|
|
1493
|
+
options.watchFiles = [
|
|
1494
|
+
{ paths: options.watchFiles, options: getWatchOptions() },
|
|
1495
|
+
];
|
|
917
1496
|
} else if (
|
|
918
1497
|
typeof options.watchFiles === "object" &&
|
|
919
1498
|
options.watchFiles !== null &&
|
|
@@ -922,16 +1501,19 @@ class Server {
|
|
|
922
1501
|
options.watchFiles = [
|
|
923
1502
|
{
|
|
924
1503
|
paths: options.watchFiles.paths,
|
|
925
|
-
options: options.watchFiles.options || {},
|
|
1504
|
+
options: getWatchOptions(options.watchFiles.options || {}),
|
|
926
1505
|
},
|
|
927
1506
|
];
|
|
928
1507
|
} else if (Array.isArray(options.watchFiles)) {
|
|
929
1508
|
options.watchFiles = options.watchFiles.map((item) => {
|
|
930
1509
|
if (typeof item === "string") {
|
|
931
|
-
return { paths: item, options:
|
|
1510
|
+
return { paths: item, options: getWatchOptions() };
|
|
932
1511
|
}
|
|
933
1512
|
|
|
934
|
-
return {
|
|
1513
|
+
return {
|
|
1514
|
+
paths: item.paths,
|
|
1515
|
+
options: getWatchOptions(item.options || {}),
|
|
1516
|
+
};
|
|
935
1517
|
});
|
|
936
1518
|
} else {
|
|
937
1519
|
options.watchFiles = [];
|
|
@@ -960,38 +1542,61 @@ class Server {
|
|
|
960
1542
|
};
|
|
961
1543
|
} else {
|
|
962
1544
|
options.webSocketServer = {
|
|
963
|
-
type:
|
|
1545
|
+
type:
|
|
1546
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1547
|
+
(options.webSocketServer).type || defaultWebSocketServerType,
|
|
964
1548
|
options: {
|
|
965
1549
|
...defaultWebSocketServerOptions,
|
|
966
|
-
|
|
1550
|
+
.../** @type {WebSocketServerConfiguration} */
|
|
1551
|
+
(options.webSocketServer).options,
|
|
967
1552
|
},
|
|
968
1553
|
};
|
|
969
1554
|
|
|
970
|
-
|
|
971
|
-
options
|
|
972
|
-
|
|
973
|
-
|
|
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);
|
|
974
1561
|
}
|
|
975
1562
|
}
|
|
976
1563
|
}
|
|
977
1564
|
|
|
1565
|
+
/**
|
|
1566
|
+
* @private
|
|
1567
|
+
* @returns {string}
|
|
1568
|
+
*/
|
|
978
1569
|
getClientTransport() {
|
|
979
|
-
let
|
|
1570
|
+
let clientImplementation;
|
|
980
1571
|
let clientImplementationFound = true;
|
|
981
1572
|
|
|
982
1573
|
const isKnownWebSocketServerImplementation =
|
|
983
1574
|
this.options.webSocketServer &&
|
|
984
|
-
typeof
|
|
1575
|
+
typeof (
|
|
1576
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1577
|
+
(this.options.webSocketServer).type
|
|
1578
|
+
) === "string" &&
|
|
1579
|
+
// @ts-ignore
|
|
985
1580
|
(this.options.webSocketServer.type === "ws" ||
|
|
986
|
-
|
|
1581
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1582
|
+
(this.options.webSocketServer).type === "sockjs");
|
|
987
1583
|
|
|
988
1584
|
let clientTransport;
|
|
989
1585
|
|
|
990
1586
|
if (this.options.client) {
|
|
991
|
-
if (
|
|
992
|
-
|
|
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;
|
|
993
1596
|
} else if (isKnownWebSocketServerImplementation) {
|
|
994
|
-
clientTransport =
|
|
1597
|
+
clientTransport =
|
|
1598
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1599
|
+
(this.options.webSocketServer).type;
|
|
995
1600
|
} else {
|
|
996
1601
|
clientTransport = "ws";
|
|
997
1602
|
}
|
|
@@ -1003,17 +1608,16 @@ class Server {
|
|
|
1003
1608
|
case "string":
|
|
1004
1609
|
// could be 'sockjs', 'ws', or a path that should be required
|
|
1005
1610
|
if (clientTransport === "sockjs") {
|
|
1006
|
-
|
|
1611
|
+
clientImplementation = require.resolve(
|
|
1007
1612
|
"../client/clients/SockJSClient"
|
|
1008
1613
|
);
|
|
1009
1614
|
} else if (clientTransport === "ws") {
|
|
1010
|
-
|
|
1615
|
+
clientImplementation = require.resolve(
|
|
1011
1616
|
"../client/clients/WebSocketClient"
|
|
1012
1617
|
);
|
|
1013
1618
|
} else {
|
|
1014
1619
|
try {
|
|
1015
|
-
|
|
1016
|
-
ClientImplementation = require.resolve(clientTransport);
|
|
1620
|
+
clientImplementation = require.resolve(clientTransport);
|
|
1017
1621
|
} catch (e) {
|
|
1018
1622
|
clientImplementationFound = false;
|
|
1019
1623
|
}
|
|
@@ -1033,31 +1637,52 @@ class Server {
|
|
|
1033
1637
|
);
|
|
1034
1638
|
}
|
|
1035
1639
|
|
|
1036
|
-
return
|
|
1640
|
+
return /** @type {string} */ (clientImplementation);
|
|
1037
1641
|
}
|
|
1038
1642
|
|
|
1643
|
+
/**
|
|
1644
|
+
* @private
|
|
1645
|
+
* @returns {string}
|
|
1646
|
+
*/
|
|
1039
1647
|
getServerTransport() {
|
|
1040
1648
|
let implementation;
|
|
1041
1649
|
let implementationFound = true;
|
|
1042
1650
|
|
|
1043
|
-
switch (
|
|
1651
|
+
switch (
|
|
1652
|
+
typeof (
|
|
1653
|
+
/** @type {WebSocketServerConfiguration} */
|
|
1654
|
+
(this.options.webSocketServer).type
|
|
1655
|
+
)
|
|
1656
|
+
) {
|
|
1044
1657
|
case "string":
|
|
1045
1658
|
// Could be 'sockjs', in the future 'ws', or a path that should be required
|
|
1046
|
-
if (
|
|
1659
|
+
if (
|
|
1660
|
+
/** @type {WebSocketServerConfiguration} */ (
|
|
1661
|
+
this.options.webSocketServer
|
|
1662
|
+
).type === "sockjs"
|
|
1663
|
+
) {
|
|
1047
1664
|
implementation = require("./servers/SockJSServer");
|
|
1048
|
-
} else if (
|
|
1665
|
+
} else if (
|
|
1666
|
+
/** @type {WebSocketServerConfiguration} */ (
|
|
1667
|
+
this.options.webSocketServer
|
|
1668
|
+
).type === "ws"
|
|
1669
|
+
) {
|
|
1049
1670
|
implementation = require("./servers/WebsocketServer");
|
|
1050
1671
|
} else {
|
|
1051
1672
|
try {
|
|
1052
1673
|
// eslint-disable-next-line import/no-dynamic-require
|
|
1053
|
-
implementation = require(
|
|
1674
|
+
implementation = require(/** @type {WebSocketServerConfiguration} */ (
|
|
1675
|
+
this.options.webSocketServer
|
|
1676
|
+
).type);
|
|
1054
1677
|
} catch (error) {
|
|
1055
1678
|
implementationFound = false;
|
|
1056
1679
|
}
|
|
1057
1680
|
}
|
|
1058
1681
|
break;
|
|
1059
1682
|
case "function":
|
|
1060
|
-
implementation =
|
|
1683
|
+
implementation = /** @type {WebSocketServerConfiguration} */ (
|
|
1684
|
+
this.options.webSocketServer
|
|
1685
|
+
).type;
|
|
1061
1686
|
break;
|
|
1062
1687
|
default:
|
|
1063
1688
|
implementationFound = false;
|
|
@@ -1074,39 +1699,62 @@ class Server {
|
|
|
1074
1699
|
return implementation;
|
|
1075
1700
|
}
|
|
1076
1701
|
|
|
1702
|
+
/**
|
|
1703
|
+
* @private
|
|
1704
|
+
* @returns {void}
|
|
1705
|
+
*/
|
|
1077
1706
|
setupProgressPlugin() {
|
|
1078
|
-
const { ProgressPlugin } =
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
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);
|
|
1082
1724
|
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1725
|
+
if (percent === 100) {
|
|
1726
|
+
msg = "Compilation completed";
|
|
1727
|
+
}
|
|
1086
1728
|
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1729
|
+
if (addInfo) {
|
|
1730
|
+
msg = `${msg} (${addInfo})`;
|
|
1731
|
+
}
|
|
1090
1732
|
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1733
|
+
if (this.webSocketServer) {
|
|
1734
|
+
this.sendMessage(this.webSocketServer.clients, "progress-update", {
|
|
1735
|
+
percent,
|
|
1736
|
+
msg,
|
|
1737
|
+
pluginName,
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1098
1740
|
|
|
1099
|
-
|
|
1100
|
-
|
|
1741
|
+
if (this.server) {
|
|
1742
|
+
this.server.emit("progress-update", { percent, msg, pluginName });
|
|
1743
|
+
}
|
|
1101
1744
|
}
|
|
1102
|
-
|
|
1745
|
+
).apply(this.compiler);
|
|
1103
1746
|
}
|
|
1104
1747
|
|
|
1748
|
+
/**
|
|
1749
|
+
* @private
|
|
1750
|
+
* @returns {Promise<void>}
|
|
1751
|
+
*/
|
|
1105
1752
|
async initialize() {
|
|
1106
1753
|
if (this.options.webSocketServer) {
|
|
1107
|
-
const compilers =
|
|
1754
|
+
const compilers =
|
|
1755
|
+
/** @type {MultiCompiler} */
|
|
1756
|
+
(this.compiler).compilers || [this.compiler];
|
|
1108
1757
|
|
|
1109
|
-
// eslint-disable-next-line no-shadow
|
|
1110
1758
|
compilers.forEach((compiler) => {
|
|
1111
1759
|
this.addAdditionalEntries(compiler);
|
|
1112
1760
|
|
|
@@ -1137,7 +1785,10 @@ class Server {
|
|
|
1137
1785
|
}
|
|
1138
1786
|
});
|
|
1139
1787
|
|
|
1140
|
-
if (
|
|
1788
|
+
if (
|
|
1789
|
+
this.options.client &&
|
|
1790
|
+
/** @type {ClientConfiguration} */ (this.options.client).progress
|
|
1791
|
+
) {
|
|
1141
1792
|
this.setupProgressPlugin();
|
|
1142
1793
|
}
|
|
1143
1794
|
}
|
|
@@ -1149,7 +1800,8 @@ class Server {
|
|
|
1149
1800
|
// Should be after `webpack-dev-middleware`, otherwise other middlewares might rewrite response
|
|
1150
1801
|
this.setupBuiltInRoutes();
|
|
1151
1802
|
this.setupWatchFiles();
|
|
1152
|
-
this.
|
|
1803
|
+
this.setupWatchStaticFiles();
|
|
1804
|
+
this.setupMiddlewares();
|
|
1153
1805
|
this.createServer();
|
|
1154
1806
|
|
|
1155
1807
|
if (this.options.setupExitSignals) {
|
|
@@ -1157,15 +1809,10 @@ class Server {
|
|
|
1157
1809
|
|
|
1158
1810
|
let needForceShutdown = false;
|
|
1159
1811
|
|
|
1160
|
-
const exitProcess = () => {
|
|
1161
|
-
// eslint-disable-next-line no-process-exit
|
|
1162
|
-
process.exit();
|
|
1163
|
-
};
|
|
1164
|
-
|
|
1165
1812
|
signals.forEach((signal) => {
|
|
1166
|
-
|
|
1813
|
+
const listener = () => {
|
|
1167
1814
|
if (needForceShutdown) {
|
|
1168
|
-
|
|
1815
|
+
process.exit();
|
|
1169
1816
|
}
|
|
1170
1817
|
|
|
1171
1818
|
this.logger.info(
|
|
@@ -1176,65 +1823,125 @@ class Server {
|
|
|
1176
1823
|
|
|
1177
1824
|
this.stopCallback(() => {
|
|
1178
1825
|
if (typeof this.compiler.close === "function") {
|
|
1179
|
-
this.compiler.close(
|
|
1826
|
+
this.compiler.close(() => {
|
|
1827
|
+
process.exit();
|
|
1828
|
+
});
|
|
1180
1829
|
} else {
|
|
1181
|
-
|
|
1830
|
+
process.exit();
|
|
1182
1831
|
}
|
|
1183
1832
|
});
|
|
1184
|
-
}
|
|
1833
|
+
};
|
|
1834
|
+
|
|
1835
|
+
this.listeners.push({ name: signal, listener });
|
|
1836
|
+
|
|
1837
|
+
process.on(signal, listener);
|
|
1185
1838
|
});
|
|
1186
1839
|
}
|
|
1187
1840
|
|
|
1188
1841
|
// Proxy WebSocket without the initial http request
|
|
1189
1842
|
// https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade
|
|
1190
|
-
|
|
1191
|
-
this.webSocketProxies.forEach(
|
|
1192
|
-
|
|
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
|
+
);
|
|
1193
1851
|
}, this);
|
|
1194
1852
|
}
|
|
1195
1853
|
|
|
1854
|
+
/**
|
|
1855
|
+
* @private
|
|
1856
|
+
* @returns {void}
|
|
1857
|
+
*/
|
|
1196
1858
|
setupApp() {
|
|
1197
|
-
|
|
1859
|
+
/** @type {import("express").Application | undefined}*/
|
|
1198
1860
|
// eslint-disable-next-line new-cap
|
|
1199
|
-
this.app = new express();
|
|
1861
|
+
this.app = new /** @type {any} */ (express)();
|
|
1200
1862
|
}
|
|
1201
1863
|
|
|
1864
|
+
/**
|
|
1865
|
+
* @private
|
|
1866
|
+
* @param {Stats | MultiStats} statsObj
|
|
1867
|
+
* @returns {StatsCompilation}
|
|
1868
|
+
*/
|
|
1202
1869
|
getStats(statsObj) {
|
|
1203
1870
|
const stats = Server.DEFAULT_STATS;
|
|
1204
1871
|
const compilerOptions = this.getCompilerOptions();
|
|
1205
1872
|
|
|
1873
|
+
// @ts-ignore
|
|
1206
1874
|
if (compilerOptions.stats && compilerOptions.stats.warningsFilter) {
|
|
1875
|
+
// @ts-ignore
|
|
1207
1876
|
stats.warningsFilter = compilerOptions.stats.warningsFilter;
|
|
1208
1877
|
}
|
|
1209
1878
|
|
|
1210
1879
|
return statsObj.toJson(stats);
|
|
1211
1880
|
}
|
|
1212
1881
|
|
|
1882
|
+
/**
|
|
1883
|
+
* @private
|
|
1884
|
+
* @returns {void}
|
|
1885
|
+
*/
|
|
1213
1886
|
setupHooks() {
|
|
1214
1887
|
this.compiler.hooks.invalid.tap("webpack-dev-server", () => {
|
|
1215
1888
|
if (this.webSocketServer) {
|
|
1216
1889
|
this.sendMessage(this.webSocketServer.clients, "invalid");
|
|
1217
1890
|
}
|
|
1218
1891
|
});
|
|
1219
|
-
this.compiler.hooks.done.tap(
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
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
|
+
}
|
|
1223
1901
|
|
|
1224
|
-
|
|
1225
|
-
|
|
1902
|
+
/**
|
|
1903
|
+
* @private
|
|
1904
|
+
* @type {Stats | MultiStats}
|
|
1905
|
+
*/
|
|
1906
|
+
this.stats = stats;
|
|
1907
|
+
}
|
|
1908
|
+
);
|
|
1226
1909
|
}
|
|
1227
1910
|
|
|
1911
|
+
/**
|
|
1912
|
+
* @private
|
|
1913
|
+
* @returns {void}
|
|
1914
|
+
*/
|
|
1228
1915
|
setupHostHeaderCheck() {
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
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
|
+
}
|
|
1233
1935
|
|
|
1234
|
-
|
|
1235
|
-
|
|
1936
|
+
res.send("Invalid Host header");
|
|
1937
|
+
}
|
|
1938
|
+
);
|
|
1236
1939
|
}
|
|
1237
1940
|
|
|
1941
|
+
/**
|
|
1942
|
+
* @private
|
|
1943
|
+
* @returns {void}
|
|
1944
|
+
*/
|
|
1238
1945
|
setupDevMiddleware() {
|
|
1239
1946
|
const webpackDevMiddleware = require("webpack-dev-middleware");
|
|
1240
1947
|
|
|
@@ -1245,432 +1952,616 @@ class Server {
|
|
|
1245
1952
|
);
|
|
1246
1953
|
}
|
|
1247
1954
|
|
|
1955
|
+
/**
|
|
1956
|
+
* @private
|
|
1957
|
+
* @returns {void}
|
|
1958
|
+
*/
|
|
1248
1959
|
setupBuiltInRoutes() {
|
|
1249
1960
|
const { app, middleware } = this;
|
|
1250
1961
|
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
app.get("/webpack-dev-server/invalidate", (_req, res) => {
|
|
1263
|
-
this.invalidate();
|
|
1264
|
-
|
|
1265
|
-
res.end();
|
|
1266
|
-
});
|
|
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");
|
|
1267
1972
|
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
res.setHeader("Content-Type", "text/html");
|
|
1271
|
-
res.write(
|
|
1272
|
-
'<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body>'
|
|
1273
|
-
);
|
|
1973
|
+
const { createReadStream } = fs;
|
|
1974
|
+
const clientPath = path.join(__dirname, "..", "client");
|
|
1274
1975
|
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1976
|
+
createReadStream(
|
|
1977
|
+
path.join(clientPath, "modules/sockjs-client/index.js")
|
|
1978
|
+
).pipe(res);
|
|
1979
|
+
}
|
|
1980
|
+
);
|
|
1279
1981
|
|
|
1280
|
-
|
|
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();
|
|
1281
1992
|
|
|
1282
|
-
|
|
1283
|
-
|
|
1993
|
+
res.end();
|
|
1994
|
+
}
|
|
1995
|
+
);
|
|
1284
1996
|
|
|
1285
|
-
|
|
1286
|
-
|
|
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
|
+
);
|
|
1287
2012
|
|
|
1288
|
-
|
|
1289
|
-
|
|
2013
|
+
const statsForPrint =
|
|
2014
|
+
typeof (/** @type {MultiStats} */ (stats).stats) !== "undefined"
|
|
2015
|
+
? /** @type {MultiStats} */ (stats).toJson().children
|
|
2016
|
+
: [/** @type {Stats} */ (stats).toJson()];
|
|
1290
2017
|
|
|
1291
|
-
|
|
2018
|
+
res.write(`<h1>Assets Report:</h1>`);
|
|
1292
2019
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
2020
|
+
/**
|
|
2021
|
+
* @type {StatsCompilation[]}
|
|
2022
|
+
*/
|
|
2023
|
+
(statsForPrint).forEach((item, index) => {
|
|
2024
|
+
res.write("<div>");
|
|
1296
2025
|
|
|
1297
|
-
|
|
1298
|
-
|
|
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>
|
|
1299
2048
|
<strong><a href="${assetURL}" target="_blank">${assetName}</a></strong>
|
|
1300
2049
|
</li>`
|
|
1301
|
-
|
|
1302
|
-
|
|
2050
|
+
);
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
res.write("</ul>");
|
|
2054
|
+
res.write("</div>");
|
|
2055
|
+
});
|
|
1303
2056
|
|
|
1304
|
-
res.
|
|
1305
|
-
res.write("</div>");
|
|
2057
|
+
res.end("</body></html>");
|
|
1306
2058
|
});
|
|
2059
|
+
}
|
|
2060
|
+
);
|
|
2061
|
+
}
|
|
1307
2062
|
|
|
1308
|
-
|
|
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
|
+
}
|
|
1309
2074
|
});
|
|
1310
|
-
}
|
|
2075
|
+
}
|
|
1311
2076
|
}
|
|
1312
2077
|
|
|
1313
|
-
|
|
1314
|
-
|
|
2078
|
+
/**
|
|
2079
|
+
* @private
|
|
2080
|
+
* @returns {void}
|
|
2081
|
+
*/
|
|
2082
|
+
setupWatchFiles() {
|
|
2083
|
+
const { watchFiles } = this.options;
|
|
1315
2084
|
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
// It is possible to use the `bypass` method without a `target` or `router`.
|
|
1324
|
-
// However, the proxy middleware has no use in this case, and will fail to instantiate.
|
|
1325
|
-
if (proxyConfig.target) {
|
|
1326
|
-
const context = proxyConfig.context || proxyConfig.path;
|
|
1327
|
-
|
|
1328
|
-
return createProxyMiddleware(context, proxyConfig);
|
|
1329
|
-
}
|
|
2085
|
+
if (/** @type {WatchFiles[]} */ (watchFiles).length > 0) {
|
|
2086
|
+
/** @type {WatchFiles[]} */
|
|
2087
|
+
(watchFiles).forEach((item) => {
|
|
2088
|
+
this.watchFiles(item.paths, item.options);
|
|
2089
|
+
});
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
1330
2092
|
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
2093
|
+
/**
|
|
2094
|
+
* @private
|
|
2095
|
+
* @returns {void}
|
|
2096
|
+
*/
|
|
2097
|
+
setupMiddlewares() {
|
|
1335
2098
|
/**
|
|
1336
|
-
*
|
|
1337
|
-
* proxy: [
|
|
1338
|
-
* {
|
|
1339
|
-
* context: "value",
|
|
1340
|
-
* ...options,
|
|
1341
|
-
* },
|
|
1342
|
-
* // or:
|
|
1343
|
-
* function() {
|
|
1344
|
-
* return {
|
|
1345
|
-
* context: "context",
|
|
1346
|
-
* ...options,
|
|
1347
|
-
* };
|
|
1348
|
-
* }
|
|
1349
|
-
* ]
|
|
2099
|
+
* @type {Array<Middleware>}
|
|
1350
2100
|
*/
|
|
1351
|
-
|
|
1352
|
-
let proxyMiddleware;
|
|
1353
|
-
|
|
1354
|
-
let proxyConfig =
|
|
1355
|
-
typeof proxyConfigOrCallback === "function"
|
|
1356
|
-
? proxyConfigOrCallback()
|
|
1357
|
-
: proxyConfigOrCallback;
|
|
1358
|
-
|
|
1359
|
-
proxyMiddleware = getProxyMiddleware(proxyConfig);
|
|
2101
|
+
let middlewares = [];
|
|
1360
2102
|
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
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");
|
|
1364
2106
|
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
const newProxyConfig = proxyConfigOrCallback(req, res, next);
|
|
2107
|
+
middlewares.push({ name: "compression", middleware: compression() });
|
|
2108
|
+
}
|
|
1368
2109
|
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
2110
|
+
if (typeof this.options.onBeforeSetupMiddleware === "function") {
|
|
2111
|
+
this.options.onBeforeSetupMiddleware(this);
|
|
2112
|
+
}
|
|
1374
2113
|
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
: null;
|
|
1383
|
-
|
|
1384
|
-
if (typeof bypassUrl === "boolean") {
|
|
1385
|
-
// skip the proxy
|
|
1386
|
-
req.url = null;
|
|
1387
|
-
next();
|
|
1388
|
-
} else if (typeof bypassUrl === "string") {
|
|
1389
|
-
// byPass to that url
|
|
1390
|
-
req.url = bypassUrl;
|
|
1391
|
-
next();
|
|
1392
|
-
} else if (proxyMiddleware) {
|
|
1393
|
-
return proxyMiddleware(req, res, next);
|
|
1394
|
-
} else {
|
|
1395
|
-
next();
|
|
1396
|
-
}
|
|
1397
|
-
};
|
|
2114
|
+
if (typeof this.options.headers !== "undefined") {
|
|
2115
|
+
middlewares.push({
|
|
2116
|
+
name: "set-headers",
|
|
2117
|
+
path: "*",
|
|
2118
|
+
middleware: this.setHeaders.bind(this),
|
|
2119
|
+
});
|
|
2120
|
+
}
|
|
1398
2121
|
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
2122
|
+
middlewares.push({
|
|
2123
|
+
name: "webpack-dev-middleware",
|
|
2124
|
+
middleware:
|
|
2125
|
+
/** @type {import("webpack-dev-middleware").Middleware<Request, Response>}*/
|
|
2126
|
+
(this.middleware),
|
|
1402
2127
|
});
|
|
1403
|
-
}
|
|
1404
2128
|
|
|
1405
|
-
|
|
1406
|
-
|
|
2129
|
+
if (this.options.proxy) {
|
|
2130
|
+
const { createProxyMiddleware } = require("http-proxy-middleware");
|
|
1407
2131
|
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
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
|
+
}
|
|
1417
2147
|
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
2148
|
+
if (proxyConfig.router) {
|
|
2149
|
+
return createProxyMiddleware(proxyConfig);
|
|
2150
|
+
}
|
|
2151
|
+
};
|
|
1421
2152
|
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
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
|
+
}
|
|
1432
2192
|
|
|
1433
|
-
|
|
1434
|
-
|
|
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
|
+
}
|
|
1435
2210
|
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
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();
|
|
1443
2234
|
}
|
|
2235
|
+
};
|
|
1444
2236
|
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
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),
|
|
1450
2253
|
});
|
|
1451
2254
|
}
|
|
1452
|
-
|
|
1453
|
-
});
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
|
-
setupStaticWatchFeature() {
|
|
1457
|
-
this.options.static.forEach((staticOption) => {
|
|
1458
|
-
if (staticOption.watch) {
|
|
1459
|
-
this.watchFiles(staticOption.directory, staticOption.watch);
|
|
1460
|
-
}
|
|
1461
|
-
});
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
setupOnBeforeSetupMiddlewareFeature() {
|
|
1465
|
-
this.options.onBeforeSetupMiddleware(this);
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
setupWatchFiles() {
|
|
1469
|
-
const { watchFiles } = this.options;
|
|
2255
|
+
);
|
|
1470
2256
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
2257
|
+
middlewares.push({
|
|
2258
|
+
name: "webpack-dev-middleware",
|
|
2259
|
+
middleware:
|
|
2260
|
+
/** @type {import("webpack-dev-middleware").Middleware<Request, Response>}*/
|
|
2261
|
+
(this.middleware),
|
|
1474
2262
|
});
|
|
1475
2263
|
}
|
|
1476
|
-
}
|
|
1477
2264
|
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
setupFeatures() {
|
|
1495
|
-
const features = {
|
|
1496
|
-
compress: () => {
|
|
1497
|
-
if (this.options.compress) {
|
|
1498
|
-
this.setupCompressFeature();
|
|
1499
|
-
}
|
|
1500
|
-
},
|
|
1501
|
-
proxy: () => {
|
|
1502
|
-
if (this.options.proxy) {
|
|
1503
|
-
this.setupProxyFeature();
|
|
1504
|
-
}
|
|
1505
|
-
},
|
|
1506
|
-
historyApiFallback: () => {
|
|
1507
|
-
if (this.options.historyApiFallback) {
|
|
1508
|
-
this.setupHistoryApiFallbackFeature();
|
|
1509
|
-
}
|
|
1510
|
-
},
|
|
1511
|
-
static: () => {
|
|
1512
|
-
this.setupStaticFeature();
|
|
1513
|
-
},
|
|
1514
|
-
staticServeIndex: () => {
|
|
1515
|
-
this.setupStaticServeIndexFeature();
|
|
1516
|
-
},
|
|
1517
|
-
staticWatch: () => {
|
|
1518
|
-
this.setupStaticWatchFeature();
|
|
1519
|
-
},
|
|
1520
|
-
onBeforeSetupMiddleware: () => {
|
|
1521
|
-
if (typeof this.options.onBeforeSetupMiddleware === "function") {
|
|
1522
|
-
this.setupOnBeforeSetupMiddlewareFeature();
|
|
1523
|
-
}
|
|
1524
|
-
},
|
|
1525
|
-
onAfterSetupMiddleware: () => {
|
|
1526
|
-
if (typeof this.options.onAfterSetupMiddleware === "function") {
|
|
1527
|
-
this.setupOnAfterSetupMiddlewareFeature();
|
|
1528
|
-
}
|
|
1529
|
-
},
|
|
1530
|
-
middleware: () => {
|
|
1531
|
-
// include our middleware to ensure
|
|
1532
|
-
// it is able to handle '/index.html' request after redirect
|
|
1533
|
-
this.setupMiddleware();
|
|
1534
|
-
},
|
|
1535
|
-
headers: () => {
|
|
1536
|
-
this.setupHeadersFeature();
|
|
1537
|
-
},
|
|
1538
|
-
magicHtml: () => {
|
|
1539
|
-
this.setupMagicHtmlFeature();
|
|
1540
|
-
},
|
|
1541
|
-
};
|
|
1542
|
-
|
|
1543
|
-
const runnableFeatures = [];
|
|
1544
|
-
|
|
1545
|
-
// compress is placed last and uses unshift so that it will be the first middleware used
|
|
1546
|
-
if (this.options.compress) {
|
|
1547
|
-
runnableFeatures.push("compress");
|
|
1548
|
-
}
|
|
1549
|
-
|
|
1550
|
-
if (this.options.onBeforeSetupMiddleware) {
|
|
1551
|
-
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
|
+
});
|
|
1552
2279
|
}
|
|
1553
2280
|
|
|
1554
|
-
|
|
2281
|
+
if (this.options.historyApiFallback) {
|
|
2282
|
+
const connectHistoryApiFallback = require("connect-history-api-fallback");
|
|
2283
|
+
const { historyApiFallback } = this.options;
|
|
1555
2284
|
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
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
|
+
}
|
|
1559
2301
|
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
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
|
+
});
|
|
1563
2310
|
|
|
1564
|
-
|
|
1565
|
-
|
|
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
|
+
});
|
|
1566
2319
|
|
|
1567
|
-
if (this.options.static) {
|
|
1568
|
-
|
|
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
|
+
});
|
|
1569
2334
|
}
|
|
1570
2335
|
}
|
|
1571
2336
|
|
|
1572
|
-
if (this.options.static) {
|
|
1573
|
-
|
|
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
|
+
});
|
|
1574
2369
|
}
|
|
1575
2370
|
|
|
1576
2371
|
if (this.options.magicHtml) {
|
|
1577
|
-
|
|
2372
|
+
middlewares.push({
|
|
2373
|
+
name: "serve-magic-html",
|
|
2374
|
+
middleware: this.serveMagicHtml.bind(this),
|
|
2375
|
+
});
|
|
1578
2376
|
}
|
|
1579
2377
|
|
|
1580
|
-
if (this.options.onAfterSetupMiddleware) {
|
|
1581
|
-
|
|
2378
|
+
if (typeof this.options.onAfterSetupMiddleware === "function") {
|
|
2379
|
+
this.options.onAfterSetupMiddleware(this);
|
|
1582
2380
|
}
|
|
1583
2381
|
|
|
1584
|
-
|
|
1585
|
-
|
|
2382
|
+
if (typeof this.options.setupMiddlewares === "function") {
|
|
2383
|
+
middlewares = this.options.setupMiddlewares(middlewares, this);
|
|
2384
|
+
}
|
|
2385
|
+
|
|
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
|
+
}
|
|
1586
2397
|
});
|
|
1587
2398
|
}
|
|
1588
2399
|
|
|
2400
|
+
/**
|
|
2401
|
+
* @private
|
|
2402
|
+
* @returns {void}
|
|
2403
|
+
*/
|
|
1589
2404
|
createServer() {
|
|
2405
|
+
const { type, options } = /** @type {ServerConfiguration} */ (
|
|
2406
|
+
this.options.server
|
|
2407
|
+
);
|
|
2408
|
+
|
|
2409
|
+
/** @type {import("http").Server | undefined | null} */
|
|
1590
2410
|
// eslint-disable-next-line import/no-dynamic-require
|
|
1591
|
-
this.server = require(
|
|
1592
|
-
|
|
2411
|
+
this.server = require(/** @type {string} */ (type)).createServer(
|
|
2412
|
+
options,
|
|
1593
2413
|
this.app
|
|
1594
2414
|
);
|
|
1595
2415
|
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
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);
|
|
1599
2425
|
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
2426
|
+
socket.once("close", () => {
|
|
2427
|
+
// Remove socket from list
|
|
2428
|
+
this.sockets.splice(this.sockets.indexOf(socket), 1);
|
|
2429
|
+
});
|
|
2430
|
+
}
|
|
2431
|
+
);
|
|
1605
2432
|
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
2433
|
+
/** @type {import("http").Server} */
|
|
2434
|
+
(this.server).on(
|
|
2435
|
+
"error",
|
|
2436
|
+
/**
|
|
2437
|
+
* @param {Error} error
|
|
2438
|
+
*/
|
|
2439
|
+
(error) => {
|
|
2440
|
+
throw error;
|
|
2441
|
+
}
|
|
2442
|
+
);
|
|
1609
2443
|
}
|
|
1610
2444
|
|
|
2445
|
+
/**
|
|
2446
|
+
* @private
|
|
2447
|
+
* @returns {void}
|
|
2448
|
+
*/
|
|
2449
|
+
// TODO: remove `--web-socket-server` in favor of `--web-socket-server-type`
|
|
1611
2450
|
createWebSocketServer() {
|
|
1612
|
-
|
|
1613
|
-
this.webSocketServer
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
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
|
+
}
|
|
1628
2481
|
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
2482
|
+
if (
|
|
2483
|
+
!headers ||
|
|
2484
|
+
!this.checkHeader(headers, "host") ||
|
|
2485
|
+
!this.checkHeader(headers, "origin")
|
|
2486
|
+
) {
|
|
2487
|
+
this.sendMessage([client], "error", "Invalid Host/Origin header");
|
|
1635
2488
|
|
|
1636
|
-
|
|
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();
|
|
1637
2492
|
|
|
1638
|
-
|
|
1639
|
-
|
|
2493
|
+
return;
|
|
2494
|
+
}
|
|
1640
2495
|
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
2496
|
+
if (this.options.hot === true || this.options.hot === "only") {
|
|
2497
|
+
this.sendMessage([client], "hot");
|
|
2498
|
+
}
|
|
1644
2499
|
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
2500
|
+
if (this.options.liveReload) {
|
|
2501
|
+
this.sendMessage([client], "liveReload");
|
|
2502
|
+
}
|
|
1648
2503
|
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
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
|
+
}
|
|
1652
2516
|
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
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
|
+
}
|
|
1656
2528
|
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
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
|
+
}
|
|
1660
2541
|
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
2542
|
+
if (!this.stats) {
|
|
2543
|
+
return;
|
|
2544
|
+
}
|
|
1664
2545
|
|
|
1665
|
-
|
|
1666
|
-
|
|
2546
|
+
this.sendStats([client], this.getStats(this.stats), true);
|
|
2547
|
+
}
|
|
2548
|
+
);
|
|
1667
2549
|
}
|
|
1668
2550
|
|
|
2551
|
+
/**
|
|
2552
|
+
* @private
|
|
2553
|
+
* @param {string} defaultOpenTarget
|
|
2554
|
+
* @returns {void}
|
|
2555
|
+
*/
|
|
1669
2556
|
openBrowser(defaultOpenTarget) {
|
|
1670
2557
|
const open = require("open");
|
|
1671
2558
|
|
|
1672
2559
|
Promise.all(
|
|
1673
|
-
|
|
2560
|
+
/** @type {NormalizedOpen[]} */
|
|
2561
|
+
(this.options.open).map((item) => {
|
|
2562
|
+
/**
|
|
2563
|
+
* @type {string}
|
|
2564
|
+
*/
|
|
1674
2565
|
let openTarget;
|
|
1675
2566
|
|
|
1676
2567
|
if (item.target === "<url>") {
|
|
@@ -1684,13 +2575,17 @@ class Server {
|
|
|
1684
2575
|
return open(openTarget, item.options).catch(() => {
|
|
1685
2576
|
this.logger.warn(
|
|
1686
2577
|
`Unable to open "${openTarget}" page${
|
|
1687
|
-
// eslint-disable-next-line no-nested-ternary
|
|
1688
2578
|
item.options.app
|
|
1689
|
-
? ` in "${
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
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`
|
|
1694
2589
|
: ""
|
|
1695
2590
|
}`
|
|
1696
2591
|
: ""
|
|
@@ -1701,35 +2596,68 @@ class Server {
|
|
|
1701
2596
|
);
|
|
1702
2597
|
}
|
|
1703
2598
|
|
|
2599
|
+
/**
|
|
2600
|
+
* @private
|
|
2601
|
+
* @returns {void}
|
|
2602
|
+
*/
|
|
1704
2603
|
runBonjour() {
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
2604
|
+
/**
|
|
2605
|
+
* @private
|
|
2606
|
+
* @type {import("bonjour").Bonjour | undefined}
|
|
2607
|
+
*/
|
|
2608
|
+
this.bonjour = require("bonjour")();
|
|
2609
|
+
this.bonjour.publish({
|
|
1708
2610
|
name: `Webpack Dev Server ${os.hostname()}:${this.options.port}`,
|
|
1709
|
-
port: this.options.port,
|
|
1710
|
-
type:
|
|
2611
|
+
port: /** @type {number} */ (this.options.port),
|
|
2612
|
+
type:
|
|
2613
|
+
/** @type {ServerConfiguration} */
|
|
2614
|
+
(this.options.server).type === "http" ? "http" : "https",
|
|
1711
2615
|
subtypes: ["webpack"],
|
|
1712
|
-
|
|
2616
|
+
.../** @type {BonjourOptions} */ (this.options.bonjour),
|
|
1713
2617
|
});
|
|
2618
|
+
}
|
|
1714
2619
|
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
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
|
+
}
|
|
1719
2633
|
});
|
|
1720
2634
|
}
|
|
1721
2635
|
|
|
2636
|
+
/**
|
|
2637
|
+
* @private
|
|
2638
|
+
* @returns {void}
|
|
2639
|
+
*/
|
|
1722
2640
|
logStatus() {
|
|
1723
2641
|
const { isColorSupported, cyan, red } = require("colorette");
|
|
1724
2642
|
|
|
2643
|
+
/**
|
|
2644
|
+
* @param {Compiler["options"]} compilerOptions
|
|
2645
|
+
* @returns {boolean}
|
|
2646
|
+
*/
|
|
1725
2647
|
const getColorsOption = (compilerOptions) => {
|
|
2648
|
+
/**
|
|
2649
|
+
* @type {boolean}
|
|
2650
|
+
*/
|
|
1726
2651
|
let colorsEnabled;
|
|
1727
2652
|
|
|
1728
2653
|
if (
|
|
1729
2654
|
compilerOptions.stats &&
|
|
1730
|
-
typeof compilerOptions.stats.colors !==
|
|
2655
|
+
typeof (/** @type {StatsOptions} */ (compilerOptions.stats).colors) !==
|
|
2656
|
+
"undefined"
|
|
1731
2657
|
) {
|
|
1732
|
-
colorsEnabled =
|
|
2658
|
+
colorsEnabled =
|
|
2659
|
+
/** @type {boolean} */
|
|
2660
|
+
(/** @type {StatsOptions} */ (compilerOptions.stats).colors);
|
|
1733
2661
|
} else {
|
|
1734
2662
|
colorsEnabled = isColorSupported;
|
|
1735
2663
|
}
|
|
@@ -1738,6 +2666,11 @@ class Server {
|
|
|
1738
2666
|
};
|
|
1739
2667
|
|
|
1740
2668
|
const colors = {
|
|
2669
|
+
/**
|
|
2670
|
+
* @param {boolean} useColor
|
|
2671
|
+
* @param {string} msg
|
|
2672
|
+
* @returns {string}
|
|
2673
|
+
*/
|
|
1741
2674
|
info(useColor, msg) {
|
|
1742
2675
|
if (useColor) {
|
|
1743
2676
|
return cyan(msg);
|
|
@@ -1745,6 +2678,11 @@ class Server {
|
|
|
1745
2678
|
|
|
1746
2679
|
return msg;
|
|
1747
2680
|
},
|
|
2681
|
+
/**
|
|
2682
|
+
* @param {boolean} useColor
|
|
2683
|
+
* @param {string} msg
|
|
2684
|
+
* @returns {string}
|
|
2685
|
+
*/
|
|
1748
2686
|
error(useColor, msg) {
|
|
1749
2687
|
if (useColor) {
|
|
1750
2688
|
return red(msg);
|
|
@@ -1756,10 +2694,26 @@ class Server {
|
|
|
1756
2694
|
const useColor = getColorsOption(this.getCompilerOptions());
|
|
1757
2695
|
|
|
1758
2696
|
if (this.options.ipc) {
|
|
1759
|
-
this.logger.info(
|
|
2697
|
+
this.logger.info(
|
|
2698
|
+
`Project is running at: "${
|
|
2699
|
+
/** @type {import("http").Server} */
|
|
2700
|
+
(this.server).address()
|
|
2701
|
+
}"`
|
|
2702
|
+
);
|
|
1760
2703
|
} else {
|
|
1761
|
-
const protocol =
|
|
1762
|
-
|
|
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
|
+
*/
|
|
1763
2717
|
const prettyPrintURL = (newHostname) =>
|
|
1764
2718
|
url.format({ protocol, hostname: newHostname, port, pathname: "/" });
|
|
1765
2719
|
|
|
@@ -1793,13 +2747,13 @@ class Server {
|
|
|
1793
2747
|
if (parsedIP.range() === "unspecified") {
|
|
1794
2748
|
localhost = prettyPrintURL("localhost");
|
|
1795
2749
|
|
|
1796
|
-
const networkIPv4 =
|
|
2750
|
+
const networkIPv4 = Server.internalIPSync("v4");
|
|
1797
2751
|
|
|
1798
2752
|
if (networkIPv4) {
|
|
1799
2753
|
networkUrlIPv4 = prettyPrintURL(networkIPv4);
|
|
1800
2754
|
}
|
|
1801
2755
|
|
|
1802
|
-
const networkIPv6 =
|
|
2756
|
+
const networkIPv6 = Server.internalIPSync("v6");
|
|
1803
2757
|
|
|
1804
2758
|
if (networkIPv6) {
|
|
1805
2759
|
networkUrlIPv6 = prettyPrintURL(networkIPv6);
|
|
@@ -1812,8 +2766,13 @@ class Server {
|
|
|
1812
2766
|
}
|
|
1813
2767
|
} else {
|
|
1814
2768
|
networkUrlIPv4 =
|
|
1815
|
-
parsedIP.kind() === "ipv6" &&
|
|
1816
|
-
|
|
2769
|
+
parsedIP.kind() === "ipv6" &&
|
|
2770
|
+
/** @type {IPv6} */
|
|
2771
|
+
(parsedIP).isIPv4MappedAddress()
|
|
2772
|
+
? prettyPrintURL(
|
|
2773
|
+
/** @type {IPv6} */
|
|
2774
|
+
(parsedIP).toIPv4Address().toString()
|
|
2775
|
+
)
|
|
1817
2776
|
: prettyPrintURL(address);
|
|
1818
2777
|
|
|
1819
2778
|
if (parsedIP.kind() === "ipv6") {
|
|
@@ -1828,10 +2787,19 @@ class Server {
|
|
|
1828
2787
|
}
|
|
1829
2788
|
|
|
1830
2789
|
if (localhost || loopbackIPv4 || loopbackIPv6) {
|
|
1831
|
-
const loopbacks = []
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
.
|
|
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
|
+
}
|
|
1835
2803
|
|
|
1836
2804
|
this.logger.info(`Loopback: ${loopbacks.join(", ")}`);
|
|
1837
2805
|
}
|
|
@@ -1848,18 +2816,19 @@ class Server {
|
|
|
1848
2816
|
);
|
|
1849
2817
|
}
|
|
1850
2818
|
|
|
1851
|
-
if (this.options.open.length > 0) {
|
|
2819
|
+
if (/** @type {NormalizedOpen[]} */ (this.options.open).length > 0) {
|
|
1852
2820
|
const openTarget = prettyPrintURL(this.options.host || "localhost");
|
|
1853
2821
|
|
|
1854
2822
|
this.openBrowser(openTarget);
|
|
1855
2823
|
}
|
|
1856
2824
|
}
|
|
1857
2825
|
|
|
1858
|
-
if (
|
|
2826
|
+
if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) {
|
|
1859
2827
|
this.logger.info(
|
|
1860
2828
|
`Content not from webpack is served from '${colors.info(
|
|
1861
2829
|
useColor,
|
|
1862
|
-
|
|
2830
|
+
/** @type {NormalizedStatic[]} */
|
|
2831
|
+
(this.options.static)
|
|
1863
2832
|
.map((staticOption) => staticOption.directory)
|
|
1864
2833
|
.join(", ")
|
|
1865
2834
|
)}' directory`
|
|
@@ -1870,14 +2839,19 @@ class Server {
|
|
|
1870
2839
|
this.logger.info(
|
|
1871
2840
|
`404s will fallback to '${colors.info(
|
|
1872
2841
|
useColor,
|
|
1873
|
-
|
|
2842
|
+
/** @type {ConnectHistoryApiFallbackOptions} */ (
|
|
2843
|
+
this.options.historyApiFallback
|
|
2844
|
+
).index || "/index.html"
|
|
1874
2845
|
)}'`
|
|
1875
2846
|
);
|
|
1876
2847
|
}
|
|
1877
2848
|
|
|
1878
2849
|
if (this.options.bonjour) {
|
|
1879
2850
|
const bonjourProtocol =
|
|
1880
|
-
|
|
2851
|
+
/** @type {BonjourOptions} */
|
|
2852
|
+
(this.options.bonjour).type ||
|
|
2853
|
+
/** @type {ServerConfiguration} */
|
|
2854
|
+
(this.options.server).type === "http"
|
|
1881
2855
|
? "http"
|
|
1882
2856
|
: "https";
|
|
1883
2857
|
|
|
@@ -1887,32 +2861,59 @@ class Server {
|
|
|
1887
2861
|
}
|
|
1888
2862
|
}
|
|
1889
2863
|
|
|
2864
|
+
/**
|
|
2865
|
+
* @private
|
|
2866
|
+
* @param {Request} req
|
|
2867
|
+
* @param {Response} res
|
|
2868
|
+
* @param {NextFunction} next
|
|
2869
|
+
*/
|
|
1890
2870
|
setHeaders(req, res, next) {
|
|
1891
2871
|
let { headers } = this.options;
|
|
1892
2872
|
|
|
1893
2873
|
if (headers) {
|
|
1894
2874
|
if (typeof headers === "function") {
|
|
1895
|
-
headers = headers(
|
|
2875
|
+
headers = headers(
|
|
2876
|
+
req,
|
|
2877
|
+
res,
|
|
2878
|
+
/** @type {import("webpack-dev-middleware").API<Request, Response>}*/
|
|
2879
|
+
(this.middleware).context
|
|
2880
|
+
);
|
|
1896
2881
|
}
|
|
1897
2882
|
|
|
2883
|
+
/**
|
|
2884
|
+
* @type {{key: string, value: string}[]}
|
|
2885
|
+
*/
|
|
1898
2886
|
const allHeaders = [];
|
|
1899
2887
|
|
|
1900
2888
|
if (!Array.isArray(headers)) {
|
|
1901
2889
|
// eslint-disable-next-line guard-for-in
|
|
1902
2890
|
for (const name in headers) {
|
|
2891
|
+
// @ts-ignore
|
|
1903
2892
|
allHeaders.push({ key: name, value: headers[name] });
|
|
1904
2893
|
}
|
|
2894
|
+
|
|
1905
2895
|
headers = allHeaders;
|
|
1906
2896
|
}
|
|
1907
2897
|
|
|
1908
|
-
headers.forEach(
|
|
1909
|
-
|
|
1910
|
-
|
|
2898
|
+
headers.forEach(
|
|
2899
|
+
/**
|
|
2900
|
+
* @param {{key: string, value: any}} header
|
|
2901
|
+
*/
|
|
2902
|
+
(header) => {
|
|
2903
|
+
res.setHeader(header.key, header.value);
|
|
2904
|
+
}
|
|
2905
|
+
);
|
|
1911
2906
|
}
|
|
1912
2907
|
|
|
1913
2908
|
next();
|
|
1914
2909
|
}
|
|
1915
2910
|
|
|
2911
|
+
/**
|
|
2912
|
+
* @private
|
|
2913
|
+
* @param {{ [key: string]: string | undefined }} headers
|
|
2914
|
+
* @param {string} headerToCheck
|
|
2915
|
+
* @returns {boolean}
|
|
2916
|
+
*/
|
|
1916
2917
|
checkHeader(headers, headerToCheck) {
|
|
1917
2918
|
// allow user to opt out of this security check, at their own risk
|
|
1918
2919
|
// by explicitly enabling allowedHosts
|
|
@@ -1949,8 +2950,8 @@ class Server {
|
|
|
1949
2950
|
// always allow localhost host, for convenience (hostname === 'localhost')
|
|
1950
2951
|
// allow hostname of listening address (hostname === this.options.host)
|
|
1951
2952
|
const isValidHostname =
|
|
1952
|
-
ipaddr.IPv4.isValid(hostname) ||
|
|
1953
|
-
ipaddr.IPv6.isValid(hostname) ||
|
|
2953
|
+
(hostname !== null && ipaddr.IPv4.isValid(hostname)) ||
|
|
2954
|
+
(hostname !== null && ipaddr.IPv6.isValid(hostname)) ||
|
|
1954
2955
|
hostname === "localhost" ||
|
|
1955
2956
|
hostname === this.options.host;
|
|
1956
2957
|
|
|
@@ -1977,7 +2978,7 @@ class Server {
|
|
|
1977
2978
|
// "*.example.com" (hostname.endsWith(allowedHost))
|
|
1978
2979
|
if (
|
|
1979
2980
|
hostname === allowedHost.substring(1) ||
|
|
1980
|
-
hostname.endsWith(allowedHost)
|
|
2981
|
+
/** @type {string} */ (hostname).endsWith(allowedHost)
|
|
1981
2982
|
) {
|
|
1982
2983
|
return true;
|
|
1983
2984
|
}
|
|
@@ -1988,41 +2989,73 @@ class Server {
|
|
|
1988
2989
|
// Also allow if `client.webSocketURL.hostname` provided
|
|
1989
2990
|
if (
|
|
1990
2991
|
this.options.client &&
|
|
1991
|
-
typeof
|
|
2992
|
+
typeof (
|
|
2993
|
+
/** @type {ClientConfiguration} */ (this.options.client).webSocketURL
|
|
2994
|
+
) !== "undefined"
|
|
1992
2995
|
) {
|
|
1993
|
-
return
|
|
2996
|
+
return (
|
|
2997
|
+
/** @type {WebSocketURL} */
|
|
2998
|
+
(/** @type {ClientConfiguration} */ (this.options.client).webSocketURL)
|
|
2999
|
+
.hostname === hostname
|
|
3000
|
+
);
|
|
1994
3001
|
}
|
|
1995
3002
|
|
|
1996
3003
|
// disallow
|
|
1997
3004
|
return false;
|
|
1998
3005
|
}
|
|
1999
3006
|
|
|
3007
|
+
/**
|
|
3008
|
+
* @param {ClientConnection[]} clients
|
|
3009
|
+
* @param {string} type
|
|
3010
|
+
* @param {any} [data]
|
|
3011
|
+
* @param {any} [params]
|
|
3012
|
+
*/
|
|
2000
3013
|
// eslint-disable-next-line class-methods-use-this
|
|
2001
|
-
sendMessage(clients, type, data) {
|
|
3014
|
+
sendMessage(clients, type, data, params) {
|
|
2002
3015
|
for (const client of clients) {
|
|
2003
3016
|
// `sockjs` uses `1` to indicate client is ready to accept data
|
|
2004
3017
|
// `ws` uses `WebSocket.OPEN`, but it is mean `1` too
|
|
2005
3018
|
if (client.readyState === 1) {
|
|
2006
|
-
client.send(JSON.stringify({ type, data }));
|
|
3019
|
+
client.send(JSON.stringify({ type, data, params }));
|
|
2007
3020
|
}
|
|
2008
3021
|
}
|
|
2009
3022
|
}
|
|
2010
3023
|
|
|
3024
|
+
/**
|
|
3025
|
+
* @private
|
|
3026
|
+
* @param {Request} req
|
|
3027
|
+
* @param {Response} res
|
|
3028
|
+
* @param {NextFunction} next
|
|
3029
|
+
* @returns {void}
|
|
3030
|
+
*/
|
|
2011
3031
|
serveMagicHtml(req, res, next) {
|
|
2012
|
-
|
|
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(() => {
|
|
2013
3038
|
const _path = req.path;
|
|
2014
3039
|
|
|
2015
3040
|
try {
|
|
2016
|
-
const filename =
|
|
2017
|
-
|
|
2018
|
-
.
|
|
2019
|
-
|
|
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();
|
|
2020
3052
|
|
|
2021
3053
|
if (!isFile) {
|
|
2022
3054
|
return next();
|
|
2023
3055
|
}
|
|
2024
3056
|
|
|
2025
3057
|
// Serve a page that executes the javascript
|
|
3058
|
+
// @ts-ignore
|
|
2026
3059
|
const queries = req._parsedUrl.search || "";
|
|
2027
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>`;
|
|
2028
3061
|
|
|
@@ -2034,6 +3067,12 @@ class Server {
|
|
|
2034
3067
|
}
|
|
2035
3068
|
|
|
2036
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
|
+
*/
|
|
2037
3076
|
sendStats(clients, stats, force) {
|
|
2038
3077
|
const shouldEmit =
|
|
2039
3078
|
!force &&
|
|
@@ -2051,12 +3090,33 @@ class Server {
|
|
|
2051
3090
|
this.currentHash = stats.hash;
|
|
2052
3091
|
this.sendMessage(clients, "hash", stats.hash);
|
|
2053
3092
|
|
|
2054
|
-
if (
|
|
2055
|
-
|
|
2056
|
-
|
|
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);
|
|
2057
3114
|
}
|
|
2058
3115
|
|
|
2059
|
-
if (
|
|
3116
|
+
if (
|
|
3117
|
+
/** @type {NonNullable<StatsCompilation["errors"]>} */ (stats.errors)
|
|
3118
|
+
.length > 0
|
|
3119
|
+
) {
|
|
2060
3120
|
this.sendMessage(clients, "errors", stats.errors);
|
|
2061
3121
|
}
|
|
2062
3122
|
} else {
|
|
@@ -2064,38 +3124,13 @@ class Server {
|
|
|
2064
3124
|
}
|
|
2065
3125
|
}
|
|
2066
3126
|
|
|
3127
|
+
/**
|
|
3128
|
+
* @param {string | string[]} watchPath
|
|
3129
|
+
* @param {WatchOptions} [watchOptions]
|
|
3130
|
+
*/
|
|
2067
3131
|
watchFiles(watchPath, watchOptions) {
|
|
2068
|
-
// duplicate the same massaging of options that watchpack performs
|
|
2069
|
-
// https://github.com/webpack/watchpack/blob/master/lib/DirectoryWatcher.js#L49
|
|
2070
|
-
// this isn't an elegant solution, but we'll improve it in the future
|
|
2071
|
-
// eslint-disable-next-line no-undefined
|
|
2072
|
-
const usePolling =
|
|
2073
|
-
typeof watchOptions.usePolling !== "undefined"
|
|
2074
|
-
? watchOptions.usePolling
|
|
2075
|
-
: Boolean(watchOptions.poll);
|
|
2076
|
-
const interval =
|
|
2077
|
-
// eslint-disable-next-line no-nested-ternary
|
|
2078
|
-
typeof watchOptions.interval !== "undefined"
|
|
2079
|
-
? watchOptions.interval
|
|
2080
|
-
: typeof watchOptions.poll === "number"
|
|
2081
|
-
? watchOptions.poll
|
|
2082
|
-
: // eslint-disable-next-line no-undefined
|
|
2083
|
-
undefined;
|
|
2084
|
-
|
|
2085
|
-
const finalWatchOptions = {
|
|
2086
|
-
ignoreInitial: true,
|
|
2087
|
-
persistent: true,
|
|
2088
|
-
followSymlinks: false,
|
|
2089
|
-
atomic: false,
|
|
2090
|
-
alwaysStat: true,
|
|
2091
|
-
ignorePermissionErrors: true,
|
|
2092
|
-
ignored: watchOptions.ignored,
|
|
2093
|
-
usePolling,
|
|
2094
|
-
interval,
|
|
2095
|
-
};
|
|
2096
|
-
|
|
2097
3132
|
const chokidar = require("chokidar");
|
|
2098
|
-
const watcher = chokidar.watch(watchPath,
|
|
3133
|
+
const watcher = chokidar.watch(watchPath, watchOptions);
|
|
2099
3134
|
|
|
2100
3135
|
// disabling refreshing on changing the content
|
|
2101
3136
|
if (this.options.liveReload) {
|
|
@@ -2113,44 +3148,65 @@ class Server {
|
|
|
2113
3148
|
this.staticWatchers.push(watcher);
|
|
2114
3149
|
}
|
|
2115
3150
|
|
|
2116
|
-
|
|
3151
|
+
/**
|
|
3152
|
+
* @param {import("webpack-dev-middleware").Callback} [callback]
|
|
3153
|
+
*/
|
|
3154
|
+
invalidate(callback = () => {}) {
|
|
2117
3155
|
if (this.middleware) {
|
|
2118
3156
|
this.middleware.invalidate(callback);
|
|
2119
3157
|
}
|
|
2120
3158
|
}
|
|
2121
3159
|
|
|
3160
|
+
/**
|
|
3161
|
+
* @returns {Promise<void>}
|
|
3162
|
+
*/
|
|
2122
3163
|
async start() {
|
|
2123
3164
|
await this.normalizeOptions();
|
|
2124
3165
|
|
|
2125
3166
|
if (this.options.ipc) {
|
|
2126
|
-
await
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
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
|
+
}
|
|
2143
3190
|
|
|
2144
|
-
|
|
2145
|
-
|
|
3191
|
+
reject(error);
|
|
3192
|
+
}
|
|
3193
|
+
);
|
|
2146
3194
|
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
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
|
+
);
|
|
2151
3203
|
} else {
|
|
2152
|
-
this.options.host = await Server.getHostname(
|
|
2153
|
-
|
|
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
|
+
);
|
|
2154
3210
|
}
|
|
2155
3211
|
|
|
2156
3212
|
await this.initialize();
|
|
@@ -2159,17 +3215,23 @@ class Server {
|
|
|
2159
3215
|
? { path: this.options.ipc }
|
|
2160
3216
|
: { host: this.options.host, port: this.options.port };
|
|
2161
3217
|
|
|
2162
|
-
await
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
3218
|
+
await /** @type {Promise<void>} */ (
|
|
3219
|
+
new Promise((resolve) => {
|
|
3220
|
+
/** @type {import("http").Server} */
|
|
3221
|
+
(this.server).listen(listenOptions, () => {
|
|
3222
|
+
resolve();
|
|
3223
|
+
});
|
|
3224
|
+
})
|
|
3225
|
+
);
|
|
2167
3226
|
|
|
2168
3227
|
if (this.options.ipc) {
|
|
2169
3228
|
// chmod 666 (rw rw rw)
|
|
2170
3229
|
const READ_WRITE = 438;
|
|
2171
3230
|
|
|
2172
|
-
await fs.promises.chmod(
|
|
3231
|
+
await fs.promises.chmod(
|
|
3232
|
+
/** @type {string} */ (this.options.ipc),
|
|
3233
|
+
READ_WRITE
|
|
3234
|
+
);
|
|
2173
3235
|
}
|
|
2174
3236
|
|
|
2175
3237
|
if (this.options.webSocketServer) {
|
|
@@ -2187,13 +3249,29 @@ class Server {
|
|
|
2187
3249
|
}
|
|
2188
3250
|
}
|
|
2189
3251
|
|
|
2190
|
-
|
|
3252
|
+
/**
|
|
3253
|
+
* @param {(err?: Error) => void} [callback]
|
|
3254
|
+
*/
|
|
3255
|
+
startCallback(callback = () => {}) {
|
|
2191
3256
|
this.start()
|
|
2192
|
-
.then(() => callback(
|
|
3257
|
+
.then(() => callback(), callback)
|
|
2193
3258
|
.catch(callback);
|
|
2194
3259
|
}
|
|
2195
3260
|
|
|
3261
|
+
/**
|
|
3262
|
+
* @returns {Promise<void>}
|
|
3263
|
+
*/
|
|
2196
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
|
+
|
|
2197
3275
|
this.webSocketProxies = [];
|
|
2198
3276
|
|
|
2199
3277
|
await Promise.all(this.staticWatchers.map((watcher) => watcher.close()));
|
|
@@ -2201,70 +3279,95 @@ class Server {
|
|
|
2201
3279
|
this.staticWatchers = [];
|
|
2202
3280
|
|
|
2203
3281
|
if (this.webSocketServer) {
|
|
2204
|
-
await
|
|
2205
|
-
|
|
2206
|
-
|
|
3282
|
+
await /** @type {Promise<void>} */ (
|
|
3283
|
+
new Promise((resolve) => {
|
|
3284
|
+
/** @type {WebSocketServerImplementation} */
|
|
3285
|
+
(this.webSocketServer).implementation.close(() => {
|
|
3286
|
+
this.webSocketServer = null;
|
|
2207
3287
|
|
|
2208
|
-
|
|
2209
|
-
|
|
3288
|
+
resolve();
|
|
3289
|
+
});
|
|
2210
3290
|
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
3291
|
+
for (const client of /** @type {WebSocketServerImplementation} */ (
|
|
3292
|
+
this.webSocketServer
|
|
3293
|
+
).clients) {
|
|
3294
|
+
client.terminate();
|
|
3295
|
+
}
|
|
2214
3296
|
|
|
2215
|
-
|
|
2216
|
-
|
|
3297
|
+
/** @type {WebSocketServerImplementation} */
|
|
3298
|
+
(this.webSocketServer).clients = [];
|
|
3299
|
+
})
|
|
3300
|
+
);
|
|
2217
3301
|
}
|
|
2218
3302
|
|
|
2219
3303
|
if (this.server) {
|
|
2220
|
-
await
|
|
2221
|
-
|
|
2222
|
-
|
|
3304
|
+
await /** @type {Promise<void>} */ (
|
|
3305
|
+
new Promise((resolve) => {
|
|
3306
|
+
/** @type {import("http").Server} */
|
|
3307
|
+
(this.server).close(() => {
|
|
3308
|
+
this.server = null;
|
|
2223
3309
|
|
|
2224
|
-
|
|
2225
|
-
|
|
3310
|
+
resolve();
|
|
3311
|
+
});
|
|
2226
3312
|
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
3313
|
+
for (const socket of this.sockets) {
|
|
3314
|
+
socket.destroy();
|
|
3315
|
+
}
|
|
2230
3316
|
|
|
2231
|
-
|
|
2232
|
-
|
|
3317
|
+
this.sockets = [];
|
|
3318
|
+
})
|
|
3319
|
+
);
|
|
2233
3320
|
|
|
2234
3321
|
if (this.middleware) {
|
|
2235
|
-
await
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
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
|
+
);
|
|
2246
3336
|
|
|
2247
3337
|
this.middleware = null;
|
|
2248
3338
|
}
|
|
2249
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
|
+
}
|
|
2250
3346
|
}
|
|
2251
3347
|
|
|
2252
|
-
|
|
3348
|
+
/**
|
|
3349
|
+
* @param {(err?: Error) => void} [callback]
|
|
3350
|
+
*/
|
|
3351
|
+
stopCallback(callback = () => {}) {
|
|
2253
3352
|
this.stop()
|
|
2254
|
-
.then(() => callback(
|
|
3353
|
+
.then(() => callback(), callback)
|
|
2255
3354
|
.catch(callback);
|
|
2256
3355
|
}
|
|
2257
3356
|
|
|
2258
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
|
+
*/
|
|
2259
3364
|
listen(port, hostname, fn) {
|
|
2260
3365
|
util.deprecate(
|
|
2261
3366
|
() => {},
|
|
2262
|
-
"'listen' is deprecated. Please use async 'start' or 'startCallback'
|
|
3367
|
+
"'listen' is deprecated. Please use the async 'start' or 'startCallback' method.",
|
|
2263
3368
|
"DEP_WEBPACK_DEV_SERVER_LISTEN"
|
|
2264
3369
|
)();
|
|
2265
3370
|
|
|
2266
|
-
this.logger = this.compiler.getInfrastructureLogger("webpack-dev-server");
|
|
2267
|
-
|
|
2268
3371
|
if (typeof port === "function") {
|
|
2269
3372
|
fn = port;
|
|
2270
3373
|
}
|
|
@@ -2301,7 +3404,7 @@ class Server {
|
|
|
2301
3404
|
this.options.host = hostname;
|
|
2302
3405
|
}
|
|
2303
3406
|
|
|
2304
|
-
|
|
3407
|
+
this.start()
|
|
2305
3408
|
.then(() => {
|
|
2306
3409
|
if (fn) {
|
|
2307
3410
|
fn.call(this.server);
|
|
@@ -2315,18 +3418,22 @@ class Server {
|
|
|
2315
3418
|
});
|
|
2316
3419
|
}
|
|
2317
3420
|
|
|
3421
|
+
/**
|
|
3422
|
+
* @param {(err?: Error) => void} [callback]
|
|
3423
|
+
* @returns {void}
|
|
3424
|
+
*/
|
|
2318
3425
|
// TODO remove in the next major release
|
|
2319
3426
|
close(callback) {
|
|
2320
3427
|
util.deprecate(
|
|
2321
3428
|
() => {},
|
|
2322
|
-
"'close' is deprecated. Please use async 'stop' or 'stopCallback'
|
|
3429
|
+
"'close' is deprecated. Please use the async 'stop' or 'stopCallback' method.",
|
|
2323
3430
|
"DEP_WEBPACK_DEV_SERVER_CLOSE"
|
|
2324
3431
|
)();
|
|
2325
3432
|
|
|
2326
|
-
|
|
3433
|
+
this.stop()
|
|
2327
3434
|
.then(() => {
|
|
2328
3435
|
if (callback) {
|
|
2329
|
-
callback(
|
|
3436
|
+
callback();
|
|
2330
3437
|
}
|
|
2331
3438
|
})
|
|
2332
3439
|
.catch((error) => {
|
|
@@ -2337,48 +3444,4 @@ class Server {
|
|
|
2337
3444
|
}
|
|
2338
3445
|
}
|
|
2339
3446
|
|
|
2340
|
-
|
|
2341
|
-
const descriptors = Object.getOwnPropertyDescriptors(exports);
|
|
2342
|
-
|
|
2343
|
-
for (const name of Object.keys(descriptors)) {
|
|
2344
|
-
const descriptor = descriptors[name];
|
|
2345
|
-
|
|
2346
|
-
if (descriptor.get) {
|
|
2347
|
-
const fn = descriptor.get;
|
|
2348
|
-
|
|
2349
|
-
Object.defineProperty(obj, name, {
|
|
2350
|
-
configurable: false,
|
|
2351
|
-
enumerable: true,
|
|
2352
|
-
get: fn,
|
|
2353
|
-
});
|
|
2354
|
-
} else if (typeof descriptor.value === "object") {
|
|
2355
|
-
Object.defineProperty(obj, name, {
|
|
2356
|
-
configurable: false,
|
|
2357
|
-
enumerable: true,
|
|
2358
|
-
writable: false,
|
|
2359
|
-
value: mergeExports({}, descriptor.value),
|
|
2360
|
-
});
|
|
2361
|
-
} else {
|
|
2362
|
-
throw new Error(
|
|
2363
|
-
"Exposed values must be either a getter or an nested object"
|
|
2364
|
-
);
|
|
2365
|
-
}
|
|
2366
|
-
}
|
|
2367
|
-
|
|
2368
|
-
return Object.freeze(obj);
|
|
2369
|
-
};
|
|
2370
|
-
|
|
2371
|
-
module.exports = mergeExports(Server, {
|
|
2372
|
-
get schema() {
|
|
2373
|
-
return schema;
|
|
2374
|
-
},
|
|
2375
|
-
// TODO compatibility with webpack v4, remove it after drop
|
|
2376
|
-
cli: {
|
|
2377
|
-
get getArguments() {
|
|
2378
|
-
return () => require("../bin/cli-flags");
|
|
2379
|
-
},
|
|
2380
|
-
get processArguments() {
|
|
2381
|
-
return require("../bin/process-arguments");
|
|
2382
|
-
},
|
|
2383
|
-
},
|
|
2384
|
-
});
|
|
3447
|
+
module.exports = Server;
|