1+ use std:: borrow:: Cow ;
2+ use std:: time:: Duration ;
3+
14use deno_ast:: { MediaType , ParseParams } ;
25use deno_core:: {
3- ModuleCodeString , ModuleLoadResponse , ModuleName , ModuleSpecifier , SourceMapData ,
4- error:: ModuleLoaderError ,
6+ ModuleCodeString , ModuleLoadResponse , ModuleName , ModuleSource , ModuleSourceCode ,
7+ ModuleSpecifier , ModuleType , SourceMapData , error:: ModuleLoaderError , futures :: FutureExt ,
58} ;
69use deno_error:: JsErrorBox ;
710
8- pub struct ModuleLoader ;
11+ pub struct ModuleLoader {
12+ http_client : reqwest:: Client ,
13+ }
914
1015impl ModuleLoader {
1116 pub fn new ( ) -> Self {
12- Self { }
17+ let http_client = reqwest:: Client :: builder ( )
18+ . connect_timeout ( Duration :: from_secs ( 10 ) )
19+ . user_agent ( "func.gg/module_loader" )
20+ . build ( )
21+ . expect ( "Unable to build HTTP client" ) ;
22+
23+ Self { http_client }
1324 }
1425}
1526
@@ -26,15 +37,87 @@ impl deno_core::ModuleLoader for ModuleLoader {
2637
2738 fn load (
2839 & self ,
29- module_specifier : & ModuleSpecifier ,
40+ specifier : & ModuleSpecifier ,
3041 _maybe_referrer : Option < & ModuleSpecifier > ,
31- _is_dynamic : bool ,
42+ is_dyn_import : bool ,
3243 _requested_module_type : deno_core:: RequestedModuleType ,
33- ) -> deno_core:: ModuleLoadResponse {
34- tracing:: error!( "attempting to load module: {}" , module_specifier) ;
35- ModuleLoadResponse :: Sync ( Err ( ModuleLoaderError :: generic (
36- "module loading is not supported" ,
37- ) ) )
44+ ) -> ModuleLoadResponse {
45+ if is_dyn_import {
46+ return ModuleLoadResponse :: Sync ( Err ( ModuleLoaderError :: generic (
47+ "dynamic module loading is not supported" ,
48+ ) ) ) ;
49+ }
50+
51+ if specifier. scheme ( ) != "https" {
52+ return ModuleLoadResponse :: Sync ( Err ( ModuleLoaderError :: generic (
53+ "only modules with an 'https' scheme are supported" ,
54+ ) ) ) ;
55+ }
56+
57+ let specifier = specifier. clone ( ) ;
58+ let http_client = self . http_client . clone ( ) ;
59+ return ModuleLoadResponse :: Async (
60+ async move {
61+ let res = http_client
62+ . get ( specifier. clone ( ) )
63+ . query ( & [ ( "target" , "deno" ) ] ) // tells services to explicitly redirect
64+ . send ( )
65+ . await
66+ . map_err ( |err| ModuleLoaderError :: generic ( err. to_string ( ) ) ) ?;
67+
68+ let original_specifier = {
69+ let mut url = specifier. clone ( ) ;
70+ url. set_query ( None ) ;
71+ url
72+ } ;
73+
74+ let found_specifier = {
75+ let mut url = res. url ( ) . clone ( ) ;
76+ url. set_query ( None ) ;
77+ url
78+ } ;
79+
80+ let content_type = res
81+ . headers ( )
82+ . get ( "content-type" )
83+ . map ( |ct| ct. to_str ( ) . unwrap_or_default ( ) )
84+ . unwrap_or_default ( )
85+ . split ( ';' )
86+ . next ( )
87+ . unwrap_or_default ( )
88+ . trim ( )
89+ . to_lowercase ( ) ;
90+ let module_type = match content_type. as_ref ( ) {
91+ "application/javascript"
92+ | "text/javascript"
93+ | "application/ecmascript"
94+ | "text/ecmascript" => ModuleType :: JavaScript ,
95+ "application/wasm" => ModuleType :: Wasm ,
96+ "application/json" | "text/json" => ModuleType :: Json ,
97+ "text/plain" | "application/octet-stream" => ModuleType :: Text ,
98+ s => ModuleType :: Other ( Cow :: Owned ( s. to_string ( ) ) ) ,
99+ } ;
100+
101+ if !res. status ( ) . is_success ( ) {
102+ return Err ( ModuleLoaderError :: generic ( "failed to load module" ) ) ;
103+ }
104+
105+ // TODO: probably use bytes
106+ let src_text = res
107+ . text ( )
108+ . await
109+ . map_err ( |err| ModuleLoaderError :: generic ( err. to_string ( ) ) ) ?;
110+
111+ return Ok ( ModuleSource :: new_with_redirect (
112+ module_type,
113+ ModuleSourceCode :: String ( src_text. into ( ) ) ,
114+ & original_specifier,
115+ & found_specifier,
116+ None ,
117+ ) ) ;
118+ }
119+ . boxed_local ( ) ,
120+ ) ;
38121 }
39122}
40123
0 commit comments