diff --git a/packages/vite/src/node/__tests__/plugins/css.spec.ts b/packages/vite/src/node/__tests__/plugins/css.spec.ts index d925ee96a45dc8..da69d04330350a 100644 --- a/packages/vite/src/node/__tests__/plugins/css.spec.ts +++ b/packages/vite/src/node/__tests__/plugins/css.spec.ts @@ -421,6 +421,22 @@ describe('preprocessCSS', () => { " `) }) + + test('lightningcss preserves $ in external @import url', async () => { + const resolvedConfig = await resolveConfig( + { + configFile: false, + css: { transformer: 'lightningcss' }, + }, + 'serve', + ) + const result = await preprocessCSS( + `@import 'http://example.com/a.css?x=$&y';`, + 'foo.css', + resolvedConfig, + ) + expect(result.code).toContain('http://example.com/a.css?x=$&y') + }) }) // Sass does not consult the `main` field; see diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 3669462c422d53..da43fdef44eb7c 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -3400,7 +3400,9 @@ async function compileLightningCSS( break } case 'import': { - css = css.replace(dep.placeholder, dep.url) + // use a function replacer so `$` sequences in the URL are inserted + // verbatim instead of being interpreted as replacement patterns + css = css.replace(dep.placeholder, () => dep.url) break } default: