Skip to content

Commit 6b0db4d

Browse files
fix(vite): reload page on route change
1 parent 671af16 commit 6b0db4d

1 file changed

Lines changed: 75 additions & 3 deletions

File tree

packages/plugin-vite/src/plugins/server_snapshot.ts

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import type { Manifest, Plugin, ViteDevServer } from "vite";
1+
import type {
2+
EnvironmentModuleNode,
3+
Manifest,
4+
Plugin,
5+
ViteDevServer,
6+
} from "vite";
27
import {
38
crawlFsItem,
49
fsAdapter,
@@ -26,6 +31,7 @@ export function serverSnapshot(options: ResolvedFreshViteConfig): Plugin[] {
2631
let publicDir = "";
2732

2833
const islands = new Map<string, { name: string; chunk: string | null }>();
34+
const islandsByFile = new Set<string>();
2935
const islandSpecByName = new Map<string, string>();
3036

3137
return [{
@@ -47,8 +53,55 @@ export function serverSnapshot(options: ResolvedFreshViteConfig): Plugin[] {
4753
},
4854
configureServer(viteServer) {
4955
server = viteServer;
50-
},
51-
async buildStart() {
56+
57+
viteServer.watcher.on("all", (ev, filePath) => {
58+
const { client, ssr } = viteServer.environments;
59+
60+
// We can't just check if it's in the client module graph
61+
// because plugins like tailwindcss import _everything_.
62+
// Instead, we walk up the module graph to see if the file
63+
// is imported by an island or the client entry file.
64+
const mods = client.moduleGraph.getModulesByFile(filePath);
65+
if (mods !== undefined) {
66+
const seen = new Set<EnvironmentModuleNode>();
67+
const maybe = Array.from(mods);
68+
for (let i = 0; i < maybe.length; i++) {
69+
const mod = maybe[i];
70+
71+
const isIslandFile = walkUp(
72+
mod,
73+
(m) => m.file !== null && islandsByFile.has(m.file),
74+
seen,
75+
);
76+
77+
// No need to notify manually, vite takes care of this.
78+
if (isIslandFile) return;
79+
}
80+
}
81+
82+
// Check for route files. We need to invalidate the snapshot if
83+
// they are removed or added.
84+
if (
85+
(ev === "add" || ev === "unlink") &&
86+
!/[\\/]+\(_[^)]+\)[\\/]+/.test(filePath)
87+
) {
88+
const relRoutes = path.relative(options.routeDir, filePath);
89+
if (!relRoutes.startsWith("..")) {
90+
const mod = ssr.moduleGraph.getModuleById(`\0${modName}`);
91+
if (mod !== undefined) {
92+
// Clear state
93+
islands.clear();
94+
islandsByFile.clear();
95+
islandSpecByName.clear();
96+
97+
ssr.moduleGraph.invalidateModule(mod);
98+
}
99+
}
100+
}
101+
102+
// Finally, notify the client
103+
viteServer.ws.send("fresh:reload");
104+
});
52105
},
53106
resolveId(id) {
54107
if (id === modName) {
@@ -71,6 +124,7 @@ export function serverSnapshot(options: ResolvedFreshViteConfig): Plugin[] {
71124

72125
islands.set(spec, { name, chunk: null });
73126
islandSpecByName.set(name, spec);
127+
islandsByFile.add(spec);
74128
}
75129

76130
const staticFiles: PendingStaticFile[] = [];
@@ -221,3 +275,21 @@ export function serverSnapshot(options: ResolvedFreshViteConfig): Plugin[] {
221275
},
222276
}];
223277
}
278+
279+
function walkUp(
280+
mod: EnvironmentModuleNode,
281+
fn: (mod: EnvironmentModuleNode) => boolean,
282+
seen: Set<EnvironmentModuleNode>,
283+
): boolean {
284+
if (seen.has(mod)) return false;
285+
286+
if (fn(mod)) return true;
287+
288+
const importers = Array.from(mod.importers);
289+
for (let i = 0; i < importers.length; i++) {
290+
const imp = importers[i];
291+
if (walkUp(imp, fn, seen)) return true;
292+
}
293+
294+
return false;
295+
}

0 commit comments

Comments
 (0)