Skip to content

Commit f8d94f0

Browse files
authored
fix(core): fix module runner dynamicRequest relative URL resolution (#599)
## Summary - Fix a bug in vite's `ModuleRunner.directRequest()` where `dynamicRequest` uses the raw URL for relative path resolution, which breaks for `file://` URLs - Re-enable the `VITE_TEST_BUILD=1 vp run tests-e2e#test` VitePress E2E test in CI Stacked on #597. ## Root Cause In `ModuleRunner.directRequest()` (`runner.ts:347-352`), the `dynamicRequest` closure resolves relative deps with: ```typescript dep = posixResolve(posixDirname(url), dep) ``` The `url` captured here is the **raw, unnormalized URL** that flows through `import()` → `cachedRequest()` → `directRequest()`. But the server already resolved and normalized this URL — the normalized version is stored in `mod.url` (e.g. `/@fs/path/.temp/app.js`). The code uses the wrong one. When `url` is a `file://` URL (e.g. `file:///path/.temp/app.js` from VitePress's `import(pathToFileURL(entryPath).href)`), `pathe.resolve` doesn't recognize it as absolute (it starts with `f`, not `/`), so it prepends CWD, producing malformed paths like `<cwd>/file:/path/.temp/page.md.js`. ### Why it doesn't happen with vanilla vitest With vanilla vitest, VitePress is a regular npm dependency living in `node_modules/`. Vitest's externalization check (`id.includes('/node_modules/')`) returns `true`, so VitePress is **externalized** — loaded via native Node.js `import()` which handles `file://` URLs correctly. The module runner's `dynamicRequest` is never involved. In vite-plus's ecosystem-ci, VitePress is a `workspace:*` dependency. Its files aren't in `node_modules/`, so it's **inlined** (SSR-transformed). Its `import(pathToFileURL(...))` becomes `__vite_ssr_dynamic_import__('file://...')`, which flows through `dynamicRequest` and hits the bug. ### Why it didn't fail before #588 Before #588, `vp test` commands had caching enabled. When CI ran `vp run tests-e2e#test` (without `VITE_TEST_BUILD`), it succeeded and created a cache entry. The next step `VITE_TEST_BUILD=1 vp run tests-e2e#test` ignored `VITE_TEST_BUILD` since it wasn't in the fingerprinted envs, hit the cache, and `vp test` never actually ran with `VITE_TEST_BUILD=1`. #588 disabled caching on `vp test` commands, so the second step actually ran for the first time and exposed the bug. ## Fix Use `mod.url` (the server-normalized URL) instead of the raw `url` parameter in the `dynamicRequest` closure: ```typescript // Before (broken for file:// URLs): dep = posixResolve(posixDirname(url), dep) // After (uses server-normalized URL): dep = posixResolve(posixDirname(mod.url), dep) ``` `mod.url` is always a properly normalized URL from the server (e.g. `/@fs/...` or `/src/...`) that `posixResolve` handles correctly. Applied as a build-time transform in `packages/core/build.ts`.
1 parent dd9f320 commit f8d94f0

2 files changed

Lines changed: 26 additions & 0 deletions

File tree

.github/workflows/e2e-test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ jobs:
212212
vp run build
213213
vp test run -r __tests__/unit
214214
vp run tests-e2e#test
215+
VITE_TEST_BUILD=1 vp run tests-e2e#test
215216
vp run tests-init#test
216217
- name: tanstack-start-helloworld
217218
node-version: 24

packages/core/build.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,31 @@ async function buildVite() {
9393
config.plugins = [
9494
// Add RewriteImportsPlugin to handle vite/rolldown import rewrites
9595
RewriteImportsPlugin,
96+
{
97+
name: 'fix-module-runner-dynamic-request-url',
98+
transform(_, id, meta) {
99+
if (id.endsWith(join('vite', 'src', 'module-runner', 'runner.ts'))) {
100+
const { magicString } = meta;
101+
if (magicString) {
102+
// Fix dynamicRequest to use the server-normalized module URL
103+
// (mod.url) instead of the raw URL parameter for relative path
104+
// resolution. The raw `url` can be a file:// URL (e.g. from
105+
// VitePress's `import(pathToFileURL(entryPath).href)`) which
106+
// pathe.resolve cannot handle as an absolute path, producing
107+
// malformed paths like "<cwd>/file:<path>".
108+
// mod.url is always a server-normalized URL (e.g. /@fs/...)
109+
// that posixResolve handles correctly.
110+
magicString.replace(
111+
`if (dep[0] === '.') {\n dep = posixResolve(posixDirname(url), dep)\n }`,
112+
`if (dep[0] === '.') {\n dep = posixResolve(posixDirname(mod.url), dep)\n }`,
113+
);
114+
return {
115+
code: magicString,
116+
};
117+
}
118+
}
119+
},
120+
},
96121
{
97122
name: 'rewrite-static-paths',
98123
transform(_, id, meta) {

0 commit comments

Comments
 (0)