@@ -2,7 +2,7 @@ use std::{collections::BTreeSet, str::FromStr};
2
2
3
3
use anyhow:: { Context , Result , bail} ;
4
4
use rustc_hash:: FxHashSet ;
5
- use serde:: { Deserialize , Deserializer , Serialize } ;
5
+ use serde:: { Deserialize , Deserializer , Serialize , Serializer } ;
6
6
use serde_json:: Value as JsonValue ;
7
7
use turbo_esregex:: EsRegex ;
8
8
use turbo_rcstr:: { RcStr , rcstr} ;
@@ -566,53 +566,102 @@ pub struct TurbopackConfig {
566
566
pub module_ids : Option < ModuleIds > ,
567
567
}
568
568
569
- #[ derive( Serialize , Deserialize , Clone , PartialEq , Eq , Debug ) ]
569
+ #[ derive(
570
+ Serialize , Deserialize , Clone , PartialEq , Eq , Debug , TraceRawVcs , NonLocalValue , OperationValue ,
571
+ ) ]
570
572
pub struct RegexComponents {
571
573
source : RcStr ,
572
574
flags : RcStr ,
573
575
}
574
576
575
- #[ derive( Clone , PartialEq , Eq , Debug , Serialize , Deserialize ) ]
577
+ #[ derive(
578
+ Clone , PartialEq , Eq , Debug , Serialize , Deserialize , TraceRawVcs , NonLocalValue , OperationValue ,
579
+ ) ]
576
580
#[ serde( tag = "type" , content = "value" , rename_all = "camelCase" ) ]
577
581
pub enum ConfigConditionPath {
578
582
Glob ( RcStr ) ,
579
583
Regex ( RegexComponents ) ,
580
584
}
581
585
582
- impl TryInto < ConditionPath > for ConfigConditionPath {
583
- fn try_into ( self ) -> Result < ConditionPath > {
584
- Ok ( match self {
586
+ impl TryFrom < ConfigConditionPath > for ConditionPath {
587
+ type Error = anyhow:: Error ;
588
+
589
+ fn try_from ( config : ConfigConditionPath ) -> Result < ConditionPath > {
590
+ Ok ( match config {
585
591
ConfigConditionPath :: Glob ( path) => ConditionPath :: Glob ( path) ,
586
- ConfigConditionPath :: Regex ( path) => ConditionPath :: Regex ( path. try_into ( ) ?) ,
592
+ ConfigConditionPath :: Regex ( path) => {
593
+ ConditionPath :: Regex ( EsRegex :: try_from ( path) ?. resolved_cell ( ) )
594
+ }
587
595
} )
588
596
}
597
+ }
589
598
599
+ impl TryFrom < RegexComponents > for EsRegex {
590
600
type Error = anyhow:: Error ;
591
- }
592
601
593
- impl TryInto < ResolvedVc < EsRegex > > for RegexComponents {
594
- fn try_into ( self ) -> Result < ResolvedVc < EsRegex > > {
595
- Ok ( EsRegex :: new ( & self . source , & self . flags ) ?. resolved_cell ( ) )
602
+ fn try_from ( components : RegexComponents ) -> Result < EsRegex > {
603
+ EsRegex :: new ( & components. source , & components. flags )
596
604
}
605
+ }
597
606
598
- type Error = anyhow:: Error ;
607
+ #[ derive(
608
+ Serialize , Deserialize , Clone , PartialEq , Eq , Debug , TraceRawVcs , NonLocalValue , OperationValue ,
609
+ ) ]
610
+ pub enum ConfigConditionItem {
611
+ #[ serde( rename = "all" , alias = "and" ) ]
612
+ All ( Box < [ ConfigConditionItem ] > ) ,
613
+ #[ serde( rename = "any" , alias = "or" ) ]
614
+ Any ( Box < [ ConfigConditionItem ] > ) ,
615
+ #[ serde( rename = "not" ) ]
616
+ Not ( Box < ConfigConditionItem > ) ,
617
+ #[ serde(
618
+ untagged,
619
+ serialize_with = "serialize_rcstr" ,
620
+ deserialize_with = "deserialize_rcstr"
621
+ ) ]
622
+ Builtin ( RcStr ) ,
623
+ #[ serde( untagged) ]
624
+ Base {
625
+ path : Option < ConfigConditionPath > ,
626
+ content : Option < RegexComponents > ,
627
+ } ,
599
628
}
600
629
601
- #[ derive( Serialize , Deserialize , Clone , Debug , PartialEq ) ]
602
- pub struct ConfigConditionItem {
603
- pub path : Option < ConfigConditionPath > ,
604
- pub content : Option < RegexComponents > ,
630
+ fn serialize_rcstr < S > ( field : & RcStr , serializer : S ) -> Result < S :: Ok , S :: Error >
631
+ where
632
+ S : Serializer ,
633
+ {
634
+ field. serialize ( serializer)
605
635
}
606
636
607
- impl TryInto < ConditionItem > for ConfigConditionItem {
608
- fn try_into ( self ) -> Result < ConditionItem > {
609
- Ok ( ConditionItem {
610
- path : self . path . map ( |p| p. try_into ( ) ) . transpose ( ) ?,
611
- content : self . content . map ( |r| r. try_into ( ) ) . transpose ( ) ?,
612
- } )
613
- }
637
+ fn deserialize_rcstr < ' de , D > ( deserializer : D ) -> Result < RcStr , D :: Error >
638
+ where
639
+ D : Deserializer < ' de > ,
640
+ {
641
+ RcStr :: deserialize ( deserializer)
642
+ }
614
643
644
+ impl TryFrom < ConfigConditionItem > for ConditionItem {
615
645
type Error = anyhow:: Error ;
646
+
647
+ fn try_from ( config : ConfigConditionItem ) -> Result < Self > {
648
+ Ok ( match config {
649
+ ConfigConditionItem :: All ( conds) => ConditionItem :: All (
650
+ conds
651
+ . into_iter ( )
652
+ . map ( ConditionItem :: try_from)
653
+ . collect :: < Result < _ > > ( ) ?,
654
+ ) ,
655
+ ConfigConditionItem :: Base { path, content } => ConditionItem :: Base {
656
+ path : path. map ( ConditionPath :: try_from) . transpose ( ) ?,
657
+ content : content
658
+ . map ( EsRegex :: try_from)
659
+ . transpose ( ) ?
660
+ . map ( EsRegex :: resolved_cell) ,
661
+ } ,
662
+ _ => todo ! ( ) ,
663
+ } )
664
+ }
616
665
}
617
666
618
667
#[ derive(
@@ -623,6 +672,7 @@ pub struct RuleConfigItemOptions {
623
672
pub loaders : Vec < LoaderItem > ,
624
673
#[ serde( default , alias = "as" ) ]
625
674
pub rename_as : Option < RcStr > ,
675
+ pub condition : Option < ConfigConditionItem > ,
626
676
}
627
677
628
678
#[ derive(
@@ -640,8 +690,8 @@ pub enum RuleConfigItemOrShortcut {
640
690
#[ serde( rename_all = "camelCase" , untagged) ]
641
691
pub enum RuleConfigItem {
642
692
Options ( RuleConfigItemOptions ) ,
643
- Conditional ( FxIndexMap < RcStr , RuleConfigItem > ) ,
644
- Boolean ( bool ) ,
693
+ LegacyConditional ( FxIndexMap < RcStr , RuleConfigItem > ) ,
694
+ LegacyBoolean ( bool ) ,
645
695
}
646
696
647
697
#[ derive(
@@ -1320,13 +1370,16 @@ impl NextConfig {
1320
1370
NotFound ,
1321
1371
Break ,
1322
1372
}
1373
+ // This logic is needed for the `LegacyConditional`/`LegacyBoolean` configuration
1374
+ // syntax. This is technically public syntax, but was never documented and it is
1375
+ // unlikely that anyone is depending on it (outside of some Next.js internals).
1323
1376
fn find_rule < ' a > (
1324
1377
rule : & ' a RuleConfigItem ,
1325
1378
active_conditions : & BTreeSet < WebpackLoaderBuiltinCondition > ,
1326
1379
) -> FindRuleResult < ' a > {
1327
1380
match rule {
1328
1381
RuleConfigItem :: Options ( rule) => FindRuleResult :: Found ( rule) ,
1329
- RuleConfigItem :: Conditional ( map) => {
1382
+ RuleConfigItem :: LegacyConditional ( map) => {
1330
1383
for ( condition, rule) in map. iter ( ) {
1331
1384
let condition = WebpackLoaderBuiltinCondition :: from_str ( condition) ;
1332
1385
if let Ok ( condition) = condition
@@ -1346,7 +1399,7 @@ impl NextConfig {
1346
1399
}
1347
1400
FindRuleResult :: NotFound
1348
1401
}
1349
- RuleConfigItem :: Boolean ( _) => FindRuleResult :: Break ,
1402
+ RuleConfigItem :: LegacyBoolean ( _) => FindRuleResult :: Break ,
1350
1403
}
1351
1404
}
1352
1405
match rule {
@@ -1356,12 +1409,16 @@ impl NextConfig {
1356
1409
LoaderRuleItem {
1357
1410
loaders : transform_loaders ( loaders) ,
1358
1411
rename_as : None ,
1412
+ condition : None ,
1359
1413
} ,
1360
1414
) ;
1361
1415
}
1362
1416
RuleConfigItemOrShortcut :: Advanced ( rule) => {
1363
- if let FindRuleResult :: Found ( RuleConfigItemOptions { loaders, rename_as } ) =
1364
- find_rule ( rule, & active_conditions)
1417
+ if let FindRuleResult :: Found ( RuleConfigItemOptions {
1418
+ loaders,
1419
+ rename_as,
1420
+ condition,
1421
+ } ) = find_rule ( rule, & active_conditions)
1365
1422
{
1366
1423
// If the extension contains a wildcard, and the rename_as does not,
1367
1424
// emit an issue to prevent users from encountering duplicate module names.
@@ -1383,6 +1440,12 @@ impl NextConfig {
1383
1440
LoaderRuleItem {
1384
1441
loaders : transform_loaders ( loaders) ,
1385
1442
rename_as : rename_as. clone ( ) ,
1443
+ // TODO(bgw): Emit `InvalidLoaderRuleError` if this fails instead of
1444
+ // using try (?)
1445
+ condition : condition
1446
+ . clone ( )
1447
+ . map ( ConditionItem :: try_from)
1448
+ . transpose ( ) ?,
1386
1449
} ,
1387
1450
) ;
1388
1451
}
0 commit comments