@@ -114,7 +114,7 @@ export interface CSSOptions {
114
114
/**
115
115
* Using lightningcss is an experimental option to handle CSS modules,
116
116
* assets and imports via Lightning CSS. It requires to install it as a
117
- * peer dependency. This is incompatible with the use of preprocessors.
117
+ * peer dependency.
118
118
*
119
119
* @default 'postcss'
120
120
* @experimental
@@ -206,7 +206,7 @@ export const cssConfigDefaults = Object.freeze({
206
206
} satisfies CSSOptions )
207
207
208
208
export type ResolvedCSSOptions = Omit < CSSOptions , 'lightningcss' > &
209
- Required < Pick < CSSOptions , 'transformer' > > & {
209
+ Required < Pick < CSSOptions , 'transformer' | 'devSourcemap' > > & {
210
210
lightningcss ?: LightningCSSOptions
211
211
}
212
212
@@ -1267,7 +1267,11 @@ async function compileCSSPreprocessors(
1267
1267
lang : PreprocessLang ,
1268
1268
code : string ,
1269
1269
workerController : PreprocessorWorkerController ,
1270
- ) : Promise < { code : string ; map ?: ExistingRawSourceMap ; deps ?: Set < string > } > {
1270
+ ) : Promise < {
1271
+ code : string
1272
+ map ?: ExistingRawSourceMap | { mappings : '' }
1273
+ deps ?: Set < string >
1274
+ } > {
1271
1275
const { config } = environment
1272
1276
const { preprocessorOptions, devSourcemap } = config . css
1273
1277
const atImportResolvers = getAtImportResolvers (
@@ -1341,15 +1345,11 @@ async function compileCSS(
1341
1345
deps ?: Set < string >
1342
1346
} > {
1343
1347
const { config } = environment
1344
- if ( config . css . transformer === 'lightningcss' ) {
1345
- return compileLightningCSS ( id , code , environment , urlResolver )
1346
- }
1347
-
1348
1348
const lang = CSS_LANGS_RE . exec ( id ) ?. [ 1 ] as CssLang | undefined
1349
1349
const deps = new Set < string > ( )
1350
1350
1351
1351
// pre-processors: sass etc.
1352
- let preprocessorMap : ExistingRawSourceMap | undefined
1352
+ let preprocessorMap : ExistingRawSourceMap | { mappings : '' } | undefined
1353
1353
if ( isPreProcessor ( lang ) ) {
1354
1354
const preprocessorResult = await compileCSSPreprocessors (
1355
1355
environment ,
@@ -1361,8 +1361,71 @@ async function compileCSS(
1361
1361
code = preprocessorResult . code
1362
1362
preprocessorMap = preprocessorResult . map
1363
1363
preprocessorResult . deps ?. forEach ( ( dep ) => deps . add ( dep ) )
1364
+ } else if ( lang === 'sss' && config . css . transformer === 'lightningcss' ) {
1365
+ const sssResult = await transformSugarSS ( environment , id , code )
1366
+ code = sssResult . code
1367
+ preprocessorMap = sssResult . map
1368
+ }
1369
+
1370
+ const transformResult = await ( config . css . transformer === 'lightningcss'
1371
+ ? compileLightningCSS (
1372
+ environment ,
1373
+ id ,
1374
+ code ,
1375
+ deps ,
1376
+ workerController ,
1377
+ urlResolver ,
1378
+ )
1379
+ : compilePostCSS (
1380
+ environment ,
1381
+ id ,
1382
+ code ,
1383
+ deps ,
1384
+ lang ,
1385
+ workerController ,
1386
+ urlResolver ,
1387
+ ) )
1388
+
1389
+ if ( ! transformResult ) {
1390
+ return {
1391
+ code,
1392
+ map : config . css . devSourcemap ? preprocessorMap : { mappings : '' } ,
1393
+ deps,
1394
+ }
1364
1395
}
1365
1396
1397
+ return {
1398
+ ...transformResult ,
1399
+ map : config . css . devSourcemap
1400
+ ? combineSourcemapsIfExists (
1401
+ cleanUrl ( id ) ,
1402
+ typeof transformResult . map === 'string'
1403
+ ? JSON . parse ( transformResult . map )
1404
+ : transformResult . map ,
1405
+ preprocessorMap ,
1406
+ )
1407
+ : { mappings : '' } ,
1408
+ deps,
1409
+ }
1410
+ }
1411
+
1412
+ async function compilePostCSS (
1413
+ environment : PartialEnvironment ,
1414
+ id : string ,
1415
+ code : string ,
1416
+ deps : Set < string > ,
1417
+ lang : CssLang | undefined ,
1418
+ workerController : PreprocessorWorkerController ,
1419
+ urlResolver ?: CssUrlResolver ,
1420
+ ) : Promise <
1421
+ | {
1422
+ code : string
1423
+ map ?: Exclude < SourceMapInput , string >
1424
+ modules ?: Record < string , string >
1425
+ }
1426
+ | undefined
1427
+ > {
1428
+ const { config } = environment
1366
1429
const { modules : modulesOptions , devSourcemap } = config . css
1367
1430
const isModule = modulesOptions !== false && cssModuleRE . test ( id )
1368
1431
// although at serve time it can work without processing, we do need to
@@ -1381,7 +1444,7 @@ async function compileCSS(
1381
1444
! needInlineImport &&
1382
1445
! hasUrl
1383
1446
) {
1384
- return { code , map : preprocessorMap ?? null , deps }
1447
+ return
1385
1448
}
1386
1449
1387
1450
// postcss
@@ -1506,25 +1569,61 @@ async function compileCSS(
1506
1569
lang === 'sss' ? loadSss ( config . root ) : postcssOptions . parser
1507
1570
1508
1571
if ( ! postcssPlugins . length && ! postcssParser ) {
1509
- return {
1510
- code,
1511
- map : preprocessorMap ,
1512
- deps,
1513
- }
1572
+ return
1514
1573
}
1515
1574
1575
+ const result = await runPostCSS (
1576
+ id ,
1577
+ code ,
1578
+ postcssPlugins ,
1579
+ { ...postcssOptions , parser : postcssParser } ,
1580
+ deps ,
1581
+ environment . logger ,
1582
+ devSourcemap ,
1583
+ )
1584
+ return { ...result , modules }
1585
+ }
1586
+
1587
+ async function transformSugarSS (
1588
+ environment : PartialEnvironment ,
1589
+ id : string ,
1590
+ code : string ,
1591
+ ) {
1592
+ const { config } = environment
1593
+ const { devSourcemap } = config . css
1594
+
1595
+ const result = await runPostCSS (
1596
+ id ,
1597
+ code ,
1598
+ [ ] ,
1599
+ { parser : loadSss ( config . root ) } ,
1600
+ undefined ,
1601
+ environment . logger ,
1602
+ devSourcemap ,
1603
+ )
1604
+ return result
1605
+ }
1606
+
1607
+ async function runPostCSS (
1608
+ id : string ,
1609
+ code : string ,
1610
+ plugins : PostCSS . AcceptedPlugin [ ] ,
1611
+ options : PostCSS . ProcessOptions ,
1612
+ deps : Set < string > | undefined ,
1613
+ logger : Logger ,
1614
+ enableSourcemap : boolean ,
1615
+ ) {
1516
1616
let postcssResult : PostCSS . Result
1517
1617
try {
1518
1618
const source = removeDirectQuery ( id )
1519
1619
const postcss = await importPostcss ( )
1520
1620
1521
1621
// postcss is an unbundled dep and should be lazy imported
1522
- postcssResult = await postcss . default ( postcssPlugins ) . process ( code , {
1523
- ...postcssOptions ,
1524
- parser : postcssParser ,
1622
+ postcssResult = await postcss . default ( plugins ) . process ( code , {
1623
+ ...options ,
1525
1624
to : source ,
1526
1625
from : source ,
1527
- ...( devSourcemap
1626
+ ...( enableSourcemap
1528
1627
? {
1529
1628
map : {
1530
1629
inline : false ,
@@ -1542,7 +1641,7 @@ async function compileCSS(
1542
1641
// record CSS dependencies from @imports
1543
1642
for ( const message of postcssResult . messages ) {
1544
1643
if ( message . type === 'dependency' ) {
1545
- deps . add ( normalizePath ( message . file as string ) )
1644
+ deps ? .add ( normalizePath ( message . file as string ) )
1546
1645
} else if ( message . type === 'dir-dependency' ) {
1547
1646
// https://github.com/postcss/postcss/blob/main/docs/guidelines/plugin.md#3-dependencies
1548
1647
const { dir, glob : globPattern = '**' } = message
@@ -1553,7 +1652,7 @@ async function compileCSS(
1553
1652
ignore : [ '**/node_modules/**' ] ,
1554
1653
} )
1555
1654
for ( let i = 0 ; i < files . length ; i ++ ) {
1556
- deps . add ( files [ i ] )
1655
+ deps ? .add ( files [ i ] )
1557
1656
}
1558
1657
} else if ( message . type === 'warning' ) {
1559
1658
const warning = message as PostCSS . Warning
@@ -1571,7 +1670,7 @@ async function compileCSS(
1571
1670
}
1572
1671
: undefined ,
1573
1672
) } `
1574
- environment . logger . warn ( colors . yellow ( msg ) )
1673
+ logger . warn ( colors . yellow ( msg ) )
1575
1674
}
1576
1675
}
1577
1676
} catch ( e ) {
@@ -1585,17 +1684,14 @@ async function compileCSS(
1585
1684
throw e
1586
1685
}
1587
1686
1588
- if ( ! devSourcemap ) {
1687
+ if ( ! enableSourcemap ) {
1589
1688
return {
1590
1689
code : postcssResult . css ,
1591
- map : { mappings : '' } ,
1592
- modules,
1593
- deps,
1690
+ map : { mappings : '' as const } ,
1594
1691
}
1595
1692
}
1596
1693
1597
1694
const rawPostcssMap = postcssResult . map . toJSON ( )
1598
-
1599
1695
const postcssMap = await formatPostcssSourceMap (
1600
1696
// version property of rawPostcssMap is declared as string
1601
1697
// but actually it is a number
@@ -1605,9 +1701,7 @@ async function compileCSS(
1605
1701
1606
1702
return {
1607
1703
code : postcssResult . css ,
1608
- map : combineSourcemapsIfExists ( cleanUrl ( id ) , postcssMap , preprocessorMap ) ,
1609
- modules,
1610
- deps,
1704
+ map : postcssMap ,
1611
1705
}
1612
1706
}
1613
1707
@@ -1702,17 +1796,21 @@ export async function formatPostcssSourceMap(
1702
1796
1703
1797
function combineSourcemapsIfExists (
1704
1798
filename : string ,
1705
- map1 : ExistingRawSourceMap | undefined ,
1706
- map2 : ExistingRawSourceMap | undefined ,
1707
- ) : ExistingRawSourceMap | undefined {
1708
- return map1 && map2
1709
- ? ( combineSourcemaps ( filename , [
1710
- // type of version property of ExistingRawSourceMap is number
1711
- // but it is always 3
1712
- map1 as RawSourceMap ,
1713
- map2 as RawSourceMap ,
1714
- ] ) as ExistingRawSourceMap )
1715
- : map1
1799
+ map1 : ExistingRawSourceMap | { mappings : '' } | undefined ,
1800
+ map2 : ExistingRawSourceMap | { mappings : '' } | undefined ,
1801
+ ) : ExistingRawSourceMap | { mappings : '' } | undefined {
1802
+ if ( ! map1 || ! map2 ) {
1803
+ return map1
1804
+ }
1805
+ if ( map1 . mappings === '' || map2 . mappings === '' ) {
1806
+ return { mappings : '' }
1807
+ }
1808
+ return combineSourcemaps ( filename , [
1809
+ // type of version property of ExistingRawSourceMap is number
1810
+ // but it is always 3
1811
+ map1 as RawSourceMap ,
1812
+ map2 as RawSourceMap ,
1813
+ ] ) as ExistingRawSourceMap
1716
1814
}
1717
1815
1718
1816
const viteHashUpdateMarker = '/*$vite$:1*/'
@@ -3269,13 +3367,18 @@ function isPreProcessor(lang: any): lang is PreprocessLang {
3269
3367
3270
3368
const importLightningCSS = createCachedImport ( ( ) => import ( 'lightningcss' ) )
3271
3369
async function compileLightningCSS (
3370
+ environment : PartialEnvironment ,
3272
3371
id : string ,
3273
3372
src : string ,
3274
- environment : PartialEnvironment ,
3373
+ deps : Set < string > ,
3374
+ workerController : PreprocessorWorkerController ,
3275
3375
urlResolver ?: CssUrlResolver ,
3276
- ) : ReturnType < typeof compileCSS > {
3376
+ ) : Promise < {
3377
+ code : string
3378
+ map ?: string | undefined
3379
+ modules ?: Record < string , string >
3380
+ } > {
3277
3381
const { config } = environment
3278
- const deps = new Set < string > ( )
3279
3382
// replace null byte as lightningcss treats that as a string terminator
3280
3383
// https://github.com/parcel-bundler/lightningcss/issues/874
3281
3384
const filename = removeDirectQuery ( id ) . replace ( '\0' , NULL_BYTE_PLACEHOLDER )
@@ -3298,11 +3401,32 @@ async function compileLightningCSS(
3298
3401
// projectRoot is needed to get stable hash when using CSS modules
3299
3402
projectRoot : config . root ,
3300
3403
resolver : {
3301
- read ( filePath ) {
3404
+ async read ( filePath ) {
3302
3405
if ( filePath === filename ) {
3303
3406
return src
3304
3407
}
3305
- return fs . readFileSync ( filePath , 'utf-8' )
3408
+
3409
+ const code = fs . readFileSync ( filePath , 'utf-8' )
3410
+ const lang = CSS_LANGS_RE . exec ( filePath ) ?. [ 1 ] as
3411
+ | CssLang
3412
+ | undefined
3413
+ if ( isPreProcessor ( lang ) ) {
3414
+ const result = await compileCSSPreprocessors (
3415
+ environment ,
3416
+ id ,
3417
+ lang ,
3418
+ code ,
3419
+ workerController ,
3420
+ )
3421
+ result . deps ?. forEach ( ( dep ) => deps . add ( dep ) )
3422
+ // TODO: support source map
3423
+ return result . code
3424
+ } else if ( lang === 'sss' ) {
3425
+ const sssResult = await transformSugarSS ( environment , id , code )
3426
+ // TODO: support source map
3427
+ return sssResult . code
3428
+ }
3429
+ return code
3306
3430
} ,
3307
3431
async resolve ( id , from ) {
3308
3432
const publicFile = checkPublicFile (
@@ -3313,10 +3437,34 @@ async function compileLightningCSS(
3313
3437
return publicFile
3314
3438
}
3315
3439
3316
- const resolved = await getAtImportResolvers (
3440
+ // NOTE: with `transformer: 'postcss'`, CSS modules `composes` tried to resolve with
3441
+ // all resolvers, but in `transformer: 'lightningcss'`, only the one for the
3442
+ // current file type is used.
3443
+ const atImportResolvers = getAtImportResolvers (
3317
3444
environment . getTopLevelConfig ( ) ,
3318
- ) . css ( environment , id , from )
3445
+ )
3446
+ const lang = CSS_LANGS_RE . exec ( from ) ?. [ 1 ] as CssLang | undefined
3447
+ let resolver : ResolveIdFn
3448
+ switch ( lang ) {
3449
+ case 'css' :
3450
+ case 'sss' :
3451
+ case 'styl' :
3452
+ case 'stylus' :
3453
+ case undefined :
3454
+ resolver = atImportResolvers . css
3455
+ break
3456
+ case 'sass' :
3457
+ case 'scss' :
3458
+ resolver = atImportResolvers . sass
3459
+ break
3460
+ case 'less' :
3461
+ resolver = atImportResolvers . less
3462
+ break
3463
+ default :
3464
+ throw new Error ( `Unknown lang: ${ lang satisfies never } ` )
3465
+ }
3319
3466
3467
+ const resolved = await resolver ( environment , id , from )
3320
3468
if ( resolved ) {
3321
3469
deps . add ( resolved )
3322
3470
return resolved
@@ -3431,7 +3579,6 @@ async function compileLightningCSS(
3431
3579
return {
3432
3580
code : css ,
3433
3581
map : 'map' in res ? res . map ?. toString ( ) : undefined ,
3434
- deps,
3435
3582
modules,
3436
3583
}
3437
3584
}
0 commit comments