Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 25 additions & 12 deletions src/helpers/injectRefreshEntry.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const querystring = require('querystring');

/** @typedef {string | string[] | import('webpack').Entry} StaticEntry */
/** @typedef {StaticEntry | import('webpack').EntryFunc} WebpackEntry */

Expand All @@ -7,27 +9,38 @@
* @param {import('../types').ReactRefreshPluginOptions} [options] Configuration options for this plugin.
* @returns {WebpackEntry} An injected entry object.
*/
const injectRefreshEntry = (originalEntry, options) => {
const sockHost = options.sockHost ? `&sockHost=${options.sockHost}` : '';
const sockPort = options.sockPort ? `&sockPort=${options.sockPort}` : '';
const sockPath = options.sockPath ? `&sockPath=${options.sockPath}` : '';
const queryParams = `?options${sockHost}${sockPort}${sockPath}`;
const entryInjects = [
// Legacy WDS SockJS integration
options.useLegacyWDSSockets && require.resolve('../runtime/LegacyWebpackDevServerSocket'),
function injectRefreshEntry(originalEntry, options) {
let resourceQuery = {};
if (options.overlay) {
options.overlay.sockHost && (resourceQuery.sockHost = options.overlay.sockHost);
options.overlay.sockPath && (resourceQuery.sockPath = options.overlay.sockPath);
options.overlay.sockPort && (resourceQuery.sockPort = options.overlay.sockPort);
}

// We don't need to URI encode the resourceQuery as it will be parsed by Webpack
const queryString = querystring.stringify(resourceQuery, null, null, {
encodeURIComponent: (string) => string,
});

const prependEntries = [
// React-refresh runtime
require.resolve('../runtime/ReactRefreshEntry'),
];

const appendEntries = [
// Legacy WDS SockJS integration
options.useLegacyWDSSockets && require.resolve('../runtime/LegacyWDSSocketEntry'),
// Error overlay runtime
options.overlay && options.overlay.entry + queryParams,
options.overlay && options.overlay.entry + (queryString && `?${queryString}`),
].filter(Boolean);

// Single string entry point
if (typeof originalEntry === 'string') {
return [...entryInjects, originalEntry];
return [...prependEntries, originalEntry, ...appendEntries];
}
// Single array entry point
if (Array.isArray(originalEntry)) {
return [...entryInjects, ...originalEntry];
return [...prependEntries, ...originalEntry, ...appendEntries];
}
// Multiple entry points
if (typeof originalEntry === 'object') {
Expand All @@ -48,6 +61,6 @@ const injectRefreshEntry = (originalEntry, options) => {
}

throw new Error('Failed to parse the Webpack `entry` object!');
};
}

module.exports = injectRefreshEntry;
17 changes: 16 additions & 1 deletion src/helpers/validateOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ function isBooleanOrUndefined(name, value) {
}
}

function isNumberOrUndefined(name, value) {
const valueType = typeof value;
if (valueType !== 'undefined' && valueType !== 'number') {
throw new Error(
[
`The "${name}" option, if defined, must be a number.`,
`Instead received: "${valueType}".`,
].join('\n')
);
}
}

function isStringOrUndefined(name, value) {
const valueType = typeof value;
if (valueType !== 'undefined' && valueType !== 'string') {
Expand Down Expand Up @@ -69,9 +81,12 @@ function validateOptions(options) {
typeof defaultedOptions.overlay !== 'undefined' &&
typeof defaultedOptions.overlay !== 'boolean'
) {
const { entry, module: overlayModule } = defaultedOptions.overlay;
const { entry, module: overlayModule, sockHost, sockPath, sockPort } = defaultedOptions.overlay;
isStringOrUndefined('overlay.entry', entry);
isStringOrUndefined('overlay.module', overlayModule);
isStringOrUndefined('overlay.sockHost', sockHost);
isStringOrUndefined('overlay.sockPath', sockPath);
isNumberOrUndefined('overlay.sockPort', sockPort);

defaultedOptions.overlay = {
...defaultedOptions.overlay,
Expand Down
5 changes: 4 additions & 1 deletion src/runtime/ErrorOverlayEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ function compileMessageHandler(message) {

let overrides = {};
if (__resourceQuery) {
overrides = require('querystring').parse(__resourceQuery.slice(1));
const searchParams = new URLSearchParams(__resourceQuery.slice(1));
searchParams.forEach(function (value, key) {
overrides[key] = value;
});
}

// Registers handlers for compile errors
Expand Down
8 changes: 4 additions & 4 deletions src/types.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/**
* @typedef {Object} ErrorOverlayOptions
* @property {string} [entry] Path to a JS file that sets up the error overlay integration.
* @property {string} module The error overlay module to use.
* @property {string} [module] The error overlay module to use.
* @property {string} [sockHost] The socket host to use.
* @property {string} [sockPath] The socket path to use.
* @property {number} [sockPort] The socket port to use.
*/

/**
* @typedef {Object} ReactRefreshPluginOptions
* @property {boolean} [disableRefreshCheck] Disables detection of react-refresh's Babel plugin. (Deprecated since v0.3.0)
* @property {boolean} [forceEnable] Enables the plugin forcefully.
* @property {boolean | ErrorOverlayOptions} [overlay] Modifies how the error overlay integration works in the plugin.
* @property {string} [sockHost] The socket host to use for the error overlay integration.
* @property {string} [sockPath] The socket path to use for the error overlay integration.
* @property {number} [sockPort] The socket port to use for the error overlay integration.
* @property {boolean} [useLegacyWDSSockets] Uses a custom SocketJS implementation for older versions of webpack-dev-server.
*/

Expand Down