Skip to content

Commit a40e793

Browse files
committed
feat: async module loading
1 parent 4451d16 commit a40e793

File tree

4 files changed

+125
-1
lines changed

4 files changed

+125
-1
lines changed

rs_lib/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ default = ["file_fetcher"]
1414
file_fetcher = ["async-trait", "base64", "cache_control", "chrono", "data-url", "http"]
1515
wasm = ["console_error_panic_hook", "js-sys", "serde-wasm-bindgen", "wasm-bindgen", "sys_traits/wasm"]
1616
sync = []
17+
async-module-loading = ["tokio"]
1718

1819
[dependencies]
1920
async-trait = { version = "0.1.73", optional = true }
@@ -44,6 +45,11 @@ js-sys = { version = "=0.3.68", optional = true }
4445
wasm-bindgen = { version = "=0.2.91", optional = true }
4546
serde-wasm-bindgen = { version = "0.6.5", optional = true }
4647

48+
[dependencies.tokio]
49+
version = "1"
50+
features = ["rt", "sync", "macros", "rt-multi-thread", "blocking"]
51+
optional = true
52+
4753
[dev-dependencies]
4854
pretty_assertions = "1.4.0"
4955
sys_traits = { workspace = true, features = ["memory", "real", "getrandom", "libc", "winapi"] }
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright 2018-2025 the Deno authors. MIT license.
2+
3+
use std::borrow::Cow;
4+
5+
use url::Url;
6+
7+
use super::FetchCachedError;
8+
use super::FetchCachedErrorKind;
9+
use super::FetchCachedNoFollowError;
10+
use super::FetchCachedNoFollowErrorKind;
11+
use super::FileOrRedirect;
12+
use super::File;
13+
use super::TooManyRedirectsError;
14+
use crate::Checksum;
15+
16+
pub trait AsyncFileFetcherExt {
17+
/// Fetch cached remote file asynchronously.
18+
///
19+
/// This is a recursive operation if source file has redirections.
20+
async fn fetch_cached_async(
21+
&self,
22+
url: &Url,
23+
redirect_limit: i64,
24+
) -> Result<Option<File>, FetchCachedError>;
25+
26+
/// Fetches from cache without following redirects asynchronously.
27+
async fn fetch_cached_no_follow_async(
28+
&self,
29+
url: &Url,
30+
maybe_checksum: Option<Checksum<'_>>,
31+
) -> Result<Option<FileOrRedirect>, FetchCachedNoFollowError>;
32+
}
33+
34+
#[cfg(feature = "async-module-loading")]
35+
impl<TBlobStore, TSys, THttpClient> AsyncFileFetcherExt for super::FileFetcher<TBlobStore, TSys, THttpClient>
36+
where
37+
TBlobStore: super::BlobStore + Clone,
38+
TSys: crate::sync::MaybeSend + crate::sync::MaybeSync + sys_traits::FsRead + sys_traits::SystemTimeNow + Clone,
39+
THttpClient: super::HttpClient + Clone,
40+
{
41+
async fn fetch_cached_async(
42+
&self,
43+
url: &Url,
44+
redirect_limit: i64,
45+
) -> Result<Option<File>, FetchCachedError> {
46+
if !matches!(url.scheme(), "http" | "https") {
47+
return Ok(None);
48+
}
49+
50+
let mut url = Cow::Borrowed(url);
51+
for _ in 0..=redirect_limit {
52+
match self.fetch_cached_no_follow_async(&url, None).await? {
53+
Some(FileOrRedirect::File(file)) => {
54+
return Ok(Some(file));
55+
}
56+
Some(FileOrRedirect::Redirect(redirect_url)) => {
57+
url = Cow::Owned(redirect_url);
58+
}
59+
None => {
60+
return Ok(None);
61+
}
62+
}
63+
}
64+
Err(
65+
FetchCachedErrorKind::TooManyRedirects(TooManyRedirectsError(
66+
url.into_owned(),
67+
))
68+
.into_box(),
69+
)
70+
}
71+
72+
async fn fetch_cached_no_follow_async(
73+
&self,
74+
url: &Url,
75+
maybe_checksum: Option<Checksum<'_>>,
76+
) -> Result<Option<FileOrRedirect>, FetchCachedNoFollowError> {
77+
let url = url.clone();
78+
let maybe_checksum_owned = maybe_checksum.map(|checksum| Checksum::new(checksum.as_str()));
79+
let this = self.clone();
80+
81+
// Run in a blocking task to avoid holding the JS thread
82+
tokio::task::spawn_blocking(move || {
83+
this.fetch_cached_no_follow(&url, maybe_checksum_owned)
84+
})
85+
.await
86+
.unwrap()
87+
}
88+
}
89+
90+
#[cfg(not(feature = "async-module-loading"))]
91+
impl<TBlobStore, TSys, THttpClient> AsyncFileFetcherExt for super::FileFetcher<TBlobStore, TSys, THttpClient>
92+
where
93+
TBlobStore: super::BlobStore + Clone,
94+
TSys: crate::sync::MaybeSend + crate::sync::MaybeSync + sys_traits::FsRead + sys_traits::SystemTimeNow + Clone,
95+
THttpClient: super::HttpClient + Clone,
96+
{
97+
async fn fetch_cached_async(
98+
&self,
99+
url: &Url,
100+
redirect_limit: i64,
101+
) -> Result<Option<File>, FetchCachedError> {
102+
self.fetch_cached(url, redirect_limit)
103+
}
104+
105+
async fn fetch_cached_no_follow_async(
106+
&self,
107+
url: &Url,
108+
maybe_checksum: Option<Checksum<'_>>,
109+
) -> Result<Option<FileOrRedirect>, FetchCachedNoFollowError> {
110+
self.fetch_cached_no_follow(url, maybe_checksum)
111+
}
112+
}

rs_lib/src/file_fetcher/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ use crate::ChecksumIntegrityError;
3434
mod auth_tokens;
3535
mod http_util;
3636

37+
#[cfg(feature = "async-module-loading")]
38+
pub mod r#async;
39+
3740
pub use auth_tokens::AuthDomain;
3841
pub use auth_tokens::AuthToken;
3942
pub use auth_tokens::AuthTokenData;
@@ -442,7 +445,7 @@ pub struct FileFetcherOptions {
442445
}
443446

444447
/// A structure for resolving, fetching and caching source files.
445-
#[derive(Debug)]
448+
#[derive(Debug, Clone)]
446449
pub struct FileFetcher<
447450
TBlobStore: BlobStore,
448451
TSys: FsRead + SystemTimeNow,

rs_lib/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ pub use local::LocalHttpCache;
3333
pub use local::LocalHttpCacheRc;
3434
pub use local::LocalLspHttpCache;
3535

36+
#[cfg(all(feature = "file_fetcher", feature = "async-module-loading"))]
37+
pub use file_fetcher::r#async::AsyncFileFetcherExt;
38+
3639
#[cfg(feature = "wasm")]
3740
pub mod wasm {
3841
use std::collections::HashMap;

0 commit comments

Comments
 (0)