@@ -46,27 +46,67 @@ impl Mappings {
4646 pub fn new (
4747 module_graph : & ModuleGraph ,
4848 specifiers : & Specifiers ,
49+ main_entry_points : & [ ModuleSpecifier ] ,
50+ test_entry_points : & [ ModuleSpecifier ] ,
4951 ) -> Result < Self > {
5052 let mut mappings = HashMap :: new ( ) ;
5153 let mut mapped_filepaths_no_ext = HashSet :: new ( ) ;
52- let base_dir = get_base_dir ( & specifiers. local ) ?;
54+ let local_main_entry_points = local_entry_points ( main_entry_points) ;
55+ let local_test_entry_points = local_entry_points ( test_entry_points) ;
56+ let main_base_dir = if local_main_entry_points. is_empty ( ) {
57+ get_base_dir ( & specifiers. local ) ?
58+ } else {
59+ get_base_dir ( & local_main_entry_points) ?
60+ } ;
61+ let mut test_base_dir_candidates = vec ! [ main_base_dir. clone( ) ] ;
62+ for specifier in & local_test_entry_points {
63+ let file_path = url_to_file_path ( specifier) ?;
64+ test_base_dir_candidates. push ( file_path. parent ( ) . unwrap ( ) . to_path_buf ( ) ) ;
65+ }
66+ let test_base_dir = get_common_dir ( test_base_dir_candidates) ;
67+ ensure_nonempty_base_dir ( & test_base_dir) ?;
68+ let mut external_base_dir_candidates =
69+ vec ! [ main_base_dir. clone( ) , test_base_dir. clone( ) ] ;
70+ for specifier in & specifiers. local {
71+ let file_path = url_to_file_path ( specifier) ?;
72+ let base_dir = if specifiers. test_modules . contains ( specifier) {
73+ & test_base_dir
74+ } else {
75+ & main_base_dir
76+ } ;
77+ if !file_path. starts_with ( base_dir) {
78+ external_base_dir_candidates
79+ . push ( file_path. parent ( ) . unwrap ( ) . to_path_buf ( ) ) ;
80+ }
81+ }
82+ let external_base_dir = get_common_dir ( external_base_dir_candidates) ;
83+ ensure_nonempty_base_dir ( & external_base_dir) ?;
5384 let mut root_local_dirs = HashSet :: new ( ) ;
5485
5586 for specifier in specifiers. local . iter ( ) {
5687 let file_path = url_to_file_path ( specifier) ?;
57- let relative_file_path =
58- file_path. strip_prefix ( & base_dir) . map_err ( |_| {
59- anyhow:: anyhow!(
60- "Error stripping prefix of {} with base {}" ,
61- file_path. display( ) ,
62- base_dir. display( )
63- )
64- } ) ?;
88+ let base_dir = if specifiers. test_modules . contains ( specifier) {
89+ & test_base_dir
90+ } else {
91+ & main_base_dir
92+ } ;
93+ let relative_file_path = match file_path. strip_prefix ( base_dir) {
94+ Ok ( path) => path. to_path_buf ( ) ,
95+ Err ( _) => PathBuf :: from ( "deps" ) . join (
96+ file_path. strip_prefix ( & external_base_dir) . map_err ( |_| {
97+ anyhow:: anyhow!(
98+ "Error stripping prefix of {} with base {}" ,
99+ file_path. display( ) ,
100+ external_base_dir. display( )
101+ )
102+ } ) ?,
103+ ) ,
104+ } ;
65105 mappings. insert (
66106 specifier. clone ( ) ,
67107 get_mapped_file_path (
68- MediaType :: from_path ( relative_file_path) ,
69- relative_file_path,
108+ MediaType :: from_path ( & relative_file_path) ,
109+ & relative_file_path,
70110 & mut mapped_filepaths_no_ext,
71111 ) ,
72112 ) ;
@@ -187,6 +227,16 @@ impl Mappings {
187227 }
188228}
189229
230+ fn local_entry_points (
231+ entry_points : & [ ModuleSpecifier ] ,
232+ ) -> Vec < ModuleSpecifier > {
233+ entry_points
234+ . iter ( )
235+ . filter ( |specifier| specifier. scheme ( ) == "file" )
236+ . cloned ( )
237+ . collect ( )
238+ }
239+
190240/// Takes a group of remote specifiers for the provided base directory
191241/// and gets their output paths.
192242fn remote_specifiers_to_paths < ' a > (
@@ -499,20 +549,37 @@ fn get_base_dir(specifiers: &[ModuleSpecifier]) -> Result<PathBuf> {
499549 if specifiers. is_empty ( ) {
500550 bail ! ( "Did not find any local files. Specifying only remote files is not currently supported." ) ;
501551 }
552+ let base_dir = get_common_dir (
553+ specifiers
554+ . iter ( )
555+ . map ( |specifier| {
556+ Ok ( url_to_file_path ( specifier) ?. parent ( ) . unwrap ( ) . into ( ) )
557+ } )
558+ . collect :: < Result < Vec < PathBuf > > > ( ) ?,
559+ ) ;
560+ ensure_nonempty_base_dir ( & base_dir) ?;
561+ Ok ( base_dir)
562+ }
563+
564+ fn ensure_nonempty_base_dir ( base_dir : & Path ) -> Result < ( ) > {
565+ if base_dir. as_os_str ( ) . is_empty ( ) {
566+ bail ! ( "Local files span different filesystem roots." ) ;
567+ }
568+ Ok ( ( ) )
569+ }
570+
571+ fn get_common_dir ( dirs : impl IntoIterator < Item = PathBuf > ) -> PathBuf {
502572 // todo(dsherret): should maybe error on windows when the files
503573 // span different drives...
504- let mut base_dir = url_to_file_path ( & specifiers[ 0 ] ) ?
505- . parent ( )
506- . unwrap ( )
507- . to_path_buf ( ) ;
508- for specifier in specifiers {
509- let file_path = url_to_file_path ( specifier) ?;
510- let parent_dir = file_path. parent ( ) . unwrap ( ) ;
574+ let mut dirs = dirs. into_iter ( ) ;
575+ let Some ( mut base_dir) = dirs. next ( ) else {
576+ return PathBuf :: new ( ) ;
577+ } ;
578+ for parent_dir in dirs {
511579 if base_dir != parent_dir {
512- if base_dir. starts_with ( parent_dir) {
513- base_dir = parent_dir. to_path_buf ( ) ;
514- } else if base_dir. components ( ) . count ( ) == parent_dir. components ( ) . count ( )
515- {
580+ if base_dir. starts_with ( & parent_dir) {
581+ base_dir = parent_dir;
582+ } else {
516583 let mut final_path = PathBuf :: new ( ) ;
517584 for ( a, b) in base_dir. components ( ) . zip ( parent_dir. components ( ) ) {
518585 if a == b {
@@ -525,7 +592,7 @@ fn get_base_dir(specifiers: &[ModuleSpecifier]) -> Result<PathBuf> {
525592 }
526593 }
527594 }
528- Ok ( base_dir)
595+ base_dir
529596}
530597
531598#[ cfg( test) ]
@@ -553,6 +620,13 @@ mod test {
553620 vec ! [ "file:///project/b/other.ts" , "file:///project/a/other.ts" ] ,
554621 "/project" ,
555622 ) ;
623+ run_test (
624+ vec ! [
625+ "file:///project-one/mod.ts" ,
626+ "file:///project-two/deeper/mod.ts" ,
627+ ] ,
628+ "/" ,
629+ ) ;
556630
557631 fn run_test ( urls : Vec < & str > , expected : & str ) {
558632 let result = get_base_dir (
0 commit comments