diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2022-12-09 09:40:48 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-09 09:40:48 -0500 |
commit | 6541a0a9fd818424688003c617e4a84c3cf7d34d (patch) | |
tree | 0a2281e9f119bb992fe34908665a79f5eaddaa77 /cli/tools | |
parent | 9daf6e197a642a88d79614fb53888b5bb954463a (diff) |
refactor: cleanup main.rs (#16996)
1. Extracts out some code from main.rs
2. Inlines all the `x_command` functions in main.rs
Diffstat (limited to 'cli/tools')
-rw-r--r-- | cli/tools/bundle.rs | 158 | ||||
-rw-r--r-- | cli/tools/coverage/mod.rs | 5 | ||||
-rw-r--r-- | cli/tools/installer.rs | 37 | ||||
-rw-r--r-- | cli/tools/mod.rs | 2 | ||||
-rw-r--r-- | cli/tools/repl/mod.rs | 22 | ||||
-rw-r--r-- | cli/tools/run.rs | 169 | ||||
-rw-r--r-- | cli/tools/standalone.rs | 61 |
7 files changed, 432 insertions, 22 deletions
diff --git a/cli/tools/bundle.rs b/cli/tools/bundle.rs new file mode 100644 index 000000000..606bab46f --- /dev/null +++ b/cli/tools/bundle.rs @@ -0,0 +1,158 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use std::path::PathBuf; +use std::sync::Arc; + +use deno_core::error::AnyError; +use deno_core::futures::FutureExt; +use deno_core::resolve_url_or_path; +use deno_runtime::colors; + +use crate::args::BundleFlags; +use crate::args::CliOptions; +use crate::args::Flags; +use crate::args::TsConfigType; +use crate::args::TypeCheckMode; +use crate::graph_util::create_graph_and_maybe_check; +use crate::graph_util::error_for_any_npm_specifier; +use crate::proc_state::ProcState; +use crate::util; +use crate::util::display; +use crate::util::file_watcher::ResolutionResult; + +pub async fn bundle( + flags: Flags, + bundle_flags: BundleFlags, +) -> Result<(), AnyError> { + let cli_options = Arc::new(CliOptions::from_flags(flags)?); + let resolver = |_| { + let cli_options = cli_options.clone(); + let source_file1 = bundle_flags.source_file.clone(); + let source_file2 = bundle_flags.source_file.clone(); + async move { + let module_specifier = resolve_url_or_path(&source_file1)?; + + log::debug!(">>>>> bundle START"); + let ps = ProcState::from_options(cli_options).await?; + let graph = create_graph_and_maybe_check(module_specifier, &ps).await?; + + let mut paths_to_watch: Vec<PathBuf> = graph + .specifiers() + .filter_map(|(_, r)| { + r.as_ref().ok().and_then(|(s, _, _)| s.to_file_path().ok()) + }) + .collect(); + + if let Ok(Some(import_map_path)) = ps + .options + .resolve_import_map_specifier() + .map(|ms| ms.and_then(|ref s| s.to_file_path().ok())) + { + paths_to_watch.push(import_map_path); + } + + Ok((paths_to_watch, graph, ps)) + } + .map(move |result| match result { + Ok((paths_to_watch, graph, ps)) => ResolutionResult::Restart { + paths_to_watch, + result: Ok((ps, graph)), + }, + Err(e) => ResolutionResult::Restart { + paths_to_watch: vec![PathBuf::from(source_file2)], + result: Err(e), + }, + }) + }; + + let operation = |(ps, graph): (ProcState, Arc<deno_graph::ModuleGraph>)| { + let out_file = bundle_flags.out_file.clone(); + async move { + // at the moment, we don't support npm specifiers in deno bundle, so show an error + error_for_any_npm_specifier(&graph)?; + + let bundle_output = bundle_module_graph(graph.as_ref(), &ps)?; + log::debug!(">>>>> bundle END"); + + if let Some(out_file) = out_file.as_ref() { + let output_bytes = bundle_output.code.as_bytes(); + let output_len = output_bytes.len(); + util::fs::write_file(out_file, output_bytes, 0o644)?; + log::info!( + "{} {:?} ({})", + colors::green("Emit"), + out_file, + colors::gray(display::human_size(output_len as f64)) + ); + if let Some(bundle_map) = bundle_output.maybe_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); + util::fs::write_file(&map_out_file, map_bytes, 0o644)?; + log::info!( + "{} {:?} ({})", + colors::green("Emit"), + map_out_file, + colors::gray(display::human_size(map_len as f64)) + ); + } + } else { + println!("{}", bundle_output.code); + } + + Ok(()) + } + }; + + if cli_options.watch_paths().is_some() { + util::file_watcher::watch_func( + resolver, + operation, + util::file_watcher::PrintConfig { + job_name: "Bundle".to_string(), + clear_screen: !cli_options.no_clear_screen(), + }, + ) + .await?; + } else { + let module_graph = + if let ResolutionResult::Restart { result, .. } = resolver(None).await { + result? + } else { + unreachable!(); + }; + operation(module_graph).await?; + } + + Ok(()) +} + +fn bundle_module_graph( + graph: &deno_graph::ModuleGraph, + ps: &ProcState, +) -> Result<deno_emit::BundleEmit, AnyError> { + log::info!("{} {}", colors::green("Bundle"), graph.roots[0].0); + + let ts_config_result = ps + .options + .resolve_ts_config_for_emit(TsConfigType::Bundle)?; + if ps.options.type_check_mode() == TypeCheckMode::None { + if let Some(ignored_options) = ts_config_result.maybe_ignored_options { + eprintln!("{}", ignored_options); + } + } + + deno_emit::bundle_graph( + graph, + deno_emit::BundleOptions { + bundle_type: deno_emit::BundleType::Module, + emit_options: ts_config_result.ts_config.into(), + emit_ignore_directives: true, + }, + ) +} diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index aacaf3d83..04ed9d033 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -13,6 +13,7 @@ use deno_ast::MediaType; use deno_ast::ModuleSpecifier; use deno_core::anyhow::anyhow; use deno_core::anyhow::Context; +use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::sourcemap::SourceMap; @@ -608,6 +609,10 @@ pub async fn cover_files( flags: Flags, coverage_flags: CoverageFlags, ) -> Result<(), AnyError> { + if coverage_flags.files.is_empty() { + return Err(generic_error("No matching coverage profiles found")); + } + let ps = ProcState::build(flags).await?; let script_coverages = diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index 0e915677a..fd7f68b71 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -6,6 +6,7 @@ use crate::args::Flags; use crate::args::InstallFlags; use crate::args::TypeCheckMode; use crate::npm::NpmPackageReference; +use crate::proc_state::ProcState; use crate::util::fs::canonicalize_path_maybe_not_exists; use deno_core::anyhow::Context; use deno_core::error::generic_error; @@ -215,7 +216,21 @@ pub fn uninstall(name: String, root: Option<PathBuf>) -> Result<(), AnyError> { Ok(()) } -pub fn install( +pub async fn install_command( + flags: Flags, + install_flags: InstallFlags, +) -> Result<(), AnyError> { + // ensure the module is cached + ProcState::build(flags.clone()) + .await? + .load_and_type_check_files(&[install_flags.module_url.clone()]) + .await?; + + // create the install shim + create_install_shim(flags, install_flags) +} + +fn create_install_shim( flags: Flags, install_flags: InstallFlags, ) -> Result<(), AnyError> { @@ -567,7 +582,7 @@ mod tests { let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap(); - install( + create_install_shim( Flags { unstable: true, ..Flags::default() @@ -810,7 +825,7 @@ mod tests { let local_module_url = Url::from_file_path(&local_module).unwrap(); let local_module_str = local_module.to_string_lossy(); - install( + create_install_shim( Flags::default(), InstallFlags { module_url: local_module_str.to_string(), @@ -838,7 +853,7 @@ mod tests { let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap(); - install( + create_install_shim( Flags::default(), InstallFlags { module_url: "http://localhost:4545/echo_server.ts".to_string(), @@ -857,7 +872,7 @@ mod tests { assert!(file_path.exists()); // No force. Install failed. - let no_force_result = install( + let no_force_result = create_install_shim( Flags::default(), InstallFlags { module_url: "http://localhost:4545/cat.ts".to_string(), // using a different URL @@ -877,7 +892,7 @@ mod tests { assert!(file_content.contains("echo_server.ts")); // Force. Install success. - let force_result = install( + let force_result = create_install_shim( Flags::default(), InstallFlags { module_url: "http://localhost:4545/cat.ts".to_string(), // using a different URL @@ -903,7 +918,7 @@ mod tests { let result = config_file.write_all(config.as_bytes()); assert!(result.is_ok()); - let result = install( + let result = create_install_shim( Flags { config_flag: ConfigFlag::Path( config_file_path.to_string_lossy().to_string(), @@ -936,7 +951,7 @@ mod tests { let bin_dir = temp_dir.path().join("bin"); std::fs::create_dir(&bin_dir).unwrap(); - install( + create_install_shim( Flags::default(), InstallFlags { module_url: "http://localhost:4545/echo_server.ts".to_string(), @@ -976,7 +991,7 @@ mod tests { let local_module_str = local_module.to_string_lossy(); std::fs::write(&local_module, "// Some JavaScript I guess").unwrap(); - install( + create_install_shim( Flags::default(), InstallFlags { module_url: local_module_str.to_string(), @@ -1016,7 +1031,7 @@ mod tests { let result = import_map_file.write_all(import_map.as_bytes()); assert!(result.is_ok()); - let result = install( + let result = create_install_shim( Flags { import_map_path: Some(import_map_path.to_string_lossy().to_string()), ..Flags::default() @@ -1062,7 +1077,7 @@ mod tests { Url::from_file_path(module_path).unwrap().to_string(); assert!(file_module_string.starts_with("file:///")); - let result = install( + let result = create_install_shim( Flags::default(), InstallFlags { module_url: file_module_string.to_string(), diff --git a/cli/tools/mod.rs b/cli/tools/mod.rs index b992a2e9e..595fdd760 100644 --- a/cli/tools/mod.rs +++ b/cli/tools/mod.rs @@ -1,6 +1,7 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. pub mod bench; +pub mod bundle; pub mod check; pub mod coverage; pub mod doc; @@ -10,6 +11,7 @@ pub mod init; pub mod installer; pub mod lint; pub mod repl; +pub mod run; pub mod standalone; pub mod task; pub mod test; diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs index 502105139..cde4efa28 100644 --- a/cli/tools/repl/mod.rs +++ b/cli/tools/repl/mod.rs @@ -1,11 +1,13 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +use crate::args::Flags; use crate::args::ReplFlags; use crate::colors; use crate::proc_state::ProcState; +use crate::worker::create_main_worker; use deno_core::error::AnyError; +use deno_core::resolve_url_or_path; use deno_runtime::permissions::Permissions; -use deno_runtime::worker::MainWorker; use rustyline::error::ReadlineError; mod cdp; @@ -76,11 +78,17 @@ async fn read_eval_file( Ok((*file.source).to_string()) } -pub async fn run( - ps: &ProcState, - worker: MainWorker, - repl_flags: ReplFlags, -) -> Result<i32, AnyError> { +pub async fn run(flags: Flags, repl_flags: ReplFlags) -> Result<i32, AnyError> { + let main_module = resolve_url_or_path("./$deno$repl.ts").unwrap(); + let ps = ProcState::build(flags).await?; + let mut worker = create_main_worker( + &ps, + main_module.clone(), + Permissions::from_options(&ps.options.permissions_options())?, + ) + .await?; + worker.setup_repl().await?; + let worker = worker.into_main_worker(); let mut repl_session = ReplSession::initialize(worker).await?; let mut rustyline_channel = rustyline_channel(); let mut should_exit_on_interrupt = false; @@ -95,7 +103,7 @@ pub async fn run( if let Some(eval_files) = repl_flags.eval_files { for eval_file in eval_files { - match read_eval_file(ps, &eval_file).await { + match read_eval_file(&ps, &eval_file).await { Ok(eval_source) => { let output = repl_session .evaluate_line_and_get_output(&eval_source) diff --git a/cli/tools/run.rs b/cli/tools/run.rs new file mode 100644 index 000000000..d714c55d3 --- /dev/null +++ b/cli/tools/run.rs @@ -0,0 +1,169 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use std::io::Read; +use std::path::PathBuf; +use std::sync::Arc; + +use deno_ast::MediaType; +use deno_ast::ModuleSpecifier; +use deno_core::error::AnyError; +use deno_core::resolve_url_or_path; +use deno_runtime::permissions::Permissions; + +use crate::args::EvalFlags; +use crate::args::Flags; +use crate::args::RunFlags; +use crate::file_fetcher::File; +use crate::npm::NpmPackageReference; +use crate::proc_state::ProcState; +use crate::util; +use crate::worker::create_main_worker; + +pub async fn run_script( + flags: Flags, + run_flags: RunFlags, +) -> Result<i32, AnyError> { + if !flags.has_permission() && flags.has_permission_in_argv() { + log::warn!( + "{}", + crate::colors::yellow( + r#"Permission flags have likely been incorrectly set after the script argument. +To grant permissions, set them before the script argument. For example: + deno run --allow-read=. main.js"# + ) + ); + } + + if flags.watch.is_some() { + return run_with_watch(flags, run_flags.script).await; + } + + // TODO(bartlomieju): actually I think it will also fail if there's an import + // map specified and bare specifier is used on the command line - this should + // probably call `ProcState::resolve` instead + let ps = ProcState::build(flags).await?; + + // Run a background task that checks for available upgrades. If an earlier + // run of this background task found a new version of Deno. + super::upgrade::check_for_upgrades(ps.dir.upgrade_check_file_path()); + + let main_module = if NpmPackageReference::from_str(&run_flags.script).is_ok() + { + ModuleSpecifier::parse(&run_flags.script)? + } else { + resolve_url_or_path(&run_flags.script)? + }; + let permissions = + Permissions::from_options(&ps.options.permissions_options())?; + let mut worker = + create_main_worker(&ps, main_module.clone(), permissions).await?; + + let exit_code = worker.run().await?; + Ok(exit_code) +} + +pub async fn run_from_stdin(flags: Flags) -> Result<i32, AnyError> { + let ps = ProcState::build(flags).await?; + let main_module = resolve_url_or_path("./$deno$stdin.ts").unwrap(); + let mut worker = create_main_worker( + &ps.clone(), + main_module.clone(), + Permissions::from_options(&ps.options.permissions_options())?, + ) + .await?; + + let mut source = Vec::new(); + std::io::stdin().read_to_end(&mut source)?; + // Create a dummy source file. + let source_file = File { + local: main_module.clone().to_file_path().unwrap(), + maybe_types: None, + media_type: MediaType::TypeScript, + source: String::from_utf8(source)?.into(), + specifier: main_module.clone(), + maybe_headers: None, + }; + // Save our fake file into file fetcher cache + // to allow module access by TS compiler + ps.file_fetcher.insert_cached(source_file); + + let exit_code = worker.run().await?; + Ok(exit_code) +} + +// TODO(bartlomieju): this function is not handling `exit_code` set by the runtime +// code properly. +async fn run_with_watch(flags: Flags, script: String) -> Result<i32, AnyError> { + let flags = Arc::new(flags); + let main_module = resolve_url_or_path(&script)?; + let (sender, receiver) = tokio::sync::mpsc::unbounded_channel(); + + let operation = |(sender, main_module): ( + tokio::sync::mpsc::UnboundedSender<Vec<PathBuf>>, + ModuleSpecifier, + )| { + let flags = flags.clone(); + Ok(async move { + let ps = + ProcState::build_for_file_watcher((*flags).clone(), sender.clone()) + .await?; + let permissions = + Permissions::from_options(&ps.options.permissions_options())?; + let worker = + create_main_worker(&ps, main_module.clone(), permissions).await?; + worker.run_for_watcher().await?; + + Ok(()) + }) + }; + + util::file_watcher::watch_func2( + receiver, + operation, + (sender, main_module), + util::file_watcher::PrintConfig { + job_name: "Process".to_string(), + clear_screen: !flags.no_clear_screen, + }, + ) + .await?; + + Ok(0) +} + +pub async fn eval_command( + flags: Flags, + eval_flags: EvalFlags, +) -> Result<i32, AnyError> { + // 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))?; + let ps = ProcState::build(flags).await?; + let permissions = + Permissions::from_options(&ps.options.permissions_options())?; + let mut worker = + create_main_worker(&ps, main_module.clone(), permissions).await?; + // Create a dummy source file. + let source_code = if eval_flags.print { + format!("console.log({})", eval_flags.code) + } else { + eval_flags.code + } + .into_bytes(); + + let file = File { + local: main_module.clone().to_file_path().unwrap(), + maybe_types: None, + media_type: MediaType::Unknown, + source: String::from_utf8(source_code)?.into(), + specifier: main_module.clone(), + maybe_headers: None, + }; + + // Save our fake file into file fetcher cache + // to allow module access by TS compiler. + ps.file_fetcher.insert_cached(file); + let exit_code = worker.run().await?; + Ok(exit_code) +} diff --git a/cli/tools/standalone.rs b/cli/tools/standalone.rs index 5e9867b12..f7a258a73 100644 --- a/cli/tools/standalone.rs +++ b/cli/tools/standalone.rs @@ -1,7 +1,10 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. use crate::args::CompileFlags; +use crate::args::Flags; use crate::cache::DenoDir; +use crate::graph_util::create_graph_and_maybe_check; +use crate::graph_util::error_for_any_npm_specifier; use crate::standalone::Metadata; use crate::standalone::MAGIC_TRAILER; use crate::util::path::path_has_trailing_slash; @@ -14,6 +17,7 @@ use deno_core::resolve_url_or_path; use deno_core::serde_json; use deno_core::url::Url; use deno_graph::ModuleSpecifier; +use deno_runtime::colors; use deno_runtime::deno_fetch::reqwest::Client; use deno_runtime::permissions::Permissions; use std::env; @@ -25,10 +29,59 @@ use std::io::SeekFrom; use std::io::Write; use std::path::Path; use std::path::PathBuf; +use std::sync::Arc; use super::installer::infer_name_from_url; -pub async fn get_base_binary( +pub async fn compile( + flags: Flags, + compile_flags: CompileFlags, +) -> Result<(), AnyError> { + let ps = ProcState::build(flags.clone()).await?; + let module_specifier = resolve_url_or_path(&compile_flags.source_file)?; + let deno_dir = &ps.dir; + + let output_path = resolve_compile_executable_output_path(&compile_flags)?; + + let graph = Arc::try_unwrap( + create_graph_and_maybe_check(module_specifier.clone(), &ps).await?, + ) + .unwrap(); + + // at the moment, we don't support npm specifiers in deno_compile, so show an error + error_for_any_npm_specifier(&graph)?; + + graph.valid()?; + + let parser = ps.parsed_source_cache.as_capturing_parser(); + let eszip = eszip::EszipV2::from_graph(graph, &parser, Default::default())?; + + log::info!( + "{} {}", + colors::green("Compile"), + module_specifier.to_string() + ); + + // Select base binary based on target + let original_binary = + get_base_binary(deno_dir, compile_flags.target.clone()).await?; + + let final_bin = create_standalone_binary( + original_binary, + eszip, + module_specifier.clone(), + &compile_flags, + ps, + ) + .await?; + + log::info!("{} {}", colors::green("Emit"), output_path.display()); + + write_standalone_binary(output_path, final_bin).await?; + Ok(()) +} + +async fn get_base_binary( deno_dir: &DenoDir, target: Option<String>, ) -> Result<Vec<u8>, AnyError> { @@ -90,7 +143,7 @@ async fn download_base_binary( /// This functions creates a standalone deno binary by appending a bundle /// and magic trailer to the currently executing binary. -pub async fn create_standalone_binary( +async fn create_standalone_binary( mut original_bin: Vec<u8>, eszip: eszip::EszipV2, entrypoint: ModuleSpecifier, @@ -159,7 +212,7 @@ pub async fn create_standalone_binary( /// This function writes out a final binary to specified path. If output path /// is not already standalone binary it will return error instead. -pub async fn write_standalone_binary( +async fn write_standalone_binary( output_path: PathBuf, final_bin: Vec<u8>, ) -> Result<(), AnyError> { @@ -228,7 +281,7 @@ pub async fn write_standalone_binary( Ok(()) } -pub fn resolve_compile_executable_output_path( +fn resolve_compile_executable_output_path( compile_flags: &CompileFlags, ) -> Result<PathBuf, AnyError> { let module_specifier = resolve_url_or_path(&compile_flags.source_file)?; |