Skip to content

Commit d64a1a5

Browse files
authored
fix(css): support external CSS with lightningcss (#18389)
1 parent c23d85b commit d64a1a5

9 files changed

Lines changed: 44 additions & 2 deletions

File tree

packages/vite/src/node/plugins/css.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3194,6 +3194,8 @@ function isPreProcessor(lang: any): lang is PreprocessLang {
31943194
return lang && preprocessorSet.has(lang)
31953195
}
31963196

3197+
const absoluteOrProtocolRelativeUrlRE = /^(?:[a-z]+:)?\/\//i
3198+
31973199
const importEsbuild = createCachedImport(() => import('esbuild'))
31983200

31993201
const importLightningCSS = createCachedImport(() => import('lightningcss'))
@@ -3268,6 +3270,12 @@ async function compileLightningCSS(
32683270
return publicFile
32693271
}
32703272

3273+
// contrary to lightningcss, postcss-import does this internally
3274+
if (absoluteOrProtocolRelativeUrlRE.test(id)) {
3275+
// @ts-expect-error -- https://github.com/parcel-bundler/lightningcss/pull/1261
3276+
return { external: id } as string
3277+
}
3278+
32713279
// NOTE: with `transformer: 'postcss'`, CSS modules `composes` tried to resolve with
32723280
// all resolvers, but in `transformer: 'lightningcss'`, only the one for the
32733281
// current file type is used.
@@ -3308,7 +3316,7 @@ async function compileLightningCSS(
33083316
config.command === 'build'
33093317
? !!config.build.sourcemap
33103318
: config.css.devSourcemap,
3311-
analyzeDependencies: true,
3319+
analyzeDependencies: { preserveImports: true },
33123320
cssModules: cssModuleRE.test(id)
33133321
? (config.css.lightningcss?.cssModules ?? true)
33143322
: undefined,
@@ -3391,8 +3399,14 @@ async function compileLightningCSS(
33913399
)
33923400
break
33933401
}
3402+
case 'import': {
3403+
css = css.replace(dep.placeholder, dep.url)
3404+
break
3405+
}
33943406
default:
3395-
throw new Error(`Unsupported dependency type: ${dep.type}`)
3407+
throw new Error(
3408+
`Unsupported dependency type: ${(dep satisfies never as any).type}`,
3409+
)
33963410
}
33973411
}
33983412

playground/css-lightningcss/__tests__/css-lightningcss.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ test('css import from js', async () => {
4545
await expect.poll(() => getColor(atImport)).toBe('blue')
4646
})
4747

48+
test('@import external css', async () => {
49+
const icon = page.locator('.icon--mdi-light--help-circle')
50+
expect(
51+
await icon.evaluate((span) => getComputedStyle(span).maskImage),
52+
).toContain('data:image/svg+xml,')
53+
})
54+
4855
test('css modules', async () => {
4956
const imported = await page.$('.modules')
5057
expect(await getColor(imported)).toBe('turquoise')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import 'https://api.iconify.design/mdi-light.css?icons=help-circle';

playground/css-lightningcss/index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ <h1>Lightning CSS</h1>
1111
@import in import from js: This should be purple
1212
</p>
1313

14+
<p>
15+
External CSS:
16+
<span class="icon--mdi-light icon--mdi-light--help-circle"></span>
17+
</p>
18+
1419
<p class="modules">CSS modules: this should be turquoise</p>
1520
<p>Imported CSS module:</p>
1621
<pre class="modules-code"></pre>

playground/css-lightningcss/main.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import './minify.css'
22
import './imported.css'
3+
import './external.css'
34
import mod from './mod.module.css'
45
import './external-url.css'
56
import './css-url.css'

playground/css/__tests__/tests.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,13 @@ test('layers', async () => {
242242
expect(await getColor('.layers-green')).toMatch('green')
243243
})
244244

245+
test('@import external css', async () => {
246+
const icon = page.locator('.icon--mdi-light--help-circle')
247+
expect(
248+
await icon.evaluate((span) => getComputedStyle(span).maskImage),
249+
).toContain('data:image/svg+xml,')
250+
})
251+
245252
test('@import dependency w/ style entry', async () => {
246253
expect(await getColor('.css-dep')).toBe('purple')
247254
})

playground/css/external.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import 'https://api.iconify.design/mdi-light.css?icons=help-circle';

playground/css/index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@ <h1>CSS</h1>
158158
<span class="layers-green">green</span>
159159
</p>
160160

161+
<p>
162+
External CSS:
163+
<span class="icon--mdi-light icon--mdi-light--help-circle"></span>
164+
</p>
165+
161166
<p class="css-dep">
162167
@import dependency w/ style entrypoints: this should be purple
163168
</p>

playground/css/main.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import charset from './charset.css?inline'
5252
text('.charset-css', charset)
5353

5454
import './layered/index.css'
55+
import './external.css'
5556

5657
import './dep.css'
5758
import './glob-dep.css'

0 commit comments

Comments
 (0)