123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- 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 = {};
- }
- /** @type {SocketUrlParts} */
- let urlParts = {};
- // If the resource query is available,
- // parse it and ignore everything we received from the script host.
- if (resourceQuery) {
- const parsedQuery = {};
- const searchParams = new URLSearchParams(resourceQuery.slice(1));
- searchParams.forEach(function (value, key) {
- parsedQuery[key] = value;
- });
- urlParts.hostname = parsedQuery.sockHost;
- urlParts.pathname = parsedQuery.sockPath;
- urlParts.port = parsedQuery.sockPort;
- // Make sure the protocol from resource query has a trailing colon
- if (parsedQuery.sockProtocol) {
- urlParts.protocol = parsedQuery.sockProtocol + ':';
- }
- } else {
- 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`
- }
- // 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>
- urlParts.auth = url.username;
- if (url.password) {
- urlParts.auth += ':' + url.password;
- }
- }
- // `file://` URLs has `'null'` origin
- if (url.origin !== 'null') {
- urlParts.hostname = url.hostname;
- }
- urlParts.protocol = url.protocol;
- urlParts.port = url.port;
- }
- if (!urlParts.pathname) {
- if (metadata.version === 4) {
- // This is hard-coded in WDS v4
- urlParts.pathname = '/ws';
- } else {
- // This is hard-coded in WDS v3
- urlParts.pathname = '/sockjs-node';
- }
- }
- // Check for IPv4 and IPv6 host addresses that correspond to any/empty.
- // This is important because `hostname` can be empty for some hosts,
- // such as 'about:blank' or 'file://' URLs.
- const isEmptyHostname =
- urlParts.hostname === '0.0.0.0' || urlParts.hostname === '[::]' || !urlParts.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') === 0
- ) {
- urlParts.hostname = window.location.hostname;
- }
- // We only re-assign `protocol` when `protocol` is unavailable,
- // or if `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 (
- !urlParts.protocol ||
- (urlParts.hostname && (isEmptyHostname || window.location.protocol === 'https:'))
- ) {
- urlParts.protocol = window.location.protocol;
- }
- // We only re-assign port when it is not available
- if (!urlParts.port) {
- urlParts.port = window.location.port;
- }
- if (!urlParts.hostname || !urlParts.pathname) {
- 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: urlParts.auth,
- hostname: urlParts.hostname,
- pathname: urlParts.pathname,
- protocol: urlParts.protocol,
- port: urlParts.port || undefined,
- };
- }
- module.exports = getSocketUrlParts;
|