Skip to content

Commit

Permalink
fix: respect the client.logging option for HMR logging (#3159)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Apr 9, 2021
1 parent 23331e2 commit 6f3c6ba
Show file tree
Hide file tree
Showing 40 changed files with 980 additions and 414 deletions.
1 change: 1 addition & 0 deletions babel.config.js
Expand Up @@ -14,6 +14,7 @@ module.exports = (api) => {
},
],
],
plugins: ['@babel/plugin-transform-object-assign'],
env: {
test: {
plugins: ['@babel/plugin-transform-runtime'],
Expand Down
11 changes: 6 additions & 5 deletions client-src/clients/SockJSClient.js
Expand Up @@ -8,11 +8,12 @@ module.exports = class SockJSClient extends BaseClient {
constructor(url) {
super();

const sockUrl = url.replace(/^(?:chrome-extension|file)/i, 'http');

this.sock = new SockJS(sockUrl);
this.sock.onerror = (err) => {
log.error(err);
// SockJS requires `http` and `https` protocols
this.sock = new SockJS(
url.replace(/^ws:/i, 'http:').replace(/^wss:/i, 'https:')
);
this.sock.onerror = (error) => {
log.error(error);
};
}

Expand Down
8 changes: 3 additions & 5 deletions client-src/clients/WebsocketClient.js
Expand Up @@ -7,11 +7,9 @@ module.exports = class WebsocketClient extends BaseClient {
constructor(url) {
super();

const wsUrl = url.replace(/^(?:http|chrome-extension|file)/i, 'ws');

this.client = new WebSocket(wsUrl);
this.client.onerror = (err) => {
log.error(err);
this.client = new WebSocket(url);
this.client.onerror = (error) => {
log.error(error);
};
}

Expand Down
55 changes: 40 additions & 15 deletions client-src/index.js
Expand Up @@ -2,19 +2,21 @@

/* global __resourceQuery WorkerGlobalScope */

const webpackHotLog = require('webpack/hot/log');
const stripAnsi = require('./modules/strip-ansi');
const parseURL = require('./utils/parseURL');
const socket = require('./socket');
const overlay = require('./overlay');
const { log, setLogLevel } = require('./utils/log');
const sendMessage = require('./utils/sendMessage');
const reloadApp = require('./utils/reloadApp');
const createSocketUrl = require('./utils/createSocketUrl');
const createSocketURL = require('./utils/createSocketURL');

const status = {
isUnloading: false,
currentHash: '',
};
const options = {
const defaultOptions = {
hot: false,
hotReload: true,
liveReload: false,
Expand All @@ -23,7 +25,40 @@ const options = {
useErrorOverlay: false,
useProgress: false,
};
const socketUrl = createSocketUrl(__resourceQuery);
const parsedResourceQuery = parseURL(__resourceQuery);
const options = defaultOptions;

// Handle Node.js legacy format and `new URL()`
if (parsedResourceQuery.query) {
Object.assign(options, parsedResourceQuery.query);
} else if (parsedResourceQuery.searchParams) {
const paramsToObject = (entries) => {
const result = {};

for (const [key, value] of entries) {
result[key] = value;
}

return result;
};

Object.assign(
options,
paramsToObject(parsedResourceQuery.searchParams.entries())
);
}

const socketURL = createSocketURL(parsedResourceQuery);

function setAllLogLevel(level) {
// This is needed because the HMR logger operate separately from dev server logger
webpackHotLog.setLogLevel(level);
setLogLevel(level);
}

if (options.logging) {
setAllLogLevel(options.logging);
}

self.addEventListener('beforeunload', () => {
status.isUnloading = true;
Expand Down Expand Up @@ -68,17 +103,7 @@ const onSocketMessage = {

sendMessage('StillOk');
},
logging: function logging(level) {
// this is needed because the HMR logger operate separately from
// dev server logger
const hotCtx = require.context('webpack/hot', false, /^\.\/log$/);

if (hotCtx.keys().indexOf('./log') !== -1) {
hotCtx('./log').setLogLevel(level);
}

setLogLevel(level);
},
logging: setAllLogLevel,
overlay(value) {
if (typeof document !== 'undefined') {
if (typeof value === 'boolean') {
Expand Down Expand Up @@ -172,4 +197,4 @@ const onSocketMessage = {
},
};

socket(socketUrl, onSocketMessage);
socket(socketURL, onSocketMessage);
96 changes: 96 additions & 0 deletions client-src/utils/createSocketURL.js
@@ -0,0 +1,96 @@
'use strict';

const url = require('url');

// We handle legacy API that is Node.js specific, and a newer API that implements the same WHATWG URL Standard used by web browsers
// Please look at https://nodejs.org/api/url.html#url_url_strings_and_url_objects
function createSocketURL(parsedURL) {
let { auth, hostname, protocol, port } = parsedURL;

const getURLSearchParam = (name) => {
if (parsedURL.searchParams) {
return parsedURL.searchParams.get(name);
}

return parsedURL.query && parsedURL.query[name];
};

// Node.js module parses it as `::`
// `new URL(urlString, [baseURLstring])` parses it as '[::]'
const isInAddrAny =
hostname === '0.0.0.0' || hostname === '::' || hostname === '[::]';

// check ipv4 and ipv6 `all hostname`
// why do we need this check?
// hostname n/a for file protocol (example, when using electron, ionic)
// see: https://github.com/webpack/webpack-dev-server/pull/384
if (
isInAddrAny &&
self.location.hostname &&
self.location.protocol.indexOf('http') === 0
) {
hostname = self.location.hostname;
}

// `hostname` can be empty when the script path is relative. In that case, specifying a protocol would result in an invalid URL.
// When https is used in the app, secure websockets are always necessary because the browser doesn't accept non-secure websockets.
if (hostname && isInAddrAny && self.location.protocol === 'https:') {
protocol = self.location.protocol;
}

const socketURLProtocol = protocol.replace(
/^(?:http|.+-extension|file)/i,
'ws'
);

// `new URL(urlString, [baseURLstring])` doesn't have `auth` property
// Parse authentication credentials in case we need them
if (parsedURL.username) {
auth = parsedURL.username;

// Since HTTP basic authentication does not allow empty username,
// we only include password if the username is not empty.
if (parsedURL.password) {
// Result: <username>:<password>
auth = auth.concat(':', parsedURL.password);
}
}

const socketURLAuth = auth;

// In case the host is a raw IPv6 address, it can be enclosed in
// the brackets as the brackets are needed in the final URL string.
// Need to remove those as url.format blindly adds its own set of brackets
// if the host string contains colons. That would lead to non-working
// double brackets (e.g. [[::]]) host
//
// All of these sock url params are optionally passed in through resourceQuery,
// so we need to fall back to the default if they are not provided
const socketURLHostname = (
getURLSearchParam('host') ||
hostname ||
'localhost'
).replace(/^\[(.*)\]$/, '$1');

if (!port || port === '0') {
port = self.location.port;
}

const socketURLPort = getURLSearchParam('port') || port;

// If path is provided it'll be passed in via the resourceQuery as a
// query param so it has to be parsed out of the querystring in order for the
// client to open the socket to the correct location.
const socketURLPathname = getURLSearchParam('path') || '/ws';

return url.format({
protocol: socketURLProtocol,
auth: socketURLAuth,
hostname: socketURLHostname,
port: socketURLPort,
pathname: socketURLPathname,
slashes: true,
});
}

module.exports = createSocketURL;
100 changes: 0 additions & 100 deletions client-src/utils/createSocketUrl.js

This file was deleted.

14 changes: 11 additions & 3 deletions client-src/utils/getCurrentScriptSource.js
Expand Up @@ -6,13 +6,21 @@ function getCurrentScriptSource() {
if (document.currentScript) {
return document.currentScript.getAttribute('src');
}
// Fall back to getting all scripts in the document.

// Fallback to getting all scripts running in the document.
const scriptElements = document.scripts || [];
const currentScript = scriptElements[scriptElements.length - 1];
const scriptElementsWithSrc = Array.prototype.filter.call(
scriptElements,
(element) => element.getAttribute('src')
);

if (scriptElementsWithSrc.length > 0) {
const currentScript =
scriptElementsWithSrc[scriptElementsWithSrc.length - 1];

if (currentScript) {
return currentScript.getAttribute('src');
}

// Fail as there was no script to use.
throw new Error('[webpack-dev-server] Failed to get current script source.');
}
Expand Down

0 comments on commit 6f3c6ba

Please sign in to comment.