1
1
use std:: { collections:: BTreeSet , str:: FromStr , sync:: LazyLock } ;
2
2
3
3
use anyhow:: { Context , Result , bail} ;
4
+ use either:: Either ;
4
5
use rustc_hash:: FxHashSet ;
5
6
use serde:: { Deserialize , Deserializer , Serialize } ;
6
7
use serde_json:: Value as JsonValue ;
@@ -558,7 +559,7 @@ pub enum RemotePatternProtocol {
558
559
pub struct TurbopackConfig {
559
560
/// This option has been replaced by `rules`.
560
561
pub loaders : Option < JsonValue > ,
561
- pub rules : Option < FxIndexMap < RcStr , RuleConfigItemOrShortcut > > ,
562
+ pub rules : Option < FxIndexMap < RcStr , RuleConfigCollection > > ,
562
563
#[ turbo_tasks( trace_ignore) ]
563
564
pub conditions : Option < FxIndexMap < RcStr , ConfigConditionItem > > ,
564
565
pub resolve_alias : Option < FxIndexMap < RcStr , JsonValue > > ,
@@ -666,6 +667,16 @@ impl TryFrom<ConfigConditionItem> for ConditionItem {
666
667
}
667
668
}
668
669
670
+ #[ derive(
671
+ Clone , Debug , PartialEq , Eq , Serialize , Deserialize , TraceRawVcs , NonLocalValue , OperationValue ,
672
+ ) ]
673
+ #[ serde( rename_all = "camelCase" , untagged) ]
674
+ pub enum RuleConfigItem {
675
+ Options ( RuleConfigItemOptions ) ,
676
+ LegacyConditional ( FxIndexMap < RcStr , RuleConfigItem > ) ,
677
+ LegacyBoolean ( bool ) ,
678
+ }
679
+
669
680
#[ derive(
670
681
Clone , Debug , PartialEq , Eq , Serialize , Deserialize , TraceRawVcs , NonLocalValue , OperationValue ,
671
682
) ]
@@ -678,23 +689,33 @@ pub struct RuleConfigItemOptions {
678
689
pub condition : Option < ConfigConditionItem > ,
679
690
}
680
691
681
- #[ derive(
682
- Clone , Debug , PartialEq , Eq , Serialize , Deserialize , TraceRawVcs , NonLocalValue , OperationValue ,
683
- ) ]
684
- #[ serde( rename_all = "camelCase" , untagged) ]
685
- pub enum RuleConfigItemOrShortcut {
686
- Loaders ( Vec < LoaderItem > ) ,
687
- Advanced ( RuleConfigItem ) ,
692
+ #[ derive( Clone , Debug , PartialEq , Eq , Serialize , TraceRawVcs , NonLocalValue , OperationValue ) ]
693
+ #[ serde( transparent) ]
694
+ pub struct RuleConfigCollection ( Vec < RuleConfigCollectionItem > ) ;
695
+
696
+ impl < ' de > Deserialize < ' de > for RuleConfigCollection {
697
+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
698
+ where
699
+ D : Deserializer < ' de > ,
700
+ {
701
+ match either:: serde_untagged:: deserialize :: < Vec < RuleConfigCollectionItem > , RuleConfigItem , D > (
702
+ deserializer,
703
+ ) ? {
704
+ Either :: Left ( collection) => Ok ( RuleConfigCollection ( collection) ) ,
705
+ Either :: Right ( item) => Ok ( RuleConfigCollection ( vec ! [ RuleConfigCollectionItem :: Full (
706
+ item,
707
+ ) ] ) ) ,
708
+ }
709
+ }
688
710
}
689
711
690
712
#[ derive(
691
713
Clone , Debug , PartialEq , Eq , Serialize , Deserialize , TraceRawVcs , NonLocalValue , OperationValue ,
692
714
) ]
693
- #[ serde( rename_all = "camelCase" , untagged) ]
694
- pub enum RuleConfigItem {
695
- Options ( RuleConfigItemOptions ) ,
696
- LegacyConditional ( FxIndexMap < RcStr , RuleConfigItem > ) ,
697
- LegacyBoolean ( bool ) ,
715
+ #[ serde( untagged) ]
716
+ pub enum RuleConfigCollectionItem {
717
+ Shorthand ( LoaderItem ) ,
718
+ Full ( RuleConfigItem ) ,
698
719
}
699
720
700
721
#[ derive(
@@ -1395,11 +1416,12 @@ impl NextConfig {
1395
1416
return Ok ( Vc :: cell ( Vec :: new ( ) ) ) ;
1396
1417
}
1397
1418
let mut rules = Vec :: new ( ) ;
1398
- for ( glob, rule) in turbo_rules. iter ( ) {
1399
- fn transform_loaders ( loaders : & [ LoaderItem ] ) -> ResolvedVc < WebpackLoaderItems > {
1419
+ for ( glob, rule_collection) in turbo_rules. iter ( ) {
1420
+ fn transform_loaders (
1421
+ loaders : Box < dyn Iterator < Item = & LoaderItem > + ' _ > ,
1422
+ ) -> ResolvedVc < WebpackLoaderItems > {
1400
1423
ResolvedVc :: cell (
1401
1424
loaders
1402
- . iter ( )
1403
1425
. map ( |item| match item {
1404
1426
LoaderItem :: LoaderName ( name) => WebpackLoaderItem {
1405
1427
loader : name. clone ( ) ,
@@ -1448,65 +1470,67 @@ impl NextConfig {
1448
1470
}
1449
1471
}
1450
1472
let config_file_path = || project_path. join ( & self . config_file_name ) ;
1451
- match rule {
1452
- RuleConfigItemOrShortcut :: Loaders ( loaders) => {
1453
- rules. push ( (
1454
- glob. clone ( ) ,
1455
- LoaderRuleItem {
1456
- loaders : transform_loaders ( loaders) ,
1457
- rename_as : None ,
1458
- condition : None ,
1459
- } ,
1460
- ) ) ;
1461
- }
1462
- RuleConfigItemOrShortcut :: Advanced ( rule) => {
1463
- if let FindRuleResult :: Found ( RuleConfigItemOptions {
1464
- loaders,
1465
- rename_as,
1466
- condition,
1467
- } ) = find_rule ( rule, & active_conditions)
1468
- {
1469
- // If the extension contains a wildcard, and the rename_as does not,
1470
- // emit an issue to prevent users from encountering duplicate module names.
1471
- if glob. contains ( "*" )
1472
- && let Some ( rename_as) = rename_as. as_ref ( )
1473
- && !rename_as. contains ( "*" )
1473
+ for item in & rule_collection. 0 {
1474
+ match item {
1475
+ RuleConfigCollectionItem :: Shorthand ( loaders) => {
1476
+ rules. push ( (
1477
+ glob. clone ( ) ,
1478
+ LoaderRuleItem {
1479
+ loaders : transform_loaders ( Box :: new ( [ loaders] . into_iter ( ) ) ) ,
1480
+ rename_as : None ,
1481
+ condition : None ,
1482
+ } ,
1483
+ ) ) ;
1484
+ }
1485
+ RuleConfigCollectionItem :: Full ( rule_config_item) => {
1486
+ if let FindRuleResult :: Found ( RuleConfigItemOptions {
1487
+ loaders,
1488
+ rename_as,
1489
+ condition,
1490
+ } ) = find_rule ( rule_config_item, & active_conditions)
1474
1491
{
1475
- InvalidLoaderRuleRenameAsIssue {
1476
- glob : glob. clone ( ) ,
1477
- config_file_path : config_file_path ( ) ?,
1478
- rename_as : rename_as. clone ( ) ,
1479
- }
1480
- . resolved_cell ( )
1481
- . emit ( ) ;
1482
- }
1483
-
1484
- // convert from Next.js-specific condition type to internal Turbopack
1485
- // condition type
1486
- let condition = if let Some ( condition) = condition {
1487
- if let Ok ( cond) = ConditionItem :: try_from ( condition. clone ( ) ) {
1488
- Some ( cond)
1489
- } else {
1490
- InvalidLoaderRuleConditionIssue {
1491
- condition : condition. clone ( ) ,
1492
+ // If the extension contains a wildcard, and the rename_as does not,
1493
+ // emit an issue to prevent users from encountering duplicate module
1494
+ // names.
1495
+ if glob. contains ( "*" )
1496
+ && let Some ( rename_as) = rename_as. as_ref ( )
1497
+ && !rename_as. contains ( "*" )
1498
+ {
1499
+ InvalidLoaderRuleRenameAsIssue {
1500
+ glob : glob. clone ( ) ,
1492
1501
config_file_path : config_file_path ( ) ?,
1502
+ rename_as : rename_as. clone ( ) ,
1493
1503
}
1494
1504
. resolved_cell ( )
1495
1505
. emit ( ) ;
1496
- None
1497
1506
}
1498
- } else {
1499
- None
1500
- } ;
1501
1507
1502
- rules. push ( (
1503
- glob. clone ( ) ,
1504
- LoaderRuleItem {
1505
- loaders : transform_loaders ( loaders) ,
1506
- rename_as : rename_as. clone ( ) ,
1507
- condition,
1508
- } ,
1509
- ) ) ;
1508
+ // convert from Next.js-specific condition type to internal Turbopack
1509
+ // condition type
1510
+ let condition = if let Some ( condition) = condition {
1511
+ if let Ok ( cond) = ConditionItem :: try_from ( condition. clone ( ) ) {
1512
+ Some ( cond)
1513
+ } else {
1514
+ InvalidLoaderRuleConditionIssue {
1515
+ condition : condition. clone ( ) ,
1516
+ config_file_path : config_file_path ( ) ?,
1517
+ }
1518
+ . resolved_cell ( )
1519
+ . emit ( ) ;
1520
+ None
1521
+ }
1522
+ } else {
1523
+ None
1524
+ } ;
1525
+ rules. push ( (
1526
+ glob. clone ( ) ,
1527
+ LoaderRuleItem {
1528
+ loaders : transform_loaders ( Box :: new ( loaders. iter ( ) ) ) ,
1529
+ rename_as : rename_as. clone ( ) ,
1530
+ condition,
1531
+ } ,
1532
+ ) ) ;
1533
+ }
1510
1534
}
1511
1535
}
1512
1536
}
0 commit comments