diff --git a/Cargo.lock b/Cargo.lock index d82040854c31b3..aad48a4622cf8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1217,116 +1217,14 @@ dependencies = [ name = "deno" version = "2.1.4" dependencies = [ - "anstream", - "async-trait", - "base64 0.21.7", - "bincode", - "bytes", - "cache_control", - "chrono", - "clap", - "clap_complete", - "clap_complete_fig", - "color-print", - "console_static_text", - "dashmap", - "data-encoding", - "deno_ast", - "deno_bench_util", - "deno_cache_dir", - "deno_config", "deno_core", - "deno_doc", - "deno_graph", - "deno_lint", - "deno_lockfile", - "deno_npm", - "deno_npm_cache", - "deno_package_json", - "deno_path_util", - "deno_resolver", + "deno_lib", "deno_runtime", - "deno_semver", - "deno_task_shell", - "deno_telemetry", - "deno_terminal 0.2.0", - "deno_tower_lsp", - "dhat", - "dissimilar", - "dotenvy", - "dprint-plugin-json", - "dprint-plugin-jupyter", - "dprint-plugin-markdown", - "dprint-plugin-typescript", - "env_logger", - "fancy-regex", - "faster-hex", "flate2", - "fs3", "glibc_version", - "glob", - "http 1.1.0", - "http-body 1.0.0", - "http-body-util", - "hyper-util", - "import_map", - "indexmap 2.3.0", - "jsonc-parser", - "junction", "lazy-regex", - "libc", - "libsui", - "libz-sys", - "log", - "lsp-types", - "malva", - "markup_fmt", - "memmem", - "monch", - "nix", - "node_resolver", - "notify", - "once_cell", - "open", - "p256", - "pathdiff", - "percent-encoding", - "phf", - "pretty_assertions", - "pretty_yaml", - "quick-junit", - "rand", - "regex", - "ring", - "runtimelib", - "rustyline", - "rustyline-derive", "serde", "serde_json", - "serde_repr", - "sha2", - "shell-escape", - "spki", - "sqlformat", - "strsim", - "tar", - "tempfile", - "test_server", - "text-size", - "text_lines", - "thiserror 2.0.3", - "tokio", - "tokio-util", - "tracing", - "twox-hash", - "typed-arena", - "uuid", - "walkdir", - "which 4.4.2", - "winapi", - "winres", - "zeromq", - "zip", "zstd", ] @@ -1824,6 +1722,123 @@ dependencies = [ "url", ] +[[package]] +name = "deno_lib" +version = "0.1.0" +dependencies = [ + "anstream", + "async-trait", + "base64 0.21.7", + "bincode", + "bytes", + "cache_control", + "chrono", + "clap", + "clap_complete", + "clap_complete_fig", + "color-print", + "console_static_text", + "dashmap", + "data-encoding", + "deno_ast", + "deno_bench_util", + "deno_cache_dir", + "deno_config", + "deno_core", + "deno_doc", + "deno_graph", + "deno_lint", + "deno_lockfile", + "deno_npm", + "deno_npm_cache", + "deno_package_json", + "deno_path_util", + "deno_resolver", + "deno_runtime", + "deno_semver", + "deno_task_shell", + "deno_telemetry", + "deno_terminal 0.2.0", + "deno_tower_lsp", + "dhat", + "dissimilar", + "dotenvy", + "dprint-plugin-json", + "dprint-plugin-jupyter", + "dprint-plugin-markdown", + "dprint-plugin-typescript", + "env_logger", + "fancy-regex", + "faster-hex", + "flate2", + "fs3", + "glibc_version", + "glob", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper-util", + "import_map", + "indexmap 2.3.0", + "jsonc-parser", + "junction", + "lazy-regex", + "libc", + "libsui", + "libz-sys", + "log", + "lsp-types", + "malva", + "markup_fmt", + "memmem", + "monch", + "nix", + "node_resolver", + "notify", + "once_cell", + "open", + "p256", + "pathdiff", + "percent-encoding", + "phf", + "pretty_assertions", + "pretty_yaml", + "quick-junit", + "rand", + "regex", + "ring", + "runtimelib", + "rustyline", + "rustyline-derive", + "serde", + "serde_json", + "serde_repr", + "sha2", + "shell-escape", + "spki", + "sqlformat", + "strsim", + "tar", + "tempfile", + "test_server", + "text-size", + "text_lines", + "thiserror 2.0.3", + "tokio", + "tokio-util", + "tracing", + "twox-hash", + "typed-arena", + "uuid", + "walkdir", + "which 4.4.2", + "winapi", + "winres", + "zeromq", + "zip", + "zstd", +] + [[package]] name = "deno_lint" version = "0.68.2" @@ -2486,6 +2501,22 @@ dependencies = [ "v8_valueserializer", ] +[[package]] +name = "denort" +version = "2.1.3" +dependencies = [ + "deno_core", + "deno_lib", + "deno_runtime", + "flate2", + "glibc_version", + "indexmap 2.3.0", + "lazy-regex", + "serde", + "serde_json", + "zstd", +] + [[package]] name = "der" version = "0.7.9" diff --git a/Cargo.toml b/Cargo.toml index 712bdae8b8904e..4917d74d8fb00f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,8 @@ resolver = "2" members = [ "bench_util", "cli", + "deno", + "denort", "ext/broadcast_channel", "ext/cache", "ext/canvas", @@ -81,6 +83,7 @@ deno_fs = { version = "0.92.0", path = "./ext/fs" } deno_http = { version = "0.180.0", path = "./ext/http" } deno_io = { version = "0.92.0", path = "./ext/io" } deno_kv = { version = "0.90.0", path = "./ext/kv" } +deno_lib = { version = "0.1.0", path = "./cli" } deno_napi = { version = "0.113.0", path = "./ext/napi" } deno_net = { version = "0.174.0", path = "./ext/net" } deno_node = { version = "0.119.0", path = "./ext/node" } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index cf76dfe69dd696..bcf207ba3cabb4 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,24 +1,17 @@ # Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. [package] -name = "deno" -version = "2.1.4" +name = "deno_lib" +version = "0.1.0" authors.workspace = true -default-run = "deno" edition.workspace = true exclude = ["bench/testdata/lsp_benchdata/"] license.workspace = true repository.workspace = true description = "Provides the deno executable" -[[bin]] -name = "deno" -path = "main.rs" -doc = false - -[[bin]] -name = "denort" -path = "mainrt.rs" +[lib] +path = "lib.rs" doc = false [[test]] diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 71f79e12e032e2..667c2abb06bbf1 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -2001,7 +2001,7 @@ pub enum NpmCachingStrategy { Manual, } -pub(crate) fn otel_runtime_config() -> OtelRuntimeConfig { +pub fn otel_runtime_config() -> OtelRuntimeConfig { OtelRuntimeConfig { runtime_name: Cow::Borrowed("deno"), runtime_version: Cow::Borrowed(crate::version::DENO_VERSION_INFO.deno), diff --git a/cli/build.rs b/cli/build.rs index 3d986612841584..427100386901c9 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -365,9 +365,6 @@ fn main() { return; } - deno_napi::print_linker_flags("deno"); - deno_napi::print_linker_flags("denort"); - // Host snapshots won't work when cross compiling. let target = env::var("TARGET").unwrap(); let host = env::var("HOST").unwrap(); @@ -400,24 +397,6 @@ fn main() { println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap()); println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap()); - if cfg!(windows) { - // these dls load slowly, so delay loading them - let dlls = [ - // webgpu - "d3dcompiler_47", - "OPENGL32", - // network related functions - "iphlpapi", - ]; - for dll in dlls { - println!("cargo:rustc-link-arg-bin=deno=/delayload:{dll}.dll"); - println!("cargo:rustc-link-arg-bin=denort=/delayload:{dll}.dll"); - } - // enable delay loading - println!("cargo:rustc-link-arg-bin=deno=delayimp.lib"); - println!("cargo:rustc-link-arg-bin=denort=delayimp.lib"); - } - let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let o = PathBuf::from(env::var_os("OUT_DIR").unwrap()); diff --git a/cli/lib.rs b/cli/lib.rs new file mode 100644 index 00000000000000..f674c606a785cc --- /dev/null +++ b/cli/lib.rs @@ -0,0 +1,399 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +pub mod args; +pub mod auth_tokens; +pub mod cache; +pub mod cdp; +pub mod emit; +pub mod errors; +pub mod factory; +pub mod file_fetcher; +pub mod graph_container; +pub mod graph_util; +pub mod http_util; +pub mod js; +pub mod jsr; +pub mod lsp; +pub mod module_loader; +pub mod node; +pub mod npm; +pub mod ops; +pub mod resolver; +pub mod shared; +pub mod standalone; +pub mod task_runner; +pub mod tools; +pub mod tsc; +pub mod util; +pub mod version; +pub mod worker; + +pub use anstream; +pub use clap; +pub use deno_config; +pub use deno_npm; +pub use deno_resolver; +pub use deno_runtime; +pub use deno_terminal; +pub use log; + +use crate::args::DenoSubcommand; +use crate::args::Flags; +use crate::util::display; + +use args::TaskFlags; +use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError; +use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; +use deno_runtime::WorkerExecutionMode; +pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS; + +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_core::error::JsError; +use deno_core::futures::FutureExt; +use deno_core::unsync::JoinHandle; +use deno_npm::resolution::SnapshotFromLockfileError; +use deno_runtime::fmt_errors::format_js_error; +use deno_terminal::colors; +use factory::CliFactory; +use standalone::MODULE_NOT_FOUND; +use standalone::UNSUPPORTED_SCHEME; +use std::env; +use std::future::Future; +use std::io::IsTerminal; +use std::ops::Deref; +use std::path::PathBuf; +use std::sync::Arc; + +#[cfg(feature = "dhat-heap")] +#[global_allocator] +static ALLOC: dhat::Alloc = dhat::Alloc; + +/// Ensures that all subcommands return an i32 exit code and an [`AnyError`] error type. +trait SubcommandOutput { + fn output(self) -> Result; +} + +impl SubcommandOutput for Result { + fn output(self) -> Result { + self + } +} + +impl SubcommandOutput for Result<(), AnyError> { + fn output(self) -> Result { + self.map(|_| 0) + } +} + +impl SubcommandOutput for Result<(), std::io::Error> { + fn output(self) -> Result { + self.map(|_| 0).map_err(|e| e.into()) + } +} + +/// Ensure that the subcommand runs in a task, rather than being directly executed. Since some of these +/// futures are very large, this prevents the stack from getting blown out from passing them by value up +/// the callchain (especially in debug mode when Rust doesn't have a chance to elide copies!). +#[inline(always)] +fn spawn_subcommand + 'static, T: SubcommandOutput>( + f: F, +) -> JoinHandle> { + // the boxed_local() is important in order to get windows to not blow the stack in debug + deno_core::unsync::spawn( + async move { f.map(|r| r.output()).await }.boxed_local(), + ) +} + +async fn run_subcommand(flags: Arc) -> Result { + let handle = match flags.subcommand.clone() { + DenoSubcommand::Add(add_flags) => spawn_subcommand(async { + tools::registry::add(flags, add_flags, tools::registry::AddCommandName::Add).await + }), + DenoSubcommand::Remove(remove_flags) => spawn_subcommand(async { + tools::registry::remove(flags, remove_flags).await + }), + DenoSubcommand::Bench(bench_flags) => spawn_subcommand(async { + if bench_flags.watch.is_some() { + tools::bench::run_benchmarks_with_watch(flags, bench_flags).await + } else { + tools::bench::run_benchmarks(flags, bench_flags).await + } + }), + DenoSubcommand::Bundle => exit_with_message("⚠️ `deno bundle` was removed in Deno 2.\n\nSee the Deno 1.x to 2.x Migration Guide for migration instructions: https://docs.deno.com/runtime/manual/advanced/migrate_deprecations", 1), + DenoSubcommand::Doc(doc_flags) => { + spawn_subcommand(async { tools::doc::doc(flags, doc_flags).await }) + } + DenoSubcommand::Eval(eval_flags) => spawn_subcommand(async { + tools::run::eval_command(flags, eval_flags).await + }), + DenoSubcommand::Cache(cache_flags) => spawn_subcommand(async move { + tools::installer::install_from_entrypoints(flags, &cache_flags.files).await + }), + DenoSubcommand::Check(check_flags) => spawn_subcommand(async move { + tools::check::check(flags, check_flags).await + }), + DenoSubcommand::Clean => spawn_subcommand(async move { + tools::clean::clean() + }), + DenoSubcommand::Compile(compile_flags) => spawn_subcommand(async { + tools::compile::compile(flags, compile_flags).await + }), + DenoSubcommand::Coverage(coverage_flags) => spawn_subcommand(async { + tools::coverage::cover_files(flags, coverage_flags) + }), + DenoSubcommand::Fmt(fmt_flags) => { + spawn_subcommand( + async move { tools::fmt::format(flags, fmt_flags).await }, + ) + } + DenoSubcommand::Init(init_flags) => { + spawn_subcommand(async { + tools::init::init_project(init_flags).await + }) + } + DenoSubcommand::Info(info_flags) => { + spawn_subcommand(async { tools::info::info(flags, info_flags).await }) + } + DenoSubcommand::Install(install_flags) => spawn_subcommand(async { + tools::installer::install_command(flags, install_flags).await + }), + DenoSubcommand::JSONReference(json_reference) => spawn_subcommand(async move { + display::write_to_stdout_ignore_sigpipe(&deno_core::serde_json::to_vec_pretty(&json_reference.json).unwrap()) + }), + DenoSubcommand::Jupyter(jupyter_flags) => spawn_subcommand(async { + tools::jupyter::kernel(flags, jupyter_flags).await + }), + DenoSubcommand::Uninstall(uninstall_flags) => spawn_subcommand(async { + tools::installer::uninstall(flags, uninstall_flags).await + }), + DenoSubcommand::Lsp => spawn_subcommand(async { + if std::io::stderr().is_terminal() { + log::warn!( + "{} command is intended to be run by text editors and IDEs and shouldn't be run manually. + + Visit https://docs.deno.com/runtime/getting_started/setup_your_environment/ for instruction + how to setup your favorite text editor. + + Press Ctrl+C to exit. + ", colors::cyan("deno lsp")); + } + lsp::start().await + }), + DenoSubcommand::Lint(lint_flags) => spawn_subcommand(async { + if lint_flags.rules { + tools::lint::print_rules_list( + lint_flags.json, + lint_flags.maybe_rules_tags, + ); + Ok(()) + } else { + tools::lint::lint(flags, lint_flags).await + } + }), + DenoSubcommand::Outdated(update_flags) => { + spawn_subcommand(async move { + tools::registry::outdated(flags, update_flags).await + }) + } + DenoSubcommand::Repl(repl_flags) => { + spawn_subcommand(async move { tools::repl::run(flags, repl_flags).await }) + } + DenoSubcommand::Run(run_flags) => spawn_subcommand(async move { + if run_flags.is_stdin() { + tools::run::run_from_stdin(flags.clone()).await + } else { + let result = tools::run::run_script(WorkerExecutionMode::Run, flags.clone(), run_flags.watch).await; + match result { + Ok(v) => Ok(v), + Err(script_err) => { + if let Some(ResolvePkgFolderFromDenoReqError::Byonm(ByonmResolvePkgFolderFromDenoReqError::UnmatchedReq(_))) = script_err.downcast_ref::() { + if flags.node_modules_dir.is_none() { + let mut flags = flags.deref().clone(); + let watch = match &flags.subcommand { + DenoSubcommand::Run(run_flags) => run_flags.watch.clone(), + _ => unreachable!(), + }; + flags.node_modules_dir = Some(deno_config::deno_json::NodeModulesDirMode::None); + // use the current lockfile, but don't write it out + if flags.frozen_lockfile.is_none() { + flags.internal.lockfile_skip_write = true; + } + return tools::run::run_script(WorkerExecutionMode::Run, Arc::new(flags), watch).await; + } + } + let script_err_msg = script_err.to_string(); + if script_err_msg.starts_with(MODULE_NOT_FOUND) || script_err_msg.starts_with(UNSUPPORTED_SCHEME) { + if run_flags.bare { + let mut cmd = args::clap_root(); + cmd.build(); + let command_names = cmd.get_subcommands().map(|command| command.get_name()).collect::>(); + let suggestions = args::did_you_mean(&run_flags.script, command_names); + if !suggestions.is_empty() { + let mut error = clap::error::Error::::new(clap::error::ErrorKind::InvalidSubcommand).with_cmd(&cmd); + error.insert( + clap::error::ContextKind::SuggestedSubcommand, + clap::error::ContextValue::Strings(suggestions), + ); + + Err(error.into()) + } else { + Err(script_err) + } + } else { + let mut new_flags = flags.deref().clone(); + let task_flags = TaskFlags { + cwd: None, + task: Some(run_flags.script.clone()), + is_run: true, + recursive: false, + filter: None, + eval: false, + }; + new_flags.subcommand = DenoSubcommand::Task(task_flags.clone()); + let result = tools::task::execute_script(Arc::new(new_flags), task_flags.clone()).await; + match result { + Ok(v) => Ok(v), + Err(_) => { + // Return script error for backwards compatibility. + Err(script_err) + } + } + } + } else { + Err(script_err) + } + } + } + } + }), + DenoSubcommand::Serve(serve_flags) => spawn_subcommand(async move { + tools::serve::serve(flags, serve_flags).await + }), + DenoSubcommand::Task(task_flags) => spawn_subcommand(async { + tools::task::execute_script(flags, task_flags).await + }), + DenoSubcommand::Test(test_flags) => { + spawn_subcommand(async { + if let Some(ref coverage_dir) = test_flags.coverage_dir { + if test_flags.clean { + let _ = std::fs::remove_dir_all(coverage_dir); + } + std::fs::create_dir_all(coverage_dir) + .with_context(|| format!("Failed creating: {coverage_dir}"))?; + // this is set in order to ensure spawned processes use the same + // coverage directory + env::set_var( + "DENO_UNSTABLE_COVERAGE_DIR", + PathBuf::from(coverage_dir).canonicalize()?, + ); + } + + if test_flags.watch.is_some() { + tools::test::run_tests_with_watch(flags, test_flags).await + } else { + tools::test::run_tests(flags, test_flags).await + } + }) + } + DenoSubcommand::Completions(completions_flags) => { + spawn_subcommand(async move { + display::write_to_stdout_ignore_sigpipe(&completions_flags.buf) + }) + } + DenoSubcommand::Types => spawn_subcommand(async move { + let types = tsc::get_types_declaration_file_text(); + display::write_to_stdout_ignore_sigpipe(types.as_bytes()) + }), + #[cfg(feature = "upgrade")] + DenoSubcommand::Upgrade(upgrade_flags) => spawn_subcommand(async { + tools::upgrade::upgrade(flags, upgrade_flags).await + }), + #[cfg(not(feature = "upgrade"))] + DenoSubcommand::Upgrade(_) => exit_with_message( + "This deno was built without the \"upgrade\" feature. Please upgrade using the installation method originally used to install Deno.", + 1, + ), + DenoSubcommand::Vendor => exit_with_message("⚠️ `deno vendor` was removed in Deno 2.\n\nSee the Deno 1.x to 2.x Migration Guide for migration instructions: https://docs.deno.com/runtime/manual/advanced/migrate_deprecations", 1), + DenoSubcommand::Publish(publish_flags) => spawn_subcommand(async { + tools::registry::publish(flags, publish_flags).await + }), + DenoSubcommand::Help(help_flags) => spawn_subcommand(async move { + use std::io::Write; + + let mut stream = anstream::AutoStream::new(std::io::stdout(), if colors::use_color() { + anstream::ColorChoice::Auto + } else { + anstream::ColorChoice::Never + }); + + match stream.write_all(help_flags.help.ansi().to_string().as_bytes()) { + Ok(()) => Ok(()), + Err(e) => match e.kind() { + std::io::ErrorKind::BrokenPipe => Ok(()), + _ => Err(e), + }, + } + }), + }; + + handle.await? +} + +#[allow(clippy::print_stderr)] +pub fn setup_panic_hook() { + // This function does two things inside of the panic hook: + // - Tokio does not exit the process when a task panics, so we define a custom + // panic hook to implement this behaviour. + // - We print a message to stderr to indicate that this is a bug in Deno, and + // should be reported to us. + let orig_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |panic_info| { + eprintln!("\n============================================================"); + eprintln!("Deno has panicked. This is a bug in Deno. Please report this"); + eprintln!("at https://github.com/denoland/deno/issues/new."); + eprintln!("If you can reliably reproduce this panic, include the"); + eprintln!("reproduction steps and re-run with the RUST_BACKTRACE=1 env"); + eprintln!("var set and include the backtrace in your report."); + eprintln!(); + eprintln!("Platform: {} {}", env::consts::OS, env::consts::ARCH); + eprintln!("Version: {}", version::DENO_VERSION_INFO.deno); + eprintln!("Args: {:?}", env::args().collect::>()); + eprintln!(); + orig_hook(panic_info); + deno_runtime::exit(1); + })); +} + +pub fn exit_with_message(message: &str, code: i32) -> ! { + log::error!( + "{}: {}", + colors::red_bold("error"), + message.trim_start_matches("error: ") + ); + deno_runtime::exit(code); +} + +pub fn exit_for_error(error: AnyError) -> ! { + let mut error_string = format!("{error:?}"); + let mut error_code = 1; + + if let Some(e) = error.downcast_ref::() { + error_string = format_js_error(e); + } else if let Some(SnapshotFromLockfileError::IntegrityCheckFailed(e)) = + error.downcast_ref::() + { + error_string = e.to_string(); + error_code = 10; + } + + exit_with_message(&error_string, error_code); +} + +pub fn unstable_exit_cb(feature: &str, api_name: &str) { + log::error!( + "Unstable API '{api_name}'. The `--unstable-{}` flag must be provided.", + feature + ); + deno_runtime::exit(70); +} diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index d7c484bebaeaec..d46e7935be1c54 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -268,7 +268,7 @@ pub async fn uninstall( Ok(()) } -pub(crate) async fn install_from_entrypoints( +pub async fn install_from_entrypoints( flags: Arc, entrypoints: &[String], ) -> Result<(), AnyError> { diff --git a/deno/Cargo.toml b/deno/Cargo.toml new file mode 100644 index 00000000000000..c23691d5309e4e --- /dev/null +++ b/deno/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "deno" +version = "2.1.4" +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true + +[[bin]] +name = "deno" +path = "main.rs" +doc = false + +[build-dependencies] +deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting", "only_snapshotted_js_sources"] } +deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } +lazy-regex.workspace = true +serde.workspace = true +serde_json.workspace = true +zstd.workspace = true +glibc_version = "0.1.2" +flate2 = { workspace = true, features = ["default"] } + +[dependencies] +deno_lib.workspace = true diff --git a/deno/build.rs b/deno/build.rs new file mode 100644 index 00000000000000..21bfc2c8d95e03 --- /dev/null +++ b/deno/build.rs @@ -0,0 +1,29 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use deno_runtime::deno_napi; +use std::env; + +fn main() { + // Skip building from docs.rs. + if env::var_os("DOCS_RS").is_some() { + return; + } + + deno_napi::print_linker_flags("deno"); + + if cfg!(windows) { + // these dls load slowly, so delay loading them + let dlls = [ + // webgpu + "d3dcompiler_47", + "OPENGL32", + // network related functions + "iphlpapi", + ]; + for dll in dlls { + println!("cargo:rustc-link-arg-bin=deno=/delayload:{dll}.dll"); + } + // enable delay loading + println!("cargo:rustc-link-arg-bin=deno=delayimp.lib"); + } +} diff --git a/deno/main.rs b/deno/main.rs new file mode 100644 index 00000000000000..4c277723818690 --- /dev/null +++ b/deno/main.rs @@ -0,0 +1,415 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use args::TaskFlags; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_core::error::JsError; +use deno_core::futures::FutureExt; +use deno_core::unsync::JoinHandle; +use deno_lib::anstream; +use deno_lib::args; +use deno_lib::args::flags_from_vec; +use deno_lib::args::DenoSubcommand; +use deno_lib::args::Flags; +use deno_lib::clap; +use deno_lib::deno_config; +use deno_lib::deno_npm; +use deno_lib::deno_resolver; +use deno_lib::deno_runtime; +use deno_lib::deno_runtime::deno_core; +use deno_lib::deno_runtime::deno_telemetry; +use deno_lib::deno_terminal; +use deno_lib::exit_for_error; +use deno_lib::exit_with_message; +use deno_lib::factory; +use deno_lib::log; +use deno_lib::lsp; +use deno_lib::setup_panic_hook; +use deno_lib::standalone; +use deno_lib::tools; +use deno_lib::tsc; +use deno_lib::util; +use deno_lib::util::display; +use deno_lib::util::v8::get_v8_flags_from_env; +use deno_lib::util::v8::init_v8_flags; +use deno_npm::resolution::SnapshotFromLockfileError; +use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError; +use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; +use deno_runtime::fmt_errors::format_js_error; +use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics; +use deno_runtime::WorkerExecutionMode; +pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS; +use deno_terminal::colors; +use factory::CliFactory; +use standalone::MODULE_NOT_FOUND; +use standalone::UNSUPPORTED_SCHEME; +use std::env; +use std::future::Future; +use std::io::IsTerminal; +use std::ops::Deref; +use std::path::PathBuf; +use std::sync::Arc; + +#[cfg(feature = "dhat-heap")] +#[global_allocator] +static ALLOC: dhat::Alloc = dhat::Alloc; + +/// Ensures that all subcommands return an i32 exit code and an [`AnyError`] error type. +trait SubcommandOutput { + fn output(self) -> Result; +} + +impl SubcommandOutput for Result { + fn output(self) -> Result { + self + } +} + +impl SubcommandOutput for Result<(), AnyError> { + fn output(self) -> Result { + self.map(|_| 0) + } +} + +impl SubcommandOutput for Result<(), std::io::Error> { + fn output(self) -> Result { + self.map(|_| 0).map_err(|e| e.into()) + } +} + +/// Ensure that the subcommand runs in a task, rather than being directly executed. Since some of these +/// futures are very large, this prevents the stack from getting blown out from passing them by value up +/// the callchain (especially in debug mode when Rust doesn't have a chance to elide copies!). +#[inline(always)] +fn spawn_subcommand + 'static, T: SubcommandOutput>( + f: F, +) -> JoinHandle> { + // the boxed_local() is important in order to get windows to not blow the stack in debug + deno_core::unsync::spawn( + async move { f.map(|r| r.output()).await }.boxed_local(), + ) +} + +async fn run_subcommand(flags: Arc) -> Result { + let handle = match flags.subcommand.clone() { + DenoSubcommand::Add(add_flags) => spawn_subcommand(async { + tools::registry::add(flags, add_flags, tools::registry::AddCommandName::Add).await + }), + DenoSubcommand::Remove(remove_flags) => spawn_subcommand(async { + tools::registry::remove(flags, remove_flags).await + }), + DenoSubcommand::Bench(bench_flags) => spawn_subcommand(async { + if bench_flags.watch.is_some() { + tools::bench::run_benchmarks_with_watch(flags, bench_flags).await + } else { + tools::bench::run_benchmarks(flags, bench_flags).await + } + }), + DenoSubcommand::Bundle => exit_with_message("⚠️ `deno bundle` was removed in Deno 2.\n\nSee the Deno 1.x to 2.x Migration Guide for migration instructions: https://docs.deno.com/runtime/manual/advanced/migrate_deprecations", 1), + DenoSubcommand::Doc(doc_flags) => { + spawn_subcommand(async { tools::doc::doc(flags, doc_flags).await }) + } + DenoSubcommand::Eval(eval_flags) => spawn_subcommand(async { + tools::run::eval_command(flags, eval_flags).await + }), + DenoSubcommand::Cache(cache_flags) => spawn_subcommand(async move { + tools::installer::install_from_entrypoints(flags, &cache_flags.files).await + }), + DenoSubcommand::Check(check_flags) => spawn_subcommand(async move { + tools::check::check(flags, check_flags).await + }), + DenoSubcommand::Clean => spawn_subcommand(async move { + tools::clean::clean() + }), + DenoSubcommand::Compile(compile_flags) => spawn_subcommand(async { + tools::compile::compile(flags, compile_flags).await + }), + DenoSubcommand::Coverage(coverage_flags) => spawn_subcommand(async { + tools::coverage::cover_files(flags, coverage_flags) + }), + DenoSubcommand::Fmt(fmt_flags) => { + spawn_subcommand( + async move { tools::fmt::format(flags, fmt_flags).await }, + ) + } + DenoSubcommand::Init(init_flags) => { + spawn_subcommand(async { + tools::init::init_project(init_flags).await + }) + } + DenoSubcommand::Info(info_flags) => { + spawn_subcommand(async { tools::info::info(flags, info_flags).await }) + } + DenoSubcommand::Install(install_flags) => spawn_subcommand(async { + tools::installer::install_command(flags, install_flags).await + }), + DenoSubcommand::JSONReference(json_reference) => spawn_subcommand(async move { + display::write_to_stdout_ignore_sigpipe(&deno_core::serde_json::to_vec_pretty(&json_reference.json).unwrap()) + }), + DenoSubcommand::Jupyter(jupyter_flags) => spawn_subcommand(async { + tools::jupyter::kernel(flags, jupyter_flags).await + }), + DenoSubcommand::Uninstall(uninstall_flags) => spawn_subcommand(async { + tools::installer::uninstall(flags, uninstall_flags).await + }), + DenoSubcommand::Lsp => spawn_subcommand(async { + if std::io::stderr().is_terminal() { + log::warn!( + "{} command is intended to be run by text editors and IDEs and shouldn't be run manually. + + Visit https://docs.deno.com/runtime/getting_started/setup_your_environment/ for instruction + how to setup your favorite text editor. + + Press Ctrl+C to exit. + ", colors::cyan("deno lsp")); + } + lsp::start().await + }), + DenoSubcommand::Lint(lint_flags) => spawn_subcommand(async { + if lint_flags.rules { + tools::lint::print_rules_list( + lint_flags.json, + lint_flags.maybe_rules_tags, + ); + Ok(()) + } else { + tools::lint::lint(flags, lint_flags).await + } + }), + DenoSubcommand::Outdated(update_flags) => { + spawn_subcommand(async move { + tools::registry::outdated(flags, update_flags).await + }) + } + DenoSubcommand::Repl(repl_flags) => { + spawn_subcommand(async move { tools::repl::run(flags, repl_flags).await }) + } + DenoSubcommand::Run(run_flags) => spawn_subcommand(async move { + if run_flags.is_stdin() { + tools::run::run_from_stdin(flags.clone()).await + } else { + let result = tools::run::run_script(WorkerExecutionMode::Run, flags.clone(), run_flags.watch).await; + match result { + Ok(v) => Ok(v), + Err(script_err) => { + if let Some(ResolvePkgFolderFromDenoReqError::Byonm(ByonmResolvePkgFolderFromDenoReqError::UnmatchedReq(_))) = script_err.downcast_ref::() { + if flags.node_modules_dir.is_none() { + let mut flags = flags.deref().clone(); + let watch = match &flags.subcommand { + DenoSubcommand::Run(run_flags) => run_flags.watch.clone(), + _ => unreachable!(), + }; + flags.node_modules_dir = Some(deno_config::deno_json::NodeModulesDirMode::None); + // use the current lockfile, but don't write it out + if flags.frozen_lockfile.is_none() { + flags.internal.lockfile_skip_write = true; + } + return tools::run::run_script(WorkerExecutionMode::Run, Arc::new(flags), watch).await; + } + } + let script_err_msg = script_err.to_string(); + if script_err_msg.starts_with(MODULE_NOT_FOUND) || script_err_msg.starts_with(UNSUPPORTED_SCHEME) { + if run_flags.bare { + let mut cmd = args::clap_root(); + cmd.build(); + let command_names = cmd.get_subcommands().map(|command| command.get_name()).collect::>(); + let suggestions = args::did_you_mean(&run_flags.script, command_names); + if !suggestions.is_empty() { + let mut error = clap::error::Error::::new(clap::error::ErrorKind::InvalidSubcommand).with_cmd(&cmd); + error.insert( + clap::error::ContextKind::SuggestedSubcommand, + clap::error::ContextValue::Strings(suggestions), + ); + + Err(error.into()) + } else { + Err(script_err) + } + } else { + let mut new_flags = flags.deref().clone(); + let task_flags = TaskFlags { + cwd: None, + task: Some(run_flags.script.clone()), + is_run: true, + recursive: false, + filter: None, + eval: false, + }; + new_flags.subcommand = DenoSubcommand::Task(task_flags.clone()); + let result = tools::task::execute_script(Arc::new(new_flags), task_flags.clone()).await; + match result { + Ok(v) => Ok(v), + Err(_) => { + // Return script error for backwards compatibility. + Err(script_err) + } + } + } + } else { + Err(script_err) + } + } + } + } + }), + DenoSubcommand::Serve(serve_flags) => spawn_subcommand(async move { + tools::serve::serve(flags, serve_flags).await + }), + DenoSubcommand::Task(task_flags) => spawn_subcommand(async { + tools::task::execute_script(flags, task_flags).await + }), + DenoSubcommand::Test(test_flags) => { + spawn_subcommand(async { + if let Some(ref coverage_dir) = test_flags.coverage_dir { + if test_flags.clean { + let _ = std::fs::remove_dir_all(coverage_dir); + } + std::fs::create_dir_all(coverage_dir) + .with_context(|| format!("Failed creating: {coverage_dir}"))?; + // this is set in order to ensure spawned processes use the same + // coverage directory + env::set_var( + "DENO_UNSTABLE_COVERAGE_DIR", + PathBuf::from(coverage_dir).canonicalize()?, + ); + } + + if test_flags.watch.is_some() { + tools::test::run_tests_with_watch(flags, test_flags).await + } else { + tools::test::run_tests(flags, test_flags).await + } + }) + } + DenoSubcommand::Completions(completions_flags) => { + spawn_subcommand(async move { + display::write_to_stdout_ignore_sigpipe(&completions_flags.buf) + }) + } + DenoSubcommand::Types => spawn_subcommand(async move { + let types = tsc::get_types_declaration_file_text(); + display::write_to_stdout_ignore_sigpipe(types.as_bytes()) + }), + #[cfg(feature = "upgrade")] + DenoSubcommand::Upgrade(upgrade_flags) => spawn_subcommand(async { + tools::upgrade::upgrade(flags, upgrade_flags).await + }), + #[cfg(not(feature = "upgrade"))] + DenoSubcommand::Upgrade(_) => exit_with_message( + "This deno was built without the \"upgrade\" feature. Please upgrade using the installation method originally used to install Deno.", + 1, + ), + DenoSubcommand::Vendor => exit_with_message("⚠️ `deno vendor` was removed in Deno 2.\n\nSee the Deno 1.x to 2.x Migration Guide for migration instructions: https://docs.deno.com/runtime/manual/advanced/migrate_deprecations", 1), + DenoSubcommand::Publish(publish_flags) => spawn_subcommand(async { + tools::registry::publish(flags, publish_flags).await + }), + DenoSubcommand::Help(help_flags) => spawn_subcommand(async move { + use std::io::Write; + + let mut stream = anstream::AutoStream::new(std::io::stdout(), if colors::use_color() { + anstream::ColorChoice::Auto + } else { + anstream::ColorChoice::Never + }); + + match stream.write_all(help_flags.help.ansi().to_string().as_bytes()) { + Ok(()) => Ok(()), + Err(e) => match e.kind() { + std::io::ErrorKind::BrokenPipe => Ok(()), + _ => Err(e), + }, + } + }), + }; + + handle.await? +} + +pub fn main() { + #[cfg(feature = "dhat-heap")] + let profiler = dhat::Profiler::new_heap(); + + setup_panic_hook(); + + util::unix::raise_fd_limit(); + util::windows::ensure_stdio_open(); + #[cfg(windows)] + colors::enable_ansi(); // For Windows 10 + deno_runtime::deno_permissions::set_prompt_callbacks( + Box::new(util::draw_thread::DrawThread::hide), + Box::new(util::draw_thread::DrawThread::show), + ); + + let args: Vec<_> = env::args_os().collect(); + let future = async move { + // NOTE(lucacasonato): due to new PKU feature introduced in V8 11.6 we need to + // initialize the V8 platform on a parent thread of all threads that will spawn + // V8 isolates. + let flags = resolve_flags_and_init(args)?; + run_subcommand(Arc::new(flags)).await + }; + + let result = create_and_run_current_thread_with_maybe_metrics(future); + + #[cfg(feature = "dhat-heap")] + drop(profiler); + + match result { + Ok(exit_code) => deno_runtime::exit(exit_code), + Err(err) => exit_for_error(err), + } +} + +fn resolve_flags_and_init( + args: Vec, +) -> Result { + let flags = match flags_from_vec(args) { + Ok(flags) => flags, + Err(err @ clap::Error { .. }) + if err.kind() == clap::error::ErrorKind::DisplayVersion => + { + // Ignore results to avoid BrokenPipe errors. + util::logger::init(None, None); + let _ = err.print(); + deno_runtime::exit(0); + } + Err(err) => { + util::logger::init(None, None); + exit_for_error(AnyError::from(err)) + } + }; + + deno_telemetry::init(deno_lib::args::otel_runtime_config())?; + util::logger::init(flags.log_level, Some(flags.otel_config())); + + // TODO(bartlomieju): remove in Deno v2.5 and hard error then. + if flags.unstable_config.legacy_flag_enabled { + log::warn!( + "⚠️ {}", + colors::yellow( + "The `--unstable` flag has been removed in Deno 2.0. Use granular `--unstable-*` flags instead.\nLearn more at: https://docs.deno.com/runtime/manual/tools/unstable_flags" + ) + ); + } + + let default_v8_flags = match flags.subcommand { + // Using same default as VSCode: + // https://github.com/microsoft/vscode/blob/48d4ba271686e8072fc6674137415bc80d936bc7/extensions/typescript-language-features/src/configuration/configuration.ts#L213-L214 + DenoSubcommand::Lsp => vec!["--max-old-space-size=3072".to_string()], + _ => { + // TODO(bartlomieju): I think this can be removed as it's handled by `deno_core` + // and its settings. + // deno_ast removes TypeScript `assert` keywords, so this flag only affects JavaScript + // TODO(petamoriken): Need to check TypeScript `assert` keywords in deno_ast + vec!["--no-harmony-import-assertions".to_string()] + } + }; + + init_v8_flags(&default_v8_flags, &flags.v8_flags, get_v8_flags_from_env()); + // TODO(bartlomieju): remove last argument once Deploy no longer needs it + deno_core::JsRuntime::init_platform( + None, /* import assertions enabled */ false, + ); + + Ok(flags) +} diff --git a/denort/Cargo.toml b/denort/Cargo.toml new file mode 100644 index 00000000000000..4d00f3239ede1c --- /dev/null +++ b/denort/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "denort" +version = "2.1.3" +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true + +[[bin]] +name = "denort" +path = "main.rs" +doc = false + +[build-dependencies] +deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting", "only_snapshotted_js_sources"] } +deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } +lazy-regex.workspace = true +serde.workspace = true +serde_json.workspace = true +zstd.workspace = true +glibc_version = "0.1.2" +flate2 = { workspace = true, features = ["default"] } + +[dependencies] +deno_lib.workspace = true +indexmap.workspace = true diff --git a/denort/main.rs b/denort/main.rs new file mode 100644 index 00000000000000..24a0f966a2b8c9 --- /dev/null +++ b/denort/main.rs @@ -0,0 +1,88 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use deno_core::error::AnyError; +use deno_core::error::JsError; +use deno_lib::deno_runtime; +use deno_lib::deno_runtime::deno_core; +use deno_lib::deno_runtime::deno_telemetry; +use deno_lib::deno_terminal; +use deno_lib::log; +use deno_lib::standalone; +use deno_lib::util; +use deno_runtime::fmt_errors::format_js_error; +use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics; +pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS; +use deno_terminal::colors; +use indexmap::IndexMap; + +use std::borrow::Cow; +use std::collections::HashMap; +use std::env; +use std::env::current_exe; + +pub(crate) fn unstable_exit_cb(feature: &str, api_name: &str) { + log::error!( + "Unstable API '{api_name}'. The `--unstable-{}` flag must be provided.", + feature + ); + deno_runtime::exit(70); +} + +fn exit_with_message(message: &str, code: i32) -> ! { + log::error!( + "{}: {}", + colors::red_bold("error"), + message.trim_start_matches("error: ") + ); + deno_runtime::exit(code); +} + +fn unwrap_or_exit(result: Result) -> T { + match result { + Ok(value) => value, + Err(error) => { + let mut error_string = format!("{:?}", error); + + if let Some(e) = error.downcast_ref::() { + error_string = format_js_error(e); + } + + exit_with_message(&error_string, 1); + } + } +} + +fn load_env_vars(env_vars: &IndexMap) { + env_vars.iter().for_each(|env_var| { + if env::var(env_var.0).is_err() { + std::env::set_var(env_var.0, env_var.1); + } + }) +} + +fn main() { + deno_runtime::deno_permissions::mark_standalone(); + let args: Vec<_> = env::args_os().collect(); + let standalone = standalone::extract_standalone(Cow::Owned(args)); + let future = async move { + match standalone { + Ok(Some(data)) => { + deno_telemetry::init(deno_lib::args::otel_runtime_config())?; + util::logger::init( + data.metadata.log_level, + Some(data.metadata.otel_config.clone()), + ); + load_env_vars(&data.metadata.env_vars_from_env_file); + let exit_code = standalone::run(data).await?; + deno_runtime::exit(exit_code); + } + Ok(None) => Ok(()), + Err(err) => { + util::logger::init(None, None); + Err(err) + } + } + }; + + unwrap_or_exit(create_and_run_current_thread_with_maybe_metrics(future)); +} diff --git a/runtime/lib.rs b/runtime/lib.rs index 1f449dc69a7711..1455fb2dcd744d 100644 --- a/runtime/lib.rs +++ b/runtime/lib.rs @@ -17,6 +17,7 @@ pub use deno_napi; pub use deno_net; pub use deno_node; pub use deno_permissions; +pub use deno_telemetry; pub use deno_terminal::colors; pub use deno_tls; pub use deno_url;