From 3734f8099e3922c189497ce404fe7ff2f8929ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Wed, 18 Dec 2024 18:49:21 +0900 Subject: [PATCH] fix(css): escape double quotes in `url()` when lightningcss is used (#18997) --- packages/vite/src/node/plugins/css.ts | 26 +++++++++++-------- playground/assets/__tests__/assets.spec.ts | 6 +++++ playground/assets/css/css-url.css | 5 ++++ playground/assets/index.html | 3 +++ .../__tests__/css-lightningcss.spec.ts | 6 +++++ playground/css-lightningcss/css-url.css | 5 ++++ playground/css-lightningcss/index.html | 3 +++ 7 files changed, 43 insertions(+), 11 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 873bcfc360d79b..46db13a0f15f51 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -3237,21 +3237,25 @@ async function compileLightningCSS( let css = decoder.decode(res.code) for (const dep of res.dependencies!) { switch (dep.type) { - case 'url': + case 'url': { + let replaceUrl: string if (skipUrlReplacer(dep.url)) { - css = css.replace(dep.placeholder, () => dep.url) - break - } - if (urlReplacer) { - const replaceUrl = await urlReplacer( - dep.url, - toAbsolute(dep.loc.filePath), - ) - css = css.replace(dep.placeholder, () => replaceUrl) + replaceUrl = dep.url + } else if (urlReplacer) { + replaceUrl = await urlReplacer(dep.url, toAbsolute(dep.loc.filePath)) } else { - css = css.replace(dep.placeholder, () => dep.url) + replaceUrl = dep.url } + + css = css.replace( + dep.placeholder, + // lightningcss always generates `url("placeholder")` + // (`url('placeholder')`, `url(placeholder)` is not generated) + // so escape double quotes + () => replaceUrl.replaceAll('"', '\\"'), + ) break + } default: throw new Error(`Unsupported dependency type: ${dep.type}`) } diff --git a/playground/assets/__tests__/assets.spec.ts b/playground/assets/__tests__/assets.spec.ts index 0f33ca7d4b55fb..b778c6718cdf69 100644 --- a/playground/assets/__tests__/assets.spec.ts +++ b/playground/assets/__tests__/assets.spec.ts @@ -276,6 +276,12 @@ describe('css url() references', () => { expect(bg).toMatch(assetMatch) }) + test('preinlined SVG', async () => { + expect(await getBg('.css-url-preinlined-svg')).toMatch( + /data:image\/svg\+xml,.+/, + ) + }) + test.runIf(isBuild)('generated paths in CSS', () => { const css = findAssetFile(/index-[-\w]{8}\.css$/, 'foo') diff --git a/playground/assets/css/css-url.css b/playground/assets/css/css-url.css index 61282fb20fa3b7..caab9bf8f96cf6 100644 --- a/playground/assets/css/css-url.css +++ b/playground/assets/css/css-url.css @@ -104,6 +104,11 @@ background-size: 10px; } +.css-url-preinlined-svg { + background: url('data:image/svg+xml,'); + background-size: 20px; +} + /* urls inside comments should be ignored diff --git a/playground/assets/index.html b/playground/assets/index.html index e41ae78cff0227..b6055c78cc4e3f 100644 --- a/playground/assets/index.html +++ b/playground/assets/index.html @@ -139,6 +139,9 @@

CSS url references

CSS background (aliased)
+
+ CSS background (pre inlined SVG) +
CSS nested manual chunks relative base background { const bg = await getBg('.css-url-aliased') expect(bg).toMatch('data:image/svg+xml,') }) + +test('preinlined SVG', async () => { + expect(await getBg('.css-url-preinlined-svg')).toMatch( + /data:image\/svg\+xml,.+/, + ) +}) diff --git a/playground/css-lightningcss/css-url.css b/playground/css-lightningcss/css-url.css index 2694e5f5136fec..6695ad2b9b0bd3 100644 --- a/playground/css-lightningcss/css-url.css +++ b/playground/css-lightningcss/css-url.css @@ -2,3 +2,8 @@ background: url('@/fragment.svg'); background-size: 10px; } + +.css-url-preinlined-svg { + background: url('data:image/svg+xml,'); + background-size: 20px; +} diff --git a/playground/css-lightningcss/index.html b/playground/css-lightningcss/index.html index e2ee95e640e2bd..c0756b11314831 100644 --- a/playground/css-lightningcss/index.html +++ b/playground/css-lightningcss/index.html @@ -33,6 +33,9 @@

Lightning CSS

CSS background (aliased)
+
+ CSS background (pre inlined SVG) +