@react-router/dev
Version:
Dev tools and CLI for React Router
171 lines (155 loc) • 5.74 kB
JavaScript
// adapted from https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/src/refreshUtils.js
// This file gets injected into the browser as a part of the HMR runtime
function debounce(fn, delay) {
let handle;
return () => {
clearTimeout(handle);
handle = setTimeout(fn, delay);
};
}
/* eslint-disable no-undef */
const enqueueUpdate = debounce(async () => {
let manifest;
if (routeUpdates.size > 0) {
manifest = JSON.parse(JSON.stringify(__reactRouterManifest));
for (let route of routeUpdates.values()) {
manifest.routes[route.id] = route;
let imported = window.__reactRouterRouteModuleUpdates.get(route.id);
if (!imported) {
throw Error(
`[react-router:hmr] No module update found for route ${route.id}`,
);
}
let routeModule = {
...imported,
// react-refresh takes care of updating these in-place,
// if we don't preserve existing values we'll loose state.
default: imported.default
? (window.__reactRouterRouteModules[route.id]?.default ??
imported.default)
: imported.default,
ErrorBoundary: imported.ErrorBoundary
? (window.__reactRouterRouteModules[route.id]?.ErrorBoundary ??
imported.ErrorBoundary)
: imported.ErrorBoundary,
HydrateFallback: imported.HydrateFallback
? (window.__reactRouterRouteModules[route.id]?.HydrateFallback ??
imported.HydrateFallback)
: imported.HydrateFallback,
};
window.__reactRouterRouteModules[route.id] = routeModule;
}
let needsRevalidation = new Set(
Array.from(routeUpdates.values())
.filter(
(route) =>
route.hasLoader ||
route.hasClientLoader ||
route.hasClientMiddleware,
)
.map((route) => route.id),
);
let routes = __reactRouterDataRouter.createRoutesForHMR(
needsRevalidation,
manifest.routes,
window.__reactRouterRouteModules,
window.__reactRouterContext.ssr,
window.__reactRouterContext.isSpaMode,
);
__reactRouterDataRouter._internalSetRoutes(routes);
routeUpdates.clear();
window.__reactRouterRouteModuleUpdates.clear();
}
try {
window.__reactRouterHdrActive = true;
await __reactRouterDataRouter.revalidate();
} finally {
window.__reactRouterHdrActive = false;
}
if (manifest) {
Object.assign(window.__reactRouterManifest, manifest);
}
exports.performReactRefresh();
}, 16);
// Taken from https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/lib/runtime/RefreshUtils.js#L141
// This allows to resister components not detected by SWC like styled component
function registerExportsForReactRefresh(filename, moduleExports) {
for (let key in moduleExports) {
if (key === "__esModule") continue;
let exportValue = moduleExports[key];
if (exports.isLikelyComponentType(exportValue)) {
// 'export' is required to avoid key collision when renamed exports that
// shadow a local component name: https://github.com/vitejs/vite-plugin-react/issues/116
// The register function has an identity check to not register twice the same component,
// so this is safe to not used the same key here.
exports.register(exportValue, filename + " export " + key);
}
}
}
function validateRefreshBoundaryAndEnqueueUpdate(
prevExports,
nextExports,
// non-component exports that are handled by the framework (e.g. `meta` and `links` for route modules)
acceptExports = [],
) {
if (
!predicateOnExport(
prevExports,
(key) => key in nextExports || acceptExports.includes(key),
)
) {
return "Could not Fast Refresh (export removed)";
}
if (
!predicateOnExport(
nextExports,
(key) => key in prevExports || acceptExports.includes(key),
)
) {
return "Could not Fast Refresh (new export)";
}
let hasExports = false;
let allExportsAreHandledOrUnchanged = predicateOnExport(
nextExports,
(key, value) => {
hasExports = true;
// Remix can handle Remix-specific exports (e.g. `meta` and `links`)
if (acceptExports.includes(key)) return true;
// React Fast Refresh can handle component exports
if (exports.isLikelyComponentType(value)) return true;
// Unchanged exports are implicitly handled
return prevExports[key] === nextExports[key];
},
);
if (hasExports && allExportsAreHandledOrUnchanged) {
enqueueUpdate();
} else {
return "Could not Fast Refresh. Learn more at https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#consistent-components-exports";
}
}
function predicateOnExport(moduleExports, predicate) {
for (let key in moduleExports) {
if (key === "__esModule") continue;
let desc = Object.getOwnPropertyDescriptor(moduleExports, key);
if (desc && desc.get) return false;
if (!predicate(key, moduleExports[key])) return false;
}
return true;
}
// Hides vite-ignored dynamic import so that Vite can skip analysis if no other
// dynamic import is present (https://github.com/vitejs/vite/pull/12732)
function __hmr_import(module) {
return import(/* @vite-ignore */ module);
}
const routeUpdates = new Map();
window.__reactRouterRouteModuleUpdates = new Map();
import.meta.hot.on("react-router:hmr", async ({ route }) => {
if (route) {
routeUpdates.set(route.id, route);
}
});
exports.__hmr_import = __hmr_import;
exports.registerExportsForReactRefresh = registerExportsForReactRefresh;
exports.validateRefreshBoundaryAndEnqueueUpdate =
validateRefreshBoundaryAndEnqueueUpdate;
exports.enqueueUpdate = enqueueUpdate;