From b98aa14184d6c7d15293413ba5a7824c839ae5e7 Mon Sep 17 00:00:00 2001 From: XploitMonk0x01 Date: Mon, 16 Mar 2026 01:12:57 +0530 Subject: [PATCH] fix(html): handle trailing slash in transformIndexHtml pre-transform --- .../middlewares/__tests__/indexHtml.spec.ts | 29 +++++++++++++++++++ .../src/node/server/middlewares/indexHtml.ts | 6 +++- 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 packages/vite/src/node/server/middlewares/__tests__/indexHtml.spec.ts diff --git a/packages/vite/src/node/server/middlewares/__tests__/indexHtml.spec.ts b/packages/vite/src/node/server/middlewares/__tests__/indexHtml.spec.ts new file mode 100644 index 00000000000000..11ab318117a858 --- /dev/null +++ b/packages/vite/src/node/server/middlewares/__tests__/indexHtml.spec.ts @@ -0,0 +1,29 @@ +import path from 'node:path' +import { describe, expect, test } from 'vitest' +import { getHtmlDirnameForRelativeUrl } from '../indexHtml' + +describe('getHtmlDirnameForRelativeUrl', () => { + test('keeps trailing slash paths unchanged', () => { + expect(getHtmlDirnameForRelativeUrl('/example/dir/')).toBe('/example/dir/') + }) + + test('returns dirname for html files', () => { + expect(getHtmlDirnameForRelativeUrl('/example/dir/index.html')).toBe( + '/example/dir', + ) + }) + + test('preserves root path', () => { + expect(getHtmlDirnameForRelativeUrl('/')).toBe('/') + }) + + test('resolves relative script from trailing slash directory', () => { + const preTransformUrl = path.posix.join( + '/', + getHtmlDirnameForRelativeUrl('/example/dir/'), + './filename.js', + ) + + expect(preTransformUrl).toBe('/example/dir/filename.js') + }) +}) diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index 0e9c48518cac45..ea6b31f810f9e5 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -128,6 +128,10 @@ function isBareRelative(url: string) { return wordCharRE.test(url[0]) && !url.includes(':') } +export function getHtmlDirnameForRelativeUrl(htmlPath: string): string { + return htmlPath.endsWith('/') ? htmlPath : path.posix.dirname(htmlPath) +} + const processNodeUrl = ( url: string, useSrcSetReplacer: boolean, @@ -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, ) }