diff options
author | Ryan Dahl <ry@tinyclouds.org> | 2021-09-24 11:10:42 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-24 11:10:42 -0400 |
commit | 01da1d0adc0d09d60eb765ac583be8108eb31e36 (patch) | |
tree | 21819769192f9b86c0c95f8a1bdcc911ceb2bd7b /cli/program_state.rs | |
parent | 703ca905fd2ddd1860d704bf43aa66679a47a1e5 (diff) |
refactor: Rename ProgramState to ProcState (#12204)
Move Arc into struct
Diffstat (limited to 'cli/program_state.rs')
-rw-r--r-- | cli/program_state.rs | 504 |
1 files changed, 0 insertions, 504 deletions
diff --git a/cli/program_state.rs b/cli/program_state.rs deleted file mode 100644 index 6b723ca3d..000000000 --- a/cli/program_state.rs +++ /dev/null @@ -1,504 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -use crate::colors; -use crate::config_file::ConfigFile; -use crate::deno_dir; -use crate::file_fetcher::CacheSetting; -use crate::file_fetcher::FileFetcher; -use crate::flags; -use crate::http_cache; -use crate::lockfile::Lockfile; -use crate::module_graph::CheckOptions; -use crate::module_graph::GraphBuilder; -use crate::module_graph::TranspileOptions; -use crate::module_graph::TypeLib; -use crate::source_maps::SourceMapGetter; -use crate::specifier_handler::FetchHandler; -use crate::version; - -use deno_core::error::anyhow; -use deno_core::error::get_custom_error_class; -use deno_core::error::AnyError; -use deno_core::error::Context; -use deno_core::parking_lot::Mutex; -use deno_core::resolve_url; -use deno_core::url::Url; -use deno_core::ModuleSource; -use deno_core::ModuleSpecifier; -use deno_core::SharedArrayBufferStore; -use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; -use deno_runtime::deno_web::BlobStore; -use deno_runtime::inspector_server::InspectorServer; -use deno_runtime::permissions::Permissions; -use deno_tls::rustls::RootCertStore; -use deno_tls::rustls_native_certs::load_native_certs; -use deno_tls::webpki_roots::TLS_SERVER_ROOTS; -use import_map::ImportMap; -use log::debug; -use log::warn; -use std::collections::HashMap; -use std::collections::HashSet; -use std::env; -use std::fs::File; -use std::io::BufReader; -use std::sync::Arc; - -/// This structure represents state of single "deno" program. -/// -/// It is shared by all created workers (thus V8 isolates). -pub struct ProgramState { - /// Flags parsed from `argv` contents. - pub flags: flags::Flags, - pub dir: deno_dir::DenoDir, - pub coverage_dir: Option<String>, - pub file_fetcher: FileFetcher, - pub modules: - Arc<Mutex<HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>>>>, - pub lockfile: Option<Arc<Mutex<Lockfile>>>, - pub maybe_config_file: Option<ConfigFile>, - pub maybe_import_map: Option<ImportMap>, - pub maybe_inspector_server: Option<Arc<InspectorServer>>, - pub root_cert_store: Option<RootCertStore>, - pub blob_store: BlobStore, - pub broadcast_channel: InMemoryBroadcastChannel, - pub shared_array_buffer_store: SharedArrayBufferStore, -} - -impl ProgramState { - pub async fn build(flags: flags::Flags) -> Result<Arc<Self>, AnyError> { - let maybe_custom_root = flags - .cache_path - .clone() - .or_else(|| env::var("DENO_DIR").map(String::into).ok()); - let dir = deno_dir::DenoDir::new(maybe_custom_root)?; - let deps_cache_location = dir.root.join("deps"); - let http_cache = http_cache::HttpCache::new(&deps_cache_location); - - let mut root_cert_store = RootCertStore::empty(); - let ca_stores: Vec<String> = flags - .ca_stores - .clone() - .or_else(|| { - let env_ca_store = env::var("DENO_TLS_CA_STORE").ok()?; - Some( - env_ca_store - .split(',') - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()) - .collect(), - ) - }) - .unwrap_or_else(|| vec!["mozilla".to_string()]); - - for store in ca_stores.iter() { - match store.as_str() { - "mozilla" => { - root_cert_store.add_server_trust_anchors(&TLS_SERVER_ROOTS); - } - "system" => { - let roots = load_native_certs() - .expect("could not load platform certs") - .roots; - root_cert_store.roots.extend(roots); - } - _ => { - return Err(anyhow!("Unknown certificate store \"{}\" specified (allowed: \"system,mozilla\")", store)); - } - } - } - - let ca_file = flags.ca_file.clone().or_else(|| env::var("DENO_CERT").ok()); - if let Some(ca_file) = ca_file { - let certfile = File::open(&ca_file)?; - let mut reader = BufReader::new(certfile); - - // This function does not return specific errors, if it fails give a generic message. - if let Err(_err) = root_cert_store.add_pem_file(&mut reader) { - return Err(anyhow!("Unable to add pem file to certificate store")); - } - } - - if let Some(insecure_allowlist) = - flags.unsafely_ignore_certificate_errors.as_ref() - { - let domains = if insecure_allowlist.is_empty() { - "for all hostnames".to_string() - } else { - format!("for: {}", insecure_allowlist.join(", ")) - }; - let msg = - format!("DANGER: TLS certificate validation is disabled {}", domains); - eprintln!("{}", colors::yellow(msg)); - } - - let cache_usage = if flags.cached_only { - CacheSetting::Only - } else if !flags.cache_blocklist.is_empty() { - CacheSetting::ReloadSome(flags.cache_blocklist.clone()) - } else if flags.reload { - CacheSetting::ReloadAll - } else { - CacheSetting::Use - }; - - let blob_store = BlobStore::default(); - let broadcast_channel = InMemoryBroadcastChannel::default(); - let shared_array_buffer_store = SharedArrayBufferStore::default(); - - let file_fetcher = FileFetcher::new( - http_cache, - cache_usage, - !flags.no_remote, - Some(root_cert_store.clone()), - blob_store.clone(), - flags.unsafely_ignore_certificate_errors.clone(), - )?; - - let lockfile = if let Some(filename) = &flags.lock { - let lockfile = Lockfile::new(filename.clone(), flags.lock_write)?; - Some(Arc::new(Mutex::new(lockfile))) - } else { - None - }; - - let maybe_config_file = - if let Some(config_path) = flags.config_path.as_ref() { - Some(ConfigFile::read(config_path)?) - } else { - None - }; - - let maybe_import_map: Option<ImportMap> = - match flags.import_map_path.as_ref() { - None => None, - Some(import_map_url) => { - let import_map_specifier = - deno_core::resolve_url_or_path(import_map_url).context(format!( - "Bad URL (\"{}\") for import map.", - import_map_url - ))?; - let file = file_fetcher - .fetch(&import_map_specifier, &mut Permissions::allow_all()) - .await - .context(format!( - "Unable to load '{}' import map", - import_map_specifier - ))?; - let import_map = - ImportMap::from_json(import_map_specifier.as_str(), &file.source)?; - Some(import_map) - } - }; - - let maybe_inspect_host = flags.inspect.or(flags.inspect_brk); - let maybe_inspector_server = maybe_inspect_host.map(|host| { - Arc::new(InspectorServer::new(host, version::get_user_agent())) - }); - - let coverage_dir = flags - .coverage_dir - .clone() - .or_else(|| env::var("DENO_UNSTABLE_COVERAGE_DIR").ok()); - - let program_state = ProgramState { - dir, - coverage_dir, - flags, - file_fetcher, - modules: Default::default(), - lockfile, - maybe_config_file, - maybe_import_map, - maybe_inspector_server, - root_cert_store: Some(root_cert_store.clone()), - blob_store, - broadcast_channel, - shared_array_buffer_store, - }; - Ok(Arc::new(program_state)) - } - - /// Prepares a set of module specifiers for loading in one shot. - /// - pub async fn prepare_module_graph( - self: &Arc<Self>, - specifiers: Vec<ModuleSpecifier>, - lib: TypeLib, - root_permissions: Permissions, - dynamic_permissions: Permissions, - maybe_import_map: Option<ImportMap>, - ) -> Result<(), AnyError> { - let handler = Arc::new(Mutex::new(FetchHandler::new( - self, - root_permissions, - dynamic_permissions, - )?)); - - let mut builder = - GraphBuilder::new(handler, maybe_import_map, self.lockfile.clone()); - - for specifier in specifiers { - builder.add(&specifier, false).await?; - } - builder.analyze_config_file(&self.maybe_config_file).await?; - - let mut graph = builder.get_graph(); - let debug = self.flags.log_level == Some(log::Level::Debug); - let maybe_config_file = self.maybe_config_file.clone(); - let reload_exclusions = { - let modules = self.modules.lock(); - modules.keys().cloned().collect::<HashSet<_>>() - }; - - let result_modules = if self.flags.no_check { - let result_info = graph.transpile(TranspileOptions { - debug, - maybe_config_file, - reload: self.flags.reload, - reload_exclusions, - })?; - debug!("{}", result_info.stats); - if let Some(ignored_options) = result_info.maybe_ignored_options { - warn!("{}", ignored_options); - } - result_info.loadable_modules - } else { - let result_info = graph.check(CheckOptions { - debug, - emit: true, - lib, - maybe_config_file, - reload: self.flags.reload, - reload_exclusions, - })?; - - debug!("{}", result_info.stats); - if let Some(ignored_options) = result_info.maybe_ignored_options { - eprintln!("{}", ignored_options); - } - if !result_info.diagnostics.is_empty() { - return Err(anyhow!(result_info.diagnostics)); - } - result_info.loadable_modules - }; - - let mut loadable_modules = self.modules.lock(); - loadable_modules.extend(result_modules); - - if let Some(ref lockfile) = self.lockfile { - let g = lockfile.lock(); - g.write()?; - } - - Ok(()) - } - - /// This function is called when new module load is - /// initialized by the JsRuntime. Its resposibility is to collect - /// all dependencies and if it is required then also perform TS typecheck - /// and traspilation. - pub async fn prepare_module_load( - self: &Arc<Self>, - specifier: ModuleSpecifier, - lib: TypeLib, - root_permissions: Permissions, - dynamic_permissions: Permissions, - is_dynamic: bool, - maybe_import_map: Option<ImportMap>, - ) -> Result<(), AnyError> { - let specifier = specifier.clone(); - let handler = Arc::new(Mutex::new(FetchHandler::new( - self, - root_permissions, - dynamic_permissions, - )?)); - let mut builder = - GraphBuilder::new(handler, maybe_import_map, self.lockfile.clone()); - builder.add(&specifier, is_dynamic).await?; - builder.analyze_config_file(&self.maybe_config_file).await?; - let mut graph = builder.get_graph(); - let debug = self.flags.log_level == Some(log::Level::Debug); - let maybe_config_file = self.maybe_config_file.clone(); - let reload_exclusions = { - let modules = self.modules.lock(); - modules.keys().cloned().collect::<HashSet<_>>() - }; - - let result_modules = if self.flags.no_check { - let result_info = graph.transpile(TranspileOptions { - debug, - maybe_config_file, - reload: self.flags.reload, - reload_exclusions, - })?; - debug!("{}", result_info.stats); - if let Some(ignored_options) = result_info.maybe_ignored_options { - warn!("{}", ignored_options); - } - result_info.loadable_modules - } else { - let result_info = graph.check(CheckOptions { - debug, - emit: true, - lib, - maybe_config_file, - reload: self.flags.reload, - reload_exclusions, - })?; - - debug!("{}", result_info.stats); - if let Some(ignored_options) = result_info.maybe_ignored_options { - eprintln!("{}", ignored_options); - } - if !result_info.diagnostics.is_empty() { - return Err(anyhow!(result_info.diagnostics)); - } - result_info.loadable_modules - }; - - let mut loadable_modules = self.modules.lock(); - loadable_modules.extend(result_modules); - - if let Some(ref lockfile) = self.lockfile { - let g = lockfile.lock(); - g.write()?; - } - - Ok(()) - } - - pub fn load( - &self, - specifier: ModuleSpecifier, - maybe_referrer: Option<ModuleSpecifier>, - ) -> Result<ModuleSource, AnyError> { - let modules = self.modules.lock(); - modules - .get(&specifier) - .map(|r| match r { - Ok(module_source) => Ok(module_source.clone()), - Err(err) => { - // TODO(@kitsonk) this feels a bit hacky but it works, without - // introducing another enum to have to try to deal with. - if get_custom_error_class(err) == Some("NotFound") { - let message = if let Some(referrer) = &maybe_referrer { - format!("{}\n From: {}\n If the source module contains only types, use `import type` and `export type` to import it instead.", err, referrer) - } else { - format!("{}\n If the source module contains only types, use `import type` and `export type` to import it instead.", err) - }; - warn!("{}: {}", crate::colors::yellow("warning"), message); - Ok(ModuleSource { - code: "".to_string(), - module_url_found: specifier.to_string(), - module_url_specified: specifier.to_string(), - }) - } else { - // anyhow errors don't support cloning, so we have to manage this - // ourselves - Err(anyhow!(err.to_string())) - } - }, - }) - .unwrap_or_else(|| { - if let Some(referrer) = maybe_referrer { - Err(anyhow!( - "Module \"{}\" is missing from the graph.\n From: {}", - specifier, - referrer - )) - } else { - Err(anyhow!( - "Module \"{}\" is missing from the graph.", - specifier - )) - } - }) - } - - // TODO(@kitsonk) this should be refactored to get it from the module graph - fn get_emit(&self, url: &Url) -> Option<(Vec<u8>, Option<Vec<u8>>)> { - match url.scheme() { - // we should only be looking for emits for schemes that denote external - // modules, which the disk_cache supports - "wasm" | "file" | "http" | "https" | "data" | "blob" => (), - _ => { - return None; - } - } - let emit_path = self - .dir - .gen_cache - .get_cache_filename_with_extension(url, "js")?; - let emit_map_path = self - .dir - .gen_cache - .get_cache_filename_with_extension(url, "js.map")?; - if let Ok(code) = self.dir.gen_cache.get(&emit_path) { - let maybe_map = if let Ok(map) = self.dir.gen_cache.get(&emit_map_path) { - Some(map) - } else { - None - }; - Some((code, maybe_map)) - } else { - None - } - } -} - -// TODO(@kitsonk) this is only temporary, but should be refactored to somewhere -// else, like a refactored file_fetcher. -impl SourceMapGetter for ProgramState { - fn get_source_map(&self, file_name: &str) -> Option<Vec<u8>> { - if let Ok(specifier) = resolve_url(file_name) { - if let Some((code, maybe_map)) = self.get_emit(&specifier) { - let code = String::from_utf8(code).unwrap(); - source_map_from_code(code).or(maybe_map) - } else if let Ok(source) = self.load(specifier, None) { - source_map_from_code(source.code) - } else { - None - } - } else { - None - } - } - - fn get_source_line( - &self, - file_name: &str, - line_number: usize, - ) -> Option<String> { - if let Ok(specifier) = resolve_url(file_name) { - self.file_fetcher.get_source(&specifier).map(|out| { - // Do NOT use .lines(): it skips the terminating empty line. - // (due to internally using .split_terminator() instead of .split()) - let lines: Vec<&str> = out.source.split('\n').collect(); - assert!(lines.len() > line_number); - lines[line_number].to_string() - }) - } else { - None - } - } -} - -fn source_map_from_code(code: String) -> Option<Vec<u8>> { - let lines: Vec<&str> = code.split('\n').collect(); - if let Some(last_line) = lines.last() { - if last_line - .starts_with("//# sourceMappingURL=data:application/json;base64,") - { - let input = last_line.trim_start_matches( - "//# sourceMappingURL=data:application/json;base64,", - ); - let decoded_map = base64::decode(input) - .expect("Unable to decode source map from emitted file."); - Some(decoded_map) - } else { - None - } - } else { - None - } -} |