diff options
Diffstat (limited to 'cli/main.rs')
-rw-r--r-- | cli/main.rs | 365 |
1 files changed, 216 insertions, 149 deletions
diff --git a/cli/main.rs b/cli/main.rs index 4ffa8340c..8a85cacfb 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -2,6 +2,7 @@ mod ast; mod auth_tokens; +mod cache; mod checksum; mod compat; mod config_file; @@ -9,6 +10,7 @@ mod deno_dir; mod diagnostics; mod diff; mod disk_cache; +mod emit; mod errors; mod file_fetcher; mod file_watcher; @@ -18,16 +20,14 @@ mod fmt_errors; mod fs_util; mod http_cache; mod http_util; -mod info; mod lockfile; mod logger; mod lsp; -mod module_graph; mod module_loader; mod ops; mod proc_state; +mod resolver; mod source_maps; -mod specifier_handler; mod standalone; mod text_encoding; mod tokio_util; @@ -59,8 +59,8 @@ use crate::flags::UpgradeFlags; use crate::fmt_errors::PrettyJsError; use crate::module_loader::CliModuleLoader; use crate::proc_state::ProcState; +use crate::resolver::ImportMapResolver; use crate::source_maps::apply_source_map; -use crate::specifier_handler::FetchHandler; use crate::tools::installer::infer_name_from_url; use deno_ast::MediaType; use deno_core::error::generic_error; @@ -68,7 +68,6 @@ use deno_core::error::AnyError; use deno_core::futures::future::FutureExt; use deno_core::futures::Future; use deno_core::located_script_name; -use deno_core::parking_lot::Mutex; use deno_core::resolve_url_or_path; use deno_core::serde_json; use deno_core::serde_json::json; @@ -299,7 +298,7 @@ where fn print_cache_info( state: &ProcState, json: bool, - location: Option<deno_core::url::Url>, + location: Option<&deno_core::url::Url>, ) -> Result<(), AnyError> { let deno_dir = &state.dir.root; let modules_cache = &state.file_fetcher.get_http_cache_location(); @@ -402,19 +401,9 @@ async fn compile_command( "An executable name was not provided. One could not be inferred from the URL. Aborting.", ))?; - let module_graph = create_module_graph_and_maybe_check( - module_specifier.clone(), - ps.clone(), - debug, - ) - .await?; - - info!( - "{} {}", - colors::green("Bundle"), - module_specifier.to_string() - ); - let bundle_str = bundle_module_graph(module_graph, ps.clone(), flags, debug)?; + let graph = + create_graph_and_maybe_check(module_specifier.clone(), &ps, debug).await?; + let (bundle_str, _) = bundle_module_graph(graph.as_ref(), &ps, &flags)?; info!( "{} {}", @@ -449,36 +438,38 @@ async fn info_command( flags: Flags, info_flags: InfoFlags, ) -> Result<(), AnyError> { - let location = flags.location.clone(); let ps = ProcState::build(flags).await?; if let Some(specifier) = info_flags.file { let specifier = resolve_url_or_path(&specifier)?; - let handler = Arc::new(Mutex::new(specifier_handler::FetchHandler::new( - &ps, - // info accesses dynamically imported modules just for their information - // so we allow access to all of them. + let mut cache = cache::FetchCacher::new( + ps.dir.gen_cache.clone(), + ps.file_fetcher.clone(), Permissions::allow_all(), Permissions::allow_all(), - )?)); - let mut builder = module_graph::GraphBuilder::new( - handler, - ps.maybe_import_map.clone(), - ps.lockfile.clone(), ); - builder.add(&specifier, false).await?; - builder.analyze_config_file(&ps.maybe_config_file).await?; - let graph = builder.get_graph(); - let info = graph.info()?; + let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone()); + let maybe_resolver = + ps.maybe_import_map.as_ref().map(ImportMapResolver::new); + let graph = deno_graph::create_graph( + vec![specifier], + false, + None, + &mut cache, + maybe_resolver.as_ref().map(|r| r.as_resolver()), + maybe_locker, + None, + ) + .await; if info_flags.json { - write_json_to_stdout(&json!(info)) + write_json_to_stdout(&json!(graph)) } else { - write_to_stdout_ignore_sigpipe(info.to_string().as_bytes()) + write_to_stdout_ignore_sigpipe(graph.to_string().as_bytes()) .map_err(|err| err.into()) } } else { // If it was just "deno info" print location of caches and exit - print_cache_info(&ps, info_flags.json, location) + print_cache_info(&ps, info_flags.json, ps.flags.location.as_ref()) } } @@ -541,23 +532,25 @@ async fn cache_command( cache_flags: CacheFlags, ) -> Result<(), AnyError> { let lib = if flags.unstable { - module_graph::TypeLib::UnstableDenoWindow + emit::TypeLib::UnstableDenoWindow } else { - module_graph::TypeLib::DenoWindow + emit::TypeLib::DenoWindow }; let ps = ProcState::build(flags).await?; for file in cache_flags.files { let specifier = resolve_url_or_path(&file)?; ps.prepare_module_load( - specifier, + vec![specifier], + false, lib.clone(), Permissions::allow_all(), Permissions::allow_all(), - false, - ps.maybe_import_map.clone(), ) .await?; + if let Some(graph_error) = ps.maybe_graph_error.lock().take() { + return Err(graph_error.into()); + } } Ok(()) @@ -567,8 +560,10 @@ async fn eval_command( flags: Flags, eval_flags: EvalFlags, ) -> Result<(), AnyError> { - // Force TypeScript compile. - let main_module = resolve_url_or_path("./$deno$eval.ts").unwrap(); + // deno_graph works off of extensions for local files to determine the media + // type, and so our "fake" specifier needs to have the proper extension. + let main_module = + resolve_url_or_path(&format!("./$deno$eval.{}", eval_flags.ext)).unwrap(); let permissions = Permissions::from_options(&flags.clone().into()); let ps = ProcState::build(flags.clone()).await?; let mut worker = @@ -584,15 +579,7 @@ async fn eval_command( let file = File { local: main_module.clone().to_file_path().unwrap(), maybe_types: None, - media_type: if eval_flags.ext.as_str() == "ts" { - MediaType::TypeScript - } else if eval_flags.ext.as_str() == "tsx" { - MediaType::Tsx - } else if eval_flags.ext.as_str() == "js" { - MediaType::JavaScript - } else { - MediaType::Jsx - }, + media_type: MediaType::Unknown, source: Arc::new(String::from_utf8(source_code)?), specifier: main_module.clone(), maybe_headers: None, @@ -603,9 +590,7 @@ async fn eval_command( ps.file_fetcher.insert_cached(file); debug!("main_module {}", &main_module); if flags.compat { - worker - .execute_side_module(&compat::get_node_globals_url()) - .await?; + worker.execute_side_module(&compat::GLOBAL_URL).await?; } worker.execute_main_module(&main_module).await?; worker.execute_script( @@ -620,75 +605,133 @@ async fn eval_command( Ok(()) } -async fn create_module_graph_and_maybe_check( - module_specifier: ModuleSpecifier, - ps: ProcState, +async fn create_graph_and_maybe_check( + root: ModuleSpecifier, + ps: &ProcState, debug: bool, -) -> Result<module_graph::Graph, AnyError> { - let handler = Arc::new(Mutex::new(FetchHandler::new( - &ps, - // when bundling, dynamic imports are only access for their type safety, - // therefore we will allow the graph to access any module. +) -> Result<Arc<deno_graph::ModuleGraph>, AnyError> { + let mut cache = cache::FetchCacher::new( + ps.dir.gen_cache.clone(), + ps.file_fetcher.clone(), Permissions::allow_all(), Permissions::allow_all(), - )?)); - let mut builder = module_graph::GraphBuilder::new( - handler, - ps.maybe_import_map.clone(), - ps.lockfile.clone(), ); - builder.add(&module_specifier, false).await?; - builder.analyze_config_file(&ps.maybe_config_file).await?; - let module_graph = builder.get_graph(); + let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone()); + let maybe_imports = ps + .maybe_config_file + .as_ref() + .map(|cf| cf.to_maybe_imports()) + .flatten(); + let maybe_resolver = ps.maybe_import_map.as_ref().map(ImportMapResolver::new); + let graph = Arc::new( + deno_graph::create_graph( + vec![root], + false, + maybe_imports, + &mut cache, + maybe_resolver.as_ref().map(|r| r.as_resolver()), + maybe_locker, + None, + ) + .await, + ); + + // Ensure that all non-dynamic, non-type only imports are properly loaded and + // if not, error with the first issue encountered. + graph.valid().map_err(emit::GraphError::from)?; + // If there was a locker, validate the integrity of all the modules in the + // locker. + emit::lock(graph.as_ref()); if !ps.flags.no_check { - // TODO(@kitsonk) support bundling for workers + graph.valid_types_only().map_err(emit::GraphError::from)?; let lib = if ps.flags.unstable { - module_graph::TypeLib::UnstableDenoWindow + emit::TypeLib::UnstableDenoWindow } else { - module_graph::TypeLib::DenoWindow + emit::TypeLib::DenoWindow }; - let result_info = - module_graph.clone().check(module_graph::CheckOptions { - debug, - emit: false, + let (ts_config, maybe_ignored_options) = emit::get_ts_config( + emit::ConfigType::Check { + tsc_emit: false, lib, - maybe_config_file: ps.maybe_config_file.clone(), - reload: ps.flags.reload, - ..Default::default() - })?; - - debug!("{}", result_info.stats); - if let Some(ignored_options) = result_info.maybe_ignored_options { + }, + ps.maybe_config_file.as_ref(), + None, + )?; + log::info!("{} {}", colors::green("Check"), graph.roots[0]); + if let Some(ignored_options) = maybe_ignored_options { eprintln!("{}", ignored_options); } - if !result_info.diagnostics.is_empty() { - return Err(generic_error(result_info.diagnostics.to_string())); + let maybe_config_specifier = ps + .maybe_config_file + .as_ref() + .map(|cf| ModuleSpecifier::from_file_path(&cf.path).unwrap()); + let check_result = emit::check_and_maybe_emit( + graph.clone(), + &mut cache, + emit::CheckOptions { + debug, + emit_with_diagnostics: false, + maybe_config_specifier, + ts_config, + }, + )?; + debug!("{}", check_result.stats); + if !check_result.diagnostics.is_empty() { + return Err(check_result.diagnostics.into()); } } - Ok(module_graph) + Ok(graph) } fn bundle_module_graph( - module_graph: module_graph::Graph, - ps: ProcState, - flags: Flags, - debug: bool, -) -> Result<String, AnyError> { - let (bundle, stats, maybe_ignored_options) = - module_graph.bundle(module_graph::BundleOptions { - debug, - maybe_config_file: ps.maybe_config_file.clone(), - })?; - match maybe_ignored_options { - Some(ignored_options) if flags.no_check => { + graph: &deno_graph::ModuleGraph, + ps: &ProcState, + flags: &Flags, +) -> Result<(String, Option<String>), AnyError> { + info!("{} {}", colors::green("Bundle"), graph.roots[0]); + + let (ts_config, maybe_ignored_options) = emit::get_ts_config( + emit::ConfigType::Bundle, + ps.maybe_config_file.as_ref(), + None, + )?; + if flags.no_check { + if let Some(ignored_options) = maybe_ignored_options { eprintln!("{}", ignored_options); } - _ => {} } - debug!("{}", stats); - Ok(bundle) + + emit::bundle( + graph, + emit::BundleOptions { + bundle_type: emit::BundleType::Module, + ts_config, + }, + ) +} + +/// A function that converts a float to a string the represents a human +/// readable version of that number. +fn human_size(size: f64) -> String { + let negative = if size.is_sign_positive() { "" } else { "-" }; + let size = size.abs(); + let units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + if size < 1_f64 { + return format!("{}{}{}", negative, size, "B"); + } + let delimiter = 1024_f64; + let exponent = std::cmp::min( + (size.ln() / delimiter.ln()).floor() as i32, + (units.len() - 1) as i32, + ); + let pretty_bytes = format!("{:.2}", size / delimiter.powi(exponent)) + .parse::<f64>() + .unwrap() + * 1_f64; + let unit = units[exponent as usize]; + format!("{}{}{}", negative, pretty_bytes, unit) } async fn bundle_command( @@ -707,17 +750,18 @@ async fn bundle_command( debug!(">>>>> bundle START"); let ps = ProcState::build(flags.clone()).await?; - let module_graph = create_module_graph_and_maybe_check( - module_specifier, - ps.clone(), - debug, - ) - .await?; + let graph = + create_graph_and_maybe_check(module_specifier, &ps, debug).await?; - let mut paths_to_watch: Vec<PathBuf> = module_graph - .get_modules() + let mut paths_to_watch: Vec<PathBuf> = graph + .specifiers() .iter() - .filter_map(|specifier| specifier.to_file_path().ok()) + .filter_map(|(_, r)| { + r.as_ref() + .ok() + .map(|(s, _)| s.to_file_path().ok()) + .flatten() + }) .collect(); if let Some(import_map) = ps.flags.import_map_path.as_ref() { @@ -725,12 +769,12 @@ async fn bundle_command( .push(fs_util::resolve_from_cwd(std::path::Path::new(import_map))?); } - Ok((paths_to_watch, module_graph, ps)) + Ok((paths_to_watch, graph, ps)) } .map(move |result| match result { - Ok((paths_to_watch, module_graph, ps)) => ResolutionResult::Restart { + Ok((paths_to_watch, graph, ps)) => ResolutionResult::Restart { paths_to_watch, - result: Ok((ps, module_graph)), + result: Ok((ps, graph)), }, Err(e) => ResolutionResult::Restart { paths_to_watch: vec![PathBuf::from(source_file2)], @@ -739,28 +783,43 @@ async fn bundle_command( }) }; - let operation = |(ps, module_graph): (ProcState, module_graph::Graph)| { + let operation = |(ps, graph): (ProcState, Arc<deno_graph::ModuleGraph>)| { let flags = flags.clone(); let out_file = bundle_flags.out_file.clone(); async move { - info!("{} {}", colors::green("Bundle"), module_graph.info()?.root); - - let output = bundle_module_graph(module_graph, ps, flags, debug)?; - + let (bundle_emit, maybe_bundle_map) = + bundle_module_graph(graph.as_ref(), &ps, &flags)?; debug!(">>>>> bundle END"); if let Some(out_file) = out_file.as_ref() { - let output_bytes = output.as_bytes(); + let output_bytes = bundle_emit.as_bytes(); let output_len = output_bytes.len(); fs_util::write_file(out_file, output_bytes, 0o644)?; info!( "{} {:?} ({})", colors::green("Emit"), out_file, - colors::gray(&info::human_size(output_len as f64)) + colors::gray(human_size(output_len as f64)) ); + if let Some(bundle_map) = maybe_bundle_map { + let map_bytes = bundle_map.as_bytes(); + let map_len = map_bytes.len(); + let ext = if let Some(curr_ext) = out_file.extension() { + format!("{}.map", curr_ext.to_string_lossy()) + } else { + "map".to_string() + }; + let map_out_file = out_file.with_extension(ext); + fs_util::write_file(&map_out_file, map_bytes, 0o644)?; + info!( + "{} {:?} ({})", + colors::green("Emit"), + map_out_file, + colors::gray(human_size(map_len as f64)) + ); + } } else { - println!("{}", output); + println!("{}", bundle_emit); } Ok(()) @@ -825,9 +884,7 @@ async fn run_repl(flags: Flags, repl_flags: ReplFlags) -> Result<(), AnyError> { let mut worker = create_main_worker(&ps, main_module.clone(), permissions, None); if flags.compat { - worker - .execute_side_module(&compat::get_node_globals_url()) - .await?; + worker.execute_side_module(&compat::GLOBAL_URL).await?; } worker.run_event_loop(false).await?; @@ -858,9 +915,7 @@ async fn run_from_stdin(flags: Flags) -> Result<(), AnyError> { debug!("main_module {}", main_module); if flags.compat { - worker - .execute_side_module(&compat::get_node_globals_url()) - .await?; + worker.execute_side_module(&compat::GLOBAL_URL).await?; } worker.execute_main_module(&main_module).await?; worker.execute_script( @@ -883,25 +938,42 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> { async move { let main_module = resolve_url_or_path(&script1)?; let ps = ProcState::build(flags).await?; - let handler = Arc::new(Mutex::new(FetchHandler::new( - &ps, + let mut cache = cache::FetchCacher::new( + ps.dir.gen_cache.clone(), + ps.file_fetcher.clone(), Permissions::allow_all(), Permissions::allow_all(), - )?)); - let mut builder = module_graph::GraphBuilder::new( - handler, - ps.maybe_import_map.clone(), - ps.lockfile.clone(), ); - builder.add(&main_module, false).await?; - builder.analyze_config_file(&ps.maybe_config_file).await?; - let module_graph = builder.get_graph(); + let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone()); + let maybe_imports = ps + .maybe_config_file + .as_ref() + .map(|cf| cf.to_maybe_imports()) + .flatten(); + let maybe_resolver = + ps.maybe_import_map.as_ref().map(ImportMapResolver::new); + let graph = deno_graph::create_graph( + vec![main_module.clone()], + false, + maybe_imports, + &mut cache, + maybe_resolver.as_ref().map(|r| r.as_resolver()), + maybe_locker, + None, + ) + .await; + graph.valid()?; // Find all local files in graph - let mut paths_to_watch: Vec<PathBuf> = module_graph - .get_modules() + let mut paths_to_watch: Vec<PathBuf> = graph + .specifiers() .iter() - .filter_map(|specifier| specifier.to_file_path().ok()) + .filter_map(|(_, r)| { + r.as_ref() + .ok() + .map(|(s, _)| s.to_file_path().ok()) + .flatten() + }) .collect(); if let Some(import_map) = ps.flags.import_map_path.as_ref() { @@ -948,10 +1020,7 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> { main_module: &ModuleSpecifier, ) -> Result<(), AnyError> { if self.compat { - self - .worker - .execute_side_module(&compat::get_node_globals_url()) - .await?; + self.worker.execute_side_module(&compat::GLOBAL_URL).await?; } self.worker.execute_main_module(main_module).await?; self.worker.execute_script( @@ -1046,9 +1115,7 @@ async fn run_command( debug!("main_module {}", main_module); if flags.compat { - worker - .execute_side_module(&compat::get_node_globals_url()) - .await?; + worker.execute_side_module(&compat::GLOBAL_URL).await?; } worker.execute_main_module(&main_module).await?; worker.execute_script( |