diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2020-02-18 10:08:18 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-18 10:08:18 -0500 |
commit | 302debebb3a3b765b3d13f3244eb4625d7d610c2 (patch) | |
tree | d54b4d01f391f7396e8f1c92dc2e05c82acc2a8c | |
parent | 5a3292047c42b8a65d164f127fc57e57046fadf7 (diff) |
refactor: cleanup cli/lib.rs (#4006)
* rename methods on Worker related to module loading
* reorganize cli/lib.rs
* remove cli/progress.rs and cli/shell.rs
-rw-r--r-- | cli/colors.rs | 8 | ||||
-rw-r--r-- | cli/compilers/ts.rs | 13 | ||||
-rw-r--r-- | cli/file_fetcher.rs | 21 | ||||
-rw-r--r-- | cli/global_state.rs | 17 | ||||
-rw-r--r-- | cli/lib.rs | 367 | ||||
-rw-r--r-- | cli/ops/worker_host.rs | 4 | ||||
-rw-r--r-- | cli/progress.rs | 168 | ||||
-rw-r--r-- | cli/shell.rs | 285 | ||||
-rw-r--r-- | cli/worker.rs | 56 | ||||
-rw-r--r-- | core/es_isolate.rs | 9 | ||||
-rw-r--r-- | core/modules.rs | 21 |
11 files changed, 229 insertions, 740 deletions
diff --git a/cli/colors.rs b/cli/colors.rs index f792e2b33..0bf048943 100644 --- a/cli/colors.rs +++ b/cli/colors.rs @@ -78,6 +78,14 @@ pub fn red(s: String) -> impl fmt::Display { style.paint(s) } +pub fn green(s: String) -> impl fmt::Display { + let mut style = Style::new(); + if use_color() { + style = style.fg(Fixed(10)).bold(); + } + style.paint(s) +} + pub fn bold(s: String) -> impl fmt::Display { let mut style = Style::new(); if use_color() { diff --git a/cli/compilers/ts.rs b/cli/compilers/ts.rs index a6ed8237d..9bfd93eeb 100644 --- a/cli/compilers/ts.rs +++ b/cli/compilers/ts.rs @@ -1,5 +1,6 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::compiler_worker::CompilerWorker; +use crate::colors; use crate::compilers::CompilationResultFuture; use crate::compilers::CompiledModule; use crate::diagnostics::Diagnostic; @@ -372,18 +373,18 @@ impl TsCompiler { let ts_compiler = self.clone(); - let compiling_job = global_state - .progress - .add("Compile", &module_url.to_string()); + eprintln!( + "{} {}", + colors::green("Compile".to_string()), + module_url.to_string() + ); let msg = execute_in_thread(global_state.clone(), req_msg).await?; let json_str = std::str::from_utf8(&msg).unwrap(); if let Some(diagnostics) = Diagnostic::from_emit_result(json_str) { return Err(ErrBox::from(diagnostics)); } - let compiled_module = ts_compiler.get_compiled_module(&source_file_.url)?; - drop(compiling_job); - Ok(compiled_module) + ts_compiler.get_compiled_module(&source_file_.url) } /// Get associated `CompiledFileMetadata` for given module if it exists. diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index e69756b20..75774ed5b 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -1,4 +1,5 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +use crate::colors; use crate::deno_error::DenoError; use crate::deno_error::ErrorKind; use crate::deno_error::GetErrorKind; @@ -8,7 +9,6 @@ use crate::http_util::create_http_client; use crate::http_util::FetchOnceResult; use crate::http_util::ResultPayload; use crate::msg; -use crate::progress::Progress; use deno_core::ErrBox; use deno_core::ModuleSpecifier; use futures::future::Either; @@ -95,7 +95,6 @@ const SUPPORTED_URL_SCHEMES: [&str; 3] = ["http", "https", "file"]; #[derive(Clone)] pub struct SourceFileFetcher { deps_cache: DiskCache, - progress: Progress, source_file_cache: SourceFileCache, cache_blacklist: Vec<String>, use_disk_cache: bool, @@ -107,7 +106,6 @@ pub struct SourceFileFetcher { impl SourceFileFetcher { pub fn new( deps_cache: DiskCache, - progress: Progress, use_disk_cache: bool, cache_blacklist: Vec<String>, no_remote: bool, @@ -116,7 +114,6 @@ impl SourceFileFetcher { ) -> Result<Self, ErrBox> { let file_fetcher = Self { deps_cache, - progress, source_file_cache: SourceFileCache::default(), cache_blacklist, use_disk_cache, @@ -441,7 +438,11 @@ impl SourceFileFetcher { .boxed(); } - let download_job = self.progress.add("Download", &module_url.to_string()); + eprintln!( + "{} {}", + colors::green("Download".to_string()), + module_url.to_string() + ); let dir = self.clone(); let module_url = module_url.clone(); let headers = self.get_source_code_headers(&module_url); @@ -455,9 +456,6 @@ impl SourceFileFetcher { let source_file = dir.fetch_cached_remote_source(&module_url)?.unwrap(); - // Explicit drop to keep reference alive until future completes. - drop(download_job); - Ok(source_file) } FetchOnceResult::Redirect(new_module_url) => { @@ -475,9 +473,6 @@ impl SourceFileFetcher { )); } - // Explicit drop to keep reference alive until future completes. - drop(download_job); - // Recurse dir .fetch_remote_source_async( @@ -542,9 +537,6 @@ impl SourceFileFetcher { types_url, }; - // Explicit drop to keep reference alive until future completes. - drop(download_job); - Ok(source_file) } } @@ -858,7 +850,6 @@ mod tests { fn setup_file_fetcher(dir_path: &Path) -> SourceFileFetcher { SourceFileFetcher::new( DiskCache::new(&dir_path.to_path_buf().join("deps")), - Progress::new(), true, vec![], false, diff --git a/cli/global_state.rs b/cli/global_state.rs index a11900218..1d7a3a40f 100644 --- a/cli/global_state.rs +++ b/cli/global_state.rs @@ -12,8 +12,6 @@ use crate::flags; use crate::lockfile::Lockfile; use crate::msg; use crate::permissions::DenoPermissions; -use crate::progress::Progress; -use crate::shell::Shell; use deno_core::ErrBox; use deno_core::ModuleSpecifier; use std; @@ -39,7 +37,6 @@ pub struct GlobalStateInner { /// Permissions parsed from `flags`. pub permissions: DenoPermissions, pub dir: deno_dir::DenoDir, - pub progress: Progress, pub file_fetcher: SourceFileFetcher, pub js_compiler: JsCompiler, pub json_compiler: JsonCompiler, @@ -62,21 +59,8 @@ impl GlobalState { let custom_root = env::var("DENO_DIR").map(String::into).ok(); let dir = deno_dir::DenoDir::new(custom_root)?; - // TODO(ry) Shell is a useless abstraction and should be removed at - // some point. - let shell = Arc::new(Mutex::new(Shell::new())); - - let progress = Progress::new(); - progress.set_callback(move |_done, _completed, _total, status, msg| { - if !status.is_empty() { - let mut s = shell.lock().unwrap(); - s.status(status, msg).expect("shell problem"); - } - }); - let file_fetcher = SourceFileFetcher::new( dir.deps_cache.clone(), - progress.clone(), !flags.reload, flags.cache_blacklist.clone(), flags.no_remote, @@ -102,7 +86,6 @@ impl GlobalState { dir, permissions: DenoPermissions::from_flags(&flags), flags, - progress, file_fetcher, ts_compiler, js_compiler: JsCompiler {}, diff --git a/cli/lib.rs b/cli/lib.rs index 81fd8810e..7737d1741 100644 --- a/cli/lib.rs +++ b/cli/lib.rs @@ -42,10 +42,8 @@ mod metrics; pub mod msg; pub mod ops; pub mod permissions; -mod progress; mod repl; pub mod resolve_addr; -mod shell; pub mod signal; pub mod source_maps; mod startup_data; @@ -58,8 +56,6 @@ mod web_worker; pub mod worker; use crate::compilers::TargetLib; -use crate::deno_error::js_check; -use crate::deno_error::{print_err_and_exit, print_msg_and_exit}; use crate::fs as deno_fs; use crate::global_state::GlobalState; use crate::ops::io::get_stdio; @@ -70,12 +66,15 @@ use deno_core::ErrBox; use deno_core::ModuleSpecifier; use flags::DenoFlags; use flags::DenoSubcommand; +use futures::future::FutureExt; use log::Level; use log::Metadata; use log::Record; use std::env; use std::fs as std_fs; +use std::io::Write; use std::path::PathBuf; +use url::Url; static LOGGER: Logger = Logger; @@ -101,42 +100,27 @@ impl log::Log for Logger { fn flush(&self) {} } -fn create_global_state(flags: DenoFlags) -> GlobalState { - GlobalState::new(flags) - .map_err(deno_error::print_err_and_exit) - .unwrap() -} - fn create_main_worker( global_state: GlobalState, main_module: ModuleSpecifier, -) -> MainWorker { - let state = State::new(global_state, None, main_module) - .map_err(deno_error::print_err_and_exit) - .unwrap(); +) -> Result<MainWorker, ErrBox> { + let state = State::new(global_state, None, main_module)?; - let state_ = state.clone(); { - let mut state = state_.borrow_mut(); + let mut s = state.borrow_mut(); let (stdin, stdout, stderr) = get_stdio(); - state.resource_table.add("stdin", Box::new(stdin)); - state.resource_table.add("stdout", Box::new(stdout)); - state.resource_table.add("stderr", Box::new(stderr)); + s.resource_table.add("stdin", Box::new(stdin)); + s.resource_table.add("stdout", Box::new(stdout)); + s.resource_table.add("stderr", Box::new(stderr)); } - MainWorker::new("main".to_string(), startup_data::deno_isolate_init(), state) -} - -fn types_command() { - let types = format!( - "{}\n{}\n{}", - crate::js::DENO_NS_LIB, - crate::js::SHARED_GLOBALS_LIB, - crate::js::WINDOW_LIB + let mut worker = MainWorker::new( + "main".to_string(), + startup_data::deno_isolate_init(), + state, ); - use std::io::Write; - let _r = std::io::stdout().write_all(types.as_bytes()); - // TODO(ry) Only ignore SIGPIPE. Currently ignoring all errors. + worker.execute("bootstrapMainRuntime()")?; + Ok(worker) } fn print_cache_info(state: &GlobalState) { @@ -157,21 +141,19 @@ fn print_cache_info(state: &GlobalState) { ); } +// TODO(bartlomieju): this function de facto repeats +// whole compilation stack. Can this be done better somehow? async fn print_file_info( worker: &MainWorker, module_specifier: ModuleSpecifier, -) { +) -> Result<(), ErrBox> { let global_state = worker.state.borrow().global_state.clone(); - let maybe_source_file = global_state + let out = global_state .file_fetcher .fetch_source_file_async(&module_specifier, None) - .await; - if let Err(err) = maybe_source_file { - println!("{}", err); - return; - } - let out = maybe_source_file.unwrap(); + .await?; + println!( "{} {}", colors::bold("local:".to_string()), @@ -185,15 +167,11 @@ async fn print_file_info( ); let module_specifier_ = module_specifier.clone(); - let maybe_compiled = global_state + global_state .clone() .fetch_compiled_module(module_specifier_, None, TargetLib::Main) - .await; - if let Err(e) = maybe_compiled { - debug!("compiler error exiting!"); - eprintln!("\n{}", e.to_string()); - std::process::exit(1); - } + .await?; + if out.media_type == msg::MediaType::TypeScript || (out.media_type == msg::MediaType::JavaScript && global_state.ts_compiler.compile_js) @@ -235,29 +213,25 @@ async fn print_file_info( colors::bold("deps:".to_string()), ); } + + Ok(()) } -async fn info_command(flags: DenoFlags, file: Option<String>) { - let global_state = create_global_state(flags); +async fn info_command( + flags: DenoFlags, + file: Option<String>, +) -> Result<(), ErrBox> { + let global_state = GlobalState::new(flags)?; // If it was just "deno info" print location of caches and exit if file.is_none() { - return print_cache_info(&global_state); + print_cache_info(&global_state); + return Ok(()); } - // Setup runtime. - let main_module = ModuleSpecifier::resolve_url_or_path(&file.unwrap()) - .expect("Bad specifier"); - let mut worker = create_main_worker(global_state, main_module.clone()); - // TODO(bartlomieju): not needed? - js_check(worker.execute("bootstrapMainRuntime()")); - - let main_result = worker.execute_mod_async(&main_module, None, true).await; - if let Err(e) = main_result { - print_err_and_exit(e); - } - print_file_info(&worker, main_module.clone()).await; - let result = (&mut *worker).await; - js_check(result); + let main_module = ModuleSpecifier::resolve_url_or_path(&file.unwrap())?; + let mut worker = create_main_worker(global_state, main_module.clone())?; + worker.preload_module(&main_module).await?; + print_file_info(&worker, main_module.clone()).await } async fn install_command( @@ -267,199 +241,149 @@ async fn install_command( module_url: String, args: Vec<String>, force: bool, -) { - // Firstly fetch and compile module, this - // ensures the module exists. +) -> Result<(), ErrBox> { + // Firstly fetch and compile module, this step ensures that module exists. let mut fetch_flags = flags.clone(); fetch_flags.reload = true; - fetch_command(fetch_flags, vec![module_url.to_string()]).await; - - let install_result = - installer::install(flags, dir, &exe_name, &module_url, args, force); - if let Err(e) = install_result { - print_msg_and_exit(&e.to_string()); - } + let global_state = GlobalState::new(fetch_flags)?; + let main_module = ModuleSpecifier::resolve_url_or_path(&module_url)?; + let mut worker = create_main_worker(global_state, main_module.clone())?; + worker.preload_module(&main_module).await?; + installer::install(flags, dir, &exe_name, &module_url, args, force) + .map_err(ErrBox::from) } -async fn fetch_command(flags: DenoFlags, files: Vec<String>) { +async fn fetch_command( + flags: DenoFlags, + files: Vec<String>, +) -> Result<(), ErrBox> { let main_module = ModuleSpecifier::resolve_url_or_path("./__$deno$fetch.ts").unwrap(); - let global_state = create_global_state(flags); + let global_state = GlobalState::new(flags)?; let mut worker = - create_main_worker(global_state.clone(), main_module.clone()); - - // TODO(bartlomieju): not needed? - js_check(worker.execute("bootstrapMainRuntime()")); + create_main_worker(global_state.clone(), main_module.clone())?; for file in files { - let specifier = ModuleSpecifier::resolve_url_or_path(&file).unwrap(); - let result = worker.execute_mod_async(&specifier, None, true).await; - js_check(result); + let specifier = ModuleSpecifier::resolve_url_or_path(&file)?; + worker.preload_module(&specifier).await.map(|_| ())?; } if global_state.flags.lock_write { if let Some(ref lockfile) = global_state.lockfile { let g = lockfile.lock().unwrap(); - if let Err(e) = g.write() { - print_err_and_exit(ErrBox::from(e)); - } + g.write()?; } else { eprintln!("--lock flag must be specified when using --lock-write"); std::process::exit(11); } } + + Ok(()) } -async fn eval_command(flags: DenoFlags, code: String) { +async fn eval_command(flags: DenoFlags, code: String) -> Result<(), ErrBox> { // Force TypeScript compile. let main_module = ModuleSpecifier::resolve_url_or_path("./__$deno$eval.ts").unwrap(); - let global_state = create_global_state(flags); - let mut worker = create_main_worker(global_state, main_module.clone()); - - js_check(worker.execute("bootstrapMainRuntime()")); + let global_state = GlobalState::new(flags)?; + let mut worker = create_main_worker(global_state, main_module.clone())?; debug!("main_module {}", &main_module); - - let exec_result = worker - .execute_mod_async(&main_module, Some(code), false) - .await; - if let Err(e) = exec_result { - print_err_and_exit(e); - } - js_check(worker.execute("window.dispatchEvent(new Event('load'))")); - let result = (&mut *worker).await; - js_check(result); - js_check(worker.execute("window.dispatchEvent(new Event('unload'))")); + worker.execute_module_from_code(&main_module, code).await?; + worker.execute("window.dispatchEvent(new Event('load'))")?; + (&mut *worker).await?; + worker.execute("window.dispatchEvent(new Event('unload'))")?; + Ok(()) } async fn bundle_command( flags: DenoFlags, source_file: String, out_file: Option<PathBuf>, -) { +) -> Result<(), ErrBox> { + let module_name = ModuleSpecifier::resolve_url_or_path(&source_file)?; + let global_state = GlobalState::new(flags)?; debug!(">>>>> bundle_async START"); - let source_file_specifier = - ModuleSpecifier::resolve_url_or_path(&source_file).expect("Bad specifier"); - let global_state = create_global_state(flags); - let mut worker = - create_main_worker(global_state.clone(), source_file_specifier.clone()); - - // NOTE: we need to poll `worker` otherwise TS compiler worker won't run properly - let result = (&mut *worker).await; - js_check(result); let bundle_result = global_state .ts_compiler - .bundle_async( - global_state.clone(), - source_file_specifier.to_string(), - out_file, - ) + .bundle_async(global_state.clone(), module_name.to_string(), out_file) .await; - if let Err(err) = bundle_result { - debug!("diagnostics returned, exiting!"); - eprintln!(""); - print_err_and_exit(err); - } debug!(">>>>> bundle_async END"); + bundle_result } -async fn run_repl(flags: DenoFlags) { +async fn run_repl(flags: DenoFlags) -> Result<(), ErrBox> { let main_module = ModuleSpecifier::resolve_url_or_path("./__$deno$repl.ts").unwrap(); - let global_state = create_global_state(flags); - let mut worker = create_main_worker(global_state, main_module); - js_check(worker.execute("bootstrapMainRuntime()")); + let global_state = GlobalState::new(flags)?; + let mut worker = create_main_worker(global_state, main_module)?; loop { - let result = (&mut *worker).await; - if let Err(err) = result { - eprintln!("{}", err.to_string()); - } + (&mut *worker).await?; } } -async fn run_command(flags: DenoFlags, script: String) { - let global_state = create_global_state(flags.clone()); - let result = run_script(global_state.clone(), script).await; - if let Err(err) = result { - print_err_and_exit(err); - } +async fn run_command(flags: DenoFlags, script: String) -> Result<(), ErrBox> { + let global_state = GlobalState::new(flags.clone())?; + let main_module = ModuleSpecifier::resolve_url_or_path(&script).unwrap(); + let mut worker = + create_main_worker(global_state.clone(), main_module.clone())?; + debug!("main_module {}", main_module); + worker.execute_module(&main_module).await?; + worker.execute("window.dispatchEvent(new Event('load'))")?; + (&mut *worker).await?; + worker.execute("window.dispatchEvent(new Event('unload'))")?; if global_state.flags.lock_write { if let Some(ref lockfile) = global_state.lockfile { let g = lockfile.lock().unwrap(); - if let Err(e) = g.write() { - print_err_and_exit(ErrBox::from(e)); - } + g.write()?; } else { eprintln!("--lock flag must be specified when using --lock-write"); std::process::exit(11); } } -} - -async fn run_script( - global_state: GlobalState, - script: String, -) -> Result<(), ErrBox> { - let main_module = ModuleSpecifier::resolve_url_or_path(&script).unwrap(); - let mut worker = - create_main_worker(global_state.clone(), main_module.clone()); - // Setup runtime. - worker.execute("bootstrapMainRuntime()")?; - debug!("main_module {}", main_module); - worker.execute_mod_async(&main_module, None, false).await?; - worker.execute("window.dispatchEvent(new Event('load'))")?; - (&mut *worker).await?; - worker.execute("window.dispatchEvent(new Event('unload'))")?; Ok(()) } -async fn fmt_command(files: Vec<String>, check: bool) { - if let Err(err) = fmt::format_files(files, check) { - print_err_and_exit(err); - } -} - async fn test_command( flags: DenoFlags, include: Option<Vec<String>>, fail_fast: bool, _quiet: bool, allow_none: bool, -) { - let global_state = create_global_state(flags.clone()); +) -> Result<(), ErrBox> { + let global_state = GlobalState::new(flags.clone())?; let cwd = std::env::current_dir().expect("No current directory"); let include = include.unwrap_or_else(|| vec![".".to_string()]); - let res = test_runner::prepare_test_modules_urls(include, &cwd); + let test_modules = test_runner::prepare_test_modules_urls(include, &cwd)?; - let test_modules = match res { - Ok(modules) => modules, - Err(e) => return print_err_and_exit(e), - }; if test_modules.is_empty() { println!("No matching test modules found"); if !allow_none { std::process::exit(1); } - return; + return Ok(()); } let test_file = test_runner::render_test_file(test_modules, fail_fast); let test_file_path = cwd.join(".deno.test.ts"); + let test_file_url = + Url::from_file_path(&test_file_path).expect("Should be valid file url"); + let main_module = + ModuleSpecifier::resolve_url(&test_file_url.to_string()).unwrap(); + // First create worker with specified test file and only then write + // file to disk. Then test file will be executed and removed + // immediately after. That way even if compilation/tests fail test + // file can be cleaned up. + let mut worker = + create_main_worker(global_state.clone(), main_module.clone())?; deno_fs::write_file(&test_file_path, test_file.as_bytes(), 0o666) .expect("Can't write test file"); - - let mut flags = flags.clone(); - flags - .argv - .push(test_file_path.to_string_lossy().to_string()); - - let result = - run_script(global_state, test_file_path.to_string_lossy().to_string()) - .await; + let execute_result = worker.execute_module(&main_module).await; + // Remove temporary test file std_fs::remove_file(&test_file_path).expect("Failed to remove temp file"); - if let Err(err) = result { - print_err_and_exit(err); - } + execute_result?; + worker.execute("window.dispatchEvent(new Event('load'))")?; + (&mut *worker).await?; + worker.execute("window.dispatchEvent(new Event('unload'))") } pub fn main() { @@ -482,37 +406,58 @@ pub fn main() { }; log::set_max_level(log_level.to_level_filter()); - let fut = async move { - match flags.clone().subcommand { - DenoSubcommand::Bundle { - source_file, - out_file, - } => bundle_command(flags, source_file, out_file).await, - DenoSubcommand::Completions { buf } => { - print!("{}", std::str::from_utf8(&buf).unwrap()); - } - DenoSubcommand::Eval { code } => eval_command(flags, code).await, - DenoSubcommand::Fetch { files } => fetch_command(flags, files).await, - DenoSubcommand::Fmt { check, files } => fmt_command(files, check).await, - DenoSubcommand::Info { file } => info_command(flags, file).await, - DenoSubcommand::Install { - dir, - exe_name, - module_url, - args, - force, - } => install_command(flags, dir, exe_name, module_url, args, force).await, - DenoSubcommand::Repl => run_repl(flags).await, - DenoSubcommand::Run { script } => run_command(flags, script).await, - DenoSubcommand::Test { - quiet, - fail_fast, - include, - allow_none, - } => test_command(flags, include, fail_fast, quiet, allow_none).await, - DenoSubcommand::Types => types_command(), - _ => panic!("bad subcommand"), + let fut = match flags.clone().subcommand { + DenoSubcommand::Bundle { + source_file, + out_file, + } => bundle_command(flags, source_file, out_file).boxed_local(), + DenoSubcommand::Eval { code } => eval_command(flags, code).boxed_local(), + DenoSubcommand::Fetch { files } => { + fetch_command(flags, files).boxed_local() + } + DenoSubcommand::Fmt { check, files } => { + async move { fmt::format_files(files, check) }.boxed_local() + } + DenoSubcommand::Info { file } => info_command(flags, file).boxed_local(), + DenoSubcommand::Install { + dir, + exe_name, + module_url, + args, + force, + } => install_command(flags, dir, exe_name, module_url, args, force) + .boxed_local(), + DenoSubcommand::Repl => run_repl(flags).boxed_local(), + DenoSubcommand::Run { script } => run_command(flags, script).boxed_local(), + DenoSubcommand::Test { + quiet, + fail_fast, + include, + allow_none, + } => { + test_command(flags, include, fail_fast, quiet, allow_none).boxed_local() + } + DenoSubcommand::Completions { buf } => { + print!("{}", std::str::from_utf8(&buf).unwrap()); + return; } + DenoSubcommand::Types => { + let types = format!( + "{}\n{}\n{}", + crate::js::DENO_NS_LIB, + crate::js::SHARED_GLOBALS_LIB, + crate::js::WINDOW_LIB + ); + // TODO(ry) Only ignore SIGPIPE. Currently ignoring all errors. + let _r = std::io::stdout().write_all(types.as_bytes()); + return; + } + _ => unreachable!(), }; - tokio_util::run_basic(fut); + + let result = tokio_util::run_basic(fut); + if let Err(err) = result { + eprintln!("{}", err.to_string()); + std::process::exit(1); + } } diff --git a/cli/ops/worker_host.rs b/cli/ops/worker_host.rs index 4f6f996ee..81e8f76da 100644 --- a/cli/ops/worker_host.rs +++ b/cli/ops/worker_host.rs @@ -172,9 +172,7 @@ fn run_worker_thread( } else { // TODO(bartlomieju): add "type": "classic", ie. ability to load // script instead of module - let load_future = worker - .execute_mod_async(&specifier, None, false) - .boxed_local(); + let load_future = worker.execute_module(&specifier).boxed_local(); rt.block_on(load_future) }; diff --git a/cli/progress.rs b/cli/progress.rs deleted file mode 100644 index 3eb0d9136..000000000 --- a/cli/progress.rs +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -use std::sync::Arc; -use std::sync::Mutex; - -#[derive(Clone, Default)] -pub struct Progress(Arc<Mutex<Inner>>); - -impl Progress { - pub fn new() -> Self { - Progress::default() - } - - pub fn set_callback<F>(&self, f: F) - where - F: Fn(bool, usize, usize, &str, &str) + Send + Sync + 'static, - { - let mut s = self.0.lock().unwrap(); - assert!(s.callback.is_none()); - s.callback = Some(Arc::new(f)); - } - - /// Returns job counts: (complete, total) - pub fn progress(&self) -> (usize, usize) { - let s = self.0.lock().unwrap(); - s.progress() - } - - pub fn history(&self) -> Vec<String> { - let s = self.0.lock().unwrap(); - s.job_names.clone() - } - - pub fn add(&self, status: &str, name: &str) -> Job { - let mut s = self.0.lock().unwrap(); - let id = s.job_names.len(); - s.maybe_call_callback( - false, - s.complete, - s.job_names.len() + 1, - status, - name, - ); - s.job_names.push(name.to_string()); - Job { - id, - inner: self.0.clone(), - } - } - - pub fn done(&self) { - let s = self.0.lock().unwrap(); - s.maybe_call_callback(true, s.complete, s.job_names.len(), "", ""); - } -} - -type Callback = dyn Fn(bool, usize, usize, &str, &str) + Send + Sync; - -#[derive(Default)] -struct Inner { - job_names: Vec<String>, - complete: usize, - callback: Option<Arc<Callback>>, -} - -impl Inner { - pub fn maybe_call_callback( - &self, - done: bool, - complete: usize, - total: usize, - status: &str, - msg: &str, - ) { - if let Some(ref cb) = self.callback { - cb(done, complete, total, status, msg); - } - } - - /// Returns job counts: (complete, total) - pub fn progress(&self) -> (usize, usize) { - let total = self.job_names.len(); - (self.complete, total) - } -} - -pub struct Job { - inner: Arc<Mutex<Inner>>, - id: usize, -} - -impl Drop for Job { - fn drop(&mut self) { - let mut s = self.inner.lock().unwrap(); - s.complete += 1; - let name = &s.job_names[self.id]; - let (complete, total) = s.progress(); - s.maybe_call_callback(false, complete, total, "", name); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn progress() { - let p = Progress::new(); - assert_eq!(p.progress(), (0, 0)); - { - let _j1 = p.add("status", "hello"); - assert_eq!(p.progress(), (0, 1)); - } - assert_eq!(p.progress(), (1, 1)); - { - let _j2 = p.add("status", "hello"); - assert_eq!(p.progress(), (1, 2)); - } - assert_eq!(p.progress(), (2, 2)); - } - - #[test] - fn history() { - let p = Progress::new(); - let _a = p.add("status", "a"); - let _b = p.add("status", "b"); - assert_eq!(p.history(), vec!["a", "b"]); - } - - #[test] - fn callback() { - let callback_history: Arc<Mutex<Vec<(usize, usize, String)>>> = - Arc::new(Mutex::new(Vec::new())); - { - let p = Progress::new(); - let callback_history_ = callback_history.clone(); - - p.set_callback(move |_done, complete, total, _status, msg| { - // println!("callback: {}, {}, {}", complete, total, msg); - let mut h = callback_history_.lock().unwrap(); - h.push((complete, total, String::from(msg))); - }); - { - let _a = p.add("status", "a"); - let _b = p.add("status", "b"); - } - let _c = p.add("status", "c"); - } - - let h = callback_history.lock().unwrap(); - assert_eq!( - h.to_vec(), - vec![ - (0, 1, "a".to_string()), - (0, 2, "b".to_string()), - (1, 2, "b".to_string()), - (2, 2, "a".to_string()), - (2, 3, "c".to_string()), - (3, 3, "c".to_string()), - ] - ); - } - - #[test] - fn thread_safe() { - fn f<S: Send + Sync>(_: S) {} - f(Progress::new()); - } -} diff --git a/cli/shell.rs b/cli/shell.rs deleted file mode 100644 index 8e52f51fd..000000000 --- a/cli/shell.rs +++ /dev/null @@ -1,285 +0,0 @@ -// This file was forked from Cargo on 2019.05.29: -// https://github.com/rust-lang/cargo/blob/edd874/src/cargo/core/shell.rs -// Cargo is MIT licenced: -// https://github.com/rust-lang/cargo/blob/edd874/LICENSE-MIT - -use std::fmt; -use std::io::prelude::*; - -use atty; -use deno_core::ErrBox; -use termcolor::Color::Green; -use termcolor::{self, Color, ColorSpec, StandardStream, WriteColor}; - -/// The requested verbosity of output. -#[derive(Debug, Clone, Copy, PartialEq)] -#[allow(dead_code)] -pub enum Verbosity { - Verbose, - Normal, - Quiet, -} - -/// An abstraction around a `Write`able object that remembers preferences for output verbosity and -/// color. -pub struct Shell { - /// the `Write`able object, either with or without color support (represented by different enum - /// variants) - err: ShellOut, - /// How verbose messages should be - verbosity: Verbosity, - /// Flag that indicates the current line needs to be cleared before - /// printing. Used when a progress bar is currently displayed. - needs_clear: bool, -} - -impl fmt::Debug for Shell { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.err { - /* - ShellOut::Write(_) => f - .debug_struct("Shell") - .field("verbosity", &self.verbosity) - .finish(), - */ - ShellOut::Stream { color_choice, .. } => f - .debug_struct("Shell") - .field("verbosity", &self.verbosity) - .field("color_choice", &color_choice) - .finish(), - } - } -} - -/// A `Write`able object, either with or without color support -enum ShellOut { - /// A plain write object without color support - // TODO(ry) Disabling this type of output because it makes Shell - // not thread safe and thus not includable in State. - // But I think we will want this in the future. - //Write(Box<dyn Write>), - /// Color-enabled stdio, with information on whether color should be used - Stream { - stream: StandardStream, - tty: bool, - color_choice: ColorChoice, - }, -} - -/// Whether messages should use color output -#[derive(Debug, PartialEq, Clone, Copy)] -#[allow(dead_code)] -pub enum ColorChoice { - /// Force color output - Always, - /// Force disable color output - Never, - /// Intelligently guess whether to use color output - CargoAuto, -} - -impl Shell { - /// Creates a new shell (color choice and verbosity), defaulting to 'auto' color and verbose - /// output. - pub fn new() -> Shell { - Shell { - err: ShellOut::Stream { - stream: StandardStream::stderr( - ColorChoice::CargoAuto.to_termcolor_color_choice(), - ), - color_choice: ColorChoice::CargoAuto, - tty: atty::is(atty::Stream::Stderr), - }, - verbosity: Verbosity::Verbose, - needs_clear: false, - } - } - - /// Prints a message, where the status will have `color` color, and can be justified. The - /// messages follows without color. - fn print( - &mut self, - status: &dyn fmt::Display, - message: Option<&dyn fmt::Display>, - color: Color, - justified: bool, - ) -> Result<(), ErrBox> { - match self.verbosity { - Verbosity::Quiet => Ok(()), - _ => { - if self.needs_clear { - self.err_erase_line(); - } - self.err.print(status, message, color, justified) - } - } - } - - /// Erase from cursor to end of line. - pub fn err_erase_line(&mut self) { - if let ShellOut::Stream { tty: true, .. } = self.err { - imp::err_erase_line(self); - self.needs_clear = false; - } - } - - /// Shortcut to right-align and color green a status message. - pub fn status<T, U>(&mut self, status: T, message: U) -> Result<(), ErrBox> - where - T: fmt::Display, - U: fmt::Display, - { - self.print(&status, Some(&message), Green, false) - } -} - -impl Default for Shell { - fn default() -> Self { - Self::new() - } -} - -impl ShellOut { - /// Prints out a message with a status. The status comes first, and is bold plus the given - /// color. The status can be justified, in which case the max width that will right align is - /// 12 chars. - fn print( - &mut self, - status: &dyn fmt::Display, - message: Option<&dyn fmt::Display>, - color: Color, - justified: bool, - ) -> Result<(), ErrBox> { - match *self { - ShellOut::Stream { ref mut stream, .. } => { - stream.reset()?; - stream - .set_color(ColorSpec::new().set_bold(true).set_fg(Some(color)))?; - if justified { - write!(stream, "{:>12}", status)?; - } else { - write!(stream, "{}", status)?; - } - stream.reset()?; - match message { - Some(message) => writeln!(stream, " {}", message)?, - None => write!(stream, " ")?, - } - } - } - Ok(()) - } - - /// Gets this object as a `io::Write`. - fn as_write(&mut self) -> &mut dyn Write { - match *self { - ShellOut::Stream { ref mut stream, .. } => stream, - // ShellOut::Write(ref mut w) => w, - } - } -} - -impl ColorChoice { - /// Converts our color choice to termcolor's version. - fn to_termcolor_color_choice(self) -> termcolor::ColorChoice { - match self { - ColorChoice::Always => termcolor::ColorChoice::Always, - ColorChoice::Never => termcolor::ColorChoice::Never, - ColorChoice::CargoAuto => { - if atty::is(atty::Stream::Stderr) { - termcolor::ColorChoice::Auto - } else { - termcolor::ColorChoice::Never - } - } - } - } -} - -#[cfg(any(target_os = "linux", target_os = "macos"))] -mod imp { - use super::Shell; - - pub fn err_erase_line(shell: &mut Shell) { - // This is the "EL - Erase in Line" sequence. It clears from the cursor - // to the end of line. - // https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences - let _ = shell.err.as_write().write_all(b"\x1B[K"); - } -} - -#[cfg(all(unix, not(any(target_os = "linux", target_os = "macos"))))] -mod imp { - pub(super) use super::default_err_erase_line as err_erase_line; - - pub fn stderr_width() -> Option<usize> { - None - } -} - -#[cfg(windows)] -mod imp { - use std::{cmp, mem, ptr}; - use winapi::um::fileapi::*; - use winapi::um::handleapi::*; - use winapi::um::processenv::*; - use winapi::um::winbase::*; - use winapi::um::wincon::*; - use winapi::um::winnt::*; - - pub(super) use super::default_err_erase_line as err_erase_line; - - pub fn stderr_width() -> Option<usize> { - unsafe { - let stdout = GetStdHandle(STD_ERROR_HANDLE); - let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed(); - if GetConsoleScreenBufferInfo(stdout, &mut csbi) != 0 { - return Some((csbi.srWindow.Right - csbi.srWindow.Left) as usize); - } - - // On mintty/msys/cygwin based terminals, the above fails with - // INVALID_HANDLE_VALUE. Use an alternate method which works - // in that case as well. - let h = CreateFileA( - "CONOUT$\0".as_ptr() as *const CHAR, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - ptr::null_mut(), - OPEN_EXISTING, - 0, - ptr::null_mut(), - ); - if h == INVALID_HANDLE_VALUE { - return None; - } - - let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed(); - let rc = GetConsoleScreenBufferInfo(h, &mut csbi); - CloseHandle(h); - if rc != 0 { - let width = (csbi.srWindow.Right - csbi.srWindow.Left) as usize; - // Unfortunately cygwin/mintty does not set the size of the - // backing console to match the actual window size. This - // always reports a size of 80 or 120 (not sure what - // determines that). Use a conservative max of 60 which should - // work in most circumstances. ConEmu does some magic to - // resize the console correctly, but there's no reasonable way - // to detect which kind of terminal we are running in, or if - // GetConsoleScreenBufferInfo returns accurate information. - return Some(cmp::min(60, width)); - } - None - } - } -} - -#[cfg(any( - all(unix, not(any(target_os = "linux", target_os = "macos"))), - windows -))] -fn default_err_erase_line(shell: &mut Shell) { - if let Some(max_width) = imp::stderr_width() { - let blank = " ".repeat(max_width); - drop(write!(shell.err.as_write(), "{}\r", blank)); - } -} diff --git a/cli/worker.rs b/cli/worker.rs index fa04d5a8f..bfcc1afdc 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -5,6 +5,7 @@ use crate::state::State; use deno_core; use deno_core::Buf; use deno_core::ErrBox; +use deno_core::ModuleId; use deno_core::ModuleSpecifier; use deno_core::StartupData; use futures::channel::mpsc; @@ -137,20 +138,35 @@ impl Worker { self.isolate.execute(js_filename, js_source) } - /// Executes the provided JavaScript module. - pub async fn execute_mod_async( + /// Loads and instantiates specified JavaScript module. + pub async fn preload_module( + &mut self, + module_specifier: &ModuleSpecifier, + ) -> Result<ModuleId, ErrBox> { + self.isolate.load_module(module_specifier, None).await + } + + /// Loads, instantiates and executes specified JavaScript module. + pub async fn execute_module( &mut self, module_specifier: &ModuleSpecifier, - maybe_code: Option<String>, - is_prefetch: bool, ) -> Result<(), ErrBox> { - let specifier = module_specifier.to_string(); - let id = self.isolate.load_module(&specifier, maybe_code).await?; - self.state.borrow().global_state.progress.done(); - if !is_prefetch { - return self.isolate.mod_evaluate(id); - } - Ok(()) + let id = self.preload_module(module_specifier).await?; + self.isolate.mod_evaluate(id) + } + + /// Loads, instantiates and executes provided source code + /// as module. + pub async fn execute_module_from_code( + &mut self, + module_specifier: &ModuleSpecifier, + code: String, + ) -> Result<(), ErrBox> { + let id = self + .isolate + .load_module(module_specifier, Some(code)) + .await?; + self.isolate.mod_evaluate(id) } /// Returns a way to communicate with the Worker from other threads. @@ -256,9 +272,7 @@ mod tests { tokio_util::run_basic(async move { let mut worker = MainWorker::new("TEST".to_string(), StartupData::None, state); - let result = worker - .execute_mod_async(&module_specifier, None, false) - .await; + let result = worker.execute_module(&module_specifier).await; if let Err(err) = result { eprintln!("execute_mod err {:?}", err); } @@ -287,9 +301,7 @@ mod tests { tokio_util::run_basic(async move { let mut worker = MainWorker::new("TEST".to_string(), StartupData::None, state); - let result = worker - .execute_mod_async(&module_specifier, None, false) - .await; + let result = worker.execute_module(&module_specifier).await; if let Err(err) = result { eprintln!("execute_mod err {:?}", err); } @@ -329,9 +341,7 @@ mod tests { state.clone(), ); worker.execute("bootstrapMainRuntime()").unwrap(); - let result = worker - .execute_mod_async(&module_specifier, None, false) - .await; + let result = worker.execute_module(&module_specifier).await; if let Err(err) = result { eprintln!("execute_mod err {:?}", err); } @@ -363,8 +373,7 @@ mod tests { let mut worker = create_test_worker(); let module_specifier = ModuleSpecifier::resolve_url_or_path("does-not-exist").unwrap(); - let result = - block_on(worker.execute_mod_async(&module_specifier, None, false)); + let result = block_on(worker.execute_module(&module_specifier)); assert!(result.is_err()); }) } @@ -381,8 +390,7 @@ mod tests { .join("cli/tests/002_hello.ts"); let module_specifier = ModuleSpecifier::resolve_url_or_path(&p.to_string_lossy()).unwrap(); - let result = - block_on(worker.execute_mod_async(&module_specifier, None, false)); + let result = block_on(worker.execute_module(&module_specifier)); assert!(result.is_ok()); }) } diff --git a/core/es_isolate.rs b/core/es_isolate.rs index 197c548d7..152805f3f 100644 --- a/core/es_isolate.rs +++ b/core/es_isolate.rs @@ -553,11 +553,14 @@ impl EsIsolate { /// manually after load is finished. pub async fn load_module( &mut self, - specifier: &str, + specifier: &ModuleSpecifier, code: Option<String>, ) -> Result<ModuleId, ErrBox> { - let mut load = - RecursiveModuleLoad::main(specifier, code, self.loader.clone()); + let mut load = RecursiveModuleLoad::main( + &specifier.to_string(), + code, + self.loader.clone(), + ); while let Some(info_result) = load.next().await { let info = info_result?; diff --git a/core/modules.rs b/core/modules.rs index 29f6067cd..764aadb64 100644 --- a/core/modules.rs +++ b/core/modules.rs @@ -653,7 +653,8 @@ mod tests { let loader = MockLoader::new(); let loads = loader.loads.clone(); let mut isolate = EsIsolate::new(Rc::new(loader), StartupData::None, false); - let a_id_fut = isolate.load_module("/a.js", None); + let spec = ModuleSpecifier::resolve_url("file:///a.js").unwrap(); + let a_id_fut = isolate.load_module(&spec, None); let a_id = futures::executor::block_on(a_id_fut).expect("Failed to load"); js_check(isolate.mod_evaluate(a_id)); @@ -714,7 +715,8 @@ mod tests { let mut isolate = EsIsolate::new(Rc::new(loader), StartupData::None, false); let fut = async move { - let result = isolate.load_module("/circular1.js", None).await; + let spec = ModuleSpecifier::resolve_url("file:///circular1.js").unwrap(); + let result = isolate.load_module(&spec, None).await; assert!(result.is_ok()); let circular1_id = result.unwrap(); js_check(isolate.mod_evaluate(circular1_id)); @@ -784,7 +786,8 @@ mod tests { let mut isolate = EsIsolate::new(Rc::new(loader), StartupData::None, false); let fut = async move { - let result = isolate.load_module("/redirect1.js", None).await; + let spec = ModuleSpecifier::resolve_url("file:///redirect1.js").unwrap(); + let result = isolate.load_module(&spec, None).await; println!(">> result {:?}", result); assert!(result.is_ok()); let redirect1_id = result.unwrap(); @@ -844,8 +847,8 @@ mod tests { let loads = loader.loads.clone(); let mut isolate = EsIsolate::new(Rc::new(loader), StartupData::None, false); - let mut recursive_load = - isolate.load_module("/main.js", None).boxed_local(); + let spec = ModuleSpecifier::resolve_url("file:///main.js").unwrap(); + let mut recursive_load = isolate.load_module(&spec, None).boxed_local(); let result = recursive_load.poll_unpin(&mut cx); assert!(result.is_pending()); @@ -890,8 +893,8 @@ mod tests { let loader = MockLoader::new(); let mut isolate = EsIsolate::new(Rc::new(loader), StartupData::None, false); - let mut load_fut = - isolate.load_module("/bad_import.js", None).boxed_local(); + let spec = ModuleSpecifier::resolve_url("file:///bad_import.js").unwrap(); + let mut load_fut = isolate.load_module(&spec, None).boxed_local(); let result = load_fut.poll_unpin(&mut cx); if let Poll::Ready(Err(err)) = result { assert_eq!( @@ -921,8 +924,10 @@ mod tests { // In default resolution code should be empty. // Instead we explicitly pass in our own code. // The behavior should be very similar to /a.js. + let spec = + ModuleSpecifier::resolve_url("file:///main_with_code.js").unwrap(); let main_id_fut = isolate - .load_module("/main_with_code.js", Some(MAIN_WITH_CODE_SRC.to_owned())) + .load_module(&spec, Some(MAIN_WITH_CODE_SRC.to_owned())) .boxed_local(); let main_id = futures::executor::block_on(main_id_fut).expect("Failed to load"); |