Skip to content
Merged
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
6 changes: 5 additions & 1 deletion packages/vite/src/node/server/middlewares/indexHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ function isBareRelative(url: string) {
return wordCharRE.test(url[0]) && !url.includes(':')
}

function getHtmlDirnameForRelativeUrl(htmlPath: string): string {
return htmlPath.endsWith('/') ? htmlPath : path.posix.dirname(htmlPath)
}

const processNodeUrl = (
url: string,
useSrcSetReplacer: boolean,
Expand Down Expand Up @@ -165,7 +169,7 @@ const processNodeUrl = (
} else if (url[0] === '.' || isBareRelative(url)) {
preTransformUrl = path.posix.join(
config.base,
path.posix.dirname(htmlPath),
getHtmlDirnameForRelativeUrl(htmlPath),
url,
)
}
Expand Down
9 changes: 7 additions & 2 deletions playground/ssr-html/__tests__/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@

import path from 'node:path'
import kill from 'kill-port'
import { hmrPorts, ports, rootDir } from '~utils'
import { createInMemoryLogger, hmrPorts, ports, rootDir } from '~utils'

export const port = ports['ssr-html']
export const serverLogs: string[] = []

export async function serve(): Promise<{ close(): Promise<void> }> {
await kill(port)

const { createServer } = await import(path.resolve(rootDir, 'server.js'))
const { app, vite } = await createServer(rootDir, hmrPorts['ssr-html'])
const { app, vite } = await createServer(
rootDir,
hmrPorts['ssr-html'],
createInMemoryLogger(serverLogs),
)

return new Promise((resolve, reject) => {
try {
Expand Down
29 changes: 28 additions & 1 deletion playground/ssr-html/__tests__/ssr-html.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { execFile } from 'node:child_process'
import { promisify } from 'node:util'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { setTimeout } from 'node:timers/promises'
import { describe, expect, test } from 'vitest'
import { port } from './serve'
import { port, serverLogs } from './serve'
import { editFile, isServe, page } from '~utils'

const url = `http://localhost:${port}`
Expand Down Expand Up @@ -43,6 +44,32 @@ describe.runIf(isServe)('injected inline scripts', () => {
})
})

describe.runIf(isServe)('trailing slash html paths', () => {
test('pre-transforms relative module scripts from the trailing slash directory', async () => {
serverLogs.length = 0

const response = await fetch(`${url}/trailing-slash/dir/`)
expect(response.status).toBe(200)
await response.text()

await setTimeout(100) // wait for pre-transform to happen
expect(serverLogs).not.toEqual(
expect.arrayContaining([expect.stringContaining('Pre-transform error')]),
)
})

test('loads relative module scripts from the trailing slash directory', async () => {
await page.goto(`${url}/trailing-slash/dir/`)

await expect
.poll(() => page.textContent('.relative-script'))
.toBe('relative module loaded')
await expect
.poll(() => page.textContent('.relative-parent-script'))
.toBe('relative parent module loaded')
})
})

describe.runIf(isServe)('hmr', () => {
test('handle virtual module updates', async () => {
await page.goto(url)
Expand Down
14 changes: 13 additions & 1 deletion playground/ssr-html/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ const DYNAMIC_STYLES = `
</style>
`

export async function createServer(root = process.cwd(), hmrPort) {
export async function createServer(
root = process.cwd(),
hmrPort,
customLogger,
) {
const resolve = (p) => path.resolve(import.meta.dirname, p)

const app = express()
Expand All @@ -47,6 +51,7 @@ export async function createServer(root = process.cwd(), hmrPort) {
},
},
appType: 'custom',
customLogger,
plugins: [
{
name: 'virtual-file',
Expand All @@ -69,6 +74,13 @@ export async function createServer(root = process.cwd(), hmrPort) {
app.use('*all', async (req, res, next) => {
try {
let [url] = req.originalUrl.split('?')

if (url === '/trailing-slash/dir/') {
const template = fs.readFileSync(resolve(`.${url}index.html`), 'utf-8')
const html = await vite.transformIndexHtml(url, template)
return res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
}

if (url.endsWith('/')) url += 'index.html'

if (url.startsWith('/favicon.ico')) {
Expand Down
2 changes: 2 additions & 0 deletions playground/ssr-html/trailing-slash/dir/filename.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
document.querySelector('.relative-script').textContent =
'relative module loaded'
13 changes: 13 additions & 0 deletions playground/ssr-html/trailing-slash/dir/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Trailing slash</title>
</head>
<body>
<div class="relative-script"></div>
<div class="relative-parent-script"></div>
<script type="module" src="./filename.js"></script>
<script type="module" src="../other.js"></script>
</body>
</html>
2 changes: 2 additions & 0 deletions playground/ssr-html/trailing-slash/other.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
document.querySelector('.relative-parent-script').textContent =
'relative parent module loaded'
Loading