@@ -1299,10 +1299,33 @@ pub enum ModuleEntryRef<'a> {
1299
1299
Redirect ( & ' a ModuleSpecifier ) ,
1300
1300
}
1301
1301
1302
+ pub trait CheckJsResolver : std:: fmt:: Debug {
1303
+ fn resolve ( & self , specifier : & ModuleSpecifier ) -> bool ;
1304
+ }
1305
+
1306
+ #[ derive( Debug , Clone , Copy ) ]
1307
+ pub enum CheckJsOption < ' a > {
1308
+ True ,
1309
+ False ,
1310
+ Custom ( & ' a dyn CheckJsResolver ) ,
1311
+ }
1312
+
1313
+ impl < ' a > CheckJsOption < ' a > {
1314
+ pub fn resolve ( & self , specifier : & ModuleSpecifier ) -> bool {
1315
+ match self {
1316
+ CheckJsOption :: True => true ,
1317
+ CheckJsOption :: False => false ,
1318
+ CheckJsOption :: Custom ( check_js_resolver) => {
1319
+ check_js_resolver. resolve ( specifier)
1320
+ }
1321
+ }
1322
+ }
1323
+ }
1324
+
1302
1325
#[ derive( Debug , Clone ) ]
1303
- pub struct WalkOptions {
1326
+ pub struct WalkOptions < ' a > {
1304
1327
/// Whether to walk js modules when `kind` is `GraphKind::TypesOnly`.
1305
- pub check_js : bool ,
1328
+ pub check_js : CheckJsOption < ' a > ,
1306
1329
pub follow_dynamic : bool ,
1307
1330
/// Part of the graph to walk.
1308
1331
pub kind : GraphKind ,
@@ -1324,22 +1347,22 @@ pub struct FillFromLockfileOptions<
1324
1347
pub package_specifiers : TPackageSpecifiersIter ,
1325
1348
}
1326
1349
1327
- pub struct ModuleEntryIterator < ' a > {
1350
+ pub struct ModuleEntryIterator < ' a , ' options > {
1328
1351
graph : & ' a ModuleGraph ,
1329
1352
seen : HashSet < & ' a ModuleSpecifier > ,
1330
1353
visiting : VecDeque < & ' a ModuleSpecifier > ,
1331
1354
follow_dynamic : bool ,
1332
1355
kind : GraphKind ,
1333
- check_js : bool ,
1356
+ check_js : CheckJsOption < ' options > ,
1334
1357
prefer_fast_check_graph : bool ,
1335
1358
previous_module : Option < ModuleEntryRef < ' a > > ,
1336
1359
}
1337
1360
1338
- impl < ' a > ModuleEntryIterator < ' a > {
1361
+ impl < ' a , ' options > ModuleEntryIterator < ' a , ' options > {
1339
1362
fn new (
1340
1363
graph : & ' a ModuleGraph ,
1341
1364
roots : impl Iterator < Item = & ' a ModuleSpecifier > ,
1342
- options : WalkOptions ,
1365
+ options : WalkOptions < ' options > ,
1343
1366
) -> Self {
1344
1367
let mut seen =
1345
1368
HashSet :: < & ' a ModuleSpecifier > :: with_capacity ( graph. specifiers_count ( ) ) ;
@@ -1385,7 +1408,7 @@ impl<'a> ModuleEntryIterator<'a> {
1385
1408
/// An iterator over all the errors found when walking this iterator.
1386
1409
///
1387
1410
/// This can be useful in scenarios where you want to filter or ignore an error.
1388
- pub fn errors ( self ) -> ModuleGraphErrorIterator < ' a > {
1411
+ pub fn errors ( self ) -> ModuleGraphErrorIterator < ' a , ' options > {
1389
1412
ModuleGraphErrorIterator :: new ( self )
1390
1413
}
1391
1414
@@ -1405,15 +1428,27 @@ impl<'a> ModuleEntryIterator<'a> {
1405
1428
}
1406
1429
1407
1430
/// Gets if the specified media type can be type checked.
1408
- fn is_checkable ( & self , media_type : MediaType ) -> bool {
1409
- self . check_js
1410
- || !matches ! (
1411
- media_type,
1412
- MediaType :: JavaScript
1413
- | MediaType :: Mjs
1414
- | MediaType :: Cjs
1415
- | MediaType :: Jsx
1416
- )
1431
+ fn is_checkable (
1432
+ & self ,
1433
+ specifier : & ModuleSpecifier ,
1434
+ media_type : MediaType ,
1435
+ ) -> bool {
1436
+ match media_type {
1437
+ MediaType :: TypeScript
1438
+ | MediaType :: Mts
1439
+ | MediaType :: Cts
1440
+ | MediaType :: Dts
1441
+ | MediaType :: Dmts
1442
+ | MediaType :: Dcts
1443
+ | MediaType :: Tsx
1444
+ | MediaType :: Json
1445
+ | MediaType :: Wasm => true ,
1446
+ MediaType :: Css | MediaType :: SourceMap | MediaType :: Unknown => false ,
1447
+ MediaType :: JavaScript
1448
+ | MediaType :: Jsx
1449
+ | MediaType :: Mjs
1450
+ | MediaType :: Cjs => self . check_js . resolve ( specifier) ,
1451
+ }
1417
1452
}
1418
1453
1419
1454
fn analyze_module_deps (
@@ -1441,15 +1476,15 @@ impl<'a> ModuleEntryIterator<'a> {
1441
1476
}
1442
1477
}
1443
1478
1444
- impl < ' a > Iterator for ModuleEntryIterator < ' a > {
1479
+ impl < ' a , ' options > Iterator for ModuleEntryIterator < ' a , ' options > {
1445
1480
type Item = ( & ' a ModuleSpecifier , ModuleEntryRef < ' a > ) ;
1446
1481
1447
1482
fn next ( & mut self ) -> Option < Self :: Item > {
1448
1483
match self . previous_module . take ( ) {
1449
1484
Some ( ModuleEntryRef :: Module ( module) ) => match module {
1450
1485
Module :: Js ( module) => {
1451
- let check_types =
1452
- self . kind . include_types ( ) && self . is_checkable ( module. media_type ) ;
1486
+ let check_types = self . kind . include_types ( )
1487
+ && self . is_checkable ( & module . specifier , module. media_type ) ;
1453
1488
let module_deps = if check_types && self . prefer_fast_check_graph {
1454
1489
module. dependencies_prefer_fast_check ( )
1455
1490
} else {
@@ -1497,7 +1532,7 @@ impl<'a> Iterator for ModuleEntryIterator<'a> {
1497
1532
continue ; // skip visiting the code module
1498
1533
}
1499
1534
} else if self . kind == GraphKind :: TypesOnly
1500
- && !self . is_checkable ( module. media_type )
1535
+ && !self . is_checkable ( & module . specifier , module. media_type )
1501
1536
{
1502
1537
continue ; // skip visiting
1503
1538
}
@@ -1526,13 +1561,13 @@ impl<'a> Iterator for ModuleEntryIterator<'a> {
1526
1561
}
1527
1562
}
1528
1563
1529
- pub struct ModuleGraphErrorIterator < ' a > {
1530
- iterator : ModuleEntryIterator < ' a > ,
1564
+ pub struct ModuleGraphErrorIterator < ' a , ' options > {
1565
+ iterator : ModuleEntryIterator < ' a , ' options > ,
1531
1566
next_errors : Vec < ModuleGraphError > ,
1532
1567
}
1533
1568
1534
- impl < ' a > ModuleGraphErrorIterator < ' a > {
1535
- pub fn new ( iterator : ModuleEntryIterator < ' a > ) -> Self {
1569
+ impl < ' a , ' options > ModuleGraphErrorIterator < ' a , ' options > {
1570
+ pub fn new ( iterator : ModuleEntryIterator < ' a , ' options > ) -> Self {
1536
1571
Self {
1537
1572
iterator,
1538
1573
next_errors : Default :: default ( ) ,
@@ -1607,7 +1642,7 @@ impl<'a> ModuleGraphErrorIterator<'a> {
1607
1642
}
1608
1643
}
1609
1644
1610
- impl < ' a > Iterator for ModuleGraphErrorIterator < ' a > {
1645
+ impl < ' a , ' options > Iterator for ModuleGraphErrorIterator < ' a , ' options > {
1611
1646
type Item = ModuleGraphError ;
1612
1647
1613
1648
fn next ( & mut self ) -> Option < Self :: Item > {
@@ -1634,7 +1669,9 @@ impl<'a> Iterator for ModuleGraphErrorIterator<'a> {
1634
1669
}
1635
1670
1636
1671
let check_types = kind. include_types ( )
1637
- && self . iterator . is_checkable ( module. media_type ) ;
1672
+ && self
1673
+ . iterator
1674
+ . is_checkable ( & module. specifier , module. media_type ) ;
1638
1675
let module_deps = if check_types && prefer_fast_check_graph {
1639
1676
module. dependencies_prefer_fast_check ( )
1640
1677
} else {
@@ -1886,7 +1923,7 @@ impl ModuleGraph {
1886
1923
WalkOptions {
1887
1924
follow_dynamic : true ,
1888
1925
kind : self . graph_kind ,
1889
- check_js : true ,
1926
+ check_js : CheckJsOption :: True ,
1890
1927
prefer_fast_check_graph : false ,
1891
1928
} ,
1892
1929
) ;
@@ -1921,11 +1958,11 @@ impl ModuleGraph {
1921
1958
}
1922
1959
1923
1960
/// Iterates over all the module entries in the module graph searching from the provided roots.
1924
- pub fn walk < ' a > (
1961
+ pub fn walk < ' a , ' options > (
1925
1962
& ' a self ,
1926
1963
roots : impl Iterator < Item = & ' a ModuleSpecifier > ,
1927
- options : WalkOptions ,
1928
- ) -> ModuleEntryIterator < ' a > {
1964
+ options : WalkOptions < ' options > ,
1965
+ ) -> ModuleEntryIterator < ' a , ' options > {
1929
1966
ModuleEntryIterator :: new ( self , roots, options)
1930
1967
}
1931
1968
@@ -2159,7 +2196,7 @@ impl ModuleGraph {
2159
2196
. walk (
2160
2197
self . roots . iter ( ) ,
2161
2198
WalkOptions {
2162
- check_js : true ,
2199
+ check_js : CheckJsOption :: True ,
2163
2200
kind : GraphKind :: CodeOnly ,
2164
2201
follow_dynamic : false ,
2165
2202
prefer_fast_check_graph : false ,
@@ -5915,7 +5952,7 @@ mod tests {
5915
5952
WalkOptions {
5916
5953
follow_dynamic : false ,
5917
5954
kind : GraphKind :: All ,
5918
- check_js : true ,
5955
+ check_js : CheckJsOption :: True ,
5919
5956
prefer_fast_check_graph : false ,
5920
5957
} ,
5921
5958
)
@@ -5930,7 +5967,7 @@ mod tests {
5930
5967
WalkOptions {
5931
5968
follow_dynamic : true ,
5932
5969
kind : GraphKind :: All ,
5933
- check_js : true ,
5970
+ check_js : CheckJsOption :: True ,
5934
5971
prefer_fast_check_graph : false ,
5935
5972
} ,
5936
5973
)
@@ -6066,7 +6103,7 @@ mod tests {
6066
6103
. walk (
6067
6104
roots. iter ( ) ,
6068
6105
WalkOptions {
6069
- check_js : true ,
6106
+ check_js : CheckJsOption :: True ,
6070
6107
follow_dynamic : false ,
6071
6108
kind : GraphKind :: All ,
6072
6109
prefer_fast_check_graph : false ,
@@ -6789,4 +6826,87 @@ mod tests {
6789
6826
) ;
6790
6827
}
6791
6828
}
6829
+
6830
+ #[ tokio:: test]
6831
+ async fn check_js_option_custom ( ) {
6832
+ #[ derive( Debug ) ]
6833
+ struct CustomResolver ;
6834
+
6835
+ impl CheckJsResolver for CustomResolver {
6836
+ fn resolve ( & self , specifier : & ModuleSpecifier ) -> bool {
6837
+ specifier. as_str ( ) == "file:///true.js"
6838
+ }
6839
+ }
6840
+
6841
+ struct TestLoader ;
6842
+ impl Loader for TestLoader {
6843
+ fn load (
6844
+ & self ,
6845
+ specifier : & ModuleSpecifier ,
6846
+ _options : LoadOptions ,
6847
+ ) -> LoadFuture {
6848
+ let specifier = specifier. clone ( ) ;
6849
+ match specifier. as_str ( ) {
6850
+ "file:///valid.js" => Box :: pin ( async move {
6851
+ Ok ( Some ( LoadResponse :: Module {
6852
+ specifier : specifier. clone ( ) ,
6853
+ maybe_headers : None ,
6854
+ content : b"export {}" . to_vec ( ) . into ( ) ,
6855
+ } ) )
6856
+ } ) ,
6857
+ "file:///true.js" => Box :: pin ( async move {
6858
+ Ok ( Some ( LoadResponse :: Module {
6859
+ specifier : specifier. clone ( ) ,
6860
+ maybe_headers : None ,
6861
+ content : b"// @ts-types='invalid'\n import {} from './valid.js';"
6862
+ . to_vec ( )
6863
+ . into ( ) ,
6864
+ } ) )
6865
+ } ) ,
6866
+ "file:///false.js" => Box :: pin ( async move {
6867
+ Ok ( Some ( LoadResponse :: Module {
6868
+ specifier : specifier. clone ( ) ,
6869
+ maybe_headers : None ,
6870
+ // the 'invalid' shouldn't be visited here
6871
+ content : b"// @ts-types='invalid'\n import {} from './valid.js';"
6872
+ . to_vec ( )
6873
+ . into ( ) ,
6874
+ } ) )
6875
+ } ) ,
6876
+ "file:///main.ts" => Box :: pin ( async move {
6877
+ Ok ( Some ( LoadResponse :: Module {
6878
+ specifier : specifier. clone ( ) ,
6879
+ maybe_headers : None ,
6880
+ content : b"import './true.js'; import './false.js'"
6881
+ . to_vec ( )
6882
+ . into ( ) ,
6883
+ } ) )
6884
+ } ) ,
6885
+ _ => unreachable ! ( ) ,
6886
+ }
6887
+ }
6888
+ }
6889
+ let loader = TestLoader ;
6890
+ let mut graph = ModuleGraph :: new ( GraphKind :: All ) ;
6891
+ let roots = vec ! [ Url :: parse( "file:///main.ts" ) . unwrap( ) ] ;
6892
+ graph
6893
+ . build ( roots. clone ( ) , & loader, Default :: default ( ) )
6894
+ . await ;
6895
+ assert_eq ! ( graph. specifiers_count( ) , 4 ) ;
6896
+ let errors = graph
6897
+ . walk (
6898
+ roots. iter ( ) ,
6899
+ WalkOptions {
6900
+ check_js : CheckJsOption :: Custom ( & CustomResolver ) ,
6901
+ follow_dynamic : false ,
6902
+ kind : GraphKind :: All ,
6903
+ prefer_fast_check_graph : false ,
6904
+ } ,
6905
+ )
6906
+ . errors ( )
6907
+ . collect :: < Vec < _ > > ( ) ;
6908
+
6909
+ // should only be 1 for true.js and not false.js
6910
+ assert_eq ! ( errors. len( ) , 1 ) ;
6911
+ }
6792
6912
}
0 commit comments