summaryrefslogtreecommitdiff
path: root/cli/proc_state.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/proc_state.rs')
-rw-r--r--cli/proc_state.rs426
1 files changed, 137 insertions, 289 deletions
diff --git a/cli/proc_state.rs b/cli/proc_state.rs
index d82f8d017..b6797d663 100644
--- a/cli/proc_state.rs
+++ b/cli/proc_state.rs
@@ -1,7 +1,6 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use crate::cache;
-use crate::cache::Cacher;
use crate::colors;
use crate::compat;
use crate::compat::NodeEsmResolver;
@@ -9,10 +8,12 @@ use crate::config_file::ConfigFile;
use crate::config_file::MaybeImportsResult;
use crate::deno_dir;
use crate::emit;
-use crate::errors::get_error_class_name;
use crate::file_fetcher::CacheSetting;
use crate::file_fetcher::FileFetcher;
use crate::flags;
+use crate::graph_util::graph_lock_or_exit;
+use crate::graph_util::GraphData;
+use crate::graph_util::ModuleEntry;
use crate::http_cache;
use crate::lockfile::as_maybe_locker;
use crate::lockfile::Lockfile;
@@ -25,7 +26,9 @@ use deno_core::anyhow::anyhow;
use deno_core::anyhow::Context;
use deno_core::error::custom_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;
use deno_core::url::Url;
use deno_core::CompiledWasmModuleStore;
@@ -34,10 +37,10 @@ use deno_core::ModuleSpecifier;
use deno_core::ModuleType;
use deno_core::SharedArrayBufferStore;
use deno_graph::create_graph;
-use deno_graph::Dependency;
+use deno_graph::source::CacheInfo;
+use deno_graph::source::LoadFuture;
+use deno_graph::source::Loader;
use deno_graph::MediaType;
-use deno_graph::ModuleGraphError;
-use deno_graph::Range;
use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel;
use deno_runtime::deno_tls::rustls;
use deno_runtime::deno_tls::rustls::RootCertStore;
@@ -48,10 +51,7 @@ use deno_runtime::deno_web::BlobStore;
use deno_runtime::inspector_server::InspectorServer;
use deno_runtime::permissions::Permissions;
use import_map::ImportMap;
-use std::collections::BTreeMap;
-use std::collections::HashMap;
use std::collections::HashSet;
-use std::collections::VecDeque;
use std::env;
use std::fs::File;
use std::io::BufReader;
@@ -64,104 +64,13 @@ use std::sync::Arc;
#[derive(Clone)]
pub struct ProcState(Arc<Inner>);
-#[derive(Debug, Clone)]
-#[allow(clippy::large_enum_variant)]
-enum ModuleEntry {
- Module {
- code: String,
- media_type: MediaType,
- dependencies: BTreeMap<String, Dependency>,
- },
- Error(ModuleGraphError),
- Redirect(ModuleSpecifier),
-}
-
-#[derive(Default)]
-struct GraphData {
- modules: HashMap<ModuleSpecifier, ModuleEntry>,
- /// A set of type libs that each module has passed a type check with this
- /// session. This would consist of window, worker or both.
- checked_libs_map: HashMap<ModuleSpecifier, HashSet<emit::TypeLib>>,
- /// Map of first known referrer locations for each module. Used to enhance
- /// error messages.
- referrer_map: HashMap<ModuleSpecifier, Range>,
-}
-
-impl GraphData {
- /// Check if `roots` are ready to be loaded by V8. Returns `Some(Ok(()))` if
- /// prepared. Returns `Some(Err(_))` if there is a known module graph error
- /// statically reachable from `roots`. Returns `None` if sufficient graph data
- /// is yet to be supplied.
- fn check_if_prepared(
- &self,
- roots: &[ModuleSpecifier],
- ) -> Option<Result<(), AnyError>> {
- let mut seen = HashSet::<&ModuleSpecifier>::new();
- let mut visiting = VecDeque::<&ModuleSpecifier>::new();
- for root in roots {
- visiting.push_back(root);
- }
- while let Some(specifier) = visiting.pop_front() {
- match self.modules.get(specifier) {
- Some(ModuleEntry::Module { dependencies, .. }) => {
- for (_, dep) in dependencies.iter().rev() {
- for resolved in [&dep.maybe_code, &dep.maybe_type] {
- if !dep.is_dynamic {
- match resolved {
- Some(Ok((dep_specifier, _))) => {
- if !dep.is_dynamic && !seen.contains(dep_specifier) {
- seen.insert(dep_specifier);
- visiting.push_front(dep_specifier);
- }
- }
- Some(Err(error)) => {
- let range = error.range();
- if !range.specifier.as_str().contains("$deno") {
- return Some(Err(custom_error(
- get_error_class_name(&error.clone().into()),
- format!("{}\n at {}", error.to_string(), range),
- )));
- }
- return Some(Err(error.clone().into()));
- }
- None => {}
- }
- }
- }
- }
- }
- Some(ModuleEntry::Error(error)) => {
- if !roots.contains(specifier) {
- if let Some(range) = self.referrer_map.get(specifier) {
- if !range.specifier.as_str().contains("$deno") {
- let message = error.to_string();
- return Some(Err(custom_error(
- get_error_class_name(&error.clone().into()),
- format!("{}\n at {}", message, range),
- )));
- }
- }
- }
- return Some(Err(error.clone().into()));
- }
- Some(ModuleEntry::Redirect(specifier)) => {
- seen.insert(specifier);
- visiting.push_front(specifier);
- }
- None => return None,
- }
- }
- Some(Ok(()))
- }
-}
-
pub struct Inner {
/// Flags parsed from `argv` contents.
pub flags: flags::Flags,
pub dir: deno_dir::DenoDir,
pub coverage_dir: Option<String>,
pub file_fetcher: FileFetcher,
- graph_data: Arc<Mutex<GraphData>>,
+ graph_data: Arc<RwLock<GraphData>>,
pub lockfile: Option<Arc<Mutex<Lockfile>>>,
pub maybe_config_file: Option<ConfigFile>,
pub maybe_import_map: Option<Arc<ImportMap>>,
@@ -404,8 +313,8 @@ impl ProcState {
/// This method must be called for a module or a static importer of that
/// module before attempting to `load()` it from a `JsRuntime`. It will
- /// populate `self.graph_data` in memory with the necessary source code or
- /// report any module graph / type checking errors.
+ /// populate `self.graph_data` in memory with the necessary source code, write
+ /// emits where necessary or report any module graph / type checking errors.
pub(crate) async fn prepare_module_load(
&self,
roots: Vec<ModuleSpecifier>,
@@ -425,16 +334,13 @@ impl ProcState {
roots
};
if !reload_on_watch {
- let graph_data = self.graph_data.lock();
+ let graph_data = self.graph_data.read();
if self.flags.check == flags::CheckFlag::None
- || roots.iter().all(|root| {
- graph_data
- .checked_libs_map
- .get(root)
- .map_or(false, |checked_libs| checked_libs.contains(&lib))
- })
+ || graph_data.is_type_checked(&roots, &lib)
{
- if let Some(result) = graph_data.check_if_prepared(&roots) {
+ if let Some(result) =
+ graph_data.check(&roots, self.flags.check != flags::CheckFlag::None)
+ {
return result;
}
}
@@ -453,11 +359,45 @@ impl ProcState {
} else {
None
};
+
+ struct ProcStateLoader<'a> {
+ inner: &'a mut cache::FetchCacher,
+ graph_data: Arc<RwLock<GraphData>>,
+ reload: bool,
+ }
+ 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(_) if !self.reload => Box::pin(futures::future::ready((
+ specifier.clone(),
+ Err(anyhow!("")),
+ ))),
+ _ => self.inner.load(specifier, is_dynamic),
+ }
+ }
+ }
+ let mut loader = ProcStateLoader {
+ inner: &mut cache,
+ graph_data: self.graph_data.clone(),
+ reload: reload_on_watch,
+ };
let graph = create_graph(
roots.clone(),
is_dynamic,
maybe_imports,
- &mut cache,
+ &mut loader,
maybe_resolver,
maybe_locker,
None,
@@ -465,15 +405,23 @@ impl ProcState {
.await;
// If there was a locker, validate the integrity of all the modules in the
// locker.
- emit::lock(&graph);
+ graph_lock_or_exit(&graph);
// 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.lock();
- graph_data.modules.keys().cloned().collect()
+ let graph_data = self.graph_data.read();
+ graph_data.entries().into_keys().cloned().collect()
};
+ {
+ let mut graph_data = self.graph_data.write();
+ graph_data.add_graph(&graph, reload_on_watch);
+ graph_data
+ .check(&roots, self.flags.check != flags::CheckFlag::None)
+ .unwrap()?;
+ }
+
let config_type = if self.flags.check == flags::CheckFlag::None {
emit::ConfigType::Emit
} else {
@@ -485,166 +433,49 @@ impl ProcState {
let (ts_config, maybe_ignored_options) =
emit::get_ts_config(config_type, self.maybe_config_file.as_ref(), None)?;
- let graph = Arc::new(graph);
-
- let mut type_check_result = Ok(());
-
- if emit::valid_emit(
- graph.as_ref(),
- &cache,
- &ts_config,
- self.flags.reload,
- &reload_exclusions,
- ) {
- if let Some(root) = graph.roots.get(0) {
- log::debug!("specifier \"{}\" and dependencies have valid emit, skipping checking and emitting", root);
- } else {
- log::debug!("rootless graph, skipping checking and emitting");
- }
- } else {
- if let Some(ignored_options) = maybe_ignored_options {
- log::warn!("{}", ignored_options);
- }
- let emit_result = if self.flags.check == flags::CheckFlag::None {
- let options = emit::EmitOptions {
- ts_config,
- reload_exclusions,
- reload: self.flags.reload,
- };
- emit::emit(graph.as_ref(), &mut cache, options)?
- } else {
- // here, we are type checking, so we want to error here if any of the
- // type only dependencies are missing or we have other errors with them
- // where as if we are not type checking, we shouldn't care about these
- // errors, and they don't get returned in `graph.valid()` above.
- graph.valid_types_only()?;
-
- let maybe_config_specifier = self
- .maybe_config_file
- .as_ref()
- .map(|cf| cf.specifier.clone());
- let options = emit::CheckOptions {
- check: self.flags.check.clone(),
- debug: self.flags.log_level == Some(log::Level::Debug),
- emit_with_diagnostics: false,
- maybe_config_specifier,
- ts_config,
- reload: self.flags.reload,
- };
- for root in &graph.roots {
- let root_str = root.to_string();
- // `$deno` specifiers are internal specifiers, printing out that
- // they are being checked is confusing to a user, since they don't
- // actually exist, so we will simply indicate that a generated module
- // is being checked instead of the cryptic internal module
- if !root_str.contains("$deno") {
- log::info!("{} {}", colors::green("Check"), root);
- } else {
- log::info!("{} a generated module", colors::green("Check"))
- }
- }
- emit::check_and_maybe_emit(graph.clone(), &mut cache, options)?
+
+ if let Some(ignored_options) = maybe_ignored_options {
+ log::warn!("{}", ignored_options);
+ }
+
+ if self.flags.check == flags::CheckFlag::None {
+ let options = emit::EmitOptions {
+ ts_config,
+ reload: self.flags.reload,
+ reload_exclusions,
};
+ let emit_result = emit::emit(&graph, &mut cache, options)?;
log::debug!("{}", emit_result.stats);
+ } else {
+ let maybe_config_specifier = self
+ .maybe_config_file
+ .as_ref()
+ .map(|cf| cf.specifier.clone());
+ let options = emit::CheckOptions {
+ check: self.flags.check.clone(),
+ debug: self.flags.log_level == Some(log::Level::Debug),
+ emit_with_diagnostics: false,
+ maybe_config_specifier,
+ ts_config,
+ log_checks: true,
+ reload: self.flags.reload,
+ reload_exclusions,
+ };
+ let emit_result = emit::check_and_maybe_emit(
+ &roots,
+ self.graph_data.clone(),
+ &mut cache,
+ options,
+ )?;
if !emit_result.diagnostics.is_empty() {
- type_check_result = Err(anyhow!(emit_result.diagnostics));
+ return Err(anyhow!(emit_result.diagnostics));
}
+ log::debug!("{}", emit_result.stats);
}
- {
- let mut graph_data = self.graph_data.lock();
- let mut specifiers = graph.specifiers();
- // Set specifier results for redirects.
- // TODO(nayeemrmn): This should be done in `ModuleGraph::specifiers()`.
- for (specifier, found) in &graph.redirects {
- let actual = specifiers.get(found).unwrap().clone();
- specifiers.insert(specifier.clone(), actual);
- }
- for (specifier, result) in &specifiers {
- if let Some(found) = graph.redirects.get(specifier) {
- let module_entry = ModuleEntry::Redirect(found.clone());
- graph_data.modules.insert(specifier.clone(), module_entry);
- continue;
- }
- match result {
- Ok((_, media_type)) => {
- let module = graph.get(specifier).unwrap();
- // If there was a type check error, supply dummy code. It shouldn't
- // be used since preparation will fail.
- let code = if type_check_result.is_err() {
- "".to_string()
- // Check to see if there is an emitted file in the cache.
- } else if let Some(code) =
- cache.get(cache::CacheType::Emit, specifier)
- {
- code
- // Then if the file is JavaScript (or unknown) and wasn't emitted,
- // we will load the original source code in the module.
- } else if matches!(
- media_type,
- MediaType::JavaScript
- | MediaType::Unknown
- | MediaType::Cjs
- | MediaType::Mjs
- | MediaType::Json
- ) {
- module.maybe_source().unwrap_or("").to_string()
- // The emit may also be missing when a declaration file is in the
- // graph. There shouldn't be any runtime statements in the source
- // file and if there was, users would be shown a `TS1036`
- // diagnostic. So just return an empty emit.
- } else if !emit::is_emittable(media_type, true) {
- "".to_string()
- } else {
- unreachable!(
- "unexpected missing emit: {} media type: {}",
- specifier, media_type
- )
- };
- let dependencies =
- module.maybe_dependencies().cloned().unwrap_or_default();
- graph_data.modules.insert(
- specifier.clone(),
- ModuleEntry::Module {
- code,
- dependencies,
- media_type: *media_type,
- },
- );
- if let Some(dependencies) = module.maybe_dependencies() {
- for dep in dependencies.values() {
- #[allow(clippy::manual_flatten)]
- for resolved in [&dep.maybe_code, &dep.maybe_type] {
- if let Some(Ok((specifier, referrer_range))) = resolved {
- let specifier =
- graph.redirects.get(specifier).unwrap_or(specifier);
- let entry =
- graph_data.referrer_map.entry(specifier.clone());
- entry.or_insert_with(|| referrer_range.clone());
- }
- }
- }
- }
- }
- Err(error) => {
- let module_entry = ModuleEntry::Error(error.clone());
- graph_data.modules.insert(specifier.clone(), module_entry);
- }
- }
- }
-
- graph_data.check_if_prepared(&roots).unwrap()?;
- type_check_result?;
-
- if self.flags.check != flags::CheckFlag::None {
- for specifier in specifiers.keys() {
- let checked_libs = graph_data
- .checked_libs_map
- .entry(specifier.clone())
- .or_default();
- checked_libs.insert(lib.clone());
- }
- }
+ if self.flags.check != flags::CheckFlag::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
@@ -662,12 +493,9 @@ impl ProcState {
referrer: &str,
) -> Result<ModuleSpecifier, AnyError> {
if let Ok(referrer) = deno_core::resolve_url_or_path(referrer) {
- let graph_data = self.graph_data.lock();
- let found_referrer = match graph_data.modules.get(&referrer) {
- Some(ModuleEntry::Redirect(r)) => r,
- _ => &referrer,
- };
- let maybe_resolved = match graph_data.modules.get(found_referrer) {
+ 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)
.and_then(|dep| dep.maybe_code.clone()),
@@ -725,23 +553,43 @@ impl ProcState {
is_dynamic
);
- let graph_data = self.graph_data.lock();
- let found_specifier = match graph_data.modules.get(&specifier) {
- Some(ModuleEntry::Redirect(s)) => s,
- _ => &specifier,
- };
- match graph_data.modules.get(found_specifier) {
+ let graph_data = self.graph_data.read();
+ let found = graph_data.follow_redirect(&specifier);
+ match graph_data.get(&found) {
Some(ModuleEntry::Module {
code, media_type, ..
- }) => Ok(ModuleSource {
- code: code.clone(),
- module_url_specified: specifier.to_string(),
- module_url_found: found_specifier.to_string(),
- module_type: match media_type {
- MediaType::Json => ModuleType::Json,
- _ => ModuleType::JavaScript,
- },
- }),
+ }) => {
+ let code = match media_type {
+ MediaType::JavaScript
+ | MediaType::Unknown
+ | MediaType::Cjs
+ | MediaType::Mjs
+ | MediaType::Json => code.as_ref().clone(),
+ MediaType::Dts => "".to_string(),
+ _ => {
+ let emit_path = self
+ .dir
+ .gen_cache
+ .get_cache_filename_with_extension(&found, "js")
+ .unwrap_or_else(|| {
+ unreachable!("Unable to get cache filename: {}", &found)
+ });
+ match self.dir.gen_cache.get(&emit_path) {
+ Ok(b) => String::from_utf8(b).unwrap(),
+ Err(_) => unreachable!("Unexpected missing emit: {}", found),
+ }
+ }
+ };
+ Ok(ModuleSource {
+ code,
+ module_url_specified: specifier.to_string(),
+ module_url_found: found.to_string(),
+ module_type: match media_type {
+ MediaType::Json => ModuleType::Json,
+ _ => ModuleType::JavaScript,
+ },
+ })
+ }
_ => Err(anyhow!(
"Loading unprepared module: {}",
specifier.to_string()