@pmmmwh/react-refresh-webpack-plugin
Version:
An **EXPERIMENTAL** Webpack plugin to enable "Fast Refresh" (also previously known as _Hot Reloading_) for React components.
130 lines (113 loc) • 4.31 kB
JavaScript
const getCurrentScriptSource = require('./getCurrentScriptSource.js');
/**
* @typedef {Object} SocketUrlParts
* @property {string} [auth]
* @property {string} hostname
* @property {string} [protocol]
* @property {string} pathname
* @property {string} port
*/
/**
* Parse current location and Webpack's `__resourceQuery` into parts that can create a valid socket URL.
* @param {string} [resourceQuery] The Webpack `__resourceQuery` string.
* @param {import('./getWDSMetadata').WDSMetaObj} [metadata] The parsed WDS metadata object.
* @returns {SocketUrlParts} The parsed URL parts.
* @see https://webpack.js.org/api/module-variables/#__resourcequery-webpack-specific
*/
function getSocketUrlParts(resourceQuery, metadata) {
if (typeof metadata === 'undefined') {
metadata = {};
}
const scriptSource = getCurrentScriptSource();
let url = {};
try {
// The placeholder `baseURL` with `window.location.href`,
// is to allow parsing of path-relative or protocol-relative URLs,
// and will have no effect if `scriptSource` is a fully valid URL.
url = new URL(scriptSource, window.location.href);
} catch (e) {
// URL parsing failed, do nothing.
// We will still proceed to see if we can recover using `resourceQuery`
}
/** @type {string | undefined} */
let auth;
/** @type {string | undefined} */
let hostname = url.hostname;
/** @type {string | undefined} */
let protocol = url.protocol;
/** @type {string | undefined} */
let port = url.port;
// This is hard-coded in WDS v3
let pathname = '/sockjs-node';
if (metadata.version === 4) {
// This is hard-coded in WDS v4
pathname = '/ws';
}
// Parse authentication credentials in case we need them
if (url.username) {
// Since HTTP basic authentication does not allow empty username,
// we only include password if the username is not empty.
// Result: <username> or <username>:<password>
auth = url.username;
if (url.password) {
auth += ':' + url.password;
}
}
// If the resource query is available,
// parse it and overwrite everything we received from the script host.
const parsedQuery = {};
if (resourceQuery) {
const searchParams = new URLSearchParams(resourceQuery.slice(1));
searchParams.forEach(function (value, key) {
parsedQuery[key] = value;
});
}
hostname = parsedQuery.sockHost || hostname;
pathname = parsedQuery.sockPath || pathname;
port = parsedQuery.sockPort || port;
// Make sure the protocol from resource query has a trailing colon
if (parsedQuery.sockProtocol) {
protocol = parsedQuery.sockProtocol + ':';
}
// Check for IPv4 and IPv6 host addresses that corresponds to any/empty.
// This is important because `hostname` can be empty for some hosts,
// such as 'about:blank' or 'file://' URLs.
const isEmptyHostname = hostname === '0.0.0.0' || hostname === '[::]' || !hostname;
// We only re-assign the hostname if it is empty,
// and if we are using HTTP/HTTPS protocols.
if (
isEmptyHostname &&
window.location.hostname &&
window.location.protocol.indexOf('http') !== -1
) {
hostname = window.location.hostname;
}
// We only re-assign `protocol` when `hostname` is available and is empty,
// since otherwise we risk creating an invalid URL.
// We also do this when 'https' is used as it mandates the use of secure sockets.
if (hostname && (isEmptyHostname || window.location.protocol === 'https:')) {
protocol = window.location.protocol;
}
// We only re-assign port when it is not available
if (!port) {
port = window.location.port;
}
if (!hostname || !pathname || !port) {
throw new Error(
[
'[React Refresh] Failed to get an URL for the socket connection.',
"This usually means that the current executed script doesn't have a `src` attribute set.",
'You should either specify the socket path parameters under the `devServer` key in your Webpack config, or use the `overlay` option.',
'https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/API.md#overlay',
].join('\n')
);
}
return {
auth: auth,
hostname: hostname,
pathname: pathname,
protocol: protocol,
port: port,
};
}
module.exports = getSocketUrlParts;