webpack-dev-server 4.0.0 → 4.2.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 +44 -44
- package/bin/cli-flags.js +151 -11
- package/client/index.js +21 -5
- package/client/modules/sockjs-client/index.js +1 -1
- package/client/modules/strip-ansi/index.js +3 -3
- package/client/overlay.js +1 -1
- package/client/utils/reloadApp.js +4 -12
- package/lib/Server.js +480 -132
- package/lib/options.json +140 -14
- package/package.json +3 -3
- package/lib/utils/DevServerPlugin.js +0 -352
package/lib/Server.js
CHANGED
|
@@ -126,6 +126,227 @@ class Server {
|
|
|
126
126
|
return path.resolve(dir, "node_modules/.cache/webpack-dev-server");
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
addAdditionalEntries(compiler) {
|
|
130
|
+
const additionalEntries = [];
|
|
131
|
+
|
|
132
|
+
const isWebTarget = compiler.options.externalsPresets
|
|
133
|
+
? compiler.options.externalsPresets.web
|
|
134
|
+
: [
|
|
135
|
+
"web",
|
|
136
|
+
"webworker",
|
|
137
|
+
"electron-preload",
|
|
138
|
+
"electron-renderer",
|
|
139
|
+
"node-webkit",
|
|
140
|
+
// eslint-disable-next-line no-undefined
|
|
141
|
+
undefined,
|
|
142
|
+
null,
|
|
143
|
+
].includes(compiler.options.target);
|
|
144
|
+
|
|
145
|
+
// TODO maybe empty empty client
|
|
146
|
+
if (this.options.client && isWebTarget) {
|
|
147
|
+
let webSocketURL = "";
|
|
148
|
+
if (this.options.webSocketServer) {
|
|
149
|
+
const searchParams = new URLSearchParams();
|
|
150
|
+
|
|
151
|
+
/** @type {"ws:" | "wss:" | "http:" | "https:" | "auto:"} */
|
|
152
|
+
let protocol;
|
|
153
|
+
|
|
154
|
+
// We are proxying dev server and need to specify custom `hostname`
|
|
155
|
+
if (typeof this.options.client.webSocketURL.protocol !== "undefined") {
|
|
156
|
+
protocol = this.options.client.webSocketURL.protocol;
|
|
157
|
+
} else {
|
|
158
|
+
protocol = this.options.https ? "wss:" : "ws:";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
searchParams.set("protocol", protocol);
|
|
162
|
+
|
|
163
|
+
if (typeof this.options.client.webSocketURL.username !== "undefined") {
|
|
164
|
+
searchParams.set(
|
|
165
|
+
"username",
|
|
166
|
+
this.options.client.webSocketURL.username
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (typeof this.options.client.webSocketURL.password !== "undefined") {
|
|
171
|
+
searchParams.set(
|
|
172
|
+
"password",
|
|
173
|
+
this.options.client.webSocketURL.password
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** @type {string} */
|
|
178
|
+
let hostname;
|
|
179
|
+
|
|
180
|
+
// SockJS is not supported server mode, so `hostname` and `port` can't specified, let's ignore them
|
|
181
|
+
// TODO show warning about this
|
|
182
|
+
const isSockJSType = this.options.webSocketServer.type === "sockjs";
|
|
183
|
+
|
|
184
|
+
// We are proxying dev server and need to specify custom `hostname`
|
|
185
|
+
if (typeof this.options.client.webSocketURL.hostname !== "undefined") {
|
|
186
|
+
hostname = this.options.client.webSocketURL.hostname;
|
|
187
|
+
}
|
|
188
|
+
// Web socket server works on custom `hostname`, only for `ws` because `sock-js` is not support custom `hostname`
|
|
189
|
+
else if (
|
|
190
|
+
typeof this.options.webSocketServer.options.host !== "undefined" &&
|
|
191
|
+
!isSockJSType
|
|
192
|
+
) {
|
|
193
|
+
hostname = this.options.webSocketServer.options.host;
|
|
194
|
+
}
|
|
195
|
+
// The `host` option is specified
|
|
196
|
+
else if (typeof this.options.host !== "undefined") {
|
|
197
|
+
hostname = this.options.host;
|
|
198
|
+
}
|
|
199
|
+
// The `port` option is not specified
|
|
200
|
+
else {
|
|
201
|
+
hostname = "0.0.0.0";
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
searchParams.set("hostname", hostname);
|
|
205
|
+
|
|
206
|
+
/** @type {number | string} */
|
|
207
|
+
let port;
|
|
208
|
+
|
|
209
|
+
// We are proxying dev server and need to specify custom `port`
|
|
210
|
+
if (typeof this.options.client.webSocketURL.port !== "undefined") {
|
|
211
|
+
port = this.options.client.webSocketURL.port;
|
|
212
|
+
}
|
|
213
|
+
// Web socket server works on custom `port`, only for `ws` because `sock-js` is not support custom `port`
|
|
214
|
+
else if (
|
|
215
|
+
typeof this.options.webSocketServer.options.port !== "undefined" &&
|
|
216
|
+
!isSockJSType
|
|
217
|
+
) {
|
|
218
|
+
port = this.options.webSocketServer.options.port;
|
|
219
|
+
}
|
|
220
|
+
// The `port` option is specified
|
|
221
|
+
else if (typeof this.options.port === "number") {
|
|
222
|
+
port = this.options.port;
|
|
223
|
+
}
|
|
224
|
+
// The `port` option is specified using `string`
|
|
225
|
+
else if (
|
|
226
|
+
typeof this.options.port === "string" &&
|
|
227
|
+
this.options.port !== "auto"
|
|
228
|
+
) {
|
|
229
|
+
port = Number(this.options.port);
|
|
230
|
+
}
|
|
231
|
+
// The `port` option is not specified or set to `auto`
|
|
232
|
+
else {
|
|
233
|
+
port = "0";
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
searchParams.set("port", String(port));
|
|
237
|
+
|
|
238
|
+
/** @type {string} */
|
|
239
|
+
let pathname = "";
|
|
240
|
+
|
|
241
|
+
// We are proxying dev server and need to specify custom `pathname`
|
|
242
|
+
if (typeof this.options.client.webSocketURL.pathname !== "undefined") {
|
|
243
|
+
pathname = this.options.client.webSocketURL.pathname;
|
|
244
|
+
}
|
|
245
|
+
// Web socket server works on custom `path`
|
|
246
|
+
else if (
|
|
247
|
+
typeof this.options.webSocketServer.options.prefix !== "undefined" ||
|
|
248
|
+
typeof this.options.webSocketServer.options.path !== "undefined"
|
|
249
|
+
) {
|
|
250
|
+
pathname =
|
|
251
|
+
this.options.webSocketServer.options.prefix ||
|
|
252
|
+
this.options.webSocketServer.options.path;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
searchParams.set("pathname", pathname);
|
|
256
|
+
|
|
257
|
+
if (typeof this.options.client.logging !== "undefined") {
|
|
258
|
+
searchParams.set("logging", this.options.client.logging);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
webSocketURL = searchParams.toString();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
additionalEntries.push(
|
|
265
|
+
`${require.resolve("../client/index.js")}?${webSocketURL}`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (this.options.hot) {
|
|
270
|
+
let hotEntry;
|
|
271
|
+
|
|
272
|
+
if (this.options.hot === "only") {
|
|
273
|
+
hotEntry = require.resolve("webpack/hot/only-dev-server");
|
|
274
|
+
} else if (this.options.hot) {
|
|
275
|
+
hotEntry = require.resolve("webpack/hot/dev-server");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
additionalEntries.push(hotEntry);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const webpack = compiler.webpack || require("webpack");
|
|
282
|
+
|
|
283
|
+
// use a hook to add entries if available
|
|
284
|
+
if (typeof webpack.EntryPlugin !== "undefined") {
|
|
285
|
+
for (const additionalEntry of additionalEntries) {
|
|
286
|
+
new webpack.EntryPlugin(compiler.context, additionalEntry, {
|
|
287
|
+
// eslint-disable-next-line no-undefined
|
|
288
|
+
name: undefined,
|
|
289
|
+
}).apply(compiler);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// TODO remove after drop webpack v4 support
|
|
293
|
+
else {
|
|
294
|
+
/**
|
|
295
|
+
* prependEntry Method for webpack 4
|
|
296
|
+
* @param {Entry} originalEntry
|
|
297
|
+
* @param {Entry} newAdditionalEntries
|
|
298
|
+
* @returns {Entry}
|
|
299
|
+
*/
|
|
300
|
+
const prependEntry = (originalEntry, newAdditionalEntries) => {
|
|
301
|
+
if (typeof originalEntry === "function") {
|
|
302
|
+
return () =>
|
|
303
|
+
Promise.resolve(originalEntry()).then((entry) =>
|
|
304
|
+
prependEntry(entry, newAdditionalEntries)
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (
|
|
309
|
+
typeof originalEntry === "object" &&
|
|
310
|
+
!Array.isArray(originalEntry)
|
|
311
|
+
) {
|
|
312
|
+
/** @type {Object<string,string>} */
|
|
313
|
+
const clone = {};
|
|
314
|
+
|
|
315
|
+
Object.keys(originalEntry).forEach((key) => {
|
|
316
|
+
// entry[key] should be a string here
|
|
317
|
+
const entryDescription = originalEntry[key];
|
|
318
|
+
|
|
319
|
+
clone[key] = prependEntry(entryDescription, newAdditionalEntries);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
return clone;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// in this case, entry is a string or an array.
|
|
326
|
+
// make sure that we do not add duplicates.
|
|
327
|
+
/** @type {Entry} */
|
|
328
|
+
const entriesClone = additionalEntries.slice(0);
|
|
329
|
+
|
|
330
|
+
[].concat(originalEntry).forEach((newEntry) => {
|
|
331
|
+
if (!entriesClone.includes(newEntry)) {
|
|
332
|
+
entriesClone.push(newEntry);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
return entriesClone;
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
compiler.options.entry = prependEntry(
|
|
340
|
+
compiler.options.entry || "./src",
|
|
341
|
+
additionalEntries
|
|
342
|
+
);
|
|
343
|
+
compiler.hooks.entryOption.call(
|
|
344
|
+
compiler.options.context,
|
|
345
|
+
compiler.options.entry
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
129
350
|
getCompilerOptions() {
|
|
130
351
|
if (typeof this.compiler.compilers !== "undefined") {
|
|
131
352
|
if (this.compiler.compilers.length === 1) {
|
|
@@ -190,19 +411,24 @@ class Server {
|
|
|
190
411
|
};
|
|
191
412
|
|
|
192
413
|
if (typeof options.allowedHosts === "undefined") {
|
|
193
|
-
//
|
|
194
|
-
// `options.host` or `webSocketURL.hostname` and `localhost`
|
|
414
|
+
// AllowedHosts allows some default hosts picked from `options.host` or `webSocketURL.hostname` and `localhost`
|
|
195
415
|
options.allowedHosts = "auto";
|
|
196
416
|
}
|
|
197
|
-
|
|
198
|
-
if (
|
|
417
|
+
// We store allowedHosts as array when supplied as string
|
|
418
|
+
else if (
|
|
199
419
|
typeof options.allowedHosts === "string" &&
|
|
200
420
|
options.allowedHosts !== "auto" &&
|
|
201
421
|
options.allowedHosts !== "all"
|
|
202
422
|
) {
|
|
203
|
-
// we store allowedHosts as array when supplied as string
|
|
204
423
|
options.allowedHosts = [options.allowedHosts];
|
|
205
424
|
}
|
|
425
|
+
// CLI pass options as array, we should normalize them
|
|
426
|
+
else if (
|
|
427
|
+
Array.isArray(options.allowedHosts) &&
|
|
428
|
+
options.allowedHosts.includes("all")
|
|
429
|
+
) {
|
|
430
|
+
options.allowedHosts = "all";
|
|
431
|
+
}
|
|
206
432
|
|
|
207
433
|
if (typeof options.bonjour === "undefined") {
|
|
208
434
|
options.bonjour = false;
|
|
@@ -291,24 +517,39 @@ class Server {
|
|
|
291
517
|
|
|
292
518
|
// https option
|
|
293
519
|
if (options.https) {
|
|
294
|
-
|
|
520
|
+
// TODO remove the `cacert` option in favor `ca` in the next major release
|
|
521
|
+
for (const property of ["cacert", "ca", "cert", "crl", "key", "pfx"]) {
|
|
522
|
+
if (typeof options.https[property] === "undefined") {
|
|
523
|
+
// eslint-disable-next-line no-continue
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
|
|
295
527
|
const value = options.https[property];
|
|
296
|
-
const
|
|
528
|
+
const readFile = (item) => {
|
|
529
|
+
if (
|
|
530
|
+
Buffer.isBuffer(item) ||
|
|
531
|
+
(typeof item === "object" && item !== null && !Array.isArray(item))
|
|
532
|
+
) {
|
|
533
|
+
return item;
|
|
534
|
+
}
|
|
297
535
|
|
|
298
|
-
|
|
299
|
-
|
|
536
|
+
if (item) {
|
|
537
|
+
let stats = null;
|
|
300
538
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
539
|
+
try {
|
|
540
|
+
stats = fs.lstatSync(fs.realpathSync(item)).isFile();
|
|
541
|
+
} catch (error) {
|
|
542
|
+
// Ignore error
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// It is file
|
|
546
|
+
return stats ? fs.readFileSync(item) : item;
|
|
305
547
|
}
|
|
548
|
+
};
|
|
306
549
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
: value;
|
|
311
|
-
}
|
|
550
|
+
options.https[property] = Array.isArray(value)
|
|
551
|
+
? value.map((item) => readFile(item))
|
|
552
|
+
: readFile(value);
|
|
312
553
|
}
|
|
313
554
|
|
|
314
555
|
let fakeCert;
|
|
@@ -316,11 +557,18 @@ class Server {
|
|
|
316
557
|
if (!options.https.key || !options.https.cert) {
|
|
317
558
|
const certificateDir = Server.findCacheDir();
|
|
318
559
|
const certificatePath = path.join(certificateDir, "server.pem");
|
|
319
|
-
let certificateExists
|
|
560
|
+
let certificateExists;
|
|
561
|
+
|
|
562
|
+
try {
|
|
563
|
+
const certificate = await fs.promises.stat(certificatePath);
|
|
564
|
+
certificateExists = certificate.isFile();
|
|
565
|
+
} catch {
|
|
566
|
+
certificateExists = false;
|
|
567
|
+
}
|
|
320
568
|
|
|
321
569
|
if (certificateExists) {
|
|
322
570
|
const certificateTtl = 1000 * 60 * 60 * 24;
|
|
323
|
-
const certificateStat = fs.
|
|
571
|
+
const certificateStat = await fs.promises.stat(certificatePath);
|
|
324
572
|
|
|
325
573
|
const now = new Date();
|
|
326
574
|
|
|
@@ -332,7 +580,7 @@ class Server {
|
|
|
332
580
|
"SSL Certificate is more than 30 days old. Removing..."
|
|
333
581
|
);
|
|
334
582
|
|
|
335
|
-
del
|
|
583
|
+
await del([certificatePath], { force: true });
|
|
336
584
|
|
|
337
585
|
certificateExists = false;
|
|
338
586
|
}
|
|
@@ -405,17 +653,34 @@ class Server {
|
|
|
405
653
|
],
|
|
406
654
|
});
|
|
407
655
|
|
|
408
|
-
fs.
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
656
|
+
await fs.promises.mkdir(certificateDir, { recursive: true });
|
|
657
|
+
|
|
658
|
+
await fs.promises.writeFile(
|
|
659
|
+
certificatePath,
|
|
660
|
+
pems.private + pems.cert,
|
|
661
|
+
{
|
|
662
|
+
encoding: "utf8",
|
|
663
|
+
}
|
|
664
|
+
);
|
|
412
665
|
}
|
|
413
666
|
|
|
414
|
-
fakeCert = fs.
|
|
667
|
+
fakeCert = await fs.promises.readFile(certificatePath);
|
|
415
668
|
|
|
416
669
|
this.logger.info(`SSL certificate: ${certificatePath}`);
|
|
417
670
|
}
|
|
418
671
|
|
|
672
|
+
if (options.https.cacert) {
|
|
673
|
+
if (options.https.ca) {
|
|
674
|
+
this.logger.warn(
|
|
675
|
+
"Do not specify 'https.ca' and 'https.cacert' options together, the 'https.ca' option will be used."
|
|
676
|
+
);
|
|
677
|
+
} else {
|
|
678
|
+
options.https.ca = options.https.cacert;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
delete options.https.cacert;
|
|
682
|
+
}
|
|
683
|
+
|
|
419
684
|
options.https.key = options.https.key || fakeCert;
|
|
420
685
|
options.https.cert = options.https.cert || fakeCert;
|
|
421
686
|
}
|
|
@@ -431,6 +696,9 @@ class Server {
|
|
|
431
696
|
options.liveReload =
|
|
432
697
|
typeof options.liveReload !== "undefined" ? options.liveReload : true;
|
|
433
698
|
|
|
699
|
+
options.magicHtml =
|
|
700
|
+
typeof options.magicHtml !== "undefined" ? options.magicHtml : true;
|
|
701
|
+
|
|
434
702
|
// https://github.com/webpack/webpack-dev-server/issues/1990
|
|
435
703
|
const defaultOpenOptions = { wait: false };
|
|
436
704
|
const getOpenItemsFromObject = ({ target, ...rest }) => {
|
|
@@ -667,53 +935,103 @@ class Server {
|
|
|
667
935
|
}
|
|
668
936
|
}
|
|
669
937
|
|
|
670
|
-
|
|
671
|
-
|
|
938
|
+
getClientTransport() {
|
|
939
|
+
let ClientImplementation;
|
|
940
|
+
let clientImplementationFound = true;
|
|
672
941
|
|
|
673
|
-
|
|
674
|
-
this.
|
|
675
|
-
|
|
942
|
+
const isKnownWebSocketServerImplementation =
|
|
943
|
+
this.options.webSocketServer &&
|
|
944
|
+
typeof this.options.webSocketServer.type === "string" &&
|
|
945
|
+
(this.options.webSocketServer.type === "ws" ||
|
|
946
|
+
this.options.webSocketServer.type === "sockjs");
|
|
676
947
|
|
|
677
|
-
|
|
678
|
-
this.setupApp();
|
|
679
|
-
this.setupHostHeaderCheck();
|
|
680
|
-
this.setupDevMiddleware();
|
|
681
|
-
// Should be after `webpack-dev-middleware`, otherwise other middlewares might rewrite response
|
|
682
|
-
this.setupBuiltInRoutes();
|
|
683
|
-
this.setupWatchFiles();
|
|
684
|
-
this.setupFeatures();
|
|
685
|
-
this.createServer();
|
|
948
|
+
let clientTransport;
|
|
686
949
|
|
|
687
|
-
if (this.options.
|
|
688
|
-
|
|
950
|
+
if (this.options.client) {
|
|
951
|
+
if (typeof this.options.client.webSocketTransport !== "undefined") {
|
|
952
|
+
clientTransport = this.options.client.webSocketTransport;
|
|
953
|
+
} else if (isKnownWebSocketServerImplementation) {
|
|
954
|
+
clientTransport = this.options.webSocketServer.type;
|
|
955
|
+
} else {
|
|
956
|
+
clientTransport = "ws";
|
|
957
|
+
}
|
|
958
|
+
} else {
|
|
959
|
+
clientTransport = "ws";
|
|
960
|
+
}
|
|
689
961
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
962
|
+
switch (typeof clientTransport) {
|
|
963
|
+
case "string":
|
|
964
|
+
// could be 'sockjs', 'ws', or a path that should be required
|
|
965
|
+
if (clientTransport === "sockjs") {
|
|
966
|
+
ClientImplementation = require.resolve(
|
|
967
|
+
"../client/clients/SockJSClient"
|
|
968
|
+
);
|
|
969
|
+
} else if (clientTransport === "ws") {
|
|
970
|
+
ClientImplementation = require.resolve(
|
|
971
|
+
"../client/clients/WebSocketClient"
|
|
972
|
+
);
|
|
973
|
+
} else {
|
|
974
|
+
try {
|
|
975
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
976
|
+
ClientImplementation = require.resolve(clientTransport);
|
|
977
|
+
} catch (e) {
|
|
978
|
+
clientImplementationFound = false;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
break;
|
|
982
|
+
default:
|
|
983
|
+
clientImplementationFound = false;
|
|
698
984
|
}
|
|
699
985
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
986
|
+
if (!clientImplementationFound) {
|
|
987
|
+
throw new Error(
|
|
988
|
+
`${
|
|
989
|
+
!isKnownWebSocketServerImplementation
|
|
990
|
+
? "When you use custom web socket implementation you must explicitly specify client.webSocketTransport. "
|
|
991
|
+
: ""
|
|
992
|
+
}client.webSocketTransport must be a string denoting a default implementation (e.g. 'sockjs', 'ws') or a full path to a JS file via require.resolve(...) which exports a class `
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
return ClientImplementation;
|
|
706
997
|
}
|
|
707
998
|
|
|
708
|
-
|
|
709
|
-
|
|
999
|
+
getServerTransport() {
|
|
1000
|
+
let implementation;
|
|
1001
|
+
let implementationFound = true;
|
|
710
1002
|
|
|
711
|
-
|
|
1003
|
+
switch (typeof this.options.webSocketServer.type) {
|
|
1004
|
+
case "string":
|
|
1005
|
+
// Could be 'sockjs', in the future 'ws', or a path that should be required
|
|
1006
|
+
if (this.options.webSocketServer.type === "sockjs") {
|
|
1007
|
+
implementation = require("./servers/SockJSServer");
|
|
1008
|
+
} else if (this.options.webSocketServer.type === "ws") {
|
|
1009
|
+
implementation = require("./servers/WebsocketServer");
|
|
1010
|
+
} else {
|
|
1011
|
+
try {
|
|
1012
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
1013
|
+
implementation = require(this.options.webSocketServer.type);
|
|
1014
|
+
} catch (error) {
|
|
1015
|
+
implementationFound = false;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
break;
|
|
1019
|
+
case "function":
|
|
1020
|
+
implementation = this.options.webSocketServer.type;
|
|
1021
|
+
break;
|
|
1022
|
+
default:
|
|
1023
|
+
implementationFound = false;
|
|
1024
|
+
}
|
|
712
1025
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
1026
|
+
if (!implementationFound) {
|
|
1027
|
+
throw new Error(
|
|
1028
|
+
"webSocketServer (webSocketServer.type) must be a string denoting a default implementation (e.g. 'ws', 'sockjs'), a full path to " +
|
|
1029
|
+
"a JS file which exports a class extending BaseServer (webpack-dev-server/lib/servers/BaseServer.js) " +
|
|
1030
|
+
"via require.resolve(...), or the class itself which extends BaseServer"
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
return implementation;
|
|
717
1035
|
}
|
|
718
1036
|
|
|
719
1037
|
setupProgressPlugin() {
|
|
@@ -744,6 +1062,76 @@ class Server {
|
|
|
744
1062
|
}).apply(this.compiler);
|
|
745
1063
|
}
|
|
746
1064
|
|
|
1065
|
+
async initialize() {
|
|
1066
|
+
const compilers = this.compiler.compilers || [this.compiler];
|
|
1067
|
+
|
|
1068
|
+
// eslint-disable-next-line no-shadow
|
|
1069
|
+
compilers.forEach((compiler) => {
|
|
1070
|
+
this.addAdditionalEntries(compiler);
|
|
1071
|
+
|
|
1072
|
+
const webpack = compiler.webpack || require("webpack");
|
|
1073
|
+
|
|
1074
|
+
const providePlugin = new webpack.ProvidePlugin({
|
|
1075
|
+
__webpack_dev_server_client__: this.getClientTransport(),
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
providePlugin.apply(compiler);
|
|
1079
|
+
|
|
1080
|
+
// TODO remove after drop webpack v4 support
|
|
1081
|
+
compiler.options.plugins = compiler.options.plugins || [];
|
|
1082
|
+
|
|
1083
|
+
if (this.options.hot) {
|
|
1084
|
+
const HMRPluginExists = compiler.options.plugins.find(
|
|
1085
|
+
(p) => p.constructor === webpack.HotModuleReplacementPlugin
|
|
1086
|
+
);
|
|
1087
|
+
|
|
1088
|
+
if (HMRPluginExists) {
|
|
1089
|
+
this.logger.warn(
|
|
1090
|
+
`"hot: true" automatically applies HMR plugin, you don't have to add it manually to your webpack configuration.`
|
|
1091
|
+
);
|
|
1092
|
+
} else {
|
|
1093
|
+
// apply the HMR plugin
|
|
1094
|
+
const plugin = new webpack.HotModuleReplacementPlugin();
|
|
1095
|
+
plugin.apply(compiler);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
if (this.options.client && this.options.client.progress) {
|
|
1101
|
+
this.setupProgressPlugin();
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
this.setupHooks();
|
|
1105
|
+
this.setupApp();
|
|
1106
|
+
this.setupHostHeaderCheck();
|
|
1107
|
+
this.setupDevMiddleware();
|
|
1108
|
+
// Should be after `webpack-dev-middleware`, otherwise other middlewares might rewrite response
|
|
1109
|
+
this.setupBuiltInRoutes();
|
|
1110
|
+
this.setupWatchFiles();
|
|
1111
|
+
this.setupFeatures();
|
|
1112
|
+
this.createServer();
|
|
1113
|
+
|
|
1114
|
+
if (this.options.setupExitSignals) {
|
|
1115
|
+
const signals = ["SIGINT", "SIGTERM"];
|
|
1116
|
+
|
|
1117
|
+
signals.forEach((signal) => {
|
|
1118
|
+
process.on(signal, () => {
|
|
1119
|
+
this.stopCallback(() => {
|
|
1120
|
+
// eslint-disable-next-line no-process-exit
|
|
1121
|
+
process.exit();
|
|
1122
|
+
});
|
|
1123
|
+
});
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
// Proxy WebSocket without the initial http request
|
|
1128
|
+
// https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade
|
|
1129
|
+
// eslint-disable-next-line func-names
|
|
1130
|
+
this.webSocketProxies.forEach(function (webSocketProxy) {
|
|
1131
|
+
this.server.on("upgrade", webSocketProxy.upgrade);
|
|
1132
|
+
}, this);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
747
1135
|
setupApp() {
|
|
748
1136
|
// Init express server
|
|
749
1137
|
// eslint-disable-next-line new-cap
|
|
@@ -762,26 +1150,18 @@ class Server {
|
|
|
762
1150
|
}
|
|
763
1151
|
|
|
764
1152
|
setupHooks() {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
this.stats = stats;
|
|
777
|
-
});
|
|
778
|
-
};
|
|
1153
|
+
this.compiler.hooks.invalid.tap("webpack-dev-server", () => {
|
|
1154
|
+
if (this.webSocketServer) {
|
|
1155
|
+
this.sendMessage(this.webSocketServer.clients, "invalid");
|
|
1156
|
+
}
|
|
1157
|
+
});
|
|
1158
|
+
this.compiler.hooks.done.tap("webpack-dev-server", (stats) => {
|
|
1159
|
+
if (this.webSocketServer) {
|
|
1160
|
+
this.sendStats(this.webSocketServer.clients, this.getStats(stats));
|
|
1161
|
+
}
|
|
779
1162
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
} else {
|
|
783
|
-
addHooks(this.compiler);
|
|
784
|
-
}
|
|
1163
|
+
this.stats = stats;
|
|
1164
|
+
});
|
|
785
1165
|
}
|
|
786
1166
|
|
|
787
1167
|
setupHostHeaderCheck() {
|
|
@@ -810,7 +1190,7 @@ class Server {
|
|
|
810
1190
|
app.get("/__webpack_dev_server__/sockjs.bundle.js", (req, res) => {
|
|
811
1191
|
res.setHeader("Content-Type", "application/javascript");
|
|
812
1192
|
|
|
813
|
-
const { createReadStream } =
|
|
1193
|
+
const { createReadStream } = fs;
|
|
814
1194
|
const clientPath = path.join(__dirname, "..", "client");
|
|
815
1195
|
|
|
816
1196
|
createReadStream(
|
|
@@ -879,15 +1259,17 @@ class Server {
|
|
|
879
1259
|
const { createProxyMiddleware } = require("http-proxy-middleware");
|
|
880
1260
|
|
|
881
1261
|
const getProxyMiddleware = (proxyConfig) => {
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
// It is possible to use the `bypass` method without a `target`.
|
|
1262
|
+
// It is possible to use the `bypass` method without a `target` or `router`.
|
|
885
1263
|
// However, the proxy middleware has no use in this case, and will fail to instantiate.
|
|
886
|
-
if (
|
|
1264
|
+
if (proxyConfig.target) {
|
|
1265
|
+
const context = proxyConfig.context || proxyConfig.path;
|
|
1266
|
+
|
|
887
1267
|
return createProxyMiddleware(context, proxyConfig);
|
|
888
1268
|
}
|
|
889
1269
|
|
|
890
|
-
|
|
1270
|
+
if (proxyConfig.router) {
|
|
1271
|
+
return createProxyMiddleware(proxyConfig);
|
|
1272
|
+
}
|
|
891
1273
|
};
|
|
892
1274
|
/**
|
|
893
1275
|
* Assume a proxy configuration specified as:
|
|
@@ -913,9 +1295,7 @@ class Server {
|
|
|
913
1295
|
? proxyConfigOrCallback()
|
|
914
1296
|
: proxyConfigOrCallback;
|
|
915
1297
|
|
|
916
|
-
|
|
917
|
-
proxyMiddleware = getProxyMiddleware(proxyConfig);
|
|
918
|
-
}
|
|
1298
|
+
proxyMiddleware = getProxyMiddleware(proxyConfig);
|
|
919
1299
|
|
|
920
1300
|
if (proxyConfig.ws) {
|
|
921
1301
|
this.webSocketProxies.push(proxyMiddleware);
|
|
@@ -1132,7 +1512,9 @@ class Server {
|
|
|
1132
1512
|
runnableFeatures.push("staticServeIndex", "staticWatch");
|
|
1133
1513
|
}
|
|
1134
1514
|
|
|
1135
|
-
|
|
1515
|
+
if (this.options.magicHtml) {
|
|
1516
|
+
runnableFeatures.push("magicHtml");
|
|
1517
|
+
}
|
|
1136
1518
|
|
|
1137
1519
|
if (this.options.onAfterSetupMiddleware) {
|
|
1138
1520
|
runnableFeatures.push("onAfterSetupMiddleware");
|
|
@@ -1182,46 +1564,8 @@ class Server {
|
|
|
1182
1564
|
});
|
|
1183
1565
|
}
|
|
1184
1566
|
|
|
1185
|
-
getWebSocketServerImplementation() {
|
|
1186
|
-
let implementation;
|
|
1187
|
-
let implementationFound = true;
|
|
1188
|
-
|
|
1189
|
-
switch (typeof this.options.webSocketServer.type) {
|
|
1190
|
-
case "string":
|
|
1191
|
-
// Could be 'sockjs', in the future 'ws', or a path that should be required
|
|
1192
|
-
if (this.options.webSocketServer.type === "sockjs") {
|
|
1193
|
-
implementation = require("./servers/SockJSServer");
|
|
1194
|
-
} else if (this.options.webSocketServer.type === "ws") {
|
|
1195
|
-
implementation = require("./servers/WebsocketServer");
|
|
1196
|
-
} else {
|
|
1197
|
-
try {
|
|
1198
|
-
// eslint-disable-next-line import/no-dynamic-require
|
|
1199
|
-
implementation = require(this.options.webSocketServer.type);
|
|
1200
|
-
} catch (error) {
|
|
1201
|
-
implementationFound = false;
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
break;
|
|
1205
|
-
case "function":
|
|
1206
|
-
implementation = this.options.webSocketServer.type;
|
|
1207
|
-
break;
|
|
1208
|
-
default:
|
|
1209
|
-
implementationFound = false;
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
if (!implementationFound) {
|
|
1213
|
-
throw new Error(
|
|
1214
|
-
"webSocketServer (webSocketServer.type) must be a string denoting a default implementation (e.g. 'ws', 'sockjs'), a full path to " +
|
|
1215
|
-
"a JS file which exports a class extending BaseServer (webpack-dev-server/lib/servers/BaseServer.js) " +
|
|
1216
|
-
"via require.resolve(...), or the class itself which extends BaseServer"
|
|
1217
|
-
);
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
return implementation;
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
1567
|
createWebSocketServer() {
|
|
1224
|
-
this.webSocketServer = new (this.
|
|
1568
|
+
this.webSocketServer = new (this.getServerTransport())(this);
|
|
1225
1569
|
this.webSocketServer.implementation.on("connection", (client, request) => {
|
|
1226
1570
|
const headers =
|
|
1227
1571
|
// eslint-disable-next-line no-nested-ternary
|
|
@@ -1525,6 +1869,10 @@ class Server {
|
|
|
1525
1869
|
return false;
|
|
1526
1870
|
}
|
|
1527
1871
|
|
|
1872
|
+
if (/^(file|.+-extension):/i.test(hostHeader)) {
|
|
1873
|
+
return true;
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1528
1876
|
// use the node url-parser to retrieve the hostname from the host-header.
|
|
1529
1877
|
const hostname = url.parse(
|
|
1530
1878
|
// if hostHeader doesn't have scheme, add // for parsing.
|
|
@@ -1763,7 +2111,7 @@ class Server {
|
|
|
1763
2111
|
// chmod 666 (rw rw rw)
|
|
1764
2112
|
const READ_WRITE = 438;
|
|
1765
2113
|
|
|
1766
|
-
fs.
|
|
2114
|
+
await fs.promises.chmod(this.options.ipc, READ_WRITE);
|
|
1767
2115
|
}
|
|
1768
2116
|
|
|
1769
2117
|
if (this.options.webSocketServer) {
|