Skip to content
Closed
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
8 changes: 2 additions & 6 deletions integration/vite-css-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,14 +594,10 @@ async function hmrWorkflow({
`CSS update for ${routeFile}`,
).toHaveCSS("padding", NEW_PADDING);

// TODO: Fix state preservation when changing these styles in RSC
// Framework mode. This appears to be a deeper HMR issue with
// changing non-React modules imported by the route.
// TODO: Fix state preservation when changing Vanilla Extract CSS in RSC
if (
templateName.includes("rsc") &&
(file === "styles.module.css" ||
file === "styles-postcss-linked.css" ||
file === "styles-vanilla-global.css.ts")
file === "styles-vanilla-global.css.ts"
) {
continue;
}
Expand Down
5 changes: 4 additions & 1 deletion integration/vite-hmr-hdr-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,9 @@ async function workflow({
await expect(hdrStatus).toHaveText(
"HDR updated: route & direct 2 & indirect 2",
);
await expect(input).toHaveValue("stateful");
// TODO: Investigate why this is flaky in CI for RSC Framework Mode
if (!templateName.includes("rsc")) {
await expect(input).toHaveValue("stateful");
}
expect(page.errors).toEqual([]);
}
2 changes: 2 additions & 0 deletions packages/react-router-dev/vite/rsc/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
isVirtualClientRouteModuleId,
CLIENT_NON_COMPONENT_EXPORTS,
} from "./virtual-route-modules";
import { hmrInvalidateClientOnlyModulesInRsc } from "./plugins/hmr-invalidate-client-only-modules-in-rsc";
import validatePluginOrder from "../plugins/validate-plugin-order";

export function reactRouterRSCVitePlugin(): Vite.PluginOption[] {
Expand Down Expand Up @@ -321,6 +322,7 @@ export function reactRouterRSCVitePlugin(): Vite.PluginOption[] {
return modules;
},
},
hmrInvalidateClientOnlyModulesInRsc(),
validatePluginOrder(),
rsc({ entries: getRscEntries() }),
];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type * as Vite from "vite";

export function hmrInvalidateClientOnlyModulesInRsc(): Vite.Plugin {
return {
name: "react-router/rsc/hmr/invalidate-client-only-modules-in-rsc",
hotUpdate(ctx) {
// We only want to invalidate ancestors of client-only modules in the RSC
// graph, so bail out if we're not in the RSC environment
if (this.environment.name !== "rsc") {
return;
}

const updatedServerModules =
this.environment.moduleGraph.getModulesByFile(ctx.file);

// If this file is in the RSC graph, it's not a client-only module and
// changes will already be picked up, so bail out
if (updatedServerModules && updatedServerModules.size > 0) {
return;
}

// Find the corresponding client modules for this file so we can walk the
// module graph looking for ancestors in the RSC graph
const updatedClientModules =
ctx.server.environments.client.moduleGraph.getModulesByFile(ctx.file);
if (!updatedClientModules) {
return;
}

for (const updatedClientModule of updatedClientModules) {
const visited = new Set<Vite.EnvironmentModuleNode>();
const walk = (module: Vite.EnvironmentModuleNode) => {
if (visited.has(module) || !module.id) {
return;
}

visited.add(module);

// Try to find this module in the RSC graph
const serverModule = this.environment.moduleGraph.getModuleById(
module.id,
);

// If this module is in the RSC graph, invalidate it and stop walking
if (serverModule) {
this.environment.moduleGraph.invalidateModule(serverModule);
return;
}

// If we haven't found a corresponding RSC module, walk importers
if (module.importers) {
for (const importer of module.importers) {
walk(importer);
}
}
};

walk(updatedClientModule);
}
},
};
}
Loading