diff --git a/packages/playground/css/__tests__/css.spec.ts b/packages/playground/css/__tests__/css.spec.ts index a476826e2023cc..d3577176b4bb16 100644 --- a/packages/playground/css/__tests__/css.spec.ts +++ b/packages/playground/css/__tests__/css.spec.ts @@ -388,3 +388,11 @@ test('import css in less', async () => { expect(await getColor('.css-in-less')).toBe('yellow') expect(await getColor('.css-in-less-2')).toBe('blue') }) + +test("relative path rewritten in Less's data-uri", async () => { + // relative path passed to Less's data-uri is rewritten to absolute, + // the Less inlines it + expect(await getBg('.form-box-data-uri')).toMatch( + /^url\("data:image\/svg\+xml,%3Csvg/ + ) +}) diff --git a/packages/playground/css/index.html b/packages/playground/css/index.html index 52d6bffef134e6..dd496a8ffdce11 100644 --- a/packages/playground/css/index.html +++ b/packages/playground/css/index.html @@ -49,6 +49,10 @@

CSS

Imported Less string:


 
+  
+ tests Less's `data-uri()` function with relative image paths +
+

Stylus: This should be blue

Stylus additionalData: This should be orange diff --git a/packages/playground/css/less.less b/packages/playground/css/less.less index 69ffa830862014..49cbd3c3bb336e 100644 --- a/packages/playground/css/less.less +++ b/packages/playground/css/less.less @@ -1,6 +1,9 @@ @import '@/nested/nested'; @import './nested/css-in-less.less'; +// Test data-uri calls with relative images. +@import './less/components/form.less'; + @color: blue; .less { diff --git a/packages/playground/css/less/components/form.less b/packages/playground/css/less/components/form.less new file mode 100644 index 00000000000000..feaaea94ce1bba --- /dev/null +++ b/packages/playground/css/less/components/form.less @@ -0,0 +1,4 @@ +.form-box-data-uri { + // data-uri() calls with relative paths should be replaced just like urls. + background-image: data-uri('../images/backgrounds/form-select.svg'); +} diff --git a/packages/playground/css/less/images/backgrounds/form-select.svg b/packages/playground/css/less/images/backgrounds/form-select.svg new file mode 100644 index 00000000000000..8aaf69c09e03f4 --- /dev/null +++ b/packages/playground/css/less/images/backgrounds/form-select.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index e2ce47851b7818..dbe9e7d1dfa00f 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -965,6 +965,8 @@ type CssUrlReplacer = ( // https://drafts.csswg.org/css-syntax-3/#identifier-code-point export const cssUrlRE = /(?<=^|[^\w\-\u0080-\uffff])url\(\s*('[^']+'|"[^"]+"|[^'")]+)\s*\)/ +export const cssDataUriRE = + /(?<=^|[^\w\-\u0080-\uffff])data-uri\(\s*('[^']+'|"[^"]+"|[^'")]+)\s*\)/ export const importCssRE = /@import ('[^']+\.css'|"[^"]+\.css"|[^'")]+\.css)/ const cssImageSetRE = /image-set\(([^)]+)\)/ @@ -1015,6 +1017,16 @@ function rewriteCssUrls( }) } +function rewriteCssDataUris( + css: string, + replacer: CssUrlReplacer +): Promise { + return asyncReplace(css, cssDataUriRE, async (match) => { + const [matched, rawUrl] = match + return await doUrlReplace(rawUrl, matched, replacer, 'data-uri') + }) +} + function rewriteImportCss( css: string, replacer: CssUrlReplacer @@ -1040,7 +1052,8 @@ function rewriteCssImageSet( async function doUrlReplace( rawUrl: string, matched: string, - replacer: CssUrlReplacer + replacer: CssUrlReplacer, + funcName: string = 'url' ) { let wrap = '' const first = rawUrl[0] @@ -1057,7 +1070,7 @@ async function doUrlReplace( // The new url might need wrapping even if the original did not have it, e.g. if a space was added during replacement wrap = "'" } - return `url(${wrap}${newUrl}${wrap})` + return `${funcName}(${wrap}${newUrl}${wrap})` } async function doImportCSSReplace( @@ -1307,10 +1320,12 @@ async function rebaseUrls( const content = fs.readFileSync(file, 'utf-8') // no url() const hasUrls = cssUrlRE.test(content) + // data-uri() calls + const hasDataUris = cssDataUriRE.test(content) // no @import xxx.css const hasImportCss = importCssRE.test(content) - if (!hasUrls && !hasImportCss) { + if (!hasUrls && !hasDataUris && !hasImportCss) { return { file } } @@ -1339,6 +1354,10 @@ async function rebaseUrls( rebased = await rewriteCssUrls(rebased || content, rebaseFn) } + if (hasDataUris) { + rebased = await rewriteCssDataUris(rebased || content, rebaseFn) + } + return { file, contents: rebased