use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; use deno_ast::ModuleSpecifier; use deno_core::error::AnyError; use deno_core::futures::task::LocalFutureObj; use deno_core::futures::FutureExt; use deno_core::located_script_name; use deno_core::serde_json::json; use deno_core::Extension; use deno_core::ModuleId; use deno_graph::source::ResolveResponse; use deno_runtime::colors; use deno_runtime::ops::worker_host::CreateWebWorkerCb; use deno_runtime::ops::worker_host::WorkerEventCb; use deno_runtime::permissions::Permissions; use deno_runtime::web_worker::WebWorker; use deno_runtime::web_worker::WebWorkerOptions; use deno_runtime::worker::MainWorker; use deno_runtime::worker::WorkerOptions; use deno_runtime::BootstrapOptions; use crate::args::DenoSubcommand; use crate::checksum; use crate::compat; use crate::errors; use crate::fmt_errors::format_js_error; use crate::module_loader::CliModuleLoader; use crate::node; use crate::npm::NpmPackageReference; use crate::ops; use crate::proc_state::ProcState; use crate::tools; use crate::tools::coverage::CoverageCollector; use crate::tools::test::TestMode; use crate::version; pub struct CliMainWorker { main_module: ModuleSpecifier, is_main_cjs: bool, worker: MainWorker, ps: ProcState, } impl CliMainWorker { pub fn into_main_worker(self) -> MainWorker { self.worker } pub async fn preload_main_module(&mut self) -> Result { self.worker.preload_main_module(&self.main_module).await } pub async fn setup_repl(&mut self) -> Result<(), AnyError> { if self.ps.options.compat() { self.worker.execute_side_module(&compat::GLOBAL_URL).await?; compat::add_global_require( &mut self.worker.js_runtime, self.main_module.as_str(), )?; self.worker.run_event_loop(false).await?; compat::setup_builtin_modules(&mut self.worker.js_runtime)?; } self.worker.run_event_loop(false).await?; Ok(()) } pub async fn run(&mut self) -> Result { let mut maybe_coverage_collector = self.maybe_setup_coverage_collector().await?; log::debug!("main_module {}", self.main_module); if self.ps.options.compat() { // TODO(bartlomieju): fix me assert_eq!(self.main_module.scheme(), "file"); // Set up Node globals self.worker.execute_side_module(&compat::GLOBAL_URL).await?; // And `module` module that we'll use for checking which // loader to use and potentially load CJS module with. // This allows to skip permission check for `--allow-net` // which would otherwise be requested by dynamically importing // this file. self.worker.execute_side_module(&compat::MODULE_URL).await?; let use_esm_loader = compat::check_if_should_use_esm_loader(&self.main_module)?; if use_esm_loader { // ES module execution in Node compatiblity mode self.worker.execute_main_module(&self.main_module).await?; } else { // CJS module execution in Node compatiblity mode compat::load_cjs_module( &mut self.worker.js_runtime, &self .main_module .to_file_path() .unwrap() .display() .to_string(), true, )?; } } else if self.is_main_cjs { self.initialize_main_module_for_node().await?; node::load_cjs_module_from_ext_node( &mut self.worker.js_runtime, &self.main_module.to_file_path().unwrap().to_string_lossy(), true, )?; } else { self.execute_main_module_possibly_with_npm().await?; } self.worker.dispatch_load_event(&located_script_name!())?; loop { self .worker .run_event_loop(maybe_coverage_collector.is_none()) .await?; if !self .worker .dispatch_beforeunload_event(&located_script_name!())? { break; } } self.worker.dispatch_unload_event(&located_script_name!())?; if let Some(coverage_collector) = maybe_coverage_collector.as_mut() { self .worker .with_event_loop(coverage_collector.stop_collecting().boxed_local()) .await?; } Ok(self.worker.get_exit_code()) } pub async fn run_for_watcher(self) -> Result<(), AnyError> { /// The FileWatcherModuleExecutor provides module execution with safe dispatching of life-cycle events by tracking the /// state of any pending events and emitting accordingly on drop in the case of a future /// cancellation. struct FileWatcherModuleExecutor { inner: CliMainWorker, pending_unload: bool, } impl FileWatcherModuleExecutor { pub fn new(worker: CliMainWorker) -> FileWatcherModuleExecutor { FileWatcherModuleExecutor { inner: worker, pending_unload: false, } } /// Execute the given main module emitting load and unload events before and after execution /// respectively. pub async fn execute(&mut self) -> Result<(), AnyError> { if self.inner.ps.options.compat() { self .inner .worker .execute_side_module(&compat::GLOBAL_URL) .await?; } self.inner.execute_main_module_possibly_with_npm().await?; self .inner .worker .dispatch_load_event(&located_script_name!())?; self.pending_unload = true; let result = loop { let result = self.inner.worker.run_event_loop(false).await; if !self .inner .worker .dispatch_beforeunload_event(&located_script_name!())? { break result; } }; self.pending_unload = false; if let Err(err) = result { return Err(err); } self .inner .worker .dispatch_unload_event(&located_script_name!())?; Ok(()) } } impl Drop for FileWatcherModuleExecutor { fn drop(&mut self) { if self.pending_unload { self .inner .worker .dispatch_unload_event(&located_script_name!()) .unwrap(); } } } let mut executor = FileWatcherModuleExecutor::new(self); executor.execute().await } pub async fn run_test_specifier( &mut self, mode: TestMode, ) -> Result<(), AnyError> { self.worker.js_runtime.execute_script( &located_script_name!(), r#"Deno[Deno.internal].enableTestAndBench()"#, )?; // Enable op call tracing in core to enable better debugging of op sanitizer // failures. if self.ps.options.trace_ops() { self .worker .js_runtime .execute_script( &located_script_name!(), "Deno.core.enableOpCallTracing();", ) .unwrap(); } let mut maybe_coverage_collector = self.maybe_setup_coverage_collector().await?; // We only execute the specifier as a module if it is tagged with TestMode::Module or // TestMode::Both. if mode != TestMode::Documentation { if self.ps.options.compat() { self.worker.execute_side_module(&compat::GLOBAL_URL).await?; self.worker.execute_side_module(&compat::MODULE_URL).await?; let use_esm_loader = compat::check_if_should_use_esm_loader(&self.main_module)?; if use_esm_loader { self.worker.execute_side_module(&self.main_module).await?; } else { compat::load_cjs_module( &mut self.worker.js_runtime, &self .main_module .to_file_path() .unwrap() .display() .to_string(), false, )?; self.worker.run_event_loop(false).await?; } } else { // We execute the module module as a side module so that import.meta.main is not set. self.execute_side_module_possibly_with_npm().await?; } } self.worker.dispatch_load_event(&located_script_name!())?; let test_result = self.worker.js_runtime.execute_script( &located_script_name!(), &format!( r#"Deno[Deno.internal].runTests({})"#, json!({ "shuffle": self.ps.options.shuffle_tests() }), ), )?; self.worker.js_runtime.resolve_value(test_result).await?; loop { if !self .worker .dispatch_beforeunload_event(&located_script_name!())? { break; } self.worker.run_event_loop(false).await?; } self.worker.dispatch_unload_event(&located_script_name!())?; if let Some(coverage_collector) = maybe_coverage_collector.as_mut() { self .worker .with_event_loop(coverage_collector.stop_collecting().boxed_local()) .await?; } Ok(()) } pub async fn run_lsp_test_specifier( &mut self, mode: TestMode, ) -> Result<(), AnyError> { self.worker.js_runtime.execute_script( &located_script_name!(), r#"Deno[Deno.internal].enableTestAndBench()"#, )?; self .worker .execute_script( &located_script_name!(), "Deno.core.enableOpCallTracing();", ) .unwrap(); if mode != TestMode::Documentation { // We execute the module module as a side module so that import.meta.main is not set. self.execute_side_module_possibly_with_npm().await?; } self.worker.dispatch_load_event(&located_script_name!())?; let test_result = self.worker.js_runtime.execute_script( &located_script_name!(), r#"Deno[Deno.internal].runTests()"#, )?; self.worker.js_runtime.resolve_value(test_result).await?; loop { if !self .worker .dispatch_beforeunload_event(&located_script_name!())? { break; } self.worker.run_event_loop(false).await?; } self.worker.dispatch_unload_event(&located_script_name!())?; Ok(()) } pub async fn run_bench_specifier(&mut self) -> Result<(), AnyError> { self.worker.js_runtime.execute_script( &located_script_name!(), r#"Deno[Deno.internal].enableTestAndBench()"#, )?; if self.ps.options.compat() { self.worker.execute_side_module(&compat::GLOBAL_URL).await?; self.worker.execute_side_module(&compat::MODULE_URL).await?; let use_esm_loader = compat::check_if_should_use_esm_loader(&self.main_module)?; if use_esm_loader { self.worker.execute_side_module(&self.main_module).await?; } else { compat::load_cjs_module( &mut self.worker.js_runtime, &self .main_module .to_file_path() .unwrap() .display() .to_string(), false, )?; self.worker.run_event_loop(false).await?; } } else { // We execute the module module as a side module so that import.meta.main is not set. self.execute_side_module_possibly_with_npm().await?; } self.worker.dispatch_load_event(&located_script_name!())?; let bench_result = self.worker.js_runtime.execute_script( &located_script_name!(), r#"Deno[Deno.internal].runBenchmarks()"#, )?; self.worker.js_runtime.resolve_value(bench_result).await?; loop { if !self .worker .dispatch_beforeunload_event(&located_script_name!())? { break; } self.worker.run_event_loop(false).await?; } self.worker.dispatch_unload_event(&located_script_name!())?; Ok(()) } async fn execute_main_module_possibly_with_npm( &mut self, ) -> Result<(), AnyError> { let id = self.worker.preload_main_module(&self.main_module).await?; self.evaluate_module_possibly_with_npm(id).await } async fn execute_side_module_possibly_with_npm( &mut self, ) -> Result<(), AnyError> { let id = self.worker.preload_side_module(&self.main_module).await?; self.evaluate_module_possibly_with_npm(id).await } async fn evaluate_module_possibly_with_npm( &mut self, id: ModuleId, ) -> Result<(), AnyError> { if self.ps.npm_resolver.has_packages() { self.initialize_main_module_for_node().await?; } self.worker.evaluate_module(id).await } async fn initialize_main_module_for_node(&mut self) -> Result<(), AnyError> { node::initialize_runtime(&mut self.worker.js_runtime).await?; if let DenoSubcommand::Run(flags) = self.ps.options.sub_command() { if let Ok(pkg_ref) = NpmPackageReference::from_str(&flags.script) { // if the user ran a binary command, we'll need to set process.argv[0] // to be the name of the binary command instead of deno let binary_name = pkg_ref .sub_path .as_deref() .unwrap_or(pkg_ref.req.name.as_str()); node::initialize_binary_command( &mut self.worker.js_runtime, binary_name, ) .await?; } } Ok(()) } async fn maybe_setup_coverage_collector( &mut self, ) -> Result