@@ -21,6 +21,7 @@ use http::header::IF_NONE_MATCH;
2121use http:: header:: LOCATION ;
2222use log:: debug;
2323use sys_traits:: FsFileMetadata ;
24+ use sys_traits:: FsMetadata ;
2425use sys_traits:: FsMetadataValue ;
2526use sys_traits:: FsOpen ;
2627use sys_traits:: OpenOptions ;
@@ -251,6 +252,24 @@ pub struct FailedReadingLocalFileError {
251252 pub source : std:: io:: Error ,
252253}
253254
255+ #[ derive( Debug , Error , JsError ) ]
256+ #[ class( type ) ]
257+ #[ error(
258+ "[{}] Directory import '{}' is not supported resolving ES modules" ,
259+ self . code( ) ,
260+ url
261+ ) ]
262+ #[ property( "code" = self . code( ) ) ]
263+ pub struct UnsupportedDirImportError {
264+ pub url : Url ,
265+ }
266+
267+ impl UnsupportedDirImportError {
268+ pub fn code ( & self ) -> & ' static str {
269+ "ERR_UNSUPPORTED_DIR_IMPORT"
270+ }
271+ }
272+
254273#[ derive( Debug , Error , JsError ) ]
255274#[ class( "Http" ) ]
256275#[ error( "Fetch '{0}' failed, too many redirects." ) ]
@@ -297,6 +316,9 @@ pub enum FetchNoFollowErrorKind {
297316 #[ class( inherit) ]
298317 #[ error( transparent) ]
299318 ReadingFile ( #[ from] FailedReadingLocalFileError ) ,
319+ #[ class( inherit) ]
320+ #[ error( transparent) ]
321+ UnsupportedDirImport ( #[ from] UnsupportedDirImportError ) ,
300322 #[ class( generic) ]
301323 #[ error( "Import '{url}' failed." ) ]
302324 FetchingRemote {
@@ -386,13 +408,17 @@ pub enum FetchLocalErrorKind {
386408 #[ class( inherit) ]
387409 #[ error( transparent) ]
388410 ReadingFile ( #[ from] FailedReadingLocalFileError ) ,
411+ #[ class( inherit) ]
412+ #[ error( transparent) ]
413+ UnsupportedDirImport ( #[ from] UnsupportedDirImportError ) ,
389414}
390415
391416impl From < FetchLocalError > for FetchNoFollowError {
392417 fn from ( err : FetchLocalError ) -> Self {
393418 match err. into_kind ( ) {
394419 FetchLocalErrorKind :: UrlToFilePath ( err) => err. into ( ) ,
395420 FetchLocalErrorKind :: ReadingFile ( err) => err. into ( ) ,
421+ FetchLocalErrorKind :: UnsupportedDirImport ( err) => err. into ( ) ,
396422 }
397423 }
398424}
@@ -491,7 +517,7 @@ pub struct FileFetcherOptions {
491517}
492518
493519#[ sys_traits:: auto_impl]
494- pub trait FileFetcherSys : FsOpen + SystemTimeNow { }
520+ pub trait FileFetcherSys : FsOpen + FsMetadata + SystemTimeNow { }
495521
496522/// A structure for resolving, fetching and caching source files.
497523#[ derive( Debug ) ]
@@ -978,11 +1004,36 @@ impl<TBlobStore: BlobStore, TSys: FileFetcherSys, THttpClient: HttpClient>
9781004 options : & FetchLocalOptions ,
9791005 ) -> Result < Option < File > , FetchLocalError > {
9801006 let local = url_to_file_path ( url) ?;
1007+ let metadata = self . sys . fs_metadata ( & local) . ok ( ) ;
1008+ if metadata
1009+ . as_ref ( )
1010+ . is_some_and ( |metadata| metadata. file_type ( ) . is_dir ( ) )
1011+ {
1012+ return Err (
1013+ FetchLocalErrorKind :: UnsupportedDirImport ( UnsupportedDirImportError {
1014+ url : url. clone ( ) ,
1015+ } )
1016+ . into_box ( ) ,
1017+ ) ;
1018+ }
9811019 let Some ( file) = self . handle_open_file ( url, & local) ? else {
9821020 return Ok ( None ) ;
9831021 } ;
984- match self . fetch_local_inner ( file, url, & local, options) {
1022+ let mtime = if options. include_mtime {
1023+ metadata
1024+ . and_then ( |metadata| metadata. modified ( ) . ok ( ) )
1025+ . or_else ( || file. fs_file_metadata ( ) . and_then ( |m| m. modified ( ) ) . ok ( ) )
1026+ } else {
1027+ None
1028+ } ;
1029+ match self . fetch_local_inner ( file, url, & local, mtime) {
9851030 Ok ( file) => Ok ( Some ( file) ) ,
1031+ Err ( err) if err. kind ( ) == std:: io:: ErrorKind :: IsADirectory => Err (
1032+ FetchLocalErrorKind :: UnsupportedDirImport ( UnsupportedDirImportError {
1033+ url : url. clone ( ) ,
1034+ } )
1035+ . into_box ( ) ,
1036+ ) ,
9861037 Err ( err) => Err (
9871038 FetchLocalErrorKind :: ReadingFile ( FailedReadingLocalFileError {
9881039 url : url. clone ( ) ,
@@ -1001,6 +1052,12 @@ impl<TBlobStore: BlobStore, TSys: FileFetcherSys, THttpClient: HttpClient>
10011052 match self . sys . fs_open ( path, & OpenOptions :: new_read ( ) ) {
10021053 Ok ( file) => Ok ( Some ( file) ) ,
10031054 Err ( err) if err. kind ( ) == std:: io:: ErrorKind :: NotFound => Ok ( None ) ,
1055+ Err ( err) if err. kind ( ) == std:: io:: ErrorKind :: IsADirectory => Err (
1056+ FetchLocalErrorKind :: UnsupportedDirImport ( UnsupportedDirImportError {
1057+ url : url. clone ( ) ,
1058+ } )
1059+ . into_box ( ) ,
1060+ ) ,
10041061 Err ( err) => Err (
10051062 FetchLocalErrorKind :: ReadingFile ( FailedReadingLocalFileError {
10061063 url : url. clone ( ) ,
@@ -1016,13 +1073,8 @@ impl<TBlobStore: BlobStore, TSys: FileFetcherSys, THttpClient: HttpClient>
10161073 mut file : TSys :: File ,
10171074 url : & Url ,
10181075 path : & Path ,
1019- options : & FetchLocalOptions ,
1076+ mtime : Option < SystemTime > ,
10201077 ) -> std:: io:: Result < File > {
1021- let mtime = if options. include_mtime {
1022- file. fs_file_metadata ( ) . and_then ( |m| m. modified ( ) ) . ok ( )
1023- } else {
1024- None
1025- } ;
10261078 let mut bytes = Vec :: new ( ) ;
10271079 file. read_to_end ( & mut bytes) ?;
10281080 // If it doesnt have a extension, we want to treat it as typescript by default
0 commit comments