summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2024-02-13 21:52:30 +0530
committerGitHub <noreply@github.com>2024-02-13 21:52:30 +0530
commita68eb3fcc3997fce8680f87edce46f6450e79635 (patch)
tree6839607033226fdfb2ce1be3187ef93791096507
parent492a9fbb9194a24a1f9223f797b4f4df9efde2bd (diff)
feat: denort binary for `deno compile` (#22205)
This introduces the `denort` binary - a slim version of deno without tooling. The binary is used as the default for `deno compile`. Improves `deno compile` final size by ~2.5x (141 MB -> 61 MB) on Linux x86_64.
-rwxr-xr-x.github/workflows/ci.generate.ts12
-rw-r--r--.github/workflows/ci.yml12
-rw-r--r--cli/Cargo.toml5
-rw-r--r--cli/factory.rs32
-rw-r--r--cli/mainrt.rs82
-rw-r--r--cli/resolver.rs11
-rw-r--r--cli/standalone/binary.rs80
-rw-r--r--cli/standalone/mod.rs5
-rw-r--r--cli/tools/coverage/mod.rs97
-rw-r--r--cli/tools/run/hmr.rs179
-rw-r--r--cli/tools/upgrade.rs80
-rw-r--r--cli/worker.rs92
-rw-r--r--test_util/src/builders.rs12
-rw-r--r--test_util/src/lib.rs8
14 files changed, 453 insertions, 254 deletions
diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts
index 26ca89316..29077ab54 100755
--- a/.github/workflows/ci.generate.ts
+++ b/.github/workflows/ci.generate.ts
@@ -704,6 +704,7 @@ const ci = {
run: [
"cd target/release",
"zip -r deno-${{ matrix.arch }}-unknown-linux-gnu.zip deno",
+ "zip -r denort-${{ matrix.arch }}-unknown-linux-gnu.zip denort",
"./deno types > lib.deno.d.ts",
].join("\n"),
},
@@ -728,6 +729,7 @@ const ci = {
"--entitlements-xml-file=cli/entitlements.plist",
"cd target/release",
"zip -r deno-${{ matrix.arch }}-apple-darwin.zip deno",
+ "zip -r denort-${{ matrix.arch }}-apple-darwin.zip denort",
]
.join("\n"),
},
@@ -740,8 +742,10 @@ const ci = {
"github.repository == 'denoland/deno'",
].join("\n"),
shell: "pwsh",
- run:
+ run: [
"Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip",
+ "Compress-Archive -CompressionLevel Optimal -Force -Path target/release/denort.exe -DestinationPath target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip",
+ ].join("\n"),
},
{
name: "Upload canary to dl.deno.land",
@@ -942,6 +946,7 @@ const ci = {
run: [
'du -hd1 "./target/${{ matrix.profile }}"',
'du -ha "./target/${{ matrix.profile }}/deno"',
+ 'du -ha "./target/${{ matrix.profile }}/denort"',
].join("\n"),
},
{
@@ -1007,10 +1012,15 @@ const ci = {
with: {
files: [
"target/release/deno-x86_64-pc-windows-msvc.zip",
+ "target/release/denort-x86_64-pc-windows-msvc.zip",
"target/release/deno-x86_64-unknown-linux-gnu.zip",
+ "target/release/denort-x86_64-unknown-linux-gnu.zip",
"target/release/deno-x86_64-apple-darwin.zip",
+ "target/release/denort-x86_64-apple-darwin.zip",
"target/release/deno-aarch64-unknown-linux-gnu.zip",
+ "target/release/denort-aarch64-unknown-linux-gnu.zip",
"target/release/deno-aarch64-apple-darwin.zip",
+ "target/release/denort-aarch64-apple-darwin.zip",
"target/release/deno_src.tar.gz",
"target/release/lib.deno.d.ts",
].join("\n"),
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 79edd24c5..7342319c4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -424,6 +424,7 @@ jobs:
run: |-
cd target/release
zip -r deno-${{ matrix.arch }}-unknown-linux-gnu.zip deno
+ zip -r denort-${{ matrix.arch }}-unknown-linux-gnu.zip denort
./deno types > lib.deno.d.ts
- name: Pre-release (mac)
if: |-
@@ -439,6 +440,7 @@ jobs:
rcodesign sign target/release/deno --code-signature-flags=runtime --p12-password="$APPLE_CODESIGN_PASSWORD" --p12-file=<(echo $APPLE_CODESIGN_KEY | base64 -d) --entitlements-xml-file=cli/entitlements.plist
cd target/release
zip -r deno-${{ matrix.arch }}-apple-darwin.zip deno
+ zip -r denort-${{ matrix.arch }}-apple-darwin.zip denort
- name: Pre-release (windows)
if: |-
!(matrix.skip) && (matrix.os == 'windows' &&
@@ -446,7 +448,9 @@ jobs:
matrix.profile == 'release' &&
github.repository == 'denoland/deno')
shell: pwsh
- run: 'Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip'
+ run: |-
+ Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip
+ Compress-Archive -CompressionLevel Optimal -Force -Path target/release/denort.exe -DestinationPath target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip
- name: Upload canary to dl.deno.land
if: |-
!(matrix.skip) && (matrix.job == 'test' &&
@@ -588,6 +592,7 @@ jobs:
run: |-
du -hd1 "./target/${{ matrix.profile }}"
du -ha "./target/${{ matrix.profile }}/deno"
+ du -ha "./target/${{ matrix.profile }}/denort"
- name: Worker info
if: '!(matrix.skip) && (matrix.job == ''bench'')'
run: |-
@@ -632,10 +637,15 @@ jobs:
with:
files: |-
target/release/deno-x86_64-pc-windows-msvc.zip
+ target/release/denort-x86_64-pc-windows-msvc.zip
target/release/deno-x86_64-unknown-linux-gnu.zip
+ target/release/denort-x86_64-unknown-linux-gnu.zip
target/release/deno-x86_64-apple-darwin.zip
+ target/release/denort-x86_64-apple-darwin.zip
target/release/deno-aarch64-unknown-linux-gnu.zip
+ target/release/denort-aarch64-unknown-linux-gnu.zip
target/release/deno-aarch64-apple-darwin.zip
+ target/release/denort-aarch64-apple-darwin.zip
target/release/deno_src.tar.gz
target/release/lib.deno.d.ts
body_path: target/release/release-notes.md
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 365f26f91..000e1b17c 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -15,6 +15,11 @@ name = "deno"
path = "main.rs"
doc = false
+[[bin]]
+name = "denort"
+path = "mainrt.rs"
+doc = false
+
[[test]]
name = "integration"
path = "integration_tests_runner.rs"
diff --git a/cli/factory.rs b/cli/factory.rs
index 32cb256a3..0b24350ed 100644
--- a/cli/factory.rs
+++ b/cli/factory.rs
@@ -42,6 +42,8 @@ use crate::resolver::NpmModuleLoader;
use crate::resolver::SloppyImportsResolver;
use crate::standalone::DenoCompileBinaryWriter;
use crate::tools::check::TypeChecker;
+use crate::tools::coverage::CoverageCollector;
+use crate::tools::run::hmr::HmrRunner;
use crate::util::file_watcher::WatcherCommunicator;
use crate::util::fs::canonicalize_path_maybe_not_exists;
use crate::util::import_map::deno_json_deps;
@@ -50,6 +52,7 @@ use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
use crate::worker::CliMainWorkerFactory;
use crate::worker::CliMainWorkerOptions;
+use std::path::PathBuf;
use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
@@ -766,7 +769,6 @@ impl CliFactory {
)),
self.root_cert_store_provider().clone(),
self.fs().clone(),
- Some(self.emitter()?.clone()),
maybe_file_watcher_communicator,
self.maybe_inspector_server().clone(),
self.maybe_lockfile().clone(),
@@ -783,6 +785,32 @@ impl CliFactory {
fn create_cli_main_worker_options(
&self,
) -> Result<CliMainWorkerOptions, AnyError> {
+ let create_hmr_runner = if self.options.has_hmr() {
+ let watcher_communicator = self.watcher_communicator.clone().unwrap();
+ let emitter = self.emitter()?.clone();
+ let fn_: crate::worker::CreateHmrRunnerCb = Box::new(move |session| {
+ Box::new(HmrRunner::new(
+ emitter.clone(),
+ session,
+ watcher_communicator.clone(),
+ ))
+ });
+ Some(fn_)
+ } else {
+ None
+ };
+ let create_coverage_collector =
+ if let Some(coverage_dir) = self.options.coverage_dir() {
+ let coverage_dir = PathBuf::from(coverage_dir);
+ let fn_: crate::worker::CreateCoverageCollectorCb =
+ Box::new(move |session| {
+ Box::new(CoverageCollector::new(coverage_dir.clone(), session))
+ });
+ Some(fn_)
+ } else {
+ None
+ };
+
Ok(CliMainWorkerOptions {
argv: self.options.argv().clone(),
// This optimization is only available for "run" subcommand
@@ -814,6 +842,8 @@ impl CliFactory {
.clone(),
unstable: self.options.legacy_unstable_flag(),
maybe_root_package_json_deps: self.options.maybe_package_json_deps(),
+ create_hmr_runner,
+ create_coverage_collector,
})
}
}
diff --git a/cli/mainrt.rs b/cli/mainrt.rs
new file mode 100644
index 000000000..9c7ee3c5c
--- /dev/null
+++ b/cli/mainrt.rs
@@ -0,0 +1,82 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+// Allow unused code warnings because we share
+// code between the two bin targets.
+#![allow(dead_code)]
+#![allow(unused_imports)]
+
+mod standalone;
+
+mod args;
+mod auth_tokens;
+mod cache;
+mod emit;
+mod errors;
+mod file_fetcher;
+mod http_util;
+mod js;
+mod node;
+mod npm;
+mod resolver;
+mod util;
+mod version;
+mod worker;
+
+use deno_core::error::generic_error;
+use deno_core::error::AnyError;
+use deno_core::error::JsError;
+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 std::env;
+use std::env::current_exe;
+
+use crate::args::Flags;
+
+pub(crate) fn unstable_exit_cb(feature: &str, api_name: &str) {
+ eprintln!(
+ "Unstable API '{api_name}'. The `--unstable-{}` flag must be provided.",
+ feature
+ );
+ std::process::exit(70);
+}
+
+fn exit_with_message(message: &str, code: i32) -> ! {
+ eprintln!(
+ "{}: {}",
+ colors::red_bold("error"),
+ message.trim_start_matches("error: ")
+ );
+ std::process::exit(code);
+}
+
+fn unwrap_or_exit<T>(result: Result<T, AnyError>) -> T {
+ match result {
+ Ok(value) => value,
+ Err(error) => {
+ let mut error_string = format!("{error:?}");
+
+ if let Some(e) = error.downcast_ref::<JsError>() {
+ error_string = format_js_error(e);
+ }
+
+ exit_with_message(&error_string, 1);
+ }
+ }
+}
+
+fn main() {
+ let args: Vec<String> = env::args().collect();
+ let future = async move {
+ let current_exe_path = current_exe().unwrap();
+ match standalone::extract_standalone(&current_exe_path, args).await {
+ Ok(Some((metadata, eszip))) => standalone::run(eszip, metadata).await,
+ Ok(None) => Err(generic_error("No archive found.")),
+ Err(err) => Err(err),
+ }
+ };
+
+ unwrap_or_exit(create_and_run_current_thread_with_maybe_metrics(future));
+}
diff --git a/cli/resolver.rs b/cli/resolver.rs
index 2c04823a7..5bb9e66d0 100644
--- a/cli/resolver.rs
+++ b/cli/resolver.rs
@@ -40,7 +40,7 @@ use std::sync::Arc;
use crate::args::package_json::PackageJsonDeps;
use crate::args::JsxImportSourceConfig;
use crate::args::PackageJsonDepsProvider;
-use crate::graph_util::format_range_with_colors;
+use crate::colors;
use crate::node::CliNodeCodeTranslator;
use crate::npm::ByonmCliNpmResolver;
use crate::npm::CliNpmResolver;
@@ -48,6 +48,15 @@ use crate::npm::InnerCliNpmResolverRef;
use crate::util::path::specifier_to_file_path;
use crate::util::sync::AtomicFlag;
+pub fn format_range_with_colors(range: &deno_graph::Range) -> String {
+ format!(
+ "{}:{}:{}",
+ colors::cyan(range.specifier.as_str()),
+ colors::yellow(&(range.start.line + 1).to_string()),
+ colors::yellow(&(range.start.character + 1).to_string())
+ )
+}
+
pub struct ModuleCodeStringSource {
pub code: ModuleCodeString,
pub found_url: ModuleSpecifier,
diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs
index f8bb1e21c..f9d65fdaa 100644
--- a/cli/standalone/binary.rs
+++ b/cli/standalone/binary.rs
@@ -2,12 +2,14 @@
use std::collections::BTreeMap;
use std::env::current_exe;
+use std::fs;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
+use std::process::Command;
use deno_ast::ModuleSpecifier;
use deno_core::anyhow::bail;
@@ -337,6 +339,71 @@ fn u64_from_bytes(arr: &[u8]) -> Result<u64, AnyError> {
Ok(u64::from_be_bytes(*fixed_arr))
}
+pub fn unpack_into_dir(
+ exe_name: &str,
+ archive_name: &str,
+ archive_data: Vec<u8>,
+ is_windows: bool,
+ temp_dir: &tempfile::TempDir,
+) -> Result<PathBuf, AnyError> {
+ let temp_dir_path = temp_dir.path();
+ let exe_ext = if is_windows { "exe" } else { "" };
+ let archive_path = temp_dir_path.join(exe_name).with_extension("zip");
+ let exe_path = temp_dir_path.join(exe_name).with_extension(exe_ext);
+ assert!(!exe_path.exists());
+
+ let archive_ext = Path::new(archive_name)
+ .extension()
+ .and_then(|ext| ext.to_str())
+ .unwrap();
+ let unpack_status = match archive_ext {
+ "zip" if cfg!(windows) => {
+ fs::write(&archive_path, &archive_data)?;
+ Command::new("tar.exe")
+ .arg("xf")
+ .arg(&archive_path)
+ .arg("-C")
+ .arg(temp_dir_path)
+ .spawn()
+ .map_err(|err| {
+ if err.kind() == std::io::ErrorKind::NotFound {
+ std::io::Error::new(
+ std::io::ErrorKind::NotFound,
+ "`tar.exe` was not found in your PATH",
+ )
+ } else {
+ err
+ }
+ })?
+ .wait()?
+ }
+ "zip" => {
+ fs::write(&archive_path, &archive_data)?;
+ Command::new("unzip")
+ .current_dir(temp_dir_path)
+ .arg(&archive_path)
+ .spawn()
+ .map_err(|err| {
+ if err.kind() == std::io::ErrorKind::NotFound {
+ std::io::Error::new(
+ std::io::ErrorKind::NotFound,
+ "`unzip` was not found in your PATH, please install `unzip`",
+ )
+ } else {
+ err
+ }
+ })?
+ .wait()?
+ }
+ ext => bail!("Unsupported archive type: '{ext}'"),
+ };
+ if !unpack_status.success() {
+ bail!("Failed to unpack archive.");
+ }
+ assert!(exe_path.exists());
+ fs::remove_file(&archive_path)?;
+ Ok(exe_path)
+}
pub struct DenoCompileBinaryWriter<'a> {
file_fetcher: &'a FileFetcher,
client: &'a HttpClient,
@@ -404,13 +471,16 @@ impl<'a> DenoCompileBinaryWriter<'a> {
&self,
compile_flags: &CompileFlags,
) -> Result<Vec<u8>, AnyError> {
- if compile_flags.target.is_none() {
- let path = std::env::current_exe()?;
+ // Used for testing.
+ //
+ // Phase 2 of the 'min sized' deno compile RFC talks
+ // about adding this as a flag.
+ if let Some(path) = std::env::var_os("DENORT_BIN") {
return Ok(std::fs::read(path)?);
}
let target = compile_flags.resolve_target();
- let binary_name = format!("deno-{target}.zip");
+ let binary_name = format!("denort-{target}.zip");
let binary_path_suffix = if crate::version::is_canary() {
format!("canary/{}/{}", crate::version::GIT_COMMIT_HASH, binary_name)
@@ -429,7 +499,9 @@ impl<'a> DenoCompileBinaryWriter<'a> {
let archive_data = std::fs::read(binary_path)?;
let temp_dir = tempfile::TempDir::new()?;
- let base_binary_path = crate::tools::upgrade::unpack_into_dir(
+ let base_binary_path = unpack_into_dir(
+ "denort",
+ &binary_name,
archive_data,
target.contains("windows"),
&temp_dir,
diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs
index e25b61c31..ecbb0be82 100644
--- a/cli/standalone/mod.rs
+++ b/cli/standalone/mod.rs
@@ -55,7 +55,7 @@ use import_map::parse_from_json;
use std::rc::Rc;
use std::sync::Arc;
-mod binary;
+pub mod binary;
mod file_system;
mod virtual_fs;
@@ -520,7 +520,6 @@ pub async fn run(
None,
None,
None,
- None,
feature_checker,
CliMainWorkerOptions {
argv: metadata.argv,
@@ -548,6 +547,8 @@ pub async fn run(
.unsafely_ignore_certificate_errors,
unstable: metadata.unstable_config.legacy_flag_enabled,
maybe_root_package_json_deps: package_json_deps_provider.deps().cloned(),
+ create_hmr_runner: None,
+ create_coverage_collector: None,
},
None,
// TODO(bartlomieju): temporarily disabled
diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs
index 16c955576..aafef292f 100644
--- a/cli/tools/coverage/mod.rs
+++ b/cli/tools/coverage/mod.rs
@@ -46,6 +46,56 @@ pub struct CoverageCollector {
session: LocalInspectorSession,
}
+#[async_trait::async_trait(?Send)]
+impl crate::worker::CoverageCollector for CoverageCollector {
+ async fn start_collecting(&mut self) -> Result<(), AnyError> {
+ self.enable_debugger().await?;
+ self.enable_profiler().await?;
+ self
+ .start_precise_coverage(cdp::StartPreciseCoverageArgs {
+ call_count: true,
+ detailed: true,
+ allow_triggered_updates: false,
+ })
+ .await?;
+
+ Ok(())
+ }
+
+ async fn stop_collecting(&mut self) -> Result<(), AnyError> {
+ fs::create_dir_all(&self.dir)?;
+
+ let script_coverages = self.take_precise_coverage().await?.result;
+ for script_coverage in script_coverages {
+ // Filter out internal JS files from being included in coverage reports
+ if script_coverage.url.starts_with("ext:")
+ || script_coverage.url.starts_with("[ext:")
+ {
+ continue;
+ }
+
+ let filename = format!("{}.json", Uuid::new_v4());
+ let filepath = self.dir.join(filename);
+
+ let mut out = BufWriter::new(File::create(&filepath)?);
+ let coverage = serde_json::to_string(&script_coverage)?;
+ let formatted_coverage =
+ format_json(&filepath, &coverage, &Default::default())
+ .ok()
+ .flatten()
+ .unwrap_or(coverage);
+
+ out.write_all(formatted_coverage.as_bytes())?;
+ out.flush()?;
+ }
+
+ self.disable_debugger().await?;
+ self.disable_profiler().await?;
+
+ Ok(())
+ }
+}
+
impl CoverageCollector {
pub fn new(dir: PathBuf, session: LocalInspectorSession) -> Self {
Self { dir, session }
@@ -109,53 +159,6 @@ impl CoverageCollector {
Ok(return_object)
}
-
- pub async fn start_collecting(&mut self) -> Result<(), AnyError> {
- self.enable_debugger().await?;
- self.enable_profiler().await?;
- self
- .start_precise_coverage(cdp::StartPreciseCoverageArgs {
- call_count: true,
- detailed: true,
- allow_triggered_updates: false,
- })
- .await?;
-
- Ok(())
- }
-
- pub async fn stop_collecting(&mut self) -> Result<(), AnyError> {
- fs::create_dir_all(&self.dir)?;
-
- let script_coverages = self.take_precise_coverage().await?.result;
- for script_coverage in script_coverages {
- // Filter out internal JS files from being included in coverage reports
- if script_coverage.url.starts_with("ext:")
- || script_coverage.url.starts_with("[ext:")
- {
- continue;
- }
-
- let filename = format!("{}.json", Uuid::new_v4());
- let filepath = self.dir.join(filename);
-
- let mut out = BufWriter::new(File::create(&filepath)?);
- let coverage = serde_json::to_string(&script_coverage)?;
- let formatted_coverage =
- format_json(&filepath, &coverage, &Default::default())
- .ok()
- .flatten()
- .unwrap_or(coverage);
-
- out.write_all(formatted_coverage.as_bytes())?;
- out.flush()?;
- }
-
- self.disable_debugger().await?;
- self.disable_profiler().await?;
-
- Ok(())
- }
}
#[derive(Debug, Clone)]
diff --git a/cli/tools/run/hmr.rs b/cli/tools/run/hmr.rs
index 88f90f680..4ca9ee8b9 100644
--- a/cli/tools/run/hmr.rs
+++ b/cli/tools/run/hmr.rs
@@ -61,105 +61,22 @@ pub struct HmrRunner {
emitter: Arc<Emitter>,
}
-impl HmrRunner {
- pub fn new(
- emitter: Arc<Emitter>,
- session: LocalInspectorSession,
- watcher_communicator: Arc<WatcherCommunicator>,
- ) -> Self {
- Self {
- session,
- emitter,
- watcher_communicator,
- script_ids: HashMap::new(),
- }
- }
-
+#[async_trait::async_trait(?Send)]
+impl crate::worker::HmrRunner for HmrRunner {
// TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs`
- pub async fn start(&mut self) -> Result<(), AnyError> {
+ async fn start(&mut self) -> Result<(), AnyError> {
self.enable_debugger().await
}
// TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs`
- pub async fn stop(&mut self) -> Result<(), AnyError> {
+ async fn stop(&mut self) -> Result<(), AnyError> {
self
.watcher_communicator
.change_restart_mode(WatcherRestartMode::Automatic);
self.disable_debugger().await
}
- // TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs`
- async fn enable_debugger(&mut self) -> Result<(), AnyError> {
- self
- .session
- .post_message::<()>("Debugger.enable", None)
- .await?;
- self
- .session
- .post_message::<()>("Runtime.enable", None)
- .await?;
- Ok(())
- }
-
- // TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs`
- async fn disable_debugger(&mut self) -> Result<(), AnyError> {
- self
- .session
- .post_message::<()>("Debugger.disable", None)
- .await?;
- self
- .session
- .post_message::<()>("Runtime.disable", None)
- .await?;
- Ok(())
- }
-
- async fn set_script_source(
- &mut self,
- script_id: &str,
- source: &str,
- ) -> Result<cdp::SetScriptSourceResponse, AnyError> {
- let result = self
- .session
- .post_message(
- "Debugger.setScriptSource",
- Some(json!({
- "scriptId": script_id,
- "scriptSource": source,
- "allowTopFrameEditing": true,
- })),
- )
- .await?;
-
- Ok(serde_json::from_value::<cdp::SetScriptSourceResponse>(
- result,
- )?)
- }
-
- async fn dispatch_hmr_event(
- &mut self,
- script_id: &str,
- ) -> Result<(), AnyError> {
- let expr = format!(
- "dispatchEvent(new CustomEvent(\"hmr\", {{ detail: {{ path: \"{}\" }} }}));",
- script_id
- );
-
- let _result = self
- .session
- .post_message(
- "Runtime.evaluate",
- Some(json!({
- "expression": expr,
- "contextId": Some(1),
- })),
- )
- .await?;
-
- Ok(())
- }
-
- pub async fn run(&mut self) -> Result<(), AnyError> {
+ async fn run(&mut self) -> Result<(), AnyError> {
self
.watcher_communicator
.change_restart_mode(WatcherRestartMode::Manual);
@@ -252,3 +169,89 @@ impl HmrRunner {
}
}
}
+
+impl HmrRunner {
+ pub fn new(
+ emitter: Arc<Emitter>,
+ session: LocalInspectorSession,
+ watcher_communicator: Arc<WatcherCommunicator>,
+ ) -> Self {
+ Self {
+ session,
+ emitter,
+ watcher_communicator,
+ script_ids: HashMap::new(),
+ }
+ }
+
+ // TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs`
+ async fn enable_debugger(&mut self) -> Result<(), AnyError> {
+ self
+ .session
+ .post_message::<()>("Debugger.enable", None)
+ .await?;
+ self
+ .session
+ .post_message::<()>("Runtime.enable", None)
+ .await?;
+ Ok(())
+ }
+
+ // TODO(bartlomieju): this code is duplicated in `cli/tools/coverage/mod.rs`
+ async fn disable_debugger(&mut self) -> Result<(), AnyError> {
+ self
+ .session
+ .post_message::<()>("Debugger.disable", None)
+ .await?;
+ self
+ .session
+ .post_message::<()>("Runtime.disable", None)
+ .await?;
+ Ok(())
+ }
+
+ async fn set_script_source(
+ &mut self,
+ script_id: &str,
+ source: &str,
+ ) -> Result<cdp::SetScriptSourceResponse, AnyError> {
+ let result = self
+ .session
+ .post_message(
+ "Debugger.setScriptSource",
+ Some(json!({
+ "scriptId": script_id,
+ "scriptSource": source,
+ "allowTopFrameEditing": true,
+ })),
+ )
+ .await?;
+
+ Ok(serde_json::from_value::<cdp::SetScriptSourceResponse>(
+ result,
+ )?)
+ }
+
+ async fn dispatch_hmr_event(
+ &mut self,
+ script_id: &str,
+ ) -> Result<(), AnyError> {
+ let expr = format!(
+ "dispatchEvent(new CustomEvent(\"hmr\", {{ detail: {{ path: \"{}\" }} }}));",
+ script_id
+ );
+
+ let _result = self
+ .session
+ .post_message(
+ "Runtime.evaluate",
+ Some(json!({
+ "expression": expr,
+ "contextId": Some(1),
+ })),
+ )
+ .await?;
+
+ Ok(())
+ }
+}
diff --git a/cli/tools/upgrade.rs b/cli/tools/upgrade.rs
index 70141f571..efe7e707e 100644
--- a/cli/tools/upgrade.rs
+++ b/cli/tools/upgrade.rs
@@ -7,6 +7,7 @@ use crate::args::UpgradeFlags;
use crate::colors;
use crate::factory::CliFactory;
use crate::http_util::HttpClient;
+use crate::standalone::binary::unpack_into_dir;
use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
use crate::util::time;
@@ -30,11 +31,11 @@ use std::process::Command;
use std::sync::Arc;
use std::time::Duration;
-static ARCHIVE_NAME: Lazy<String> =
- Lazy::new(|| format!("deno-{}.zip", env!("TARGET")));
-
const RELEASE_URL: &str = "https://github.com/denoland/deno/releases";
+pub static ARCHIVE_NAME: Lazy<String> =
+ Lazy::new(|| format!("deno-{}.zip", env!("TARGET")));
+
// How often query server for new version. In hours.
const UPGRADE_CHECK_INTERVAL: i64 = 24;
@@ -501,7 +502,13 @@ pub async fn upgrade(
log::info!("Deno is upgrading to version {}", &install_version);
let temp_dir = tempfile::TempDir::new()?;
- let new_exe_path = unpack_into_dir(archive_data, cfg!(windows), &temp_dir)?;
+ let new_exe_path = unpack_into_dir(
+ "deno",
+ &ARCHIVE_NAME,
+ archive_data,
+ cfg!(windows),
+ &temp_dir,
+ )?;
fs::set_permissions(&new_exe_path, permissions)?;
check_exe(&new_exe_path)?;
@@ -628,71 +635,6 @@ async fn download_package(
}
}
-pub fn unpack_into_dir(
- archive_data: Vec<u8>,
- is_windows: bool,
- temp_dir: &tempfile::TempDir,
-) -> Result<PathBuf, AnyError> {
- const EXE_NAME: &str = "deno";
- let temp_dir_path = temp_dir.path();
- let exe_ext = if is_windows { "exe" } else { "" };
- let archive_path = temp_dir_path.join(EXE_NAME).with_extension("zip");
- let exe_path = temp_dir_path.join(EXE_NAME).with_extension(exe_ext);
- assert!(!exe_path.exists());
-
- let archive_ext = Path::new(&*ARCHIVE_NAME)
- .extension()
- .and_then(|ext| ext.to_str())
- .unwrap();
- let unpack_status = match archive_ext {
- "zip" if cfg!(windows) => {
- fs::write(&archive_path, &archive_data)?;
- Command::new("tar.exe")
- .arg("xf")
- .arg(&archive_path)
- .arg("-C")
- .arg(temp_dir_path)
- .spawn()
- .map_err(|err| {
- if err.kind() == std::io::ErrorKind::NotFound {
- std::io::Error::new(
- std::io::ErrorKind::NotFound,
- "`tar.exe` was not found in your PATH",
- )
- } else {
- err
- }
- })?
- .wait()?
- }
- "zip" => {
- fs::write(&archive_path, &archive_data)?;
- Command::new("unzip")
- .current_dir(temp_dir_path)
- .arg(&archive_path)
- .spawn()
- .map_err(|err| {
- if err.kind() == std::io::ErrorKind::NotFound {
- std::io::Error::new(
- std::io::ErrorKind::NotFound,
- "`unzip` was not found in your PATH, please install `unzip`",
- )
- } else {
- err
- }
- })?
- .wait()?
- }
- ext => bail!("Unsupported archive type: '{ext}'"),
- };
- if !unpack_status.success() {
- bail!("Failed to unpack archive.");
- }
- assert!(exe_path.exists());
- fs::remove_file(&archive_path)?;
- Ok(exe_path)
-}
-
fn replace_exe(from: &Path, to: &Path) -> Result<(), std::io::Error> {
if cfg!(windows) {
// On windows you cannot replace the currently running executable.
diff --git a/cli/worker.rs b/cli/worker.rs
index e897c1a86..3f75ebc5c 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -49,12 +49,8 @@ use tokio::select;
use crate::args::package_json::PackageJsonDeps;
use crate::args::DenoSubcommand;
use crate::args::StorageKeyResolver;
-use crate::emit::Emitter;
use crate::errors;
use crate::npm::CliNpmResolver;
-use crate::tools;
-use crate::tools::coverage::CoverageCollector;
-use crate::tools::run::hmr::HmrRunner;
use crate::util::checksum;
use crate::util::file_watcher::WatcherCommunicator;
use crate::util::file_watcher::WatcherRestartMode;
@@ -82,7 +78,29 @@ pub trait HasNodeSpecifierChecker: Send + Sync {
fn has_node_specifier(&self) -> bool;
}
-#[derive(Clone)]
+#[async_trait::async_trait(?Send)]
+pub trait HmrRunner: Send + Sync {
+ async fn start(&mut self) -> Result<(), AnyError>;
+ async fn stop(&mut self) -> Result<(), AnyError>;
+ async fn run(&mut self) -> Result<(), AnyError>;
+}
+
+#[async_trait::async_trait(?Send)]
+pub trait CoverageCollector: Send + Sync {
+ async fn start_collecting(&mut self) -> Result<(), AnyError>;
+ async fn stop_collecting(&mut self) -> Result<(), AnyError>;
+}
+
+pub type CreateHmrRunnerCb = Box<
+ dyn Fn(deno_core::LocalInspectorSession) -> Box<dyn HmrRunner> + Send + Sync,
+>;
+
+pub type CreateCoverageCollectorCb = Box<
+ dyn Fn(deno_core::LocalInspectorSession) -> Box<dyn CoverageCollector>
+ + Send
+ + Sync,
+>;
+
pub struct CliMainWorkerOptions {
pub argv: Vec<String>,
pub log_level: WorkerLogLevel,
@@ -104,6 +122,8 @@ pub struct CliMainWorkerOptions {
pub unstable: bool,
pub skip_op_registration: bool,
pub maybe_root_package_json_deps: Option<PackageJsonDeps>,
+ pub create_hmr_runner: Option<CreateHmrRunnerCb>,
+ pub create_coverage_collector: Option<CreateCoverageCollectorCb>,
}
struct SharedWorkerState {
@@ -119,7 +139,6 @@ struct SharedWorkerState {
module_loader_factory: Box<dyn ModuleLoaderFactory>,
root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
fs: Arc<dyn deno_fs::FileSystem>,
- emitter: Option<Arc<Emitter>>,
maybe_file_watcher_communicator: Option<Arc<WatcherCommunicator>>,
maybe_inspector_server: Option<Arc<InspectorServer>>,
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
@@ -324,42 +343,20 @@ impl CliMainWorker {
self.worker.evaluate_module(id).await
}
- pub async fn maybe_setup_coverage_collector(
- &mut self,
- ) -> Result<Option<CoverageCollector>, AnyError> {
- if let Some(coverage_dir) = &self.shared.options.coverage_dir {
- let session = self.worker.create_inspector_session().await;
-
- let coverage_dir = PathBuf::from(coverage_dir);
- let mut coverage_collector =
- tools::coverage::CoverageCollector::new(coverage_dir, session);
- self
- .worker
- .js_runtime
- .with_event_loop_future(
- coverage_collector.start_collecting().boxed_local(),
- PollEventLoopOptions::default(),
- )
- .await?;
- Ok(Some(coverage_collector))
- } else {
- Ok(None)
- }
- }
-
pub async fn maybe_setup_hmr_runner(
&mut self,
- ) -> Result<Option<HmrRunner>, AnyError> {
+ ) -> Result<Option<Box<dyn HmrRunner>>, AnyError> {
if !self.shared.options.hmr {
return Ok(None);
}
-
- let watcher_communicator =
- self.shared.maybe_file_watcher_communicator.clone().unwrap();
- let emitter = self.shared.emitter.clone().unwrap();
+ let Some(setup_hmr_runner) = self.shared.options.create_hmr_runner.as_ref()
+ else {
+ return Ok(None);
+ };
let session = self.worker.create_inspector_session().await;
- let mut hmr_runner = HmrRunner::new(emitter, session, watcher_communicator);
+
+ let mut hmr_runner = setup_hmr_runner(session);
self
.worker
@@ -369,10 +366,31 @@ impl CliMainWorker {
PollEventLoopOptions::default(),
)
.await?;
-
Ok(Some(hmr_runner))
}
+ pub async fn maybe_setup_coverage_collector(
+ &mut self,
+ ) -> Result<Option<Box<dyn CoverageCollector>>, AnyError> {
+ let Some(create_coverage_collector) =
+ self.shared.options.create_coverage_collector.as_ref()
+ else {
+ return Ok(None);
+ };
+
+ let session = self.worker.create_inspector_session().await;
+ let mut coverage_collector = create_coverage_collector(session);
+ self
+ .worker
+ .js_runtime
+ .with_event_loop_future(
+ coverage_collector.start_collecting().boxed_local(),
+ PollEventLoopOptions::default(),
+ )
+ .await?;
+ Ok(Some(coverage_collector))
+ }
+
pub fn execute_script_static(
&mut self,
name: &'static str,
@@ -400,7 +418,6 @@ impl CliMainWorkerFactory {
module_loader_factory: Box<dyn ModuleLoaderFactory>,
root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
fs: Arc<dyn deno_fs::FileSystem>,
- emitter: Option<Arc<Emitter>>,
maybe_file_watcher_communicator: Option<Arc<WatcherCommunicator>>,
maybe_inspector_server: Option<Arc<InspectorServer>>,
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
@@ -423,7 +440,6 @@ impl CliMainWorkerFactory {
compiled_wasm_module_store: Default::default(),
module_loader_factory,
root_cert_store_provider,
- emitter,
fs,
maybe_file_watcher_communicator,
maybe_inspector_server,
diff --git a/test_util/src/builders.rs b/test_util/src/builders.rs
index 862838dcb..9e9c64cf5 100644
--- a/test_util/src/builders.rs
+++ b/test_util/src/builders.rs
@@ -20,6 +20,7 @@ use os_pipe::pipe;
use crate::assertions::assert_wildcard_match;
use crate::deno_exe_path;
+use crate::denort_exe_path;
use crate::env_vars_for_jsr_tests;
use crate::env_vars_for_npm_tests;
use crate::fs::PathRef;
@@ -80,7 +81,7 @@ pub struct TestContextBuilder {
impl TestContextBuilder {
pub fn new() -> Self {
- Self::default()
+ Self::default().add_compile_env_vars()
}
pub fn for_npm() -> Self {
@@ -158,6 +159,13 @@ impl TestContextBuilder {
self
}
+ pub fn add_compile_env_vars(mut self) -> Self {
+ // The `denort` binary is in the same artifact directory as the `deno` binary.
+ let denort_bin = denort_exe_path();
+ self = self.env("DENORT_BIN", denort_bin.to_string());
+ self
+ }
+
pub fn add_jsr_env_vars(mut self) -> Self {
for (key, value) in env_vars_for_jsr_tests() {
self = self.env(key, value);
@@ -236,7 +244,7 @@ impl Default for TestContext {
impl TestContext {
pub fn with_http_server() -> Self {
- TestContextBuilder::default().use_http_server().build()
+ TestContextBuilder::new().use_http_server().build()
}
pub fn deno_dir(&self) -> &TempDir {
diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs
index b750cb99a..f8fbb8b65 100644
--- a/test_util/src/lib.rs
+++ b/test_util/src/lib.rs
@@ -140,6 +140,14 @@ pub fn deno_exe_path() -> PathRef {
PathRef::new(p)
}
+pub fn denort_exe_path() -> PathRef {
+ let mut p = target_dir().join("denort").to_path_buf();
+ if cfg!(windows) {
+ p.set_extension("exe");
+ }
+ PathRef::new(p)
+}
+
pub fn prebuilt_tool_path(tool: &str) -> PathRef {
let mut exe = tool.to_string();
exe.push_str(if cfg!(windows) { ".exe" } else { "" });