@@ -28,7 +28,7 @@ use turbopack_core::{
2828 diagnostics:: PlainDiagnostic ,
2929 error:: PrettyPrintError ,
3030 issue:: PlainIssue ,
31- source_map:: Token ,
31+ source_map:: { SourceMap , Token } ,
3232 version:: { PartialUpdate , TotalUpdate , Update , VersionState } ,
3333 SOURCE_MAP_PREFIX ,
3434} ;
@@ -1002,74 +1002,76 @@ pub struct StackFrame {
10021002 pub method_name : Option < String > ,
10031003}
10041004
1005+ pub async fn get_source_map (
1006+ container : Vc < ProjectContainer > ,
1007+ file_path : String ,
1008+ ) -> Result < Option < Vc < SourceMap > > > {
1009+ let ( file, module) = match Url :: parse ( & file_path) {
1010+ Ok ( url) => match url. scheme ( ) {
1011+ "file" => {
1012+ let path = urlencoding:: decode ( url. path ( ) ) ?. to_string ( ) ;
1013+ let module = url. query_pairs ( ) . find ( |( k, _) | k == "id" ) ;
1014+ (
1015+ path,
1016+ match module {
1017+ Some ( module) => Some ( urlencoding:: decode ( & module. 1 ) ?. into_owned ( ) . into ( ) ) ,
1018+ None => None ,
1019+ } ,
1020+ )
1021+ }
1022+ _ => bail ! ( "Unknown url scheme" ) ,
1023+ } ,
1024+ Err ( _) => ( file_path. to_string ( ) , None ) ,
1025+ } ;
1026+
1027+ let Some ( chunk_base) = file. strip_prefix (
1028+ & ( format ! (
1029+ "{}/{}/" ,
1030+ container. project( ) . await ?. project_path,
1031+ container. project( ) . dist_dir( ) . await ?
1032+ ) ) ,
1033+ ) else {
1034+ // File doesn't exist within the dist dir
1035+ return Ok ( None ) ;
1036+ } ;
1037+
1038+ let server_path = container. project ( ) . node_root ( ) . join ( chunk_base. into ( ) ) ;
1039+
1040+ let client_path = container
1041+ . project ( )
1042+ . client_relative_path ( )
1043+ . join ( chunk_base. into ( ) ) ;
1044+
1045+ let mut map = container
1046+ . get_source_map ( server_path, module. clone ( ) )
1047+ . await ?;
1048+
1049+ if map. is_none ( ) {
1050+ // If the chunk doesn't exist as a server chunk, try a client chunk.
1051+ // TODO: Properly tag all server chunks and use the `isServer` query param.
1052+ // Currently, this is inaccurate as it does not cover RSC server
1053+ // chunks.
1054+ map = container. get_source_map ( client_path, module) . await ?;
1055+ }
1056+
1057+ let map = map. context ( "chunk/module is missing a sourcemap" ) ?;
1058+
1059+ Ok ( Some ( map) )
1060+ }
1061+
10051062#[ napi]
10061063pub async fn project_trace_source (
10071064 #[ napi( ts_arg_type = "{ __napiType: \" Project\" }" ) ] project : External < ProjectInstance > ,
10081065 frame : StackFrame ,
10091066) -> napi:: Result < Option < StackFrame > > {
10101067 let turbo_tasks = project. turbo_tasks . clone ( ) ;
1068+ let container = project. container ;
10111069 let traced_frame = turbo_tasks
10121070 . run_once ( async move {
1013- let ( file, module) = match Url :: parse ( & frame. file ) {
1014- Ok ( url) => match url. scheme ( ) {
1015- "file" => {
1016- let path = urlencoding:: decode ( url. path ( ) ) ?. to_string ( ) ;
1017- let module = url. query_pairs ( ) . find ( |( k, _) | k == "id" ) ;
1018- (
1019- path,
1020- match module {
1021- Some ( module) => {
1022- Some ( urlencoding:: decode ( & module. 1 ) ?. into_owned ( ) . into ( ) )
1023- }
1024- None => None ,
1025- } ,
1026- )
1027- }
1028- _ => bail ! ( "Unknown url scheme" ) ,
1029- } ,
1030- Err ( _) => ( frame. file . to_string ( ) , None ) ,
1031- } ;
1032-
1033- let Some ( chunk_base) = file. strip_prefix (
1034- & ( format ! (
1035- "{}/{}/" ,
1036- project. container. project( ) . await ?. project_path,
1037- project. container. project( ) . dist_dir( ) . await ?
1038- ) ) ,
1039- ) else {
1040- // File doesn't exist within the dist dir
1071+ let Some ( map) = get_source_map ( container, frame. file ) . await ? else {
10411072 return Ok ( None ) ;
10421073 } ;
10431074
1044- let server_path = project
1045- . container
1046- . project ( )
1047- . node_root ( )
1048- . join ( chunk_base. into ( ) ) ;
1049-
1050- let client_path = project
1051- . container
1052- . project ( )
1053- . client_relative_path ( )
1054- . join ( chunk_base. into ( ) ) ;
1055-
1056- let mut map = project
1057- . container
1058- . get_source_map ( server_path, module. clone ( ) )
1059- . await ?;
1060-
1061- if map. is_none ( ) {
1062- // If the chunk doesn't exist as a server chunk, try a client chunk.
1063- // TODO: Properly tag all server chunks and use the `isServer` query param.
1064- // Currently, this is inaccurate as it does not cover RSC server
1065- // chunks.
1066- map = project
1067- . container
1068- . get_source_map ( client_path, module)
1069- . await ?;
1070- }
1071- let map = map. context ( "chunk/module is missing a sourcemap" ) ?;
1072-
10731075 let Some ( line) = frame. line else {
10741076 return Ok ( None ) ;
10751077 } ;
@@ -1152,6 +1154,28 @@ pub async fn project_get_source_for_asset(
11521154 Ok ( source)
11531155}
11541156
1157+ #[ napi]
1158+ pub async fn project_get_source_map (
1159+ #[ napi( ts_arg_type = "{ __napiType: \" Project\" }" ) ] project : External < ProjectInstance > ,
1160+ file_path : String ,
1161+ ) -> napi:: Result < Option < String > > {
1162+ let turbo_tasks = project. turbo_tasks . clone ( ) ;
1163+ let container = project. container ;
1164+
1165+ let source_map = turbo_tasks
1166+ . run_once ( async move {
1167+ let Some ( map) = get_source_map ( container, file_path) . await ? else {
1168+ return Ok ( None ) ;
1169+ } ;
1170+
1171+ Ok ( Some ( map. to_rope ( ) . await ?. to_str ( ) ?. to_string ( ) ) )
1172+ } )
1173+ . await
1174+ . map_err ( |e| napi:: Error :: from_reason ( PrettyPrintError ( & e) . to_string ( ) ) ) ?;
1175+
1176+ Ok ( source_map)
1177+ }
1178+
11551179/// Runs exit handlers for the project registered using the [`ExitHandler`] API.
11561180#[ napi]
11571181pub async fn project_on_exit (
0 commit comments