Skip to content

Commit 47c4213

Browse files
authored
fix(dev): fix html-proxy cache key mismatch for /@fs/ HTML paths (#21762)
1 parent 5c8e98f commit 47c4213

3 files changed

Lines changed: 106 additions & 2 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>Test Index HTML</title>
6+
</head>
7+
<body>
8+
<div id="app"></div>
9+
<script type="module">
10+
console.log('test module loaded')
11+
</script>
12+
</body>
13+
</html>
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import fs from 'node:fs'
2+
import path from 'node:path'
3+
import { describe, expect, onTestFinished, test } from 'vitest'
4+
import { createServer } from '../../../server'
5+
import { FS_PREFIX } from '../../../constants'
6+
7+
const FIXTURE_DIR = path.resolve(import.meta.dirname, 'fixtures')
8+
const HTML_PATH = path.resolve(FIXTURE_DIR, 'root/index.html')
9+
const HTML_CONTENT = fs.readFileSync(HTML_PATH, 'utf-8')
10+
11+
async function createTestServer(rootDir?: string) {
12+
const root = path.resolve(import.meta.dirname, rootDir ?? 'fixtures/root')
13+
14+
const server = await createServer({
15+
configFile: false,
16+
root,
17+
logLevel: 'error',
18+
server: {
19+
middlewareMode: true,
20+
ws: false,
21+
},
22+
optimizeDeps: {
23+
noDiscovery: true,
24+
include: [],
25+
},
26+
})
27+
28+
onTestFinished(() => server.close())
29+
return server
30+
}
31+
32+
describe('indexHtml middleware — /@fs/ inline script proxy cache', () => {
33+
test('inline <script type="module"> in an /@fs/ HTML file is loadable via the html-proxy module', async () => {
34+
const server = await createTestServer()
35+
const fsUrl = path.posix.join(FS_PREFIX, HTML_PATH)
36+
37+
const transformed = await server.transformIndexHtml(fsUrl, HTML_CONTENT)
38+
39+
expect(transformed).toContain('html-proxy')
40+
expect(transformed).toContain('index=0')
41+
42+
const proxyUrlMatch = transformed.match(/src="([^"]*html-proxy[^"]*)"/)
43+
expect(
44+
proxyUrlMatch,
45+
'devHtmlHook should have rewritten the inline <script> to a ?html-proxy src',
46+
).toBeTruthy()
47+
48+
const proxyModuleUrl = proxyUrlMatch![1]
49+
50+
const result =
51+
await server.environments.client.transformRequest(proxyModuleUrl)
52+
53+
expect(
54+
result,
55+
'proxy module should resolve without a cache-miss error',
56+
).not.toBeNull()
57+
expect(result!.code).toContain('module loaded')
58+
})
59+
60+
test('inline <script type="module"> in an HTML file served from root is loadable via the html-proxy module', async () => {
61+
const server = await createTestServer('fixtures/root')
62+
const url = '/index.html'
63+
64+
const htmlPath = path.resolve(
65+
import.meta.dirname,
66+
'fixtures/root/index.html',
67+
)
68+
const htmlContent = fs.readFileSync(htmlPath, 'utf-8')
69+
70+
const transformed = await server.transformIndexHtml(url, htmlContent)
71+
72+
expect(transformed).toContain('html-proxy')
73+
expect(transformed).toContain('index=0')
74+
75+
const proxyUrlMatch = transformed.match(/src="([^"]*html-proxy[^"]*)"/)
76+
expect(
77+
proxyUrlMatch,
78+
'devHtmlHook should rewrite the inline <script> to a ?html-proxy src',
79+
).toBeTruthy()
80+
81+
const proxyModuleUrl = proxyUrlMatch![1]
82+
const result =
83+
await server.environments.client.transformRequest(proxyModuleUrl)
84+
85+
expect(result, 'proxy module should resolve without error').not.toBeNull()
86+
expect(result!.code).toContain('module loaded')
87+
})
88+
})

packages/vite/src/node/server/middlewares/indexHtml.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,11 @@ const devHtmlHook: IndexHtmlTransformHook = async (
215215

216216
const trailingSlash = htmlPath.endsWith('/')
217217
if (!trailingSlash && fs.existsSync(filename)) {
218-
proxyModulePath = htmlPath
219-
proxyModuleUrl = proxyModulePath
218+
// If htmlPath is a /@fs/ URL (e.g. vitest-browser always uses this form
219+
// for testerHtmlPath), normalise to an absolute FS path so proxyCacheUrl
220+
// is always root-relative.
221+
proxyModulePath = htmlPath.startsWith(FS_PREFIX) ? filename : htmlPath
222+
proxyModuleUrl = htmlPath
220223
} else {
221224
// There are users of vite.transformIndexHtml calling it with url '/'
222225
// for SSR integrations #7993, filename is root for this case

0 commit comments

Comments
 (0)