diff --git a/lib/runtime/RefreshUtils.js b/lib/runtime/RefreshUtils.js index 98807575..69a3f9a7 100644 --- a/lib/runtime/RefreshUtils.js +++ b/lib/runtime/RefreshUtils.js @@ -62,6 +62,21 @@ function getReactRefreshBoundarySignature(moduleExports) { return signature; } +/** + * Creates a data object to be retained across refreshes. + * This object should not transtively reference previous exports, + * which can form infinite chain of objects across refreshes, which can pressure RAM. + * + * @param {*} moduleExports A Webpack module exports object. + * @returns {*} A React refresh boundary signature array. + */ +function getWebpackHotData(moduleExports) { + return { + signature: getReactRefreshBoundarySignature(moduleExports), + isReactRefreshBoundary: isReactRefreshBoundary(moduleExports) + }; +} + /** * Creates a helper that performs a delayed React refresh. * @returns {function(function(): void): void} A debounced React refresh function. @@ -167,14 +182,11 @@ function registerExportsForReactRefresh(moduleExports, moduleId) { * Compares previous and next module objects to check for mutated boundaries. * * This implementation is based on the one in [Metro](https://github.com/facebook/metro/blob/907d6af22ac6ebe58572be418e9253a90665ecbd/packages/metro/src/lib/polyfills/require.js#L776-L792). - * @param {*} prevExports The current Webpack module exports object. - * @param {*} nextExports The next Webpack module exports object. + * @param {*} prevSignature The signature of the current Webpack module exports object. + * @param {*} nextSignature The signature of the next Webpack module exports object. * @returns {boolean} Whether the React refresh boundary should be invalidated. */ -function shouldInvalidateReactRefreshBoundary(prevExports, nextExports) { - var prevSignature = getReactRefreshBoundarySignature(prevExports); - var nextSignature = getReactRefreshBoundarySignature(nextExports); - +function shouldInvalidateReactRefreshBoundary(prevSignature, nextSignature) { if (prevSignature.length !== nextSignature.length) { return true; } @@ -194,9 +206,9 @@ function executeRuntime(moduleExports, moduleId, webpackHot, refreshOverlay, isT if (webpackHot) { var isHotUpdate = !!webpackHot.data; - var prevExports; + var prevData; if (isHotUpdate) { - prevExports = webpackHot.data.prevExports; + prevData = webpackHot.data.prevData; } if (isReactRefreshBoundary(moduleExports)) { @@ -209,7 +221,7 @@ function executeRuntime(moduleExports, moduleId, webpackHot, refreshOverlay, isT */ function hotDisposeCallback(data) { // We have to mutate the data object to get data registered and cached - data.prevExports = moduleExports; + data.prevData = getWebpackHotData(moduleExports); } ); webpackHot.accept( @@ -235,8 +247,8 @@ function executeRuntime(moduleExports, moduleId, webpackHot, refreshOverlay, isT if (isHotUpdate) { if ( - isReactRefreshBoundary(prevExports) && - shouldInvalidateReactRefreshBoundary(prevExports, moduleExports) + prevData && prevData.isReactRefreshBoundary && + shouldInvalidateReactRefreshBoundary(prevData.signature, getReactRefreshBoundarySignature(moduleExports)) ) { webpackHot.invalidate(); } else { @@ -254,7 +266,7 @@ function executeRuntime(moduleExports, moduleId, webpackHot, refreshOverlay, isT } } } else { - if (isHotUpdate && typeof prevExports !== 'undefined') { + if (isHotUpdate && typeof prevData !== 'undefined') { webpackHot.invalidate(); } } @@ -266,6 +278,5 @@ module.exports = Object.freeze({ executeRuntime: executeRuntime, getModuleExports: getModuleExports, isReactRefreshBoundary: isReactRefreshBoundary, - shouldInvalidateReactRefreshBoundary: shouldInvalidateReactRefreshBoundary, registerExportsForReactRefresh: registerExportsForReactRefresh, });