Skip to content

Commit d311e2f

Browse files
authored
fix: use root-relative URLs for in-project client component modules in dev (#337)
In dev mode, client component modules were always loaded via `/@fs/<cwd>/...` URLs (both in `__webpack_require__` and `<link rel="modulepreload">`). However, Vite's module graph normalizes in-project files to root-relative URLs (e.g., `/app/providers.jsx`). This URL mismatch caused the browser to load the same module twice, once at `/@fs/...` and again at the root-relative path with a `?t=` cache-bust timestamp from Vite's HMR/dep optimization.
1 parent 6749868 commit d311e2f

2 files changed

Lines changed: 23 additions & 13 deletions

File tree

packages/react-server/lib/dev/create-server.mjs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -864,9 +864,17 @@ export default async function createServer(root, options) {
864864
if (!mod) return;
865865

866866
if (mod.__react_server_client_component__) {
867-
modules.unshift(
868-
`/@fs/${sys.normalizePath(moduleId)}`.replace(/\/+/g, "/")
869-
);
867+
const normalizedId = sys.normalizePath(moduleId);
868+
const relPath = sys.normalizePath(relative(cwd, normalizedId));
869+
if (
870+
relPath.startsWith("../") ||
871+
relPath.startsWith("__/") ||
872+
relPath.includes("node_modules")
873+
) {
874+
modules.unshift(`/@fs/${normalizedId}`.replace(/\/+/g, "/"));
875+
} else {
876+
modules.unshift(`/${relPath}`.replace(/\/+/g, "/"));
877+
}
870878
} else {
871879
if (/node_modules/.test(moduleId)) return;
872880

packages/react-server/lib/plugins/react-server-runtime.mjs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,26 +46,28 @@ export default function viteReactServerRuntime() {
4646
};
4747
self.__react_server_hydrate_init__();`;
4848
} else if (id.endsWith("/@__webpack_require__")) {
49+
const basePrefix = config.base
50+
? `${config.base}/`.replace(/\/+/g, "/")
51+
: "/";
52+
const fsPrefix = config.base
53+
? `${config.base}/@fs/${cwd()}`.replace(/\/+/g, "/")
54+
: `/@fs/${cwd()}`.replace(/\/+/g, "/");
4955
return `
5056
const moduleCache = new Map();
5157
self.__webpack_require__ = function (id) {
5258
if (!moduleCache.has(id)) {
5359
if (/^https?\\:/.test(id)) {
5460
const url = new URL(id);
55-
url.pathname = "${
56-
config.base
57-
? `${config.base}/@fs/${cwd()}`.replace(/\/+/g, "/")
58-
: `/@fs/${cwd()}`.replace(/\/+/g, "/")
59-
}" + url.pathname;
61+
url.pathname = "${fsPrefix}" + url.pathname;
6062
const mod = import(/* @vite-ignore */ url.href);
6163
moduleCache.set(id, mod);
6264
return mod;
6365
}
64-
${
65-
config.base
66-
? `const mod = import(/* @vite-ignore */ new URL("${`${config.base}/@fs/${cwd()}/`.replace(/\/+/g, "/")}" + id, location.origin).href);`
67-
: `const mod = import(/* @vite-ignore */ new URL("${`/@fs/${cwd()}/`.replace(/\/+/g, "/")}" + id, location.origin).href);`
68-
}
66+
const isExternal = id.startsWith("__/") || id.startsWith("../") || id.includes("node_modules");
67+
const prefix = isExternal
68+
? "${fsPrefix}/" + (id.startsWith("__/") ? id.replace(/^(__\\/)+/, function(m) { return m.replace(/__\\//g, "../"); }) : id)
69+
: "${basePrefix}" + id;
70+
const mod = import(/* @vite-ignore */ new URL(prefix, location.origin).href);
6971
moduleCache.set(id, mod);
7072
return mod;
7173
}

0 commit comments

Comments
 (0)