diff options
author | Kitson Kelly <me@kitsonkelly.com> | 2020-10-23 11:50:15 +1100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-23 11:50:15 +1100 |
commit | 7e2c7fb6c5454e30158d74e1a5786183ea391f07 (patch) | |
tree | 42402aa26a0422b9c46d1d441598dbe803b8ed15 /cli/specifier_handler.rs | |
parent | 9fa59f0ca8164f5e02ba2a2fa90b6fdbce5c1afb (diff) |
refactor(cli): migrate run and cache to new infrastructure (#7996)
Co-authored-by: Ryan Dahl <ry@tinyclouds.org>
Diffstat (limited to 'cli/specifier_handler.rs')
-rw-r--r-- | cli/specifier_handler.rs | 160 |
1 files changed, 133 insertions, 27 deletions
diff --git a/cli/specifier_handler.rs b/cli/specifier_handler.rs index 5d9c19a5e..016ad0468 100644 --- a/cli/specifier_handler.rs +++ b/cli/specifier_handler.rs @@ -1,5 +1,6 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +use crate::ast::Location; use crate::deno_dir::DenoDir; use crate::disk_cache::DiskCache; use crate::file_fetcher::SourceFileFetcher; @@ -25,8 +26,29 @@ pub type DependencyMap = HashMap<String, Dependency>; pub type FetchFuture = Pin<Box<(dyn Future<Output = Result<CachedModule, AnyError>> + 'static)>>; +/// A group of errors that represent errors that can occur with an +/// an implementation of `SpecifierHandler`. +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum HandlerError { + /// A fetch error, where we have a location associated with it. + FetchErrorWithLocation(String, Location), +} + +impl fmt::Display for HandlerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + HandlerError::FetchErrorWithLocation(ref err, ref location) => { + write!(f, "{}\n at {}", err, location) + } + } + } +} + +impl std::error::Error for HandlerError {} + #[derive(Debug, Clone)] pub struct CachedModule { + pub is_remote: bool, pub maybe_dependencies: Option<DependencyMap>, pub maybe_emit: Option<Emit>, pub maybe_emit_path: Option<(PathBuf, Option<PathBuf>)>, @@ -44,6 +66,7 @@ impl Default for CachedModule { fn default() -> Self { let specifier = ModuleSpecifier::resolve_url("file:///example.js").unwrap(); CachedModule { + is_remote: false, maybe_dependencies: None, maybe_emit: None, maybe_emit_path: None, @@ -76,8 +99,12 @@ impl Default for Emit { } } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct Dependency { + /// Flags if the dependency is a dynamic import or not. + pub is_dynamic: bool, + /// The location in the source code where the dependency statement occurred. + pub location: Location, /// The module specifier that resolves to the runtime code dependency for the /// module. pub maybe_code: Option<ModuleSpecifier>, @@ -86,17 +113,33 @@ pub struct Dependency { pub maybe_type: Option<ModuleSpecifier>, } +impl Dependency { + pub fn new(location: Location) -> Self { + Dependency { + is_dynamic: false, + location, + maybe_code: None, + maybe_type: None, + } + } +} + pub trait SpecifierHandler { /// Instructs the handler to fetch a specifier or retrieve its value from the /// cache. - fn fetch(&mut self, specifier: ModuleSpecifier) -> FetchFuture; + fn fetch( + &mut self, + specifier: ModuleSpecifier, + maybe_location: Option<Location>, + is_dynamic: bool, + ) -> FetchFuture; /// Get the optional build info from the cache for a given module specifier. /// Because build infos are only associated with the "root" modules, they are /// not expected to be cached for each module, but are "lazily" checked when /// a root module is identified. The `emit_type` also indicates what form /// of the module the build info is valid for. - fn get_ts_build_info( + fn get_tsbuildinfo( &self, specifier: &ModuleSpecifier, ) -> Result<Option<String>, AnyError>; @@ -117,10 +160,10 @@ pub trait SpecifierHandler { ) -> Result<(), AnyError>; /// Set the build info for a module specifier, also providing the cache type. - fn set_ts_build_info( + fn set_tsbuildinfo( &mut self, specifier: &ModuleSpecifier, - ts_build_info: String, + tsbuildinfo: String, ) -> Result<(), AnyError>; /// Set the graph dependencies for a given module specifier. @@ -170,15 +213,18 @@ impl CompiledFileMetadata { /// existing `file_fetcher` interface, which will eventually be refactored to /// align it more to the `SpecifierHandler` trait. pub struct FetchHandler { + /// An instance of disk where generated (emitted) files are stored. disk_cache: DiskCache, + /// A set of permissions to apply to dynamic imports. + dynamic_permissions: Permissions, + /// A clone of the `program_state` file fetcher. file_fetcher: SourceFileFetcher, - permissions: Permissions, } impl FetchHandler { pub fn new( program_state: &Arc<ProgramState>, - permissions: Permissions, + dynamic_permissions: Permissions, ) -> Result<Self, AnyError> { let custom_root = env::var("DENO_DIR").map(String::into).ok(); let deno_dir = DenoDir::new(custom_root)?; @@ -187,23 +233,54 @@ impl FetchHandler { Ok(FetchHandler { disk_cache, + dynamic_permissions, file_fetcher, - permissions, }) } } impl SpecifierHandler for FetchHandler { - fn fetch(&mut self, requested_specifier: ModuleSpecifier) -> FetchFuture { - let permissions = self.permissions.clone(); + fn fetch( + &mut self, + requested_specifier: ModuleSpecifier, + maybe_location: Option<Location>, + is_dynamic: bool, + ) -> FetchFuture { + // When the module graph fetches dynamic modules, the set of dynamic + // permissions need to be applied. Other static imports have all + // permissions. + let permissions = if is_dynamic { + self.dynamic_permissions.clone() + } else { + Permissions::allow_all() + }; let file_fetcher = self.file_fetcher.clone(); let disk_cache = self.disk_cache.clone(); + let maybe_referrer: Option<ModuleSpecifier> = + if let Some(location) = &maybe_location { + Some(location.clone().into()) + } else { + None + }; async move { let source_file = file_fetcher - .fetch_source_file(&requested_specifier, None, permissions) - .await?; + .fetch_source_file(&requested_specifier, maybe_referrer, permissions) + .await + .map_err(|err| { + if let Some(location) = maybe_location { + if !is_dynamic { + HandlerError::FetchErrorWithLocation(err.to_string(), location) + .into() + } else { + err + } + } else { + err + } + })?; let url = source_file.url.clone(); + let is_remote = url.scheme() != "file"; let filename = disk_cache.get_cache_filename_with_extension(&url, "meta"); let maybe_version = if let Ok(bytes) = disk_cache.get(&filename) { if let Ok(compiled_file_metadata) = @@ -237,6 +314,7 @@ impl SpecifierHandler for FetchHandler { let specifier = ModuleSpecifier::from(url); Ok(CachedModule { + is_remote, maybe_dependencies: None, maybe_emit, maybe_emit_path, @@ -252,31 +330,32 @@ impl SpecifierHandler for FetchHandler { .boxed_local() } - fn get_ts_build_info( + fn get_tsbuildinfo( &self, specifier: &ModuleSpecifier, ) -> Result<Option<String>, AnyError> { let filename = self .disk_cache .get_cache_filename_with_extension(specifier.as_url(), "buildinfo"); - if let Ok(ts_build_info) = self.disk_cache.get(&filename) { - return Ok(Some(String::from_utf8(ts_build_info)?)); + if let Ok(tsbuildinfo) = self.disk_cache.get(&filename) { + Ok(Some(String::from_utf8(tsbuildinfo)?)) + } else { + Ok(None) } - - Ok(None) } - fn set_ts_build_info( + fn set_tsbuildinfo( &mut self, specifier: &ModuleSpecifier, - ts_build_info: String, + tsbuildinfo: String, ) -> Result<(), AnyError> { let filename = self .disk_cache .get_cache_filename_with_extension(specifier.as_url(), "buildinfo"); + debug!("set_tsbuildinfo - filename {:?}", filename); self .disk_cache - .set(&filename, ts_build_info.as_bytes()) + .set(&filename, tsbuildinfo.as_bytes()) .map_err(|e| e.into()) } @@ -366,8 +445,8 @@ pub mod tests { let fetch_handler = FetchHandler { disk_cache, + dynamic_permissions: Permissions::default(), file_fetcher, - permissions: Permissions::allow_all(), }; (temp_dir, fetch_handler) @@ -381,8 +460,10 @@ pub mod tests { "http://localhost:4545/cli/tests/subdir/mod2.ts", ) .unwrap(); - let cached_module: CachedModule = - file_fetcher.fetch(specifier.clone()).await.unwrap(); + let cached_module: CachedModule = file_fetcher + .fetch(specifier.clone(), None, false) + .await + .unwrap(); assert!(cached_module.maybe_emit.is_none()); assert!(cached_module.maybe_dependencies.is_none()); assert_eq!(cached_module.media_type, MediaType::TypeScript); @@ -401,18 +482,43 @@ pub mod tests { "http://localhost:4545/cli/tests/subdir/mod2.ts", ) .unwrap(); - let cached_module: CachedModule = - file_fetcher.fetch(specifier.clone()).await.unwrap(); + let cached_module: CachedModule = file_fetcher + .fetch(specifier.clone(), None, false) + .await + .unwrap(); assert!(cached_module.maybe_emit.is_none()); let code = String::from("some code"); file_fetcher .set_cache(&specifier, &Emit::Cli((code, None))) .expect("could not set cache"); - let cached_module: CachedModule = - file_fetcher.fetch(specifier.clone()).await.unwrap(); + let cached_module: CachedModule = file_fetcher + .fetch(specifier.clone(), None, false) + .await + .unwrap(); assert_eq!( cached_module.maybe_emit, Some(Emit::Cli(("some code".to_string(), None))) ); } + + #[tokio::test] + async fn test_fetch_handler_is_remote() { + let _http_server_guard = test_util::http_server(); + let (_, mut file_fetcher) = setup(); + let specifier = ModuleSpecifier::resolve_url_or_path( + "http://localhost:4545/cli/tests/subdir/mod2.ts", + ) + .unwrap(); + let cached_module: CachedModule = + file_fetcher.fetch(specifier, None, false).await.unwrap(); + assert_eq!(cached_module.is_remote, true); + let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); + let specifier = ModuleSpecifier::resolve_url_or_path( + c.join("tests/subdir/mod1.ts").as_os_str().to_str().unwrap(), + ) + .unwrap(); + let cached_module: CachedModule = + file_fetcher.fetch(specifier, None, false).await.unwrap(); + assert_eq!(cached_module.is_remote, false); + } } |