diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index 568e0f202fcd39..161c2c9065924d 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -27,7 +27,8 @@ import { ensureWatchedFile, fsPathFromId, injectQuery, - normalizePath + normalizePath, + processSrcSetSync } from '../../utils' import type { ModuleGraph } from '../moduleGraph' @@ -92,18 +93,23 @@ const processNodeUrl = ( originalUrl !== '/' && htmlPath === '/index.html' ) { - // #3230 if some request url (localhost:5173/a/b) return to fallback html, the relative assets + const replacer = (url: string) => + path.posix.join( + config.base, + path.posix.relative(originalUrl, config.base), + url.slice(1) + ) + + // #3230 if some request url (localhost:3000/a/b) return to fallback html, the relative assets // path will add `/a/` prefix, it will caused 404. // rewrite before `./index.js` -> `localhost:5173/a/index.js`. // rewrite after `../index.js` -> `localhost:5173/index.js`. s.overwrite( node.value!.loc.start.offset, node.value!.loc.end.offset, - `"${path.posix.join( - path.posix.relative(originalUrl, '/'), - url.slice(1) - )}"`, - { contentOnly: true } + node.name === 'srcset' + ? `"${processSrcSetSync(url, ({ url }) => replacer(url))}"` + : `"${replacer(url)}"` ) } } diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 70eb3fb8229b81..d537eb9d971673 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -560,11 +560,16 @@ interface ImageCandidate { } const escapedSpaceCharacters = /( |\\t|\\n|\\f|\\r)+/g const imageSetUrlRE = /^(?:[\w\-]+\(.*?\)|'.*?'|".*?"|\S*)/ -export async function processSrcSet( - srcs: string, - replacer: (arg: ImageCandidate) => Promise -): Promise { - const imageCandidates: ImageCandidate[] = splitSrcSet(srcs) +function reduceSrcset(ret: { url: string; descriptor: string }[]) { + return ret.reduce((prev, { url, descriptor }, index) => { + descriptor ??= '' + return (prev += + url + ` ${descriptor}${index === ret.length - 1 ? '' : ', '}`) + }, '') +} + +function splitSrcSetDescriptor(srcs: string): ImageCandidate[] { + return splitSrcSet(srcs) .map((s) => { const src = s.replace(escapedSpaceCharacters, ' ').trim() const [url] = imageSetUrlRE.exec(src) || [] @@ -575,21 +580,30 @@ export async function processSrcSet( } }) .filter(({ url }) => !!url) +} - const ret = await Promise.all( - imageCandidates.map(async ({ url, descriptor }) => { - return { - url: await replacer({ url, descriptor }), - descriptor - } - }) - ) +export function processSrcSet( + srcs: string, + replacer: (arg: ImageCandidate) => Promise +): Promise { + return Promise.all( + splitSrcSetDescriptor(srcs).map(async ({ url, descriptor }) => ({ + url: await replacer({ url, descriptor }), + descriptor + })) + ).then((ret) => reduceSrcset(ret)) +} - return ret.reduce((prev, { url, descriptor }, index) => { - descriptor ??= '' - return (prev += - url + ` ${descriptor}${index === ret.length - 1 ? '' : ', '}`) - }, '') +export function processSrcSetSync( + srcs: string, + replacer: (arg: ImageCandidate) => string +): string { + return reduceSrcset( + splitSrcSetDescriptor(srcs).map(({ url, descriptor }) => ({ + url: replacer({ url, descriptor }), + descriptor + })) + ) } function splitSrcSet(srcs: string) { diff --git a/playground/assets/__tests__/assets.spec.ts b/playground/assets/__tests__/assets.spec.ts index b3da12a0003e57..20040c173c7f87 100644 --- a/playground/assets/__tests__/assets.spec.ts +++ b/playground/assets/__tests__/assets.spec.ts @@ -192,7 +192,7 @@ describe('image', () => { expect(s).toMatch( isBuild ? /\/foo\/assets\/asset\.\w{8}\.png \d{1}x/ - : /\.\/nested\/asset\.png \d{1}x/ + : /\/foo\/nested\/asset\.png \d{1}x/ ) }) }) diff --git a/playground/env/__tests__/env.spec.ts b/playground/env/__tests__/env.spec.ts index 373e051a22a66e..f9955706101bb6 100644 --- a/playground/env/__tests__/env.spec.ts +++ b/playground/env/__tests__/env.spec.ts @@ -3,7 +3,7 @@ import { isBuild, page } from '~utils' const mode = isBuild ? `production` : `development` test('base', async () => { - expect(await page.textContent('.base')).toBe('/') + expect(await page.textContent('.base')).toBe('/env/') }) test('mode', async () => { @@ -46,9 +46,15 @@ test('env object', async () => { VITE_EFFECTIVE_MODE_FILE_NAME: `.env.${mode}`, CUSTOM_PREFIX_ENV_VARIABLE: '1', VITE_CUSTOM_ENV_VARIABLE: '1', - BASE_URL: '/', + BASE_URL: '/env/', MODE: mode, DEV: !isBuild, PROD: isBuild }) }) + +if (!isBuild) { + test('relative url import script return import.meta.url', async () => { + expect(await page.textContent('.url')).toMatch('/env/index.js') + }) +} diff --git a/playground/env/index.html b/playground/env/index.html index 3728b870b1df32..a7553eeb181f73 100644 --- a/playground/env/index.html +++ b/playground/env/index.html @@ -14,6 +14,7 @@

Environment Variables

import.meta.env.VITE_INLINE:

process.env.NODE_ENV:

import.meta.env:

+

import.meta.url:

+