webpack-dev-server 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/bin/cli-flags.js +13 -0
- package/client/index.js +11 -1
- package/lib/Server.js +425 -100
- package/lib/options.json +24 -6
- package/package.json +1 -1
- package/lib/utils/DevServerPlugin.js +0 -352
package/README.md
CHANGED
|
@@ -132,6 +132,8 @@ Options:
|
|
|
132
132
|
--ipc [value] Listen to a unix socket.
|
|
133
133
|
--live-reload Enables reload/refresh the page(s) when file changes are detected (enabled by default).
|
|
134
134
|
--no-live-reload Negative 'live-reload' option.
|
|
135
|
+
--magic-html Enables/Disables magic HTML routes (enabled by default).
|
|
136
|
+
--no-magic-html Negative 'magic-html' option.
|
|
135
137
|
--open [value...] Allows to configure dev server to open the browser(s) and page(s) after server had been started (set it to
|
|
136
138
|
true to open your default browser).
|
|
137
139
|
--no-open Negative 'open' option.
|
package/bin/cli-flags.js
CHANGED
|
@@ -534,6 +534,19 @@ module.exports = {
|
|
|
534
534
|
simpleType: "boolean",
|
|
535
535
|
multiple: false,
|
|
536
536
|
},
|
|
537
|
+
"magic-html": {
|
|
538
|
+
configs: [
|
|
539
|
+
{
|
|
540
|
+
type: "boolean",
|
|
541
|
+
multiple: false,
|
|
542
|
+
description: "Enables/Disables magic HTML routes (enabled by default).",
|
|
543
|
+
path: "magicHtml",
|
|
544
|
+
},
|
|
545
|
+
],
|
|
546
|
+
description: "Enables/Disables magic HTML routes (enabled by default).",
|
|
547
|
+
simpleType: "boolean",
|
|
548
|
+
multiple: false,
|
|
549
|
+
},
|
|
537
550
|
open: {
|
|
538
551
|
configs: [
|
|
539
552
|
{
|
package/client/index.js
CHANGED
|
@@ -23,6 +23,16 @@ var options = {
|
|
|
23
23
|
};
|
|
24
24
|
var parsedResourceQuery = parseURL(__resourceQuery);
|
|
25
25
|
|
|
26
|
+
if (parsedResourceQuery.hot === "true") {
|
|
27
|
+
options.hot = true;
|
|
28
|
+
log.info("Hot Module Replacement enabled.");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (parsedResourceQuery["live-reload"] === "true") {
|
|
32
|
+
options.liveReload = true;
|
|
33
|
+
log.info("Live Reloading enabled.");
|
|
34
|
+
}
|
|
35
|
+
|
|
26
36
|
if (parsedResourceQuery.logging) {
|
|
27
37
|
options.logging = parsedResourceQuery.logging;
|
|
28
38
|
}
|
|
@@ -158,7 +168,7 @@ var onSocketMessage = {
|
|
|
158
168
|
log.error(_error);
|
|
159
169
|
},
|
|
160
170
|
close: function close() {
|
|
161
|
-
log.
|
|
171
|
+
log.info("Disconnected!");
|
|
162
172
|
sendMessage("Close");
|
|
163
173
|
}
|
|
164
174
|
};
|
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(this.options.allowedHosts) &&
|
|
428
|
+
this.options.allowedHosts.includes("all")
|
|
429
|
+
) {
|
|
430
|
+
options.allowedHosts = "all";
|
|
431
|
+
}
|
|
206
432
|
|
|
207
433
|
if (typeof options.bonjour === "undefined") {
|
|
208
434
|
options.bonjour = false;
|
|
@@ -316,11 +542,18 @@ class Server {
|
|
|
316
542
|
if (!options.https.key || !options.https.cert) {
|
|
317
543
|
const certificateDir = Server.findCacheDir();
|
|
318
544
|
const certificatePath = path.join(certificateDir, "server.pem");
|
|
319
|
-
let certificateExists
|
|
545
|
+
let certificateExists;
|
|
546
|
+
|
|
547
|
+
try {
|
|
548
|
+
const certificate = await fs.promises.stat(certificatePath);
|
|
549
|
+
certificateExists = certificate.isFile();
|
|
550
|
+
} catch {
|
|
551
|
+
certificateExists = false;
|
|
552
|
+
}
|
|
320
553
|
|
|
321
554
|
if (certificateExists) {
|
|
322
555
|
const certificateTtl = 1000 * 60 * 60 * 24;
|
|
323
|
-
const certificateStat = fs.
|
|
556
|
+
const certificateStat = await fs.promises.stat(certificatePath);
|
|
324
557
|
|
|
325
558
|
const now = new Date();
|
|
326
559
|
|
|
@@ -332,7 +565,7 @@ class Server {
|
|
|
332
565
|
"SSL Certificate is more than 30 days old. Removing..."
|
|
333
566
|
);
|
|
334
567
|
|
|
335
|
-
del
|
|
568
|
+
await del([certificatePath], { force: true });
|
|
336
569
|
|
|
337
570
|
certificateExists = false;
|
|
338
571
|
}
|
|
@@ -405,13 +638,18 @@ class Server {
|
|
|
405
638
|
],
|
|
406
639
|
});
|
|
407
640
|
|
|
408
|
-
fs.
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
641
|
+
await fs.promises.mkdir(certificateDir, { recursive: true });
|
|
642
|
+
|
|
643
|
+
await fs.promises.writeFile(
|
|
644
|
+
certificatePath,
|
|
645
|
+
pems.private + pems.cert,
|
|
646
|
+
{
|
|
647
|
+
encoding: "utf8",
|
|
648
|
+
}
|
|
649
|
+
);
|
|
412
650
|
}
|
|
413
651
|
|
|
414
|
-
fakeCert = fs.
|
|
652
|
+
fakeCert = await fs.promises.readFile(certificatePath);
|
|
415
653
|
|
|
416
654
|
this.logger.info(`SSL certificate: ${certificatePath}`);
|
|
417
655
|
}
|
|
@@ -431,6 +669,9 @@ class Server {
|
|
|
431
669
|
options.liveReload =
|
|
432
670
|
typeof options.liveReload !== "undefined" ? options.liveReload : true;
|
|
433
671
|
|
|
672
|
+
options.magicHtml =
|
|
673
|
+
typeof options.magicHtml !== "undefined" ? options.magicHtml : true;
|
|
674
|
+
|
|
434
675
|
// https://github.com/webpack/webpack-dev-server/issues/1990
|
|
435
676
|
const defaultOpenOptions = { wait: false };
|
|
436
677
|
const getOpenItemsFromObject = ({ target, ...rest }) => {
|
|
@@ -667,53 +908,103 @@ class Server {
|
|
|
667
908
|
}
|
|
668
909
|
}
|
|
669
910
|
|
|
670
|
-
|
|
671
|
-
|
|
911
|
+
getClientTransport() {
|
|
912
|
+
let ClientImplementation;
|
|
913
|
+
let clientImplementationFound = true;
|
|
672
914
|
|
|
673
|
-
|
|
674
|
-
this.
|
|
675
|
-
|
|
915
|
+
const isKnownWebSocketServerImplementation =
|
|
916
|
+
this.options.webSocketServer &&
|
|
917
|
+
typeof this.options.webSocketServer.type === "string" &&
|
|
918
|
+
(this.options.webSocketServer.type === "ws" ||
|
|
919
|
+
this.options.webSocketServer.type === "sockjs");
|
|
676
920
|
|
|
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();
|
|
921
|
+
let clientTransport;
|
|
686
922
|
|
|
687
|
-
if (this.options.
|
|
688
|
-
|
|
923
|
+
if (this.options.client) {
|
|
924
|
+
if (typeof this.options.client.webSocketTransport !== "undefined") {
|
|
925
|
+
clientTransport = this.options.client.webSocketTransport;
|
|
926
|
+
} else if (isKnownWebSocketServerImplementation) {
|
|
927
|
+
clientTransport = this.options.webSocketServer.type;
|
|
928
|
+
} else {
|
|
929
|
+
clientTransport = "ws";
|
|
930
|
+
}
|
|
931
|
+
} else {
|
|
932
|
+
clientTransport = "ws";
|
|
933
|
+
}
|
|
689
934
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
935
|
+
switch (typeof clientTransport) {
|
|
936
|
+
case "string":
|
|
937
|
+
// could be 'sockjs', 'ws', or a path that should be required
|
|
938
|
+
if (clientTransport === "sockjs") {
|
|
939
|
+
ClientImplementation = require.resolve(
|
|
940
|
+
"../client/clients/SockJSClient"
|
|
941
|
+
);
|
|
942
|
+
} else if (clientTransport === "ws") {
|
|
943
|
+
ClientImplementation = require.resolve(
|
|
944
|
+
"../client/clients/WebSocketClient"
|
|
945
|
+
);
|
|
946
|
+
} else {
|
|
947
|
+
try {
|
|
948
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
949
|
+
ClientImplementation = require.resolve(clientTransport);
|
|
950
|
+
} catch (e) {
|
|
951
|
+
clientImplementationFound = false;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
break;
|
|
955
|
+
default:
|
|
956
|
+
clientImplementationFound = false;
|
|
698
957
|
}
|
|
699
958
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
959
|
+
if (!clientImplementationFound) {
|
|
960
|
+
throw new Error(
|
|
961
|
+
`${
|
|
962
|
+
!isKnownWebSocketServerImplementation
|
|
963
|
+
? "When you use custom web socket implementation you must explicitly specify client.webSocketTransport. "
|
|
964
|
+
: ""
|
|
965
|
+
}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 `
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
return ClientImplementation;
|
|
706
970
|
}
|
|
707
971
|
|
|
708
|
-
|
|
709
|
-
|
|
972
|
+
getServerTransport() {
|
|
973
|
+
let implementation;
|
|
974
|
+
let implementationFound = true;
|
|
975
|
+
|
|
976
|
+
switch (typeof this.options.webSocketServer.type) {
|
|
977
|
+
case "string":
|
|
978
|
+
// Could be 'sockjs', in the future 'ws', or a path that should be required
|
|
979
|
+
if (this.options.webSocketServer.type === "sockjs") {
|
|
980
|
+
implementation = require("./servers/SockJSServer");
|
|
981
|
+
} else if (this.options.webSocketServer.type === "ws") {
|
|
982
|
+
implementation = require("./servers/WebsocketServer");
|
|
983
|
+
} else {
|
|
984
|
+
try {
|
|
985
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
986
|
+
implementation = require(this.options.webSocketServer.type);
|
|
987
|
+
} catch (error) {
|
|
988
|
+
implementationFound = false;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
break;
|
|
992
|
+
case "function":
|
|
993
|
+
implementation = this.options.webSocketServer.type;
|
|
994
|
+
break;
|
|
995
|
+
default:
|
|
996
|
+
implementationFound = false;
|
|
997
|
+
}
|
|
710
998
|
|
|
711
|
-
|
|
999
|
+
if (!implementationFound) {
|
|
1000
|
+
throw new Error(
|
|
1001
|
+
"webSocketServer (webSocketServer.type) must be a string denoting a default implementation (e.g. 'ws', 'sockjs'), a full path to " +
|
|
1002
|
+
"a JS file which exports a class extending BaseServer (webpack-dev-server/lib/servers/BaseServer.js) " +
|
|
1003
|
+
"via require.resolve(...), or the class itself which extends BaseServer"
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
712
1006
|
|
|
713
|
-
|
|
714
|
-
compilers.forEach((compiler) => {
|
|
715
|
-
new DevServerPlugin(this.options).apply(compiler);
|
|
716
|
-
});
|
|
1007
|
+
return implementation;
|
|
717
1008
|
}
|
|
718
1009
|
|
|
719
1010
|
setupProgressPlugin() {
|
|
@@ -744,6 +1035,76 @@ class Server {
|
|
|
744
1035
|
}).apply(this.compiler);
|
|
745
1036
|
}
|
|
746
1037
|
|
|
1038
|
+
async initialize() {
|
|
1039
|
+
const compilers = this.compiler.compilers || [this.compiler];
|
|
1040
|
+
|
|
1041
|
+
// eslint-disable-next-line no-shadow
|
|
1042
|
+
compilers.forEach((compiler) => {
|
|
1043
|
+
this.addAdditionalEntries(compiler);
|
|
1044
|
+
|
|
1045
|
+
const webpack = compiler.webpack || require("webpack");
|
|
1046
|
+
|
|
1047
|
+
const providePlugin = new webpack.ProvidePlugin({
|
|
1048
|
+
__webpack_dev_server_client__: this.getClientTransport(),
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1051
|
+
providePlugin.apply(compiler);
|
|
1052
|
+
|
|
1053
|
+
// TODO remove after drop webpack v4 support
|
|
1054
|
+
compiler.options.plugins = compiler.options.plugins || [];
|
|
1055
|
+
|
|
1056
|
+
if (this.options.hot) {
|
|
1057
|
+
const HMRPluginExists = compiler.options.plugins.find(
|
|
1058
|
+
(p) => p.constructor === webpack.HotModuleReplacementPlugin
|
|
1059
|
+
);
|
|
1060
|
+
|
|
1061
|
+
if (HMRPluginExists) {
|
|
1062
|
+
this.logger.warn(
|
|
1063
|
+
`"hot: true" automatically applies HMR plugin, you don't have to add it manually to your webpack configuration.`
|
|
1064
|
+
);
|
|
1065
|
+
} else {
|
|
1066
|
+
// apply the HMR plugin
|
|
1067
|
+
const plugin = new webpack.HotModuleReplacementPlugin();
|
|
1068
|
+
plugin.apply(compiler);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
if (this.options.client && this.options.client.progress) {
|
|
1074
|
+
this.setupProgressPlugin();
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
this.setupHooks();
|
|
1078
|
+
this.setupApp();
|
|
1079
|
+
this.setupHostHeaderCheck();
|
|
1080
|
+
this.setupDevMiddleware();
|
|
1081
|
+
// Should be after `webpack-dev-middleware`, otherwise other middlewares might rewrite response
|
|
1082
|
+
this.setupBuiltInRoutes();
|
|
1083
|
+
this.setupWatchFiles();
|
|
1084
|
+
this.setupFeatures();
|
|
1085
|
+
this.createServer();
|
|
1086
|
+
|
|
1087
|
+
if (this.options.setupExitSignals) {
|
|
1088
|
+
const signals = ["SIGINT", "SIGTERM"];
|
|
1089
|
+
|
|
1090
|
+
signals.forEach((signal) => {
|
|
1091
|
+
process.on(signal, () => {
|
|
1092
|
+
this.stopCallback(() => {
|
|
1093
|
+
// eslint-disable-next-line no-process-exit
|
|
1094
|
+
process.exit();
|
|
1095
|
+
});
|
|
1096
|
+
});
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// Proxy WebSocket without the initial http request
|
|
1101
|
+
// https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade
|
|
1102
|
+
// eslint-disable-next-line func-names
|
|
1103
|
+
this.webSocketProxies.forEach(function (webSocketProxy) {
|
|
1104
|
+
this.server.on("upgrade", webSocketProxy.upgrade);
|
|
1105
|
+
}, this);
|
|
1106
|
+
}
|
|
1107
|
+
|
|
747
1108
|
setupApp() {
|
|
748
1109
|
// Init express server
|
|
749
1110
|
// eslint-disable-next-line new-cap
|
|
@@ -810,7 +1171,7 @@ class Server {
|
|
|
810
1171
|
app.get("/__webpack_dev_server__/sockjs.bundle.js", (req, res) => {
|
|
811
1172
|
res.setHeader("Content-Type", "application/javascript");
|
|
812
1173
|
|
|
813
|
-
const { createReadStream } =
|
|
1174
|
+
const { createReadStream } = fs;
|
|
814
1175
|
const clientPath = path.join(__dirname, "..", "client");
|
|
815
1176
|
|
|
816
1177
|
createReadStream(
|
|
@@ -879,15 +1240,17 @@ class Server {
|
|
|
879
1240
|
const { createProxyMiddleware } = require("http-proxy-middleware");
|
|
880
1241
|
|
|
881
1242
|
const getProxyMiddleware = (proxyConfig) => {
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
// It is possible to use the `bypass` method without a `target`.
|
|
1243
|
+
// It is possible to use the `bypass` method without a `target` or `router`.
|
|
885
1244
|
// However, the proxy middleware has no use in this case, and will fail to instantiate.
|
|
886
|
-
if (
|
|
1245
|
+
if (proxyConfig.target) {
|
|
1246
|
+
const context = proxyConfig.context || proxyConfig.path;
|
|
1247
|
+
|
|
887
1248
|
return createProxyMiddleware(context, proxyConfig);
|
|
888
1249
|
}
|
|
889
1250
|
|
|
890
|
-
|
|
1251
|
+
if (proxyConfig.router) {
|
|
1252
|
+
return createProxyMiddleware(proxyConfig);
|
|
1253
|
+
}
|
|
891
1254
|
};
|
|
892
1255
|
/**
|
|
893
1256
|
* Assume a proxy configuration specified as:
|
|
@@ -913,9 +1276,7 @@ class Server {
|
|
|
913
1276
|
? proxyConfigOrCallback()
|
|
914
1277
|
: proxyConfigOrCallback;
|
|
915
1278
|
|
|
916
|
-
|
|
917
|
-
proxyMiddleware = getProxyMiddleware(proxyConfig);
|
|
918
|
-
}
|
|
1279
|
+
proxyMiddleware = getProxyMiddleware(proxyConfig);
|
|
919
1280
|
|
|
920
1281
|
if (proxyConfig.ws) {
|
|
921
1282
|
this.webSocketProxies.push(proxyMiddleware);
|
|
@@ -1132,7 +1493,9 @@ class Server {
|
|
|
1132
1493
|
runnableFeatures.push("staticServeIndex", "staticWatch");
|
|
1133
1494
|
}
|
|
1134
1495
|
|
|
1135
|
-
|
|
1496
|
+
if (this.options.magicHtml) {
|
|
1497
|
+
runnableFeatures.push("magicHtml");
|
|
1498
|
+
}
|
|
1136
1499
|
|
|
1137
1500
|
if (this.options.onAfterSetupMiddleware) {
|
|
1138
1501
|
runnableFeatures.push("onAfterSetupMiddleware");
|
|
@@ -1182,46 +1545,8 @@ class Server {
|
|
|
1182
1545
|
});
|
|
1183
1546
|
}
|
|
1184
1547
|
|
|
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
1548
|
createWebSocketServer() {
|
|
1224
|
-
this.webSocketServer = new (this.
|
|
1549
|
+
this.webSocketServer = new (this.getServerTransport())(this);
|
|
1225
1550
|
this.webSocketServer.implementation.on("connection", (client, request) => {
|
|
1226
1551
|
const headers =
|
|
1227
1552
|
// eslint-disable-next-line no-nested-ternary
|
|
@@ -1763,7 +2088,7 @@ class Server {
|
|
|
1763
2088
|
// chmod 666 (rw rw rw)
|
|
1764
2089
|
const READ_WRITE = 438;
|
|
1765
2090
|
|
|
1766
|
-
fs.
|
|
2091
|
+
await fs.promises.chmod(this.options.ipc, READ_WRITE);
|
|
1767
2092
|
}
|
|
1768
2093
|
|
|
1769
2094
|
if (this.options.webSocketServer) {
|
package/lib/options.json
CHANGED
|
@@ -71,12 +71,14 @@
|
|
|
71
71
|
},
|
|
72
72
|
"ClientLogging": {
|
|
73
73
|
"enum": ["none", "error", "warn", "info", "log", "verbose"],
|
|
74
|
-
"decription": "Allows to set log level in the browser."
|
|
74
|
+
"decription": "Allows to set log level in the browser.",
|
|
75
|
+
"link": "https://webpack.js.org/configuration/dev-server/#logging"
|
|
75
76
|
},
|
|
76
77
|
"ClientOverlay": {
|
|
77
78
|
"anyOf": [
|
|
78
79
|
{
|
|
79
80
|
"description": "Enables a full-screen overlay in the browser when there are compiler errors or warnings.",
|
|
81
|
+
"link": "https://webpack.js.org/configuration/dev-server/#overlay",
|
|
80
82
|
"type": "boolean"
|
|
81
83
|
},
|
|
82
84
|
{
|
|
@@ -97,6 +99,7 @@
|
|
|
97
99
|
},
|
|
98
100
|
"ClientProgress": {
|
|
99
101
|
"description": "Prints compilation progress in percentage in the browser.",
|
|
102
|
+
"link": "https://webpack.js.org/configuration/dev-server/#progress",
|
|
100
103
|
"type": "boolean"
|
|
101
104
|
},
|
|
102
105
|
"ClientWebSocketTransport": {
|
|
@@ -108,7 +111,8 @@
|
|
|
108
111
|
"$ref": "#/definitions/ClientWebSocketTransportString"
|
|
109
112
|
}
|
|
110
113
|
],
|
|
111
|
-
"description": "Allows to set custom web socket transport to communicate with dev server."
|
|
114
|
+
"description": "Allows to set custom web socket transport to communicate with dev server.",
|
|
115
|
+
"link": "https://webpack.js.org/configuration/dev-server/#websockettransport"
|
|
112
116
|
},
|
|
113
117
|
"ClientWebSocketTransportEnum": {
|
|
114
118
|
"enum": ["sockjs", "ws"]
|
|
@@ -119,6 +123,7 @@
|
|
|
119
123
|
},
|
|
120
124
|
"ClientWebSocketURL": {
|
|
121
125
|
"description": "Allows to specify URL to web socket server (useful when you're proxying dev server and client script does not always know where to connect to).",
|
|
126
|
+
"link": "https://webpack.js.org/configuration/dev-server/#websocketurl",
|
|
122
127
|
"anyOf": [
|
|
123
128
|
{
|
|
124
129
|
"type": "string",
|
|
@@ -326,6 +331,11 @@
|
|
|
326
331
|
"description": "Enables reload/refresh the page(s) when file changes are detected (enabled by default).",
|
|
327
332
|
"link": "https://webpack.js.org/configuration/dev-server/#devserverlivereload"
|
|
328
333
|
},
|
|
334
|
+
"MagicHTML": {
|
|
335
|
+
"type": "boolean",
|
|
336
|
+
"description": "Enables/Disables magic HTML routes (enabled by default).",
|
|
337
|
+
"link": "https://webpack.js.org/configuration/dev-server/#devservermagichtml"
|
|
338
|
+
},
|
|
329
339
|
"OnAfterSetupMiddleware": {
|
|
330
340
|
"instanceof": "Function",
|
|
331
341
|
"description": "Provides the ability to execute a custom function and apply custom middleware(s) after all other middlewares.",
|
|
@@ -514,10 +524,12 @@
|
|
|
514
524
|
"directory": {
|
|
515
525
|
"type": "string",
|
|
516
526
|
"minLength": 1,
|
|
517
|
-
"description": "Directory for static contents."
|
|
527
|
+
"description": "Directory for static contents.",
|
|
528
|
+
"link": "https://webpack.js.org/configuration/dev-server/#directory"
|
|
518
529
|
},
|
|
519
530
|
"staticOptions": {
|
|
520
531
|
"type": "object",
|
|
532
|
+
"link": "https://webpack.js.org/configuration/dev-server/#staticoptions",
|
|
521
533
|
"additionalProperties": true
|
|
522
534
|
},
|
|
523
535
|
"publicPath": {
|
|
@@ -533,7 +545,8 @@
|
|
|
533
545
|
"type": "string"
|
|
534
546
|
}
|
|
535
547
|
],
|
|
536
|
-
"description": "The static files will be available in the browser under this public path."
|
|
548
|
+
"description": "The static files will be available in the browser under this public path.",
|
|
549
|
+
"link": "https://webpack.js.org/configuration/dev-server/#publicpath"
|
|
537
550
|
},
|
|
538
551
|
"serveIndex": {
|
|
539
552
|
"anyOf": [
|
|
@@ -545,7 +558,8 @@
|
|
|
545
558
|
"additionalProperties": true
|
|
546
559
|
}
|
|
547
560
|
],
|
|
548
|
-
"description": "Tells dev server to use serveIndex middleware when enabled."
|
|
561
|
+
"description": "Tells dev server to use serveIndex middleware when enabled.",
|
|
562
|
+
"link": "https://webpack.js.org/configuration/dev-server/#serveindex"
|
|
549
563
|
},
|
|
550
564
|
"watch": {
|
|
551
565
|
"anyOf": [
|
|
@@ -558,7 +572,8 @@
|
|
|
558
572
|
"link": "https://github.com/paulmillr/chokidar#api"
|
|
559
573
|
}
|
|
560
574
|
],
|
|
561
|
-
"description": "Watches for files in static content directory."
|
|
575
|
+
"description": "Watches for files in static content directory.",
|
|
576
|
+
"link": "https://webpack.js.org/configuration/dev-server/#watch"
|
|
562
577
|
}
|
|
563
578
|
}
|
|
564
579
|
},
|
|
@@ -719,6 +734,9 @@
|
|
|
719
734
|
"liveReload": {
|
|
720
735
|
"$ref": "#/definitions/LiveReload"
|
|
721
736
|
},
|
|
737
|
+
"magicHtml": {
|
|
738
|
+
"$ref": "#/definitions/MagicHTML"
|
|
739
|
+
},
|
|
722
740
|
"onAfterSetupMiddleware": {
|
|
723
741
|
"$ref": "#/definitions/OnAfterSetupMiddleware"
|
|
724
742
|
},
|
package/package.json
CHANGED
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* An Entry, it can be of type string or string[] or Object<string | string[],string>
|
|
5
|
-
* @typedef {(string[] | string | Object<string | string[],string>)} Entry
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
class DevServerPlugin {
|
|
9
|
-
/**
|
|
10
|
-
* @param {Object} options - Dev-Server options
|
|
11
|
-
*/
|
|
12
|
-
constructor(options) {
|
|
13
|
-
this.options = options;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
getWebsocketTransport() {
|
|
17
|
-
let ClientImplementation;
|
|
18
|
-
let clientImplementationFound = true;
|
|
19
|
-
|
|
20
|
-
const isKnownWebSocketServerImplementation =
|
|
21
|
-
this.options.webSocketServer &&
|
|
22
|
-
typeof this.options.webSocketServer.type === "string" &&
|
|
23
|
-
(this.options.webSocketServer.type === "ws" ||
|
|
24
|
-
this.options.webSocketServer.type === "sockjs");
|
|
25
|
-
|
|
26
|
-
let clientTransport;
|
|
27
|
-
|
|
28
|
-
if (this.options.client) {
|
|
29
|
-
if (typeof this.options.client.webSocketTransport !== "undefined") {
|
|
30
|
-
clientTransport = this.options.client.webSocketTransport;
|
|
31
|
-
} else if (isKnownWebSocketServerImplementation) {
|
|
32
|
-
clientTransport = this.options.webSocketServer.type;
|
|
33
|
-
} else {
|
|
34
|
-
clientTransport = "ws";
|
|
35
|
-
}
|
|
36
|
-
} else {
|
|
37
|
-
clientTransport = "ws";
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
switch (typeof clientTransport) {
|
|
41
|
-
case "string":
|
|
42
|
-
// could be 'sockjs', 'ws', or a path that should be required
|
|
43
|
-
if (clientTransport === "sockjs") {
|
|
44
|
-
ClientImplementation = require.resolve(
|
|
45
|
-
"../../client/clients/SockJSClient"
|
|
46
|
-
);
|
|
47
|
-
} else if (clientTransport === "ws") {
|
|
48
|
-
ClientImplementation = require.resolve(
|
|
49
|
-
"../../client/clients/WebSocketClient"
|
|
50
|
-
);
|
|
51
|
-
} else {
|
|
52
|
-
try {
|
|
53
|
-
// eslint-disable-next-line import/no-dynamic-require
|
|
54
|
-
ClientImplementation = require.resolve(clientTransport);
|
|
55
|
-
} catch (e) {
|
|
56
|
-
clientImplementationFound = false;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
break;
|
|
60
|
-
default:
|
|
61
|
-
clientImplementationFound = false;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (!clientImplementationFound) {
|
|
65
|
-
throw new Error(
|
|
66
|
-
`${
|
|
67
|
-
!isKnownWebSocketServerImplementation
|
|
68
|
-
? "When you use custom web socket implementation you must explicitly specify client.webSocketTransport. "
|
|
69
|
-
: ""
|
|
70
|
-
}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 `
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return ClientImplementation;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* @returns {string}
|
|
79
|
-
*/
|
|
80
|
-
getWebSocketURL() {
|
|
81
|
-
const { options } = this;
|
|
82
|
-
const searchParams = new URLSearchParams();
|
|
83
|
-
|
|
84
|
-
/** @type {"ws:" | "wss:" | "http:" | "https:" | "auto:"} */
|
|
85
|
-
let protocol;
|
|
86
|
-
|
|
87
|
-
// We are proxying dev server and need to specify custom `hostname`
|
|
88
|
-
if (typeof options.client.webSocketURL.protocol !== "undefined") {
|
|
89
|
-
protocol = options.client.webSocketURL.protocol;
|
|
90
|
-
} else {
|
|
91
|
-
protocol = options.https ? "wss:" : "ws:";
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
searchParams.set("protocol", protocol);
|
|
95
|
-
|
|
96
|
-
if (typeof options.client.webSocketURL.username !== "undefined") {
|
|
97
|
-
searchParams.set("username", options.client.webSocketURL.username);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (typeof options.client.webSocketURL.password !== "undefined") {
|
|
101
|
-
searchParams.set("password", options.client.webSocketURL.password);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/** @type {string} */
|
|
105
|
-
let hostname;
|
|
106
|
-
|
|
107
|
-
// SockJS is not supported server mode, so `hostname` and `port` can't specified, let's ignore them
|
|
108
|
-
// TODO show warning about this
|
|
109
|
-
const isSockJSType = options.webSocketServer.type === "sockjs";
|
|
110
|
-
|
|
111
|
-
// We are proxying dev server and need to specify custom `hostname`
|
|
112
|
-
if (typeof options.client.webSocketURL.hostname !== "undefined") {
|
|
113
|
-
hostname = options.client.webSocketURL.hostname;
|
|
114
|
-
}
|
|
115
|
-
// Web socket server works on custom `hostname`, only for `ws` because `sock-js` is not support custom `hostname`
|
|
116
|
-
else if (
|
|
117
|
-
typeof options.webSocketServer.options.host !== "undefined" &&
|
|
118
|
-
!isSockJSType
|
|
119
|
-
) {
|
|
120
|
-
hostname = options.webSocketServer.options.host;
|
|
121
|
-
}
|
|
122
|
-
// The `host` option is specified
|
|
123
|
-
else if (typeof this.options.host !== "undefined") {
|
|
124
|
-
hostname = this.options.host;
|
|
125
|
-
}
|
|
126
|
-
// The `port` option is not specified
|
|
127
|
-
else {
|
|
128
|
-
hostname = "0.0.0.0";
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
searchParams.set("hostname", hostname);
|
|
132
|
-
|
|
133
|
-
/** @type {number | string} */
|
|
134
|
-
let port;
|
|
135
|
-
|
|
136
|
-
// We are proxying dev server and need to specify custom `port`
|
|
137
|
-
if (typeof options.client.webSocketURL.port !== "undefined") {
|
|
138
|
-
port = options.client.webSocketURL.port;
|
|
139
|
-
}
|
|
140
|
-
// Web socket server works on custom `port`, only for `ws` because `sock-js` is not support custom `port`
|
|
141
|
-
else if (
|
|
142
|
-
typeof options.webSocketServer.options.port !== "undefined" &&
|
|
143
|
-
!isSockJSType
|
|
144
|
-
) {
|
|
145
|
-
port = options.webSocketServer.options.port;
|
|
146
|
-
}
|
|
147
|
-
// The `port` option is specified
|
|
148
|
-
else if (typeof options.port === "number") {
|
|
149
|
-
port = options.port;
|
|
150
|
-
}
|
|
151
|
-
// The `port` option is specified using `string`
|
|
152
|
-
else if (typeof options.port === "string" && options.port !== "auto") {
|
|
153
|
-
port = Number(options.port);
|
|
154
|
-
}
|
|
155
|
-
// The `port` option is not specified or set to `auto`
|
|
156
|
-
else {
|
|
157
|
-
port = "0";
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
searchParams.set("port", String(port));
|
|
161
|
-
|
|
162
|
-
/** @type {string} */
|
|
163
|
-
let pathname = "";
|
|
164
|
-
|
|
165
|
-
// We are proxying dev server and need to specify custom `pathname`
|
|
166
|
-
if (typeof options.client.webSocketURL.pathname !== "undefined") {
|
|
167
|
-
pathname = options.client.webSocketURL.pathname;
|
|
168
|
-
}
|
|
169
|
-
// Web socket server works on custom `path`
|
|
170
|
-
else if (
|
|
171
|
-
typeof options.webSocketServer.options.prefix !== "undefined" ||
|
|
172
|
-
typeof options.webSocketServer.options.path !== "undefined"
|
|
173
|
-
) {
|
|
174
|
-
pathname =
|
|
175
|
-
options.webSocketServer.options.prefix ||
|
|
176
|
-
options.webSocketServer.options.path;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
searchParams.set("pathname", pathname);
|
|
180
|
-
|
|
181
|
-
if (typeof options.client.logging !== "undefined") {
|
|
182
|
-
searchParams.set("logging", options.client.logging);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return searchParams.toString();
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* @returns {string}
|
|
190
|
-
*/
|
|
191
|
-
getClientEntry() {
|
|
192
|
-
/** @type {string} */
|
|
193
|
-
const webSocketURL = this.options.webSocketServer
|
|
194
|
-
? this.getWebSocketURL()
|
|
195
|
-
: "";
|
|
196
|
-
|
|
197
|
-
return `${require.resolve("../../client/index.js")}?${webSocketURL}`;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
getHotEntry() {
|
|
201
|
-
const { options } = this;
|
|
202
|
-
|
|
203
|
-
/** @type {(string[] | string)} */
|
|
204
|
-
let hotEntry;
|
|
205
|
-
|
|
206
|
-
if (options.hot === "only") {
|
|
207
|
-
hotEntry = require.resolve("webpack/hot/only-dev-server");
|
|
208
|
-
} else if (options.hot) {
|
|
209
|
-
hotEntry = require.resolve("webpack/hot/dev-server");
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return hotEntry;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* @param {Object} compilerOptions
|
|
217
|
-
* @returns {boolean}
|
|
218
|
-
*/
|
|
219
|
-
// eslint-disable-next-line class-methods-use-this
|
|
220
|
-
isWebTarget(compilerOptions) {
|
|
221
|
-
return compilerOptions.externalsPresets
|
|
222
|
-
? compilerOptions.externalsPresets.web
|
|
223
|
-
: [
|
|
224
|
-
"web",
|
|
225
|
-
"webworker",
|
|
226
|
-
"electron-preload",
|
|
227
|
-
"electron-renderer",
|
|
228
|
-
"node-webkit",
|
|
229
|
-
// eslint-disable-next-line no-undefined
|
|
230
|
-
undefined,
|
|
231
|
-
null,
|
|
232
|
-
].includes(compilerOptions.target);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Apply the plugin
|
|
237
|
-
* @param {Object} compiler the compiler instance
|
|
238
|
-
* @returns {void}
|
|
239
|
-
*/
|
|
240
|
-
apply(compiler) {
|
|
241
|
-
/**
|
|
242
|
-
*
|
|
243
|
-
* Description of the option for checkInject method
|
|
244
|
-
* @typedef {Function} checkInjectOptionsParam
|
|
245
|
-
* @param {Object} _config - compilerConfig
|
|
246
|
-
* @return {Boolean}
|
|
247
|
-
*/
|
|
248
|
-
|
|
249
|
-
const additionalEntries = [];
|
|
250
|
-
|
|
251
|
-
// TODO maybe empty empty client
|
|
252
|
-
if (this.options.client && this.isWebTarget(compiler.options)) {
|
|
253
|
-
const clientEntry = this.getClientEntry();
|
|
254
|
-
|
|
255
|
-
additionalEntries.push(clientEntry);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (this.options.hot) {
|
|
259
|
-
const hotEntry = this.getHotEntry();
|
|
260
|
-
|
|
261
|
-
additionalEntries.push(hotEntry);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const webpack = compiler.webpack || require("webpack");
|
|
265
|
-
|
|
266
|
-
// use a hook to add entries if available
|
|
267
|
-
if (typeof webpack.EntryPlugin !== "undefined") {
|
|
268
|
-
for (const additionalEntry of additionalEntries) {
|
|
269
|
-
new webpack.EntryPlugin(compiler.context, additionalEntry, {
|
|
270
|
-
// eslint-disable-next-line no-undefined
|
|
271
|
-
name: undefined,
|
|
272
|
-
}).apply(compiler);
|
|
273
|
-
}
|
|
274
|
-
} else {
|
|
275
|
-
/**
|
|
276
|
-
* prependEntry Method for webpack 4
|
|
277
|
-
* @param {Entry} originalEntry
|
|
278
|
-
* @param {Entry} newAdditionalEntries
|
|
279
|
-
* @returns {Entry}
|
|
280
|
-
*/
|
|
281
|
-
const prependEntry = (originalEntry, newAdditionalEntries) => {
|
|
282
|
-
if (typeof originalEntry === "function") {
|
|
283
|
-
return () =>
|
|
284
|
-
Promise.resolve(originalEntry()).then((entry) =>
|
|
285
|
-
prependEntry(entry, newAdditionalEntries)
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (
|
|
290
|
-
typeof originalEntry === "object" &&
|
|
291
|
-
!Array.isArray(originalEntry)
|
|
292
|
-
) {
|
|
293
|
-
/** @type {Object<string,string>} */
|
|
294
|
-
const clone = {};
|
|
295
|
-
|
|
296
|
-
Object.keys(originalEntry).forEach((key) => {
|
|
297
|
-
// entry[key] should be a string here
|
|
298
|
-
const entryDescription = originalEntry[key];
|
|
299
|
-
|
|
300
|
-
clone[key] = prependEntry(entryDescription, newAdditionalEntries);
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
return clone;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// in this case, entry is a string or an array.
|
|
307
|
-
// make sure that we do not add duplicates.
|
|
308
|
-
/** @type {Entry} */
|
|
309
|
-
const entriesClone = additionalEntries.slice(0);
|
|
310
|
-
|
|
311
|
-
[].concat(originalEntry).forEach((newEntry) => {
|
|
312
|
-
if (!entriesClone.includes(newEntry)) {
|
|
313
|
-
entriesClone.push(newEntry);
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
return entriesClone;
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
compiler.options.entry = prependEntry(
|
|
321
|
-
compiler.options.entry || "./src",
|
|
322
|
-
additionalEntries
|
|
323
|
-
);
|
|
324
|
-
compiler.hooks.entryOption.call(
|
|
325
|
-
compiler.options.context,
|
|
326
|
-
compiler.options.entry
|
|
327
|
-
);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
const providePlugin = new webpack.ProvidePlugin({
|
|
331
|
-
__webpack_dev_server_client__: this.getWebsocketTransport(),
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
providePlugin.apply(compiler);
|
|
335
|
-
|
|
336
|
-
compiler.options.plugins = compiler.options.plugins || [];
|
|
337
|
-
|
|
338
|
-
if (
|
|
339
|
-
this.options.hot &&
|
|
340
|
-
!compiler.options.plugins.find(
|
|
341
|
-
(p) => p.constructor === webpack.HotModuleReplacementPlugin
|
|
342
|
-
)
|
|
343
|
-
) {
|
|
344
|
-
// apply the HMR plugin, if it didn't exist before.
|
|
345
|
-
const plugin = new webpack.HotModuleReplacementPlugin();
|
|
346
|
-
|
|
347
|
-
plugin.apply(compiler);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
module.exports = DevServerPlugin;
|