diff options
Diffstat (limited to 'cli/proc_state.rs')
-rw-r--r-- | cli/proc_state.rs | 327 |
1 files changed, 185 insertions, 142 deletions
diff --git a/cli/proc_state.rs b/cli/proc_state.rs index c481a4307..a146f24f4 100644 --- a/cli/proc_state.rs +++ b/cli/proc_state.rs @@ -18,14 +18,14 @@ use crate::cache::TypeCheckCache; use crate::emit::emit_parsed_source; use crate::file_fetcher::FileFetcher; use crate::graph_util::graph_lock_or_exit; -use crate::graph_util::GraphData; -use crate::graph_util::ModuleEntry; +use crate::graph_util::graph_valid_with_cli_options; use crate::http_util::HttpClient; use crate::node; use crate::node::NodeResolution; use crate::npm::resolve_graph_npm_info; use crate::npm::NpmCache; use crate::npm::NpmPackageReference; +use crate::npm::NpmPackageReq; use crate::npm::NpmPackageResolver; use crate::npm::RealNpmRegistryApi; use crate::resolver::CliResolver; @@ -39,19 +39,17 @@ use deno_core::anyhow::Context; use deno_core::error::custom_error; use deno_core::error::generic_error; use deno_core::error::AnyError; -use deno_core::futures; use deno_core::parking_lot::Mutex; use deno_core::parking_lot::RwLock; use deno_core::resolve_url_or_path; use deno_core::CompiledWasmModuleStore; use deno_core::ModuleSpecifier; use deno_core::SharedArrayBufferStore; -use deno_graph::create_graph; -use deno_graph::source::CacheInfo; -use deno_graph::source::LoadFuture; use deno_graph::source::Loader; use deno_graph::source::Resolver; -use deno_graph::Resolved; +use deno_graph::ModuleGraph; +use deno_graph::ModuleKind; +use deno_graph::Resolution; use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_tls::rustls::RootCertStore; @@ -60,6 +58,7 @@ use deno_runtime::inspector_server::InspectorServer; use deno_runtime::permissions::PermissionsContainer; use import_map::ImportMap; use log::warn; +use std::collections::HashMap; use std::collections::HashSet; use std::ops::Deref; use std::path::PathBuf; @@ -81,7 +80,7 @@ pub struct Inner { pub emit_cache: EmitCache, pub emit_options: deno_ast::EmitOptions, pub emit_options_hash: u64, - pub graph_data: Arc<RwLock<GraphData>>, + graph_data: Arc<RwLock<GraphData>>, pub lockfile: Option<Arc<Mutex<Lockfile>>>, pub maybe_import_map: Option<Arc<ImportMap>>, pub maybe_inspector_server: Option<Arc<InspectorServer>>, @@ -314,31 +313,6 @@ impl ProcState { log::debug!("Preparing module load."); let _pb_clear_guard = self.progress_bar.clear_guard(); - let has_root_npm_specifier = roots.iter().any(|r| { - r.scheme() == "npm" && NpmPackageReference::from_specifier(r).is_ok() - }); - - if !has_root_npm_specifier { - let graph_data = self.graph_data.read(); - if self.options.type_check_mode() == TypeCheckMode::None - || graph_data.is_type_checked(&roots, &lib) - { - if let Some(result) = graph_data.check( - &roots, - self.options.type_check_mode() != TypeCheckMode::None, - false, - ) { - // TODO(bartlomieju): this is strange... ideally there should be only - // one codepath in `prepare_module_load` so we don't forget things - // like writing a lockfile. Figure a way to refactor this function. - if let Some(ref lockfile) = self.lockfile { - let g = lockfile.lock(); - g.write()?; - } - return result; - } - } - } let mut cache = cache::FetchCacher::new( self.emit_cache.clone(), self.file_fetcher.clone(), @@ -348,36 +322,6 @@ impl ProcState { let maybe_imports = self.options.to_maybe_imports()?; let maybe_resolver = self.maybe_resolver.as_ref().map(|r| r.as_graph_resolver()); - - struct ProcStateLoader<'a> { - inner: &'a mut cache::FetchCacher, - graph_data: Arc<RwLock<GraphData>>, - } - impl Loader for ProcStateLoader<'_> { - fn get_cache_info( - &self, - specifier: &ModuleSpecifier, - ) -> Option<CacheInfo> { - self.inner.get_cache_info(specifier) - } - fn load( - &mut self, - specifier: &ModuleSpecifier, - is_dynamic: bool, - ) -> LoadFuture { - let graph_data = self.graph_data.read(); - let found_specifier = graph_data.follow_redirect(specifier); - match graph_data.get(&found_specifier) { - Some(_) => Box::pin(futures::future::ready(Err(anyhow!("")))), - _ => self.inner.load(specifier, is_dynamic), - } - } - } - let mut loader = ProcStateLoader { - inner: &mut cache, - graph_data: self.graph_data.clone(), - }; - let maybe_file_watcher_reporter: Option<&dyn deno_graph::source::Reporter> = if let Some(reporter) = &self.maybe_file_watcher_reporter { Some(reporter) @@ -386,46 +330,41 @@ impl ProcState { }; let analyzer = self.parsed_source_cache.as_analyzer(); + log::debug!("Creating module graph."); - let graph = create_graph( - roots.clone(), - &mut loader, - deno_graph::GraphOptions { - is_dynamic, - imports: maybe_imports, - resolver: maybe_resolver, - module_analyzer: Some(&*analyzer), - reporter: maybe_file_watcher_reporter, - }, - ) - .await; + let mut graph = self.graph_data.read().graph_inner_clone(); + + // Determine any modules that have already been emitted this session and + // should be skipped. + let reload_exclusions: HashSet<ModuleSpecifier> = + graph.specifiers().map(|(s, _)| s.clone()).collect(); + + graph + .build( + roots.clone(), + &mut cache, + deno_graph::BuildOptions { + is_dynamic, + imports: maybe_imports, + resolver: maybe_resolver, + module_analyzer: Some(&*analyzer), + reporter: maybe_file_watcher_reporter, + }, + ) + .await; // If there is a lockfile, validate the integrity of all the modules. if let Some(lockfile) = &self.lockfile { graph_lock_or_exit(&graph, &mut lockfile.lock()); } - // Determine any modules that have already been emitted this session and - // should be skipped. - let reload_exclusions: HashSet<ModuleSpecifier> = { - let graph_data = self.graph_data.read(); - graph_data.entries().map(|(s, _)| s).cloned().collect() - }; - let (npm_package_reqs, has_node_builtin_specifier) = { + graph_valid_with_cli_options(&graph, &roots, &self.options)?; let mut graph_data = self.graph_data.write(); - graph_data.add_graph(&graph); - let check_js = self.options.check_js(); - graph_data - .check( - &roots, - self.options.type_check_mode() != TypeCheckMode::None, - check_js, - ) - .unwrap()?; + graph_data.update_graph(Arc::new(graph)); ( - graph_data.npm_package_reqs().clone(), - graph_data.has_node_builtin_specifier(), + graph_data.npm_packages.clone(), + graph_data.has_node_builtin_specifier, ) }; @@ -447,9 +386,19 @@ impl ProcState { // type check if necessary let is_std_node = roots.len() == 1 && roots[0] == *node::MODULE_ALL_URL; - if self.options.type_check_mode() != TypeCheckMode::None && !is_std_node { + if self.options.type_check_mode() != TypeCheckMode::None + && !is_std_node + && !self.graph_data.read().is_type_checked(&roots, lib) + { log::debug!("Type checking."); let maybe_config_specifier = self.options.maybe_config_file_specifier(); + let (graph, has_node_builtin_specifier) = { + let graph_data = self.graph_data.read(); + ( + Arc::new(graph_data.graph.segment(&roots)), + graph_data.has_node_builtin_specifier, + ) + }; let options = check::CheckOptions { type_check_mode: self.options.type_check_mode(), debug: self.options.log_level() == Some(log::Level::Debug), @@ -461,28 +410,19 @@ impl ProcState { log_checks: true, reload: self.options.reload_flag() && !roots.iter().all(|r| reload_exclusions.contains(r)), + has_node_builtin_specifier, }; let check_cache = TypeCheckCache::new(&self.dir.type_checking_cache_db_file_path()); - let graph_data = self.graph_data.clone(); - let check_result = check::check( - &roots, - graph_data, - &check_cache, - &self.npm_resolver, - options, - )?; + let check_result = + check::check(graph, &check_cache, &self.npm_resolver, options)?; + self.graph_data.write().set_type_checked(&roots, lib); if !check_result.diagnostics.is_empty() { return Err(anyhow!(check_result.diagnostics)); } log::debug!("{}", check_result.stats); } - if self.options.type_check_mode() != TypeCheckMode::None { - let mut graph_data = self.graph_data.write(); - graph_data.set_type_checked(&roots, lib); - } - // any updates to the lockfile should be updated now if let Some(ref lockfile) = self.lockfile { let g = lockfile.lock(); @@ -523,10 +463,21 @@ impl ProcState { return Ok(()); } - let node_std_graph = self - .create_graph(vec![node::MODULE_ALL_URL.clone()]) - .await?; - self.graph_data.write().add_graph(&node_std_graph); + let mut graph = self.graph_data.read().graph_inner_clone(); + let mut loader = self.create_graph_loader(); + let analyzer = self.parsed_source_cache.as_analyzer(); + graph + .build( + vec![node::MODULE_ALL_URL.clone()], + &mut loader, + deno_graph::BuildOptions { + module_analyzer: Some(&*analyzer), + ..Default::default() + }, + ) + .await; + + self.graph_data.write().update_graph(Arc::new(graph)); self.node_std_graph_prepared.store(true, Ordering::Relaxed); Ok(()) } @@ -571,16 +522,18 @@ impl ProcState { } let graph_data = self.graph_data.read(); - let found_referrer = graph_data.follow_redirect(&referrer); - let maybe_resolved = match graph_data.get(&found_referrer) { - Some(ModuleEntry::Module { dependencies, .. }) => { - dependencies.get(specifier).map(|d| &d.maybe_code) - } + let graph = &graph_data.graph; + let maybe_resolved = match graph.get(&referrer) { + Some(module) => module + .dependencies + .get(specifier) + .map(|d| (&module.specifier, &d.maybe_code)), _ => None, }; match maybe_resolved { - Some(Resolved::Ok { specifier, .. }) => { + Some((found_referrer, Resolution::Ok(resolved))) => { + let specifier = &resolved.specifier; if let Ok(reference) = NpmPackageReference::from_specifier(specifier) { if !self.options.unstable() @@ -604,13 +557,13 @@ impl ProcState { return Ok(specifier.clone()); } } - Some(Resolved::Err(err)) => { + Some((_, Resolution::Err(err))) => { return Err(custom_error( "TypeError", format!("{}\n", err.to_string_with_range()), )) } - Some(Resolved::None) | None => {} + Some((_, Resolution::None)) | None => {} } } @@ -660,26 +613,24 @@ impl ProcState { } pub fn cache_module_emits(&self) -> Result<(), AnyError> { - let graph_data = self.graph_data.read(); - for (specifier, entry) in graph_data.entries() { - if let ModuleEntry::Module { - code, media_type, .. - } = entry - { - let is_emittable = matches!( - media_type, + let graph = self.graph(); + for module in graph.modules() { + let is_emittable = module.kind != ModuleKind::External + && matches!( + module.media_type, MediaType::TypeScript | MediaType::Mts | MediaType::Cts | MediaType::Jsx | MediaType::Tsx ); - if is_emittable { + if is_emittable { + if let Some(code) = &module.maybe_source { emit_parsed_source( &self.emit_cache, &self.parsed_source_cache, - specifier, - *media_type, + &module.specifier, + module.media_type, code, &self.emit_options, self.emit_options_hash, @@ -723,18 +674,20 @@ impl ProcState { maybe_cli_resolver.as_ref().map(|r| r.as_graph_resolver()); let analyzer = self.parsed_source_cache.as_analyzer(); - let graph = create_graph( - roots, - loader, - deno_graph::GraphOptions { - is_dynamic: false, - imports: maybe_imports, - resolver: maybe_graph_resolver, - module_analyzer: Some(&*analyzer), - reporter: None, - }, - ) - .await; + let mut graph = ModuleGraph::default(); + graph + .build( + roots, + loader, + deno_graph::BuildOptions { + is_dynamic: false, + imports: maybe_imports, + resolver: maybe_graph_resolver, + module_analyzer: Some(&*analyzer), + reporter: None, + }, + ) + .await; // add the found npm package requirements to the npm resolver and cache them let graph_npm_info = resolve_graph_npm_info(&graph); @@ -755,6 +708,10 @@ impl ProcState { Ok(graph) } + + pub fn graph(&self) -> Arc<ModuleGraph> { + self.graph_data.read().graph.clone() + } } #[derive(Clone, Debug)] @@ -780,3 +737,89 @@ impl deno_graph::source::Reporter for FileWatcherReporter { } } } + +#[derive(Debug, Default)] +struct GraphData { + graph: Arc<ModuleGraph>, + /// The npm package requirements from all the encountered graphs + /// in the order that they should be resolved. + npm_packages: Vec<NpmPackageReq>, + /// If the graph had a "node:" specifier. + has_node_builtin_specifier: bool, + checked_libs: HashMap<TsTypeLib, HashSet<ModuleSpecifier>>, +} + +impl GraphData { + /// Store data from `graph` into `self`. + pub fn update_graph(&mut self, graph: Arc<ModuleGraph>) { + let mut has_npm_specifier_in_graph = false; + + for (specifier, _) in graph.specifiers() { + match specifier.scheme() { + "node" => { + // We don't ever set this back to false because once it's + // on then it's on globally. + self.has_node_builtin_specifier = true; + } + "npm" => { + if !has_npm_specifier_in_graph + && NpmPackageReference::from_specifier(specifier).is_ok() + { + has_npm_specifier_in_graph = true; + } + } + _ => {} + } + + if has_npm_specifier_in_graph && self.has_node_builtin_specifier { + break; // exit early + } + } + + if has_npm_specifier_in_graph { + self.npm_packages = resolve_graph_npm_info(&graph).package_reqs; + } + self.graph = graph; + } + + // todo(dsherret): remove the need for cloning this (maybe if we used an AsyncRefCell) + pub fn graph_inner_clone(&self) -> ModuleGraph { + (*self.graph).clone() + } + + /// Mark `roots` and all of their dependencies as type checked under `lib`. + /// Assumes that all of those modules are known. + pub fn set_type_checked( + &mut self, + roots: &[ModuleSpecifier], + lib: TsTypeLib, + ) { + let entries = self.graph.walk( + roots, + deno_graph::WalkOptions { + check_js: true, + follow_dynamic: true, + follow_type_only: true, + }, + ); + let checked_lib_set = self.checked_libs.entry(lib).or_default(); + for (specifier, _) in entries { + checked_lib_set.insert(specifier.clone()); + } + } + + /// Check if `roots` are all marked as type checked under `lib`. + pub fn is_type_checked( + &self, + roots: &[ModuleSpecifier], + lib: TsTypeLib, + ) -> bool { + match self.checked_libs.get(&lib) { + Some(checked_lib_set) => roots.iter().all(|r| { + let found = self.graph.resolve(r); + checked_lib_set.contains(&found) + }), + None => false, + } + } +} |