@@ -72,6 +72,7 @@ use node_resolver::NodeResolverOptions;
7272use node_resolver:: PackageJsonThreadLocalCache ;
7373use node_resolver:: analyze:: NodeCodeTranslatorMode ;
7474use node_resolver:: cache:: NodeResolutionThreadLocalCache ;
75+ use node_resolver:: errors:: NodeJsErrorCode ;
7576use node_resolver:: errors:: NodeJsErrorCoded ;
7677use serde:: Deserialize ;
7778use serde:: Serialize ;
@@ -196,11 +197,11 @@ impl DenoWorkspace {
196197 console_error_panic_hook:: set_once ( ) ;
197198 let options = serde_wasm_bindgen:: from_value ( options) . map_err ( |err| {
198199 create_js_error (
199- anyhow:: anyhow!( "{}" , err)
200+ & anyhow:: anyhow!( "{}" , err)
200201 . context ( "Failed deserializing workspace options." ) ,
201202 )
202203 } ) ?;
203- Self :: new_inner ( options) . map_err ( create_js_error)
204+ Self :: new_inner ( options) . map_err ( |e| create_js_error ( & e ) )
204205 }
205206
206207 fn new_inner ( options : DenoWorkspaceOptions ) -> Result < Self , anyhow:: Error > {
@@ -336,7 +337,10 @@ impl DenoWorkspace {
336337 }
337338
338339 pub async fn create_loader ( & self ) -> Result < DenoLoader , JsValue > {
339- self . create_loader_inner ( ) . await . map_err ( create_js_error)
340+ self
341+ . create_loader_inner ( )
342+ . await
343+ . map_err ( |e| create_js_error ( & e) )
340344 }
341345
342346 async fn create_loader_inner ( & self ) -> Result < DenoLoader , anyhow:: Error > {
@@ -416,7 +420,7 @@ impl DenoLoader {
416420 self
417421 . add_entrypoints_internal ( entrypoints)
418422 . await
419- . map_err ( create_js_error)
423+ . map_err ( |e| create_js_error ( & e ) )
420424 }
421425
422426 async fn add_entrypoints_internal (
@@ -533,19 +537,24 @@ impl DenoLoader {
533537 importer : Option < String > ,
534538 resolution_mode : u8 ,
535539 ) -> Result < String , JsValue > {
540+ let importer = self
541+ . resolve_provided_referrer ( importer)
542+ . map_err ( |e| create_js_error ( & e) ) ?;
536543 self
537544 . resolve_sync_inner (
538545 & specifier,
539- importer,
546+ importer. as_ref ( ) ,
540547 parse_resolution_mode ( resolution_mode) ,
541548 )
542- . map_err ( create_js_error)
549+ . map_err ( |err| {
550+ self . create_resolve_js_error ( & err, & specifier, importer. as_ref ( ) )
551+ } )
543552 }
544553
545554 fn resolve_sync_inner (
546555 & self ,
547556 specifier : & str ,
548- importer : Option < String > ,
557+ importer : Option < & Url > ,
549558 resolution_mode : node_resolver:: ResolutionMode ,
550559 ) -> Result < String , anyhow:: Error > {
551560 let ( specifier, referrer) = self . resolve_specifier_and_referrer (
@@ -573,25 +582,30 @@ impl DenoLoader {
573582 importer : Option < String > ,
574583 resolution_mode : u8 ,
575584 ) -> Result < String , JsValue > {
585+ let importer = self
586+ . resolve_provided_referrer ( importer)
587+ . map_err ( |e| create_js_error ( & e) ) ?;
576588 self
577589 . resolve_inner (
578590 & specifier,
579- importer,
591+ importer. as_ref ( ) ,
580592 parse_resolution_mode ( resolution_mode) ,
581593 )
582594 . await
583- . map_err ( create_js_error)
595+ . map_err ( |err| {
596+ self . create_resolve_js_error ( & err, & specifier, importer. as_ref ( ) )
597+ } )
584598 }
585599
586600 async fn resolve_inner (
587601 & self ,
588602 specifier : & str ,
589- importer : Option < String > ,
603+ importer : Option < & Url > ,
590604 resolution_mode : node_resolver:: ResolutionMode ,
591605 ) -> Result < String , anyhow:: Error > {
592606 let ( specifier, referrer) = self . resolve_specifier_and_referrer (
593607 specifier,
594- importer. clone ( ) ,
608+ importer,
595609 resolution_mode,
596610 ) ?;
597611 let resolved = self . resolver . resolve_with_graph (
@@ -618,24 +632,11 @@ impl DenoLoader {
618632 fn resolve_specifier_and_referrer < ' a > (
619633 & self ,
620634 specifier : & ' a str ,
621- importer : Option < String > ,
635+ referrer : Option < & ' a Url > ,
622636 resolution_mode : node_resolver:: ResolutionMode ,
623- ) -> Result < ( Cow < ' a , str > , Url ) , anyhow:: Error > {
624- let importer = importer. filter ( |v| !v. is_empty ( ) ) ;
625- Ok ( match importer {
626- Some ( referrer)
627- if referrer. starts_with ( "http:" )
628- || referrer. starts_with ( "https:" )
629- || referrer. starts_with ( "file:" ) =>
630- {
631- ( Cow :: Borrowed ( specifier) , Url :: parse ( & referrer) ?)
632- }
633- Some ( referrer) => (
634- Cow :: Borrowed ( specifier) ,
635- deno_path_util:: url_from_file_path (
636- & sys_traits:: impls:: wasm_string_to_path ( referrer) ,
637- ) ?,
638- ) ,
637+ ) -> Result < ( Cow < ' a , str > , Cow < ' a , Url > ) , anyhow:: Error > {
638+ Ok ( match referrer {
639+ Some ( referrer) => ( Cow :: Borrowed ( specifier) , Cow :: Borrowed ( referrer) ) ,
639640 None => {
640641 let entrypoint = Cow :: Owned (
641642 self
@@ -644,14 +645,34 @@ impl DenoLoader {
644645 ) ;
645646 (
646647 entrypoint,
647- deno_path_util:: url_from_directory_path (
648+ Cow :: Owned ( deno_path_util:: url_from_directory_path (
648649 self . workspace_factory . initial_cwd ( ) ,
649- ) ?,
650+ ) ?) ,
650651 )
651652 }
652653 } )
653654 }
654655
656+ fn resolve_provided_referrer (
657+ & self ,
658+ importer : Option < String > ,
659+ ) -> Result < Option < Url > , anyhow:: Error > {
660+ let importer = importer. filter ( |v| !v. is_empty ( ) ) ;
661+ Ok ( match importer {
662+ Some ( referrer)
663+ if referrer. starts_with ( "http:" )
664+ || referrer. starts_with ( "https:" )
665+ || referrer. starts_with ( "file:" ) =>
666+ {
667+ Some ( Url :: parse ( & referrer) ?)
668+ }
669+ Some ( referrer) => Some ( deno_path_util:: url_from_file_path (
670+ & sys_traits:: impls:: wasm_string_to_path ( referrer) ,
671+ ) ?) ,
672+ None => None ,
673+ } )
674+ }
675+
655676 pub async fn load (
656677 & self ,
657678 url : String ,
@@ -663,7 +684,7 @@ impl DenoLoader {
663684 2 => RequestedModuleType :: Text ,
664685 3 => RequestedModuleType :: Bytes ,
665686 _ => {
666- return Err ( create_js_error ( anyhow:: anyhow!(
687+ return Err ( create_js_error ( & anyhow:: anyhow!(
667688 "Invalid requested module type: {}" ,
668689 requested_module_type
669690 ) ) ) ;
@@ -672,7 +693,7 @@ impl DenoLoader {
672693 self
673694 . load_inner ( url, & requested_module_type)
674695 . await
675- . map_err ( create_js_error)
696+ . map_err ( |err| create_js_error ( & err ) )
676697 }
677698
678699 async fn load_inner (
@@ -811,19 +832,92 @@ impl DenoLoader {
811832 node_resolver:: NodeResolutionKind :: Execution ,
812833 ) ?)
813834 }
835+
836+ fn is_optional_npm_dep ( & self , specifier : & str , referrer : & Url ) -> bool {
837+ let Ok ( referrer_path) = deno_path_util:: url_to_file_path ( referrer) else {
838+ return false ;
839+ } ;
840+ for result in self
841+ . resolver_factory
842+ . pkg_json_resolver ( )
843+ . get_closest_package_jsons ( & referrer_path)
844+ {
845+ let Ok ( pkg_json) = result else {
846+ continue ;
847+ } ;
848+ if let Some ( optional_deps) = & pkg_json. optional_dependencies
849+ && optional_deps. contains_key ( specifier)
850+ {
851+ return true ;
852+ }
853+ if let Some ( meta) = & pkg_json. peer_dependencies_meta
854+ && let Some ( obj) = meta. get ( specifier)
855+ && let Some ( value) = obj. get ( "optional" )
856+ && let Some ( is_optional) = value. as_bool ( )
857+ && is_optional
858+ {
859+ return true ;
860+ }
861+ if let Some ( deps) = & pkg_json. dependencies
862+ && deps. contains_key ( specifier)
863+ {
864+ return false ;
865+ }
866+ if let Some ( deps) = & pkg_json. peer_dependencies
867+ && deps. contains_key ( specifier)
868+ {
869+ return false ;
870+ }
871+ }
872+ false
873+ }
874+
875+ fn create_resolve_js_error (
876+ & self ,
877+ err : & anyhow:: Error ,
878+ specifier : & str ,
879+ maybe_referrer : Option < & Url > ,
880+ ) -> JsValue {
881+ let err_value = create_js_error ( err) ;
882+ if let Some ( err) = err. downcast_ref :: < ResolveWithGraphError > ( ) {
883+ if let Some ( code) = resolve_with_graph_error_code ( err) {
884+ _ = js_sys:: Reflect :: set (
885+ & err_value,
886+ & JsValue :: from_str ( "code" ) ,
887+ & JsValue :: from_str ( code. as_str ( ) ) ,
888+ ) ;
889+ if code == NodeJsErrorCode :: ERR_MODULE_NOT_FOUND
890+ && let Some ( referrer) = maybe_referrer
891+ && self . is_optional_npm_dep ( specifier, referrer)
892+ {
893+ _ = js_sys:: Reflect :: set (
894+ & err_value,
895+ & JsValue :: from_str ( "isOptionalDependency" ) ,
896+ & JsValue :: from_bool ( true ) ,
897+ ) ;
898+ }
899+ }
900+ if let Some ( specifier) = err. maybe_specifier ( )
901+ && let Ok ( url) = specifier. into_owned ( ) . into_url ( )
902+ {
903+ _ = js_sys:: Reflect :: set (
904+ & err_value,
905+ & JsValue :: from_str ( "specifier" ) ,
906+ & JsValue :: from_str ( url. as_str ( ) ) ,
907+ ) ;
908+ }
909+ }
910+ err_value
911+ }
814912}
815913
816914fn resolve_with_graph_error_code (
817915 err : & ResolveWithGraphError ,
818- ) -> Option < & ' static str > {
916+ ) -> Option < NodeJsErrorCode > {
819917 match err. as_kind ( ) {
820- ResolveWithGraphErrorKind :: CouldNotResolveNpmNv ( err) => {
821- Some ( err. code ( ) . as_str ( ) )
822- }
918+ ResolveWithGraphErrorKind :: CouldNotResolveNpmNv ( err) => Some ( err. code ( ) ) ,
823919 ResolveWithGraphErrorKind :: ResolvePkgFolderFromDenoModule ( _) => None ,
824- ResolveWithGraphErrorKind :: ResolveNpmReqRef ( err) => {
825- err. err . maybe_code ( ) . map ( |c| c. as_str ( ) )
826- }
920+ ResolveWithGraphErrorKind :: ResolveNpmReqRef ( err) => err. err . maybe_code ( ) ,
827921 ResolveWithGraphErrorKind :: Resolution ( err) => err
828922 . source ( )
829923 . and_then ( |s| s. downcast_ref :: < DenoResolveError > ( ) )
@@ -833,7 +927,7 @@ fn resolve_with_graph_error_code(
833927 }
834928}
835929
836- fn deno_resolve_error_code ( err : & DenoResolveError ) -> Option < & ' static str > {
930+ fn deno_resolve_error_code ( err : & DenoResolveError ) -> Option < NodeJsErrorCode > {
837931 match err. as_kind ( ) {
838932 DenoResolveErrorKind :: InvalidVendorFolderImport
839933 | DenoResolveErrorKind :: UnsupportedPackageJsonFileSpecifier
@@ -843,10 +937,8 @@ fn deno_resolve_error_code(err: &DenoResolveError) -> Option<&'static str> {
843937 | MappedResolutionError :: ImportMap ( _)
844938 | MappedResolutionError :: Workspace ( _) => None ,
845939 } ,
846- DenoResolveErrorKind :: Node ( err) => err. maybe_code ( ) . map ( |c| c. as_str ( ) ) ,
847- DenoResolveErrorKind :: ResolveNpmReqRef ( err) => {
848- err. err . maybe_code ( ) . map ( |c| c. as_str ( ) )
849- }
940+ DenoResolveErrorKind :: Node ( err) => err. maybe_code ( ) ,
941+ DenoResolveErrorKind :: ResolveNpmReqRef ( err) => err. err . maybe_code ( ) ,
850942 DenoResolveErrorKind :: NodeModulesOutOfDate ( _)
851943 | DenoResolveErrorKind :: PackageJsonDepValueParse ( _)
852944 | DenoResolveErrorKind :: PackageJsonDepValueUrlParse ( _)
@@ -909,28 +1001,8 @@ fn resolve_absolute_path(
9091001 }
9101002}
9111003
912- fn create_js_error ( err : anyhow:: Error ) -> JsValue {
913- let err_value: JsValue =
914- wasm_bindgen:: JsError :: new ( & format ! ( "{:#}" , err) ) . into ( ) ;
915- if let Some ( err) = err. downcast_ref :: < ResolveWithGraphError > ( ) {
916- if let Some ( code) = resolve_with_graph_error_code ( err) {
917- _ = js_sys:: Reflect :: set (
918- & err_value,
919- & JsValue :: from_str ( "code" ) ,
920- & JsValue :: from_str ( code) ,
921- ) ;
922- }
923- if let Some ( specifier) = err. maybe_specifier ( )
924- && let Ok ( url) = specifier. into_owned ( ) . into_url ( )
925- {
926- _ = js_sys:: Reflect :: set (
927- & err_value,
928- & JsValue :: from_str ( "specifier" ) ,
929- & JsValue :: from_str ( url. as_str ( ) ) ,
930- ) ;
931- }
932- }
933- err_value
1004+ fn create_js_error ( err : & anyhow:: Error ) -> JsValue {
1005+ wasm_bindgen:: JsError :: new ( & format ! ( "{:#}" , err) ) . into ( )
9341006}
9351007
9361008fn parse_resolution_mode ( resolution_mode : u8 ) -> node_resolver:: ResolutionMode {
0 commit comments