From 332f7c94e40a7f9c42456b14ddd29abede3f93a3 Mon Sep 17 00:00:00 2001 From: Michael Mok Date: Fri, 3 Apr 2020 00:16:13 +0800 Subject: [PATCH 01/10] chore: add init socket global --- src/runtime/globals.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/runtime/globals.js b/src/runtime/globals.js index 47621a5b..f4020c25 100644 --- a/src/runtime/globals.js +++ b/src/runtime/globals.js @@ -1,3 +1,5 @@ -module.exports.errorOverlay = '__react_refresh_error_overlay__'; - -module.exports.refreshUtils = '__react_refresh_utils__'; +module.exports = { + errorOverlay: '__react_refresh_error_overlay__', + initSocket: '__react_refresh_init_socket__', + refreshUtils: '__react_refresh_utils__', +}; From 7638addced9d7aedb03dfc64973ae411f4bd76c4 Mon Sep 17 00:00:00 2001 From: Michael Mok Date: Fri, 3 Apr 2020 00:22:18 +0800 Subject: [PATCH 02/10] refactor: split socket runtime files by integration --- ...erverSocket.js => LegacyWDSSocketEntry.js} | 0 src/runtime/WHMEventSource.js | 22 ---------- src/runtime/createSocket.js | 38 ----------------- src/runtime/sockets/WDSSocket.js | 33 +++++++++++++++ src/runtime/sockets/WHMEventSource.js | 41 +++++++++++++++++++ 5 files changed, 74 insertions(+), 60 deletions(-) rename src/runtime/{LegacyWebpackDevServerSocket.js => LegacyWDSSocketEntry.js} (100%) delete mode 100644 src/runtime/WHMEventSource.js delete mode 100644 src/runtime/createSocket.js create mode 100644 src/runtime/sockets/WDSSocket.js create mode 100644 src/runtime/sockets/WHMEventSource.js diff --git a/src/runtime/LegacyWebpackDevServerSocket.js b/src/runtime/LegacyWDSSocketEntry.js similarity index 100% rename from src/runtime/LegacyWebpackDevServerSocket.js rename to src/runtime/LegacyWDSSocketEntry.js diff --git a/src/runtime/WHMEventSource.js b/src/runtime/WHMEventSource.js deleted file mode 100644 index 47390378..00000000 --- a/src/runtime/WHMEventSource.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * If the consumers setup is to use webpack-hot-middleware with a custom express server - * we want to bind onto the EventSource for error tracking - */ - -module.exports = function loadWHMEventSource(messageHandler) { - const client = require('webpack-hot-middleware/client'); - - client.useCustomOverlay({ - showProblems(type, data) { - const error = { - type, - data, - }; - - messageHandler(error); - }, - clear() { - messageHandler({ type: 'ok' }); - }, - }); -}; diff --git a/src/runtime/createSocket.js b/src/runtime/createSocket.js deleted file mode 100644 index af9ad992..00000000 --- a/src/runtime/createSocket.js +++ /dev/null @@ -1,38 +0,0 @@ -// eslint-disable-next-line no-unused-vars -/* global __resourceQuery, __webpack_dev_server_client__ */ - -const url = require('native-url'); -const loadWHMEventSource = require('./WHMEventSource'); - -/** - * Creates a socket server for HMR according to the user's Webpack configuration. - * @param {function(*): void} messageHandler A handler to consume Webpack compilation messages. - */ -function createSocket(messageHandler, options) { - // This adds support for custom WDS socket transportModes - // In the future, we should add support for custom clients to better support WDM - if (typeof __webpack_dev_server_client__ !== 'undefined') { - const SocketClient = __webpack_dev_server_client__; - const connection = new SocketClient( - url.format({ - protocol: window.location.protocol, - hostname: options.sockHost || window.location.hostname, - port: options.sockPort || window.location.port, - // TODO: Support usage of custom sockets after WDS 4.0 is released - // Ref: https://github.com/webpack/webpack-dev-server/pull/2055 - pathname: options.sockPath || '/sockjs-node', - }) - ); - connection.onClose(function onSocketClose() { - // TODO: Should we reconnect? - }); - connection.onMessage(function onSocketMessage(data) { - const message = JSON.parse(data); - messageHandler(message); - }); - } else { - loadWHMEventSource(messageHandler); - } -} - -module.exports = createSocket; diff --git a/src/runtime/sockets/WDSSocket.js b/src/runtime/sockets/WDSSocket.js new file mode 100644 index 00000000..cb3fad89 --- /dev/null +++ b/src/runtime/sockets/WDSSocket.js @@ -0,0 +1,33 @@ +// eslint-disable-next-line no-unused-vars +/* global __resourceQuery, __webpack_dev_server_client__ */ + +const url = require('native-url'); + +/** + * Initializes a socket server for HMR according to the user's Webpack configuration. + * @param {function(*): void} messageHandler A handler to consume Webpack compilation messages. + * @param {*} overrides Socket integration overrides to change the connection URL. + * @returns {void} + */ +function initWDSSocket(messageHandler, overrides) { + if (typeof __webpack_dev_server_client__ !== 'undefined') { + const SocketClient = __webpack_dev_server_client__; + // TODO: Support usage of custom sockets after WDS 4.0 is released + // Ref: https://github.com/webpack/webpack-dev-server/pull/2055 + const connection = new SocketClient( + url.format({ + protocol: window.location.protocol, + hostname: overrides.sockHost || window.location.hostname, + port: overrides.sockPort || window.location.port, + pathname: overrides.sockPath || '/sockjs-node', + }) + ); + + connection.onMessage(function onSocketMessage(data) { + const message = JSON.parse(data); + messageHandler(message); + }); + } +} + +module.exports = initWDSSocket; diff --git a/src/runtime/sockets/WHMEventSource.js b/src/runtime/sockets/WHMEventSource.js new file mode 100644 index 00000000..0e4ead27 --- /dev/null +++ b/src/runtime/sockets/WHMEventSource.js @@ -0,0 +1,41 @@ +/* + * If the consumers' setup is to use webpack-hot-middleware with a custom express server, + * we want to bind onto the EventSource for error tracking. + */ + +/** + * The hard-coded singleton key for webpack-hot-middleware's client instance. + * + * [Ref](https://github.com/webpack-contrib/webpack-hot-middleware/blob/cb29abb9dde435a1ac8e9b19f82d7d36b1093198/client.js#L152) + */ +const singletonKey = '__webpack_hot_middleware_reporter__'; + +/** + * Creates a socket server for HMR according to the user's Webpack configuration. + * @param {function(*): void} messageHandler A handler to consume Webpack compilation messages. + * @param {*} overrides Socket integration overrides to change the connection URL. + * @returns {void} + */ +function initWHMEventSource(messageHandler, overrides) { + const client = + window[singletonKey] || + require(`webpack-hot-middleware/client${ + overrides.sockPath ? `?path=${overrides.sockPath}` : '' + }`); + + client.useCustomOverlay({ + showProblems(type, data) { + const error = { + type, + data, + }; + + messageHandler(error); + }, + clear() { + messageHandler({ type: 'ok' }); + }, + }); +} + +module.exports = initWHMEventSource; From 1d8dd5d8787e5e24a298e7cdbf31e8da40ec6af2 Mon Sep 17 00:00:00 2001 From: Michael Mok Date: Fri, 3 Apr 2020 00:23:01 +0800 Subject: [PATCH 03/10] feat: add helper to choose socket integration --- src/helpers/getSocketIntegration.js | 18 ++++++++++++++++++ src/helpers/index.js | 2 ++ 2 files changed, 20 insertions(+) create mode 100644 src/helpers/getSocketIntegration.js diff --git a/src/helpers/getSocketIntegration.js b/src/helpers/getSocketIntegration.js new file mode 100644 index 00000000..0dfbc866 --- /dev/null +++ b/src/helpers/getSocketIntegration.js @@ -0,0 +1,18 @@ +function getSocketIntegration(integrationType) { + let resolvedSocketIntegration; + switch (integrationType) { + case 'wds': + resolvedSocketIntegration = require.resolve('../runtime/sockets/WDSSocket'); + break; + case 'whm': + resolvedSocketIntegration = require.resolve('../runtime/sockets/WHMEventSource'); + break; + default: + resolvedSocketIntegration = require.resolve(integrationType); + break; + } + + return resolvedSocketIntegration; +} + +module.exports = getSocketIntegration; diff --git a/src/helpers/index.js b/src/helpers/index.js index f2b7b665..b4053a9d 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.js @@ -1,9 +1,11 @@ const createRefreshTemplate = require('./createRefreshTemplate'); +const getSocketIntegration = require('./getSocketIntegration'); const injectRefreshEntry = require('./injectRefreshEntry'); const validateOptions = require('./validateOptions'); module.exports = { createRefreshTemplate, + getSocketIntegration, injectRefreshEntry, validateOptions, }; From 436532f2d37c4b05d6a5b340383fc29ac0471c51 Mon Sep 17 00:00:00 2001 From: Michael Mok Date: Fri, 3 Apr 2020 00:23:58 +0800 Subject: [PATCH 04/10] feat: wire up ErrorOverlayEntry to use provided socket integration --- src/index.js | 12 +++++++++--- src/runtime/ErrorOverlayEntry.js | 7 +++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/index.js b/src/index.js index 0b2c38d3..7026c659 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,12 @@ const path = require('path'); const webpack = require('webpack'); -const { createRefreshTemplate, injectRefreshEntry, validateOptions } = require('./helpers'); -const { errorOverlay, refreshUtils } = require('./runtime/globals'); +const { + createRefreshTemplate, + getSocketIntegration, + injectRefreshEntry, + validateOptions, +} = require('./helpers'); +const { errorOverlay, initSocket, refreshUtils } = require('./runtime/globals'); class ReactRefreshPlugin { /** @@ -9,7 +14,7 @@ class ReactRefreshPlugin { * @returns {void} */ constructor(options) { - this.options = validateOptions(options); + this.options = validateOptions(options || {}); } /** @@ -39,6 +44,7 @@ class ReactRefreshPlugin { [refreshUtils]: require.resolve('./runtime/refreshUtils'), ...(!!this.options.overlay && { [errorOverlay]: require.resolve(this.options.overlay.module), + [initSocket]: getSocketIntegration(this.options.overlay.sockIntegration), }), }); providePlugin.apply(compiler); diff --git a/src/runtime/ErrorOverlayEntry.js b/src/runtime/ErrorOverlayEntry.js index 72330286..de81f879 100644 --- a/src/runtime/ErrorOverlayEntry.js +++ b/src/runtime/ErrorOverlayEntry.js @@ -1,11 +1,10 @@ -/* global __resourceQuery, __react_refresh_error_overlay__ */ +/* global __resourceQuery, __react_refresh_error_overlay__, __react_refresh_init_socket__ */ -const formatWebpackErrors = require('./formatWebpackErrors'); -const createSocket = require('./createSocket'); const { error: registerErrorHandler, unhandledRejection: registerUnhandledRejectionHandler, } = require('./errorEventHandlers'); +const formatWebpackErrors = require('./formatWebpackErrors'); // Setup error states let isHotReload = false; @@ -80,7 +79,7 @@ if (__resourceQuery) { } // Registers handlers for compile errors -createSocket(compileMessageHandler, overrides); +__react_refresh_init_socket__(compileMessageHandler, overrides); // Registers handlers for runtime errors registerErrorHandler(function handleError(error) { hasRuntimeErrors = true; From c4826675a3eefea252ff899d00496a7d9068c4dc Mon Sep 17 00:00:00 2001 From: Michael Mok Date: Fri, 3 Apr 2020 00:25:29 +0800 Subject: [PATCH 05/10] feat: add options validation for sockIntegration --- src/helpers/validateOptions.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/helpers/validateOptions.js b/src/helpers/validateOptions.js index 1b219270..d3340e03 100644 --- a/src/helpers/validateOptions.js +++ b/src/helpers/validateOptions.js @@ -81,10 +81,11 @@ function validateOptions(options) { typeof defaultedOptions.overlay !== 'undefined' && typeof defaultedOptions.overlay !== 'boolean' ) { - const { entry, module: overlayModule, sockHost, sockPath, sockPort } = defaultedOptions.overlay; + const { entry, module: overlayModule, sockHost, sockIntegration, sockPath, sockPort } = defaultedOptions.overlay; isStringOrUndefined('overlay.entry', entry); isStringOrUndefined('overlay.module', overlayModule); isStringOrUndefined('overlay.sockHost', sockHost); + isStringOrUndefined('overlay.sockIntegration', sockIntegration); isStringOrUndefined('overlay.sockPath', sockPath); isNumberOrUndefined('overlay.sockPort', sockPort); @@ -92,6 +93,7 @@ function validateOptions(options) { ...defaultedOptions.overlay, entry: entry || defaultOverlayOptions.entry, module: overlayModule || defaultOverlayOptions.module, + sockIntegration: sockIntegration || defaultOverlayOptions.sockIntegration, }; } else { defaultedOptions.overlay = From 10b3e30e63139870491043685c932047832de7e7 Mon Sep 17 00:00:00 2001 From: Michael Mok Date: Fri, 3 Apr 2020 00:33:26 +0800 Subject: [PATCH 06/10] chore: add sockIntegration to types --- src/types.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types.js b/src/types.js index 809f343d..830a4390 100644 --- a/src/types.js +++ b/src/types.js @@ -3,6 +3,7 @@ * @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} [sockHost] The socket host to use. + * @property {'wds' | 'whm' | string} [sockIntegration] Path to a JS file that sets up the Webpack socket integration. * @property {string} [sockPath] The socket path to use. * @property {number} [sockPort] The socket port to use. */ From 4cb69e1afd23c2d59956ca8503201dfaa5e442af Mon Sep 17 00:00:00 2001 From: Michael Mok Date: Fri, 3 Apr 2020 00:46:54 +0800 Subject: [PATCH 07/10] docs: update whm example to reflect the api change --- examples/webpack-hot-middleware/webpack.config.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/webpack-hot-middleware/webpack.config.js b/examples/webpack-hot-middleware/webpack.config.js index f48d5746..832a324b 100644 --- a/examples/webpack-hot-middleware/webpack.config.js +++ b/examples/webpack-hot-middleware/webpack.config.js @@ -26,7 +26,12 @@ module.exports = { }, plugins: [ isDevelopment && new webpack.HotModuleReplacementPlugin(), - isDevelopment && new ReactRefreshPlugin(), + isDevelopment && + new ReactRefreshPlugin({ + overlay: { + sockIntegration: 'whm', + }, + }), new HtmlWebpackPlugin({ filename: './index.html', template: './public/index.html', From 32b3b56ac8a6c7ead35813a740c90a7d97b1d1d5 Mon Sep 17 00:00:00 2001 From: Michael Mok Date: Fri, 3 Apr 2020 02:26:53 +0800 Subject: [PATCH 08/10] docs: add documentation on the sockIntegration option --- README.md | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 26de1887..8a425bad 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ More sample projects for common Webpack development setups are available in the > Note: If you are using TypeScript (instead of Babel) as a transpiler, you will still need to use `babel-loader` to process your source code. > Check out this [sample project](https://github.com/pmmmwh/react-refresh-webpack-plugin/tree/master/examples/typescript-without-babel) on how to set this up. -### Polyfill for Older Browsers (WDS Only) +### Polyfill for Older Browsers If you need to develop on IE11, you will need to polyfill the DOM URL API. This can be done by adding the following before any of your code in the main entry (either one is fine): @@ -157,15 +157,16 @@ Modifies how the error overlay integration works in the plugin. - If an `ErrorOverlayOptions` object is provided: (**NOTE**: This is an advanced option that exists mostly for tools like `create-react-app` or `Next.js`) - - A `module` property must be defined. - It should reference a JS file that exports at least two functions with footprints as follows: + - An optional `module` property could be defined. + If it is not defined, the bundled error overlay will be used. + If defined, it should reference a JS file that exports at least two functions with footprints as follows: ```ts function handleRuntimeError(error: Error) {} function clearRuntimeErrors() {} ``` - - An optional `entry` property could also be defined, which should also reference a JS file that contains code needed to set up your custom error overlay integration. + - An optional `entry` property could be defined, which should also reference a JS file that contains code needed to set up your custom error overlay integration. If it is not defined, the bundled error overlay entry will be used. It expects the `module` file to export two more functions: @@ -186,21 +187,33 @@ Modifies how the error overlay integration works in the plugin. }; ``` -### `options.sockHost` +#### `options.overlay.sockHost` Type: `string` Default: effectively `window.location.hostname` Set this if you are running webpack on a host other than `window.location.hostname`. This is used by the error overlay module. -### `options.sockPort` +#### `options.overlay.sockIntegration` + +Type: `wds` or `whm` or `string` +Default: `wds` + +This controls how the error overlay connects to the sockets provided by several Webpack hot reload integrations. + +- If you use `webpack-dev-server`, you don't need to set this as it defaults to `wds`. +- If you use `webpack-hot-middleware`, you should set this to `whm`. +- If you use anything else, you will have to provide a path to a module that will accept a message handler function and initializes the socket connection. + See the [`runtime/sockets`](https://github.com/pmmmwh/react-refresh-webpack-plugin/tree/master/src/runtime/sockets) folder for sample implementations. + +#### `options.overlay.sockPort` Type: `number` Default: effectively `window.location.port` Set this if you are running webpack on a port other than `window.location.port` -### `options.sockPath` +#### `options.overlay.sockPath` Type: `string` Default: `/sockjs-node` From 63e5568b6b99379c2577843c5efdb33ced3d50cb Mon Sep 17 00:00:00 2001 From: Michael Mok Date: Wed, 8 Apr 2020 04:55:51 +0800 Subject: [PATCH 09/10] fix: disregard sock path for whm event source --- src/runtime/sockets/WHMEventSource.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/runtime/sockets/WHMEventSource.js b/src/runtime/sockets/WHMEventSource.js index 0e4ead27..1665a6e6 100644 --- a/src/runtime/sockets/WHMEventSource.js +++ b/src/runtime/sockets/WHMEventSource.js @@ -17,11 +17,7 @@ const singletonKey = '__webpack_hot_middleware_reporter__'; * @returns {void} */ function initWHMEventSource(messageHandler, overrides) { - const client = - window[singletonKey] || - require(`webpack-hot-middleware/client${ - overrides.sockPath ? `?path=${overrides.sockPath}` : '' - }`); + const client = window[singletonKey] || require('webpack-hot-middleware/client'); client.useCustomOverlay({ showProblems(type, data) { From dc9705d7be3f0b06914e3ebd5102e86a24a07c15 Mon Sep 17 00:00:00 2001 From: Michael Mok Date: Wed, 8 Apr 2020 05:00:30 +0800 Subject: [PATCH 10/10] docs: sock options are only available to wds --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8a425bad..e0277b08 100644 --- a/README.md +++ b/README.md @@ -190,9 +190,10 @@ Modifies how the error overlay integration works in the plugin. #### `options.overlay.sockHost` Type: `string` -Default: effectively `window.location.hostname` +Default: `window.location.hostname` -Set this if you are running webpack on a host other than `window.location.hostname`. This is used by the error overlay module. +Set this if you are running webpack on a host other than `window.location.hostname`. +This will be used by the error overlay module, and is available for `webpack-dev-server` only. #### `options.overlay.sockIntegration` @@ -209,15 +210,19 @@ This controls how the error overlay connects to the sockets provided by several #### `options.overlay.sockPort` Type: `number` -Default: effectively `window.location.port` +Default: `window.location.port` -Set this if you are running webpack on a port other than `window.location.port` +Set this if you are running webpack on a port other than `window.location.port`. +This will be used by the error overlay module, and is available for `webpack-dev-server` only. #### `options.overlay.sockPath` Type: `string` Default: `/sockjs-node` +Set this if you are running webpack on a custom path. +This will be used by the error overlay module, and is available for `webpack-dev-server` only. + ### `options.useLegacyWDSSockets` Type: `boolean`