webpack-dev-server 3.7.1 → 3.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/Server.js CHANGED
@@ -23,6 +23,8 @@ const serveIndex = require('serve-index');
23
23
  const webpack = require('webpack');
24
24
  const webpackDevMiddleware = require('webpack-dev-middleware');
25
25
  const validateOptions = require('schema-utils');
26
+ const isAbsoluteUrl = require('is-absolute-url');
27
+ const normalizeOptions = require('./utils/normalizeOptions');
26
28
  const updateCompiler = require('./utils/updateCompiler');
27
29
  const createLogger = require('./utils/createLogger');
28
30
  const getCertificate = require('./utils/getCertificate');
@@ -54,27 +56,21 @@ class Server {
54
56
 
55
57
  validateOptions(schema, options, 'webpack Dev Server');
56
58
 
57
- updateCompiler(compiler, options);
58
-
59
59
  this.compiler = compiler;
60
60
  this.options = options;
61
61
 
62
- // Setup default value
63
- this.options.contentBase =
64
- this.options.contentBase !== undefined
65
- ? this.options.contentBase
66
- : process.cwd();
67
-
68
62
  this.log = _log || createLogger(options);
69
63
 
70
- if (this.options.serverMode === undefined) {
71
- this.options.serverMode = 'sockjs';
72
- } else {
64
+ if (this.options.transportMode !== undefined) {
73
65
  this.log.warn(
74
- 'serverMode is an experimental option, meaning its usage could potentially change without warning'
66
+ 'transportMode is an experimental option, meaning its usage could potentially change without warning'
75
67
  );
76
68
  }
77
69
 
70
+ normalizeOptions(this.compiler, this.options);
71
+
72
+ updateCompiler(this.compiler, this.options);
73
+
78
74
  // this.SocketServerImplementation is a class, so it must be instantiated before use
79
75
  this.socketServerImplementation = getSocketServerImplementation(
80
76
  this.options
@@ -102,10 +98,6 @@ class Server {
102
98
  this.allowedHosts = this.options.allowedHosts;
103
99
  this.disableHostCheck = !!this.options.disableHostCheck;
104
100
 
105
- if (!this.options.watchOptions) {
106
- this.options.watchOptions = {};
107
- }
108
-
109
101
  this.watchOptions = options.watchOptions || {};
110
102
 
111
103
  // Replace leading and trailing slashes to normalize path
@@ -144,23 +136,25 @@ class Server {
144
136
  }
145
137
 
146
138
  setupProgressPlugin() {
147
- const progressPlugin = new webpack.ProgressPlugin(
148
- (percent, msg, addInfo) => {
149
- percent = Math.floor(percent * 100);
139
+ // for CLI output
140
+ new webpack.ProgressPlugin({
141
+ profile: !!this.options.profile,
142
+ }).apply(this.compiler);
150
143
 
151
- if (percent === 100) {
152
- msg = 'Compilation completed';
153
- }
144
+ // for browser console output
145
+ new webpack.ProgressPlugin((percent, msg, addInfo) => {
146
+ percent = Math.floor(percent * 100);
154
147
 
155
- if (addInfo) {
156
- msg = `${msg} (${addInfo})`;
157
- }
148
+ if (percent === 100) {
149
+ msg = 'Compilation completed';
150
+ }
158
151
 
159
- this.sockWrite(this.sockets, 'progress-update', { percent, msg });
152
+ if (addInfo) {
153
+ msg = `${msg} (${addInfo})`;
160
154
  }
161
- );
162
155
 
163
- progressPlugin.apply(this.compiler);
156
+ this.sockWrite(this.sockets, 'progress-update', { percent, msg });
157
+ }).apply(this.compiler);
164
158
  }
165
159
 
166
160
  setupApp() {
@@ -280,14 +274,12 @@ class Server {
280
274
  * ]
281
275
  */
282
276
  this.options.proxy.forEach((proxyConfigOrCallback) => {
283
- let proxyConfig;
284
277
  let proxyMiddleware;
285
278
 
286
- if (typeof proxyConfigOrCallback === 'function') {
287
- proxyConfig = proxyConfigOrCallback();
288
- } else {
289
- proxyConfig = proxyConfigOrCallback;
290
- }
279
+ let proxyConfig =
280
+ typeof proxyConfigOrCallback === 'function'
281
+ ? proxyConfigOrCallback()
282
+ : proxyConfigOrCallback;
291
283
 
292
284
  proxyMiddleware = getProxyMiddleware(proxyConfig);
293
285
 
@@ -307,7 +299,7 @@ class Server {
307
299
 
308
300
  // - Check if we have a bypass function defined
309
301
  // - In case the bypass function is defined we'll retrieve the
310
- // bypassUrl from it otherwise byPassUrl would be null
302
+ // bypassUrl from it otherwise bypassUrl would be null
311
303
  const isByPassFuncDefined = typeof proxyConfig.bypass === 'function';
312
304
  const bypassUrl = isByPassFuncDefined
313
305
  ? proxyConfig.bypass(req, res, proxyConfig)
@@ -347,7 +339,7 @@ class Server {
347
339
  contentBase.forEach((item) => {
348
340
  this.app.get('*', express.static(item));
349
341
  });
350
- } else if (/^(https?:)?\/\//.test(contentBase)) {
342
+ } else if (isAbsoluteUrl(String(contentBase))) {
351
343
  this.log.warn(
352
344
  'Using a URL as contentBase is deprecated and will be removed in the next major version. Please use the proxy option instead.'
353
345
  );
@@ -399,8 +391,8 @@ class Server {
399
391
  this.app.get('*', serveIndex(item));
400
392
  });
401
393
  } else if (
402
- !/^(https?:)?\/\//.test(contentBase) &&
403
- typeof contentBase !== 'number'
394
+ typeof contentBase !== 'number' &&
395
+ !isAbsoluteUrl(String(contentBase))
404
396
  ) {
405
397
  this.app.get('*', serveIndex(contentBase));
406
398
  }
@@ -409,13 +401,13 @@ class Server {
409
401
  setupWatchStaticFeature() {
410
402
  const contentBase = this.options.contentBase;
411
403
 
412
- if (
413
- /^(https?:)?\/\//.test(contentBase) ||
414
- typeof contentBase === 'number'
415
- ) {
404
+ if (isAbsoluteUrl(String(contentBase)) || typeof contentBase === 'number') {
416
405
  throw new Error('Watching remote files is not supported.');
417
406
  } else if (Array.isArray(contentBase)) {
418
407
  contentBase.forEach((item) => {
408
+ if (isAbsoluteUrl(String(item)) || typeof item === 'number') {
409
+ throw new Error('Watching remote files is not supported.');
410
+ }
419
411
  this._watch(item);
420
412
  });
421
413
  } else {
@@ -653,7 +645,6 @@ class Server {
653
645
  // The relevant issues are:
654
646
  // https://github.com/spdy-http2/node-spdy/issues/350
655
647
  // https://github.com/webpack/webpack-dev-server/issues/1592
656
- // eslint-disable-next-line global-require
657
648
  this.listeningApp = require('spdy').createServer(
658
649
  this.options.https,
659
650
  this.app
@@ -668,25 +659,29 @@ class Server {
668
659
  const SocketServerImplementation = this.socketServerImplementation;
669
660
  this.socketServer = new SocketServerImplementation(this);
670
661
 
671
- this.socketServer.onConnection((connection) => {
662
+ this.socketServer.onConnection((connection, headers) => {
672
663
  if (!connection) {
673
664
  return;
674
665
  }
675
666
 
676
- if (
677
- !this.checkHost(connection.headers) ||
678
- !this.checkOrigin(connection.headers)
679
- ) {
667
+ if (!headers) {
668
+ this.log.warn(
669
+ 'transportMode.server implementation must pass headers to the callback of onConnection(f) ' +
670
+ 'via f(connection, headers) in order for clients to pass a headers security check'
671
+ );
672
+ }
673
+
674
+ if (!headers || !this.checkHost(headers) || !this.checkOrigin(headers)) {
680
675
  this.sockWrite([connection], 'error', 'Invalid Host/Origin header');
681
676
 
682
- connection.close();
677
+ this.socketServer.close(connection);
683
678
 
684
679
  return;
685
680
  }
686
681
 
687
682
  this.sockets.push(connection);
688
683
 
689
- connection.on('close', () => {
684
+ this.socketServer.onConnectionClose(connection, () => {
690
685
  const idx = this.sockets.indexOf(connection);
691
686
 
692
687
  if (idx >= 0) {
@@ -854,13 +849,18 @@ class Server {
854
849
  // an IPv6-address in URLs,
855
850
  // these are removed from the hostname in url.parse(),
856
851
  // so we have the pure IPv6-address in hostname.
857
- if (ip.isV4Format(hostname) || ip.isV6Format(hostname)) {
852
+ // always allow localhost host, for convenience (hostname === 'localhost')
853
+ // allow hostname of listening address (hostname === this.hostname)
854
+ const isValidHostname =
855
+ ip.isV4Format(hostname) ||
856
+ ip.isV6Format(hostname) ||
857
+ hostname === 'localhost' ||
858
+ hostname === this.hostname;
859
+
860
+ if (isValidHostname) {
858
861
  return true;
859
862
  }
860
863
  // always allow localhost host, for convenience
861
- if (hostname === 'localhost') {
862
- return true;
863
- }
864
864
  // allow if hostname is in allowedHosts
865
865
  if (this.allowedHosts && this.allowedHosts.length) {
866
866
  for (let hostIdx = 0; hostIdx < this.allowedHosts.length; hostIdx++) {
@@ -873,23 +873,18 @@ class Server {
873
873
  // support "." as a subdomain wildcard
874
874
  // e.g. ".example.com" will allow "example.com", "www.example.com", "subdomain.example.com", etc
875
875
  if (allowedHost[0] === '.') {
876
- // "example.com"
877
- if (hostname === allowedHost.substring(1)) {
878
- return true;
879
- }
880
- // "*.example.com"
881
- if (hostname.endsWith(allowedHost)) {
876
+ // "example.com" (hostname === allowedHost.substring(1))
877
+ // "*.example.com" (hostname.endsWith(allowedHost))
878
+ if (
879
+ hostname === allowedHost.substring(1) ||
880
+ hostname.endsWith(allowedHost)
881
+ ) {
882
882
  return true;
883
883
  }
884
884
  }
885
885
  }
886
886
  }
887
887
 
888
- // allow hostname of listening address
889
- if (hostname === this.hostname) {
890
- return true;
891
- }
892
-
893
888
  // also allow public hostname if provided
894
889
  if (typeof this.publicHost === 'string') {
895
890
  const idxPublic = this.publicHost.indexOf(':');
@@ -924,14 +919,9 @@ class Server {
924
919
  return next();
925
920
  }
926
921
  // Serve a page that executes the javascript
927
- res.write(
928
- '<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body><script type="text/javascript" charset="utf-8" src="'
929
- );
930
- res.write(_path);
931
- res.write('.js');
932
- res.write(req._parsedUrl.search || '');
933
-
934
- res.end('"></script></body></html>');
922
+ const queries = req._parsedUrl.search || '';
923
+ 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>`;
924
+ res.send(responsePage);
935
925
  } catch (err) {
936
926
  return next();
937
927
  }
@@ -939,13 +929,14 @@ class Server {
939
929
 
940
930
  // send stats to a socket or multiple sockets
941
931
  _sendStats(sockets, stats, force) {
942
- if (
932
+ const shouldEmit =
943
933
  !force &&
944
934
  stats &&
945
935
  (!stats.errors || stats.errors.length === 0) &&
946
936
  stats.assets &&
947
- stats.assets.every((asset) => !asset.emitted)
948
- ) {
937
+ stats.assets.every((asset) => !asset.emitted);
938
+
939
+ if (shouldEmit) {
949
940
  return this.sockWrite(sockets, 'still-ok');
950
941
  }
951
942
 
package/lib/options.json CHANGED
@@ -255,6 +255,9 @@
255
255
  }
256
256
  ]
257
257
  },
258
+ "profile": {
259
+ "type": "boolean"
260
+ },
258
261
  "progress": {
259
262
  "type": "boolean"
260
263
  },
@@ -297,16 +300,6 @@
297
300
  "serveIndex": {
298
301
  "type": "boolean"
299
302
  },
300
- "serverMode": {
301
- "anyOf": [
302
- {
303
- "type": "string"
304
- },
305
- {
306
- "instanceof": "Function"
307
- }
308
- ]
309
- },
310
303
  "serverSideRender": {
311
304
  "type": "boolean"
312
305
  },
@@ -358,6 +351,32 @@
358
351
  }
359
352
  ]
360
353
  },
354
+ "transportMode": {
355
+ "anyOf": [
356
+ {
357
+ "type": "object",
358
+ "properties": {
359
+ "client": {
360
+ "type": "string"
361
+ },
362
+ "server": {
363
+ "anyOf": [
364
+ {
365
+ "type": "string"
366
+ },
367
+ {
368
+ "instanceof": "Function"
369
+ }
370
+ ]
371
+ }
372
+ },
373
+ "additionalProperties": false
374
+ },
375
+ {
376
+ "enum": ["sockjs", "ws"]
377
+ }
378
+ ]
379
+ },
361
380
  "useLocalIp": {
362
381
  "type": "boolean"
363
382
  },
@@ -389,7 +408,7 @@
389
408
  "bonjour": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverbonjour)",
390
409
  "ca": "should be {String|Buffer}",
391
410
  "cert": "should be {String|Buffer}",
392
- "clientLogLevel": "should be {String} and equal to one of the allowed values\n\n [ 'info', 'warn', 'error', 'debug', 'trace', 'silent' ]\n\n (https://webpack.js.org/configuration/dev-server/#devserverclientloglevel)",
411
+ "clientLogLevel": "should be {String} and equal to one of the allowed values\n\n [ 'none', 'silent', 'info', 'debug', 'trace', 'error', 'warning', 'warn' ]\n\n (https://webpack.js.org/configuration/dev-server/#devserverclientloglevel)",
393
412
  "compress": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devservercompress)",
394
413
  "contentBase": "should be {Number|String|Array} (https://webpack.js.org/configuration/dev-server/#devservercontentbase)",
395
414
  "disableHostCheck": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverdisablehostcheck)",
@@ -422,6 +441,7 @@
422
441
  "pfx": "should be {String|Buffer} (https://webpack.js.org/configuration/dev-server/#devserverpfx)",
423
442
  "pfxPassphrase": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserverpfxpassphrase)",
424
443
  "port": "should be {Number|String|Null} (https://webpack.js.org/configuration/dev-server/#devserverport)",
444
+ "profile": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverprofile)",
425
445
  "progress": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverprogress---cli-only)",
426
446
  "proxy": "should be {Object|Array} (https://webpack.js.org/configuration/dev-server/#devserverproxy)",
427
447
  "public": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserverpublic)",
@@ -430,7 +450,6 @@
430
450
  "reporter": "should be {Function} (https://github.com/webpack/webpack-dev-middleware#reporter)",
431
451
  "requestCert": "should be {Boolean}",
432
452
  "serveIndex": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverserveindex)",
433
- "serverMode": "should be {String|Function} (https://webpack.js.org/configuration/dev-server/#devserverservermode-)",
434
453
  "serverSideRender": "should be {Boolean} (https://github.com/webpack/webpack-dev-middleware#serversiderender)",
435
454
  "setup": "should be {Function} (https://webpack.js.org/configuration/dev-server/#devserversetup)",
436
455
  "sockHost": "should be {String|Null} (https://webpack.js.org/configuration/dev-server/#devserversockhost)",
@@ -439,6 +458,7 @@
439
458
  "socket": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserversocket)",
440
459
  "staticOptions": "should be {Object} (https://webpack.js.org/configuration/dev-server/#devserverstaticoptions)",
441
460
  "stats": "should be {Object|Boolean} (https://webpack.js.org/configuration/dev-server/#devserverstats-)",
461
+ "transportMode": "should be {String|Object} (https://webpack.js.org/configuration/dev-server/#devservertransportmode)",
442
462
  "useLocalIp": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserveruselocalip)",
443
463
  "warn": "should be {Function}",
444
464
  "watchContentBase": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverwatchcontentbase)",
@@ -11,7 +11,6 @@ const BaseServer = require('./BaseServer');
11
11
  // sockjs will remove Origin header, however Origin header is required for checking host.
12
12
  // See https://github.com/webpack/webpack-dev-server/issues/1604 for more information
13
13
  {
14
- // eslint-disable-next-line global-require
15
14
  const SockjsSession = require('sockjs/lib/transport').Session;
16
15
  const decorateConnection = SockjsSession.prototype.decorateConnection;
17
16
  SockjsSession.prototype.decorateConnection = function(req) {
@@ -50,6 +49,11 @@ module.exports = class SockJSServer extends BaseServer {
50
49
  }
51
50
 
52
51
  send(connection, message) {
52
+ // prevent cases where the server is trying to send data while connection is closing
53
+ if (connection.readyState !== 1) {
54
+ return;
55
+ }
56
+
53
57
  connection.write(message);
54
58
  }
55
59
 
@@ -57,8 +61,14 @@ module.exports = class SockJSServer extends BaseServer {
57
61
  connection.close();
58
62
  }
59
63
 
60
- // f should return the resulting connection
64
+ // f should be passed the resulting connection and the connection headers
61
65
  onConnection(f) {
62
- this.socket.on('connection', f);
66
+ this.socket.on('connection', (connection) => {
67
+ f(connection, connection ? connection.headers : null);
68
+ });
69
+ }
70
+
71
+ onConnectionClose(connection, f) {
72
+ connection.on('close', f);
63
73
  }
64
74
  };
@@ -1,8 +1,45 @@
1
1
  'use strict';
2
2
 
3
- // const ws = require('ws');
3
+ /* eslint-disable
4
+ class-methods-use-this
5
+ */
6
+ const ws = require('ws');
4
7
  const BaseServer = require('./BaseServer');
5
8
 
6
- // ws server implementation under construction
7
- // will need changes in the client as well to function
8
- module.exports = class WebsocketServer extends BaseServer {};
9
+ module.exports = class WebsocketServer extends BaseServer {
10
+ constructor(server) {
11
+ super(server);
12
+ this.wsServer = new ws.Server({
13
+ server: this.server.listeningApp,
14
+ path: this.server.sockPath,
15
+ });
16
+
17
+ this.wsServer.on('error', (err) => {
18
+ this.server.log.error(err.message);
19
+ });
20
+ }
21
+
22
+ send(connection, message) {
23
+ // prevent cases where the server is trying to send data while connection is closing
24
+ if (connection.readyState !== 1) {
25
+ return;
26
+ }
27
+
28
+ connection.send(message);
29
+ }
30
+
31
+ close(connection) {
32
+ connection.close();
33
+ }
34
+
35
+ // f should be passed the resulting connection and the connection headers
36
+ onConnection(f) {
37
+ this.wsServer.on('connection', (connection, req) => {
38
+ f(connection, req.headers);
39
+ });
40
+ }
41
+
42
+ onConnectionClose(connection, f) {
43
+ connection.on('close', f);
44
+ }
45
+ };
@@ -3,6 +3,18 @@
3
3
  const webpack = require('webpack');
4
4
  const createDomain = require('./createDomain');
5
5
 
6
+ /**
7
+ * A Entry, it can be of type string or string[] or Object<string | string[],string>
8
+ * @typedef {(string[] | string | Object<string | string[],string>)} Entry
9
+ */
10
+
11
+ /**
12
+ * Add entries Method
13
+ * @param {?Object} config - Webpack config
14
+ * @param {?Object} options - Dev-Server options
15
+ * @param {?Object} server
16
+ * @returns {void}
17
+ */
6
18
  function addEntries(config, options, server) {
7
19
  if (options.inline !== false) {
8
20
  // we're stubbing the app in this method as it's static and doesn't require
@@ -15,13 +27,20 @@ function addEntries(config, options, server) {
15
27
  },
16
28
  };
17
29
 
30
+ /** @type {string} */
18
31
  const domain = createDomain(options, app);
32
+ /** @type {string} */
19
33
  const sockHost = options.sockHost ? `&sockHost=${options.sockHost}` : '';
34
+ /** @type {string} */
20
35
  const sockPath = options.sockPath ? `&sockPath=${options.sockPath}` : '';
36
+ /** @type {string} */
21
37
  const sockPort = options.sockPort ? `&sockPort=${options.sockPort}` : '';
38
+ /** @type {string} */
22
39
  const clientEntry = `${require.resolve(
23
40
  '../../client/'
24
41
  )}?${domain}${sockHost}${sockPath}${sockPort}`;
42
+
43
+ /** @type {(string[] | string)} */
25
44
  let hotEntry;
26
45
 
27
46
  if (options.hotOnly) {
@@ -29,7 +48,12 @@ function addEntries(config, options, server) {
29
48
  } else if (options.hot) {
30
49
  hotEntry = require.resolve('webpack/hot/dev-server');
31
50
  }
32
-
51
+ /**
52
+ * prependEntry Method
53
+ * @param {Entry} originalEntry
54
+ * @param {Entry} additionalEntries
55
+ * @returns {Entry}
56
+ */
33
57
  const prependEntry = (originalEntry, additionalEntries) => {
34
58
  if (typeof originalEntry === 'function') {
35
59
  return () =>
@@ -39,6 +63,7 @@ function addEntries(config, options, server) {
39
63
  }
40
64
 
41
65
  if (typeof originalEntry === 'object' && !Array.isArray(originalEntry)) {
66
+ /** @type {Object<string,string>} */
42
67
  const clone = {};
43
68
 
44
69
  Object.keys(originalEntry).forEach((key) => {
@@ -51,6 +76,7 @@ function addEntries(config, options, server) {
51
76
 
52
77
  // in this case, entry is a string or an array.
53
78
  // make sure that we do not add duplicates.
79
+ /** @type {Entry} */
54
80
  const entriesClone = additionalEntries.slice(0);
55
81
  [].concat(originalEntry).forEach((newEntry) => {
56
82
  if (!entriesClone.includes(newEntry)) {
@@ -60,21 +86,40 @@ function addEntries(config, options, server) {
60
86
  return entriesClone;
61
87
  };
62
88
 
89
+ /**
90
+ *
91
+ * Description of the option for checkInject method
92
+ * @typedef {Function} checkInjectOptionsParam
93
+ * @param {Object} _config - compilerConfig
94
+ * @return {Boolean}
95
+ */
96
+
97
+ /**
98
+ *
99
+ * @param {Boolean | checkInjectOptionsParam} option - inject(Hot|Client) it is Boolean | fn => Boolean
100
+ * @param {Object} _config
101
+ * @param {Boolean} defaultValue
102
+ * @return {Boolean}
103
+ */
63
104
  // eslint-disable-next-line no-shadow
64
- const checkInject = (option, config, defaultValue) => {
105
+ const checkInject = (option, _config, defaultValue) => {
65
106
  if (typeof option === 'boolean') return option;
66
- if (typeof option === 'function') return option(config);
107
+ if (typeof option === 'function') return option(_config);
67
108
  return defaultValue;
68
109
  };
69
110
 
70
111
  // eslint-disable-next-line no-shadow
71
112
  [].concat(config).forEach((config) => {
72
- const webTarget =
73
- config.target === 'web' ||
74
- config.target === 'webworker' ||
75
- config.target === 'electron-renderer' ||
76
- config.target === 'node-webkit' ||
77
- config.target == null;
113
+ /** @type {Boolean} */
114
+ const webTarget = [
115
+ 'web',
116
+ 'webworker',
117
+ 'electron-renderer',
118
+ 'node-webkit',
119
+ undefined, // eslint-disable-line
120
+ null,
121
+ ].includes(config.target);
122
+ /** @type {Entry} */
78
123
  const additionalEntries = checkInject(
79
124
  options.injectClient,
80
125
  config,
@@ -93,8 +138,9 @@ function addEntries(config, options, server) {
93
138
  config.plugins = config.plugins || [];
94
139
  if (
95
140
  !config.plugins.find(
96
- (plugin) =>
97
- plugin.constructor === webpack.HotModuleReplacementPlugin
141
+ // Check for the name rather than the constructor reference in case
142
+ // there are multiple copies of webpack installed
143
+ (plugin) => plugin.constructor.name === 'HotModuleReplacementPlugin'
98
144
  )
99
145
  ) {
100
146
  config.plugins.push(new webpack.HotModuleReplacementPlugin());
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const path = require('path');
4
+ const isAbsoluteUrl = require('is-absolute-url');
4
5
  const defaultTo = require('./defaultTo');
5
6
 
6
7
  function createConfig(config, argv, { port }) {
@@ -46,6 +47,10 @@ function createConfig(config, argv, { port }) {
46
47
  options.liveReload = false;
47
48
  }
48
49
 
50
+ if (argv.profile) {
51
+ options.profile = argv.profile;
52
+ }
53
+
49
54
  if (argv.progress) {
50
55
  options.progress = argv.progress;
51
56
  }
@@ -60,7 +65,7 @@ function createConfig(config, argv, { port }) {
60
65
  (firstWpOpt.output && firstWpOpt.output.publicPath) || '';
61
66
 
62
67
  if (
63
- !/^(https?:)?\/\//.test(options.publicPath) &&
68
+ !isAbsoluteUrl(String(options.publicPath)) &&
64
69
  options.publicPath[0] !== '/'
65
70
  ) {
66
71
  options.publicPath = `/${options.publicPath}`;
@@ -109,7 +114,7 @@ function createConfig(config, argv, { port }) {
109
114
  options.contentBase = options.contentBase.map((p) => path.resolve(p));
110
115
  } else if (/^[0-9]$/.test(options.contentBase)) {
111
116
  options.contentBase = +options.contentBase;
112
- } else if (!/^(https?:)?\/\//.test(options.contentBase)) {
117
+ } else if (!isAbsoluteUrl(String(options.contentBase))) {
113
118
  options.contentBase = path.resolve(options.contentBase);
114
119
  }
115
120
  }
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ function getSocketClientPath(options) {
4
+ let ClientImplementation;
5
+ let clientImplFound = true;
6
+ switch (typeof options.transportMode.client) {
7
+ case 'string':
8
+ // could be 'sockjs', 'ws', or a path that should be required
9
+ if (options.transportMode.client === 'sockjs') {
10
+ ClientImplementation = require('../../client/clients/SockJSClient');
11
+ } else if (options.transportMode.client === 'ws') {
12
+ ClientImplementation = require('../../client/clients/WebsocketClient');
13
+ } else {
14
+ try {
15
+ // eslint-disable-next-line import/no-dynamic-require
16
+ ClientImplementation = require(options.transportMode.client);
17
+ } catch (e) {
18
+ clientImplFound = false;
19
+ }
20
+ }
21
+ break;
22
+ default:
23
+ clientImplFound = false;
24
+ }
25
+
26
+ if (!clientImplFound) {
27
+ throw new Error(
28
+ "transportMode.client must be a string denoting a default implementation (e.g. 'sockjs', 'ws') or a full path to " +
29
+ 'a JS file which exports a class extending BaseClient (webpack-dev-server/client-src/clients/BaseClient) ' +
30
+ 'via require.resolve(...)'
31
+ );
32
+ }
33
+
34
+ return ClientImplementation.getClientPath(options);
35
+ }
36
+
37
+ module.exports = getSocketClientPath;