mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 15:49:44 -05:00
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.
This commit is contained in:
parent
492a9fbb91
commit
a68eb3fcc3
14 changed files with 453 additions and 254 deletions
12
.github/workflows/ci.generate.ts
vendored
12
.github/workflows/ci.generate.ts
vendored
|
@ -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"),
|
||||
|
|
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
82
cli/mainrt.rs
Normal file
82
cli/mainrt.rs
Normal file
|
@ -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(¤t_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));
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 { "" });
|
||||
|
|
Loading…
Reference in a new issue