diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 8b0463828e22e4..654113da1411bb 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -264,14 +264,15 @@ export function cssPlugin(config: ResolvedConfig): Plugin { const ssr = options?.ssr === true const urlReplacer: CssUrlReplacer = async (url, importer) => { - if (checkPublicFile(url, config)) { + const decodedUrl = decodeURI(url) + if (checkPublicFile(decodedUrl, config)) { if (encodePublicUrlsInCSS(config)) { - return publicFileToBuiltUrl(url, config) + return publicFileToBuiltUrl(decodedUrl, config) } else { - return joinUrlSegments(config.base, url) + return joinUrlSegments(config.base, decodedUrl) } } - const resolved = await resolveUrl(url, importer) + const resolved = await resolveUrl(decodedUrl, importer) if (resolved) { return fileToUrl(resolved, config, this) } @@ -279,7 +280,7 @@ export function cssPlugin(config: ResolvedConfig): Plugin { const isExternal = config.build.rollupOptions.external ? resolveUserExternal( config.build.rollupOptions.external, - url, // use URL as id since id could not be resolved + decodedUrl, // use URL as id since id could not be resolved id, false, ) @@ -288,7 +289,7 @@ export function cssPlugin(config: ResolvedConfig): Plugin { if (!isExternal) { // #9800 If we cannot resolve the css url, leave a warning. config.logger.warnOnce( - `\n${url} referenced in ${id} didn't resolve at build time, it will remain unchanged to be resolved at runtime`, + `\n${decodedUrl} referenced in ${id} didn't resolve at build time, it will remain unchanged to be resolved at runtime`, ) } } diff --git a/playground/assets/__tests__/assets.spec.ts b/playground/assets/__tests__/assets.spec.ts index c4c80e28a00c04..98ec4e39826b87 100644 --- a/playground/assets/__tests__/assets.spec.ts +++ b/playground/assets/__tests__/assets.spec.ts @@ -23,6 +23,10 @@ const assetMatch = isBuild ? /\/foo\/bar\/assets\/asset-[-\w]{8}\.png/ : '/foo/bar/nested/asset.png' +const encodedAssetMatch = isBuild + ? /\/foo\/bar\/assets\/asset_small_-[-\w]{8}\.png/ + : '/foo/bar/nested/asset[small].png' + const iconMatch = `/foo/bar/icon.png` const fetchPath = (p: string) => { @@ -153,6 +157,10 @@ describe('css url() references', () => { expect(await getBg('.css-url-relative')).toMatch(assetMatch) }) + test('encoded', async () => { + expect(await getBg('.css-url-encoded')).toMatch(encodedAssetMatch) + }) + test('image-set relative', async () => { const imageSet = await getBg('.css-image-set-relative') imageSet.split(', ').forEach((s) => { diff --git a/playground/assets/css/css-url.css b/playground/assets/css/css-url.css index 06f998f20bfea5..61282fb20fa3b7 100644 --- a/playground/assets/css/css-url.css +++ b/playground/assets/css/css-url.css @@ -10,6 +10,11 @@ background-size: 10px; } +.css-url-encoded { + background: url('/nested/asset%5Bsmall%5D.png'); + background-size: 10px; +} + .css-image-set-relative { background-image: -webkit-image-set( url('../nested/asset.png') 1x, diff --git a/playground/assets/index.html b/playground/assets/index.html index 1765bc6d8ee83c..dd013828cc96b0 100644 --- a/playground/assets/index.html +++ b/playground/assets/index.html @@ -41,6 +41,9 @@

CSS url references

CSS background (relative)
+
+ CSS background (encoded) +
CSS background with image-set() (relative)