287 lines
9.3 KiB
JavaScript
287 lines
9.3 KiB
JavaScript
/* global __webpack_require__ */
|
|
var Refresh = require('react-refresh/runtime');
|
|
|
|
/**
|
|
* Extracts exports from a webpack module object.
|
|
* @param {string} moduleId A Webpack module ID.
|
|
* @returns {*} An exports object from the module.
|
|
*/
|
|
function getModuleExports(moduleId) {
|
|
if (typeof moduleId === 'undefined') {
|
|
// `moduleId` is unavailable, which indicates that this module is not in the cache,
|
|
// which means we won't be able to capture any exports,
|
|
// and thus they cannot be refreshed safely.
|
|
// These are likely runtime or dynamically generated modules.
|
|
return {};
|
|
}
|
|
|
|
var maybeModule = __webpack_require__.c[moduleId];
|
|
if (typeof maybeModule === 'undefined') {
|
|
// `moduleId` is available but the module in cache is unavailable,
|
|
// which indicates the module is somehow corrupted (e.g. broken Webpacak `module` globals).
|
|
// We will warn the user (as this is likely a mistake) and assume they cannot be refreshed.
|
|
console.warn('[React Refresh] Failed to get exports for module: ' + moduleId + '.');
|
|
return {};
|
|
}
|
|
|
|
var exportsOrPromise = maybeModule.exports;
|
|
if (typeof Promise !== 'undefined' && exportsOrPromise instanceof Promise) {
|
|
return exportsOrPromise.then(function (exports) {
|
|
return exports;
|
|
});
|
|
}
|
|
return exportsOrPromise;
|
|
}
|
|
|
|
/**
|
|
* Calculates the signature of a React refresh boundary.
|
|
* If this signature changes, it's unsafe to accept the boundary.
|
|
*
|
|
* This implementation is based on the one in [Metro](https://github.com/facebook/metro/blob/907d6af22ac6ebe58572be418e9253a90665ecbd/packages/metro/src/lib/polyfills/require.js#L795-L816).
|
|
* @param {*} moduleExports A Webpack module exports object.
|
|
* @returns {string[]} A React refresh boundary signature array.
|
|
*/
|
|
function getReactRefreshBoundarySignature(moduleExports) {
|
|
var signature = [];
|
|
signature.push(Refresh.getFamilyByType(moduleExports));
|
|
|
|
if (moduleExports == null || typeof moduleExports !== 'object') {
|
|
// Exit if we can't iterate over exports.
|
|
return signature;
|
|
}
|
|
|
|
for (var key in moduleExports) {
|
|
if (key === '__esModule') {
|
|
continue;
|
|
}
|
|
|
|
signature.push(key);
|
|
signature.push(Refresh.getFamilyByType(moduleExports[key]));
|
|
}
|
|
|
|
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.
|
|
*/
|
|
function createDebounceUpdate() {
|
|
/**
|
|
* A cached setTimeout handler.
|
|
* @type {number | undefined}
|
|
*/
|
|
var refreshTimeout;
|
|
|
|
/**
|
|
* Performs react refresh on a delay and clears the error overlay.
|
|
* @param {function(): void} callback
|
|
* @returns {void}
|
|
*/
|
|
function enqueueUpdate(callback) {
|
|
if (typeof refreshTimeout === 'undefined') {
|
|
refreshTimeout = setTimeout(function () {
|
|
refreshTimeout = undefined;
|
|
Refresh.performReactRefresh();
|
|
callback();
|
|
}, 30);
|
|
}
|
|
}
|
|
|
|
return enqueueUpdate;
|
|
}
|
|
|
|
/**
|
|
* Checks if all exports are likely a React component.
|
|
*
|
|
* This implementation is based on the one in [Metro](https://github.com/facebook/metro/blob/febdba2383113c88296c61e28e4ef6a7f4939fda/packages/metro/src/lib/polyfills/require.js#L748-L774).
|
|
* @param {*} moduleExports A Webpack module exports object.
|
|
* @returns {boolean} Whether the exports are React component like.
|
|
*/
|
|
function isReactRefreshBoundary(moduleExports) {
|
|
if (Refresh.isLikelyComponentType(moduleExports)) {
|
|
return true;
|
|
}
|
|
if (moduleExports === undefined || moduleExports === null || typeof moduleExports !== 'object') {
|
|
// Exit if we can't iterate over exports.
|
|
return false;
|
|
}
|
|
|
|
var hasExports = false;
|
|
var areAllExportsComponents = true;
|
|
for (var key in moduleExports) {
|
|
hasExports = true;
|
|
|
|
// This is the ES Module indicator flag
|
|
if (key === '__esModule') {
|
|
continue;
|
|
}
|
|
|
|
// We can (and have to) safely execute getters here,
|
|
// as Webpack manually assigns harmony exports to getters,
|
|
// without any side-effects attached.
|
|
// Ref: https://github.com/webpack/webpack/blob/b93048643fe74de2a6931755911da1212df55897/lib/MainTemplate.js#L281
|
|
var exportValue = moduleExports[key];
|
|
if (!Refresh.isLikelyComponentType(exportValue)) {
|
|
areAllExportsComponents = false;
|
|
}
|
|
}
|
|
|
|
return hasExports && areAllExportsComponents;
|
|
}
|
|
|
|
/**
|
|
* Checks if exports are likely a React component and registers them.
|
|
*
|
|
* This implementation is based on the one in [Metro](https://github.com/facebook/metro/blob/febdba2383113c88296c61e28e4ef6a7f4939fda/packages/metro/src/lib/polyfills/require.js#L818-L835).
|
|
* @param {*} moduleExports A Webpack module exports object.
|
|
* @param {string} moduleId A Webpack module ID.
|
|
* @returns {void}
|
|
*/
|
|
function registerExportsForReactRefresh(moduleExports, moduleId) {
|
|
if (Refresh.isLikelyComponentType(moduleExports)) {
|
|
// Register module.exports if it is likely a component
|
|
Refresh.register(moduleExports, moduleId + ' %exports%');
|
|
}
|
|
|
|
if (moduleExports === undefined || moduleExports === null || typeof moduleExports !== 'object') {
|
|
// Exit if we can't iterate over the exports.
|
|
return;
|
|
}
|
|
|
|
for (var key in moduleExports) {
|
|
// Skip registering the ES Module indicator
|
|
if (key === '__esModule') {
|
|
continue;
|
|
}
|
|
|
|
var exportValue = moduleExports[key];
|
|
if (Refresh.isLikelyComponentType(exportValue)) {
|
|
var typeID = moduleId + ' %exports% ' + key;
|
|
Refresh.register(exportValue, typeID);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 {*} 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(prevSignature, nextSignature) {
|
|
if (prevSignature.length !== nextSignature.length) {
|
|
return true;
|
|
}
|
|
|
|
for (var i = 0; i < nextSignature.length; i += 1) {
|
|
if (prevSignature[i] !== nextSignature[i]) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
var enqueueUpdate = createDebounceUpdate();
|
|
function executeRuntime(moduleExports, moduleId, webpackHot, refreshOverlay, isTest) {
|
|
registerExportsForReactRefresh(moduleExports, moduleId);
|
|
|
|
if (webpackHot) {
|
|
var isHotUpdate = !!webpackHot.data;
|
|
var prevData;
|
|
if (isHotUpdate) {
|
|
prevData = webpackHot.data.prevData;
|
|
}
|
|
|
|
if (isReactRefreshBoundary(moduleExports)) {
|
|
webpackHot.dispose(
|
|
/**
|
|
* A callback to performs a full refresh if React has unrecoverable errors,
|
|
* and also caches the to-be-disposed module.
|
|
* @param {*} data A hot module data object from Webpack HMR.
|
|
* @returns {void}
|
|
*/
|
|
function hotDisposeCallback(data) {
|
|
// We have to mutate the data object to get data registered and cached
|
|
data.prevData = getWebpackHotData(moduleExports);
|
|
}
|
|
);
|
|
webpackHot.accept(
|
|
/**
|
|
* An error handler to allow self-recovering behaviours.
|
|
* @param {Error} error An error occurred during evaluation of a module.
|
|
* @returns {void}
|
|
*/
|
|
function hotErrorHandler(error) {
|
|
if (typeof refreshOverlay !== 'undefined' && refreshOverlay) {
|
|
refreshOverlay.handleRuntimeError(error);
|
|
}
|
|
|
|
if (typeof isTest !== 'undefined' && isTest) {
|
|
if (window.onHotAcceptError) {
|
|
window.onHotAcceptError(error.message);
|
|
}
|
|
}
|
|
|
|
__webpack_require__.c[moduleId].hot.accept(hotErrorHandler);
|
|
}
|
|
);
|
|
|
|
if (isHotUpdate) {
|
|
if (
|
|
prevData &&
|
|
prevData.isReactRefreshBoundary &&
|
|
shouldInvalidateReactRefreshBoundary(
|
|
prevData.signature,
|
|
getReactRefreshBoundarySignature(moduleExports)
|
|
)
|
|
) {
|
|
webpackHot.invalidate();
|
|
} else {
|
|
enqueueUpdate(
|
|
/**
|
|
* A function to dismiss the error overlay after performing React refresh.
|
|
* @returns {void}
|
|
*/
|
|
function updateCallback() {
|
|
if (typeof refreshOverlay !== 'undefined' && refreshOverlay) {
|
|
refreshOverlay.clearRuntimeErrors();
|
|
}
|
|
}
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
if (isHotUpdate && typeof prevData !== 'undefined') {
|
|
webpackHot.invalidate();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = Object.freeze({
|
|
enqueueUpdate: enqueueUpdate,
|
|
executeRuntime: executeRuntime,
|
|
getModuleExports: getModuleExports,
|
|
isReactRefreshBoundary: isReactRefreshBoundary,
|
|
registerExportsForReactRefresh: registerExportsForReactRefresh,
|
|
});
|