Skip to content

Commit 0b14013

Browse files
test: refactor client
1 parent e51d258 commit 0b14013

28 files changed

+424
-644
lines changed

client-src/clients/SockJSClient.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ module.exports = class SockJSClient extends BaseClient {
88
constructor(url) {
99
super();
1010

11-
const sockUrl = url.replace(/^(?:chrome-extension|file)/i, 'http');
12-
13-
this.sock = new SockJS(sockUrl);
14-
this.sock.onerror = (err) => {
15-
log.error(err);
11+
// SockJS requires `http` and `https` protocols
12+
this.sock = new SockJS(
13+
url.replace(/^ws:/i, 'http://').replace(/^wss:/i, 'https://')
14+
);
15+
this.sock.onerror = (error) => {
16+
log.error(error);
1617
};
1718
}
1819

client-src/clients/WebsocketClient.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@ module.exports = class WebsocketClient extends BaseClient {
77
constructor(url) {
88
super();
99

10-
const wsUrl = url.replace(/^(?:http|chrome-extension|file)/i, 'ws');
11-
12-
this.client = new WebSocket(wsUrl);
13-
this.client.onerror = (err) => {
14-
log.error(err);
10+
this.client = new WebSocket(url);
11+
this.client.onerror = (error) => {
12+
log.error(error);
1513
};
1614
}
1715

client-src/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const overlay = require('./overlay');
1010
const { log, setLogLevel } = require('./utils/log');
1111
const sendMessage = require('./utils/sendMessage');
1212
const reloadApp = require('./utils/reloadApp');
13-
const createSocketUrl = require('./utils/createSocketUrl');
13+
const createSocketURL = require('./utils/createSocketURL');
1414

1515
const status = {
1616
isUnloading: false,
@@ -27,7 +27,7 @@ const defaultOptions = {
2727
};
2828
const parsedResourceQuery = parseURL(__resourceQuery);
2929
const options = Object.assign(defaultOptions, parsedResourceQuery.query);
30-
const socketURL = createSocketUrl(parsedResourceQuery);
30+
const socketURL = createSocketURL(parsedResourceQuery);
3131

3232
function setAllLogLevel(level) {
3333
// This is needed because the HMR logger operate separately from dev server logger

client-src/utils/createSocketURL.js

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
'use strict';
2+
3+
const url = require('url');
4+
5+
// We handle legacy API that is Node.js specific, and a newer API that implements the same WHATWG URL Standard used by web browsers
6+
// Please look at https://nodejs.org/api/url.html#url_url_strings_and_url_objects
7+
function createSocketURL(parsedURL) {
8+
let { auth, hostname, protocol, port } = parsedURL;
9+
10+
const getURLSearchParam = (name) => {
11+
if (parsedURL.searchParams) {
12+
return parsedURL.searchParams.get(name);
13+
}
14+
15+
return parsedURL.query && parsedURL.query[name];
16+
};
17+
18+
// Node.js module parses it as `::`
19+
// `new URL(urlString, [baseURLstring])` parses it as '[::]'
20+
const isInAddrAny =
21+
hostname === '0.0.0.0' || hostname === '::' || hostname === '[::]';
22+
23+
// check ipv4 and ipv6 `all hostname`
24+
// why do we need this check?
25+
// hostname n/a for file protocol (example, when using electron, ionic)
26+
// see: https://github.com/webpack/webpack-dev-server/pull/384
27+
if (
28+
isInAddrAny &&
29+
self.location.hostname &&
30+
self.location.protocol.indexOf('http') === 0
31+
) {
32+
hostname = self.location.hostname;
33+
}
34+
35+
// `hostname` can be empty when the script path is relative. In that case, specifying a protocol would result in an invalid URL.
36+
// When https is used in the app, secure websockets are always necessary because the browser doesn't accept non-secure websockets.
37+
if (hostname && isInAddrAny && self.location.protocol === 'https:') {
38+
protocol = self.location.protocol;
39+
}
40+
41+
const socketURLProtocol = protocol.replace(
42+
/^(?:http|.+-extension|file)/i,
43+
'ws'
44+
);
45+
46+
// `new URL(urlString, [baseURLstring])` doesn't have `auth` property
47+
// Parse authentication credentials in case we need them
48+
if (parsedURL.username) {
49+
auth = parsedURL.username;
50+
51+
// Since HTTP basic authentication does not allow empty username,
52+
// we only include password if the username is not empty.
53+
if (parsedURL.password) {
54+
// Result: <username>:<password>
55+
auth = auth.concat(':', parsedURL.password);
56+
}
57+
}
58+
59+
const socketURLAuth = auth;
60+
61+
// In case the host is a raw IPv6 address, it can be enclosed in
62+
// the brackets as the brackets are needed in the final URL string.
63+
// Need to remove those as url.format blindly adds its own set of brackets
64+
// if the host string contains colons. That would lead to non-working
65+
// double brackets (e.g. [[::]]) host
66+
//
67+
// All of these sock url params are optionally passed in through resourceQuery,
68+
// so we need to fall back to the default if they are not provided
69+
const socketURLHostname = (getURLSearchParam('host') || hostname).replace(
70+
/^\[(.*)\]$/,
71+
'$1'
72+
);
73+
74+
if (!port || port === '0') {
75+
port = self.location.port;
76+
}
77+
78+
const socketURLPort = getURLSearchParam('port') || port;
79+
80+
// If path is provided it'll be passed in via the resourceQuery as a
81+
// query param so it has to be parsed out of the querystring in order for the
82+
// client to open the socket to the correct location.
83+
const socketURLPathname = getURLSearchParam('path') || '/ws';
84+
85+
return url.format({
86+
protocol: socketURLProtocol,
87+
auth: socketURLAuth,
88+
hostname: socketURLHostname,
89+
port: socketURLPort,
90+
pathname: socketURLPathname,
91+
});
92+
}
93+
94+
module.exports = createSocketURL;

client-src/utils/createSocketUrl.js

Lines changed: 0 additions & 61 deletions
This file was deleted.

client-src/utils/getCurrentScriptSource.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,17 @@ function getCurrentScriptSource() {
77
return document.currentScript.getAttribute('src');
88
}
99

10-
// Fall back to getting all scripts in the document.
10+
// Fallback to getting all scripts running in the document.
1111
const scriptElements = document.scripts || [];
12-
const currentScript = scriptElements[scriptElements.length - 1];
12+
const scriptElementsWithSrc = Array.prototype.filter.call(
13+
scriptElements,
14+
(element) => element.getAttribute('src')
15+
);
16+
17+
if (scriptElementsWithSrc.length > 0) {
18+
const currentScript =
19+
scriptElementsWithSrc[scriptElementsWithSrc.length - 1];
1320

14-
if (currentScript) {
1521
return currentScript.getAttribute('src');
1622
}
1723

client-src/utils/parseURL.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,27 @@ function parseURL(resourceQuery) {
1919
);
2020
} else {
2121
// Else, get the url from the <script> this file was called with.
22-
const scriptHost = getCurrentScriptSource();
22+
const scriptSource = getCurrentScriptSource();
2323

24-
options = url.parse(scriptHost || '/', true, true);
24+
if (scriptSource) {
25+
let scriptSourceURL;
26+
27+
try {
28+
// The placeholder `baseURL` with `window.location.href`,
29+
// is to allow parsing of path-relative or protocol-relative URLs,
30+
// and will have no effect if `scriptSource` is a fully valid URL.
31+
scriptSourceURL = new URL(scriptSource, self.location.href);
32+
} catch (e) {
33+
// URL parsing failed, do nothing.
34+
// We will still proceed to see if we can recover using `resourceQuery`
35+
}
36+
37+
if (scriptSourceURL) {
38+
options = scriptSourceURL;
39+
}
40+
} else {
41+
options = url.parse(self.location.href, true, true);
42+
}
2543
}
2644

2745
return options;

client-src/utils/reloadApp.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ function reloadApp(
4444

4545
function applyReload(rootWindow, intervalId) {
4646
clearInterval(intervalId);
47+
4748
log.info('App updated. Reloading...');
49+
4850
rootWindow.location.reload();
4951
}
5052
}

lib/utils/createDomain.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const url = require('url');
44

55
function createDomain(options, server) {
66
const protocol = options.https ? 'https' : 'http';
7-
// use location hostname and port by default in createSocketUrl
7+
// use location hostname and port by default in createSocketURL
88
// ipv6 detection is not required as 0.0.0.0 is just used as a placeholder
99
let hostname;
1010

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
"tcp-port-used": "^1.0.2",
105105
"typescript": "^4.2.3",
106106
"url-loader": "^4.1.1",
107-
"webpack": "^5.30.0",
107+
"webpack": "^5.31.0",
108108
"webpack-cli": "^4.6.0",
109109
"webpack-merge": "^5.7.3"
110110
},
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`getCurrentScriptSource should fail when script source doesn't exist 1`] = `[Error: [webpack-dev-server] Failed to get current script source.]`;
3+
exports[`'getCurrentScriptSource' function should fail when 'document.currentScript' doesn't exist and no 'script' tags 1`] = `[Error: [webpack-dev-server] Failed to get current script source.]`;
4+
5+
exports[`'getCurrentScriptSource' function should fail when 'document.scripts' doesn't exist and no scripts 1`] = `[Error: [webpack-dev-server] Failed to get current script source.]`;
6+
7+
exports[`'getCurrentScriptSource' function should fail when no scripts with the 'scr' attribute 1`] = `[Error: [webpack-dev-server] Failed to get current script source.]`;
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`getCurrentScriptSource should fail when script source doesn't exist 1`] = `[Error: [webpack-dev-server] Failed to get current script source.]`;
3+
exports[`'getCurrentScriptSource' function should fail when 'document.currentScript' doesn't exist and no 'script' tags 1`] = `[Error: [webpack-dev-server] Failed to get current script source.]`;
4+
5+
exports[`'getCurrentScriptSource' function should fail when 'document.scripts' doesn't exist and no scripts 1`] = `[Error: [webpack-dev-server] Failed to get current script source.]`;
6+
7+
exports[`'getCurrentScriptSource' function should fail when no scripts with the 'scr' attribute 1`] = `[Error: [webpack-dev-server] Failed to get current script source.]`;

test/client/utils/__snapshots__/log.test.js.snap.webpack4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`log should set log level via setLogLevel 1`] = `
3+
exports[`'log' function should set log level via setLogLevel 1`] = `
44
Array [
55
Array [
66
Object {

test/client/utils/__snapshots__/log.test.js.snap.webpack5

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`log should set log level via setLogLevel 1`] = `
3+
exports[`'log' function should set log level via setLogLevel 1`] = `
44
Array [
55
Array [
66
Object {

0 commit comments

Comments
 (0)