summaryrefslogtreecommitdiff
path: root/cli/util
diff options
context:
space:
mode:
Diffstat (limited to 'cli/util')
-rw-r--r--cli/util/extract.rs127
-rw-r--r--cli/util/file_watcher.rs26
-rw-r--r--cli/util/fs.rs16
-rw-r--r--cli/util/logger.rs3
-rw-r--r--cli/util/mod.rs1
-rw-r--r--cli/util/path.rs27
-rw-r--r--cli/util/progress_bar/renderer.rs8
-rw-r--r--cli/util/retry.rs41
-rw-r--r--cli/util/text_encoding.rs34
-rw-r--r--cli/util/v8.rs9
10 files changed, 233 insertions, 59 deletions
diff --git a/cli/util/extract.rs b/cli/util/extract.rs
index 841cf6eb0..be68202aa 100644
--- a/cli/util/extract.rs
+++ b/cli/util/extract.rs
@@ -64,7 +64,7 @@ fn extract_inner(
}) {
Ok(parsed) => {
let mut c = ExportCollector::default();
- c.visit_program(parsed.program_ref());
+ c.visit_program(parsed.program().as_ref());
c
}
Err(_) => ExportCollector::default(),
@@ -254,7 +254,11 @@ impl ExportCollector {
let mut import_specifiers = vec![];
if let Some(default_export) = &self.default_export {
- if !symbols_to_exclude.contains(default_export) {
+ // If the default export conflicts with a named export, a named one
+ // takes precedence.
+ if !symbols_to_exclude.contains(default_export)
+ && !self.named_exports.contains(default_export)
+ {
import_specifiers.push(ast::ImportSpecifier::Default(
ast::ImportDefaultSpecifier {
span: DUMMY_SP,
@@ -566,14 +570,14 @@ fn generate_pseudo_file(
})?;
let top_level_atoms = swc_utils::collect_decls_with_ctxt::<Atom, _>(
- parsed.program_ref(),
+ &parsed.program_ref(),
parsed.top_level_context(),
);
let transformed =
parsed
.program_ref()
- .clone()
+ .to_owned()
.fold_with(&mut as_folder(Transform {
specifier: &file.specifier,
base_file_specifier,
@@ -582,7 +586,10 @@ fn generate_pseudo_file(
wrap_kind,
}));
- let source = deno_ast::swc::codegen::to_code(&transformed);
+ let source = deno_ast::swc::codegen::to_code_with_comments(
+ Some(&parsed.comments().as_single_threaded()),
+ &transformed,
+ );
log::debug!("{}:\n{}", file.specifier, source);
@@ -1137,6 +1144,57 @@ Deno.test("file:///README.md$6-12.js", async ()=>{
media_type: MediaType::JavaScript,
}],
},
+ // https://github.com/denoland/deno/issues/26009
+ Test {
+ input: Input {
+ source: r#"
+/**
+ * ```ts
+ * console.log(Foo)
+ * ```
+ */
+export class Foo {}
+export default Foo
+"#,
+ specifier: "file:///main.ts",
+ },
+ expected: vec![Expected {
+ source: r#"import { Foo } from "file:///main.ts";
+Deno.test("file:///main.ts$3-6.ts", async ()=>{
+ console.log(Foo);
+});
+"#,
+ specifier: "file:///main.ts$3-6.ts",
+ media_type: MediaType::TypeScript,
+ }],
+ },
+ // https://github.com/denoland/deno/issues/26728
+ Test {
+ input: Input {
+ source: r#"
+/**
+ * ```ts
+ * // @ts-expect-error: can only add numbers
+ * add('1', '2');
+ * ```
+ */
+export function add(first: number, second: number) {
+ return first + second;
+}
+"#,
+ specifier: "file:///main.ts",
+ },
+ expected: vec![Expected {
+ source: r#"import { add } from "file:///main.ts";
+Deno.test("file:///main.ts$3-7.ts", async ()=>{
+ // @ts-expect-error: can only add numbers
+ add('1', '2');
+});
+"#,
+ specifier: "file:///main.ts$3-7.ts",
+ media_type: MediaType::TypeScript,
+ }],
+ },
];
for test in tests {
@@ -1326,6 +1384,53 @@ assertEquals(add(1, 2), 3);
media_type: MediaType::JavaScript,
}],
},
+ // https://github.com/denoland/deno/issues/26009
+ Test {
+ input: Input {
+ source: r#"
+/**
+ * ```ts
+ * console.log(Foo)
+ * ```
+ */
+export class Foo {}
+export default Foo
+"#,
+ specifier: "file:///main.ts",
+ },
+ expected: vec![Expected {
+ source: r#"import { Foo } from "file:///main.ts";
+console.log(Foo);
+"#,
+ specifier: "file:///main.ts$3-6.ts",
+ media_type: MediaType::TypeScript,
+ }],
+ },
+ // https://github.com/denoland/deno/issues/26728
+ Test {
+ input: Input {
+ source: r#"
+/**
+ * ```ts
+ * // @ts-expect-error: can only add numbers
+ * add('1', '2');
+ * ```
+ */
+export function add(first: number, second: number) {
+ return first + second;
+}
+"#,
+ specifier: "file:///main.ts",
+ },
+ expected: vec![Expected {
+ source: r#"import { add } from "file:///main.ts";
+// @ts-expect-error: can only add numbers
+add('1', '2');
+"#,
+ specifier: "file:///main.ts$3-7.ts",
+ media_type: MediaType::TypeScript,
+ }],
+ },
];
for test in tests {
@@ -1366,7 +1471,7 @@ assertEquals(add(1, 2), 3);
})
.unwrap();
- collector.visit_program(parsed.program_ref());
+ parsed.program_ref().visit_with(&mut collector);
collector
}
@@ -1581,6 +1686,16 @@ declare global {
named_expected: atom_set!(),
default_expected: None,
},
+ // The identifier `Foo` conflicts, but `ExportCollector` doesn't do
+ // anything about it. It is handled by `to_import_specifiers` method.
+ Test {
+ input: r#"
+export class Foo {}
+export default Foo
+"#,
+ named_expected: atom_set!("Foo"),
+ default_expected: Some("Foo".into()),
+ },
];
for test in tests {
diff --git a/cli/util/file_watcher.rs b/cli/util/file_watcher.rs
index d92d880bc..eb3fb8c60 100644
--- a/cli/util/file_watcher.rs
+++ b/cli/util/file_watcher.rs
@@ -30,7 +30,7 @@ use tokio::sync::mpsc;
use tokio::sync::mpsc::UnboundedReceiver;
use tokio::time::sleep;
-const CLEAR_SCREEN: &str = "\x1B[2J\x1B[1;1H";
+const CLEAR_SCREEN: &str = "\x1B[H\x1B[2J\x1B[3J";
const DEBOUNCE_INTERVAL: Duration = Duration::from_millis(200);
struct DebouncedReceiver {
@@ -73,7 +73,6 @@ impl DebouncedReceiver {
}
}
-#[allow(clippy::print_stderr)]
async fn error_handler<F>(watch_future: F) -> bool
where
F: Future<Output = Result<(), AnyError>>,
@@ -84,7 +83,7 @@ where
Some(e) => format_js_error(e),
None => format!("{err:?}"),
};
- eprintln!(
+ log::error!(
"{}: {}",
colors::red_bold("error"),
error_string.trim_start_matches("error: ")
@@ -128,19 +127,12 @@ impl PrintConfig {
}
}
-fn create_print_after_restart_fn(
- banner: &'static str,
- clear_screen: bool,
-) -> impl Fn() {
+fn create_print_after_restart_fn(clear_screen: bool) -> impl Fn() {
move || {
#[allow(clippy::print_stderr)]
if clear_screen && std::io::stderr().is_terminal() {
eprint!("{}", CLEAR_SCREEN);
}
- info!(
- "{} File change detected! Restarting!",
- colors::intense_blue(banner),
- );
}
}
@@ -188,7 +180,15 @@ impl WatcherCommunicator {
}
pub fn print(&self, msg: String) {
- log::info!("{} {}", self.banner, msg);
+ log::info!("{} {}", self.banner, colors::gray(msg));
+ }
+
+ pub fn show_path_changed(&self, changed_paths: Option<Vec<PathBuf>>) {
+ if let Some(paths) = changed_paths {
+ self.print(
+ format!("Restarting! File change detected: {:?}", paths[0]).to_string(),
+ )
+ }
}
}
@@ -264,7 +264,7 @@ where
clear_screen,
} = print_config;
- let print_after_restart = create_print_after_restart_fn(banner, clear_screen);
+ let print_after_restart = create_print_after_restart_fn(clear_screen);
let watcher_communicator = Arc::new(WatcherCommunicator {
paths_to_watch_tx: paths_to_watch_tx.clone(),
changed_paths_rx: changed_paths_rx.resubscribe(),
diff --git a/cli/util/fs.rs b/cli/util/fs.rs
index a021ec19c..d36c02242 100644
--- a/cli/util/fs.rs
+++ b/cli/util/fs.rs
@@ -160,11 +160,11 @@ fn atomic_write_file(
data: &[u8],
) -> std::io::Result<()> {
fs.write_file(temp_file_path, data)?;
- fs.rename_file(temp_file_path, file_path).map_err(|err| {
- // clean up the created temp file on error
- let _ = fs.remove_file(temp_file_path);
- err
- })
+ fs.rename_file(temp_file_path, file_path)
+ .inspect_err(|_err| {
+ // clean up the created temp file on error
+ let _ = fs.remove_file(temp_file_path);
+ })
}
let temp_file_path = get_atomic_file_path(file_path);
@@ -277,7 +277,7 @@ pub fn write_file_2<T: AsRef<[u8]>>(
/// Similar to `std::fs::canonicalize()` but strips UNC prefixes on Windows.
pub fn canonicalize_path(path: &Path) -> Result<PathBuf, Error> {
- Ok(deno_core::strip_unc_prefix(path.canonicalize()?))
+ Ok(deno_path_util::strip_unc_prefix(path.canonicalize()?))
}
/// Canonicalizes a path which might be non-existent by going up the
@@ -565,7 +565,9 @@ pub fn symlink_dir(oldpath: &Path, newpath: &Path) -> Result<(), Error> {
use std::os::windows::fs::symlink_dir;
symlink_dir(oldpath, newpath).map_err(|err| {
if let Some(code) = err.raw_os_error() {
- if code as u32 == winapi::shared::winerror::ERROR_PRIVILEGE_NOT_HELD {
+ if code as u32 == winapi::shared::winerror::ERROR_PRIVILEGE_NOT_HELD
+ || code as u32 == winapi::shared::winerror::ERROR_INVALID_FUNCTION
+ {
return err_mapper(err, Some(ErrorKind::PermissionDenied));
}
}
diff --git a/cli/util/logger.rs b/cli/util/logger.rs
index cdc89411f..f76663df2 100644
--- a/cli/util/logger.rs
+++ b/cli/util/logger.rs
@@ -29,6 +29,7 @@ impl log::Log for CliLogger {
// thread's state
DrawThread::hide();
self.0.log(record);
+ deno_runtime::ops::otel::handle_log(record);
DrawThread::show();
}
}
@@ -65,6 +66,8 @@ pub fn init(maybe_level: Option<log::Level>) {
.filter_module("swc_ecma_parser", log::LevelFilter::Error)
// Suppress span lifecycle logs since they are too verbose
.filter_module("tracing::span", log::LevelFilter::Off)
+ // for deno_compile, this is too verbose
+ .filter_module("editpe", log::LevelFilter::Error)
.format(|buf, record| {
let mut target = record.target().to_string();
if let Some(line_no) = record.line() {
diff --git a/cli/util/mod.rs b/cli/util/mod.rs
index e59b09d2c..f81a74c44 100644
--- a/cli/util/mod.rs
+++ b/cli/util/mod.rs
@@ -14,6 +14,7 @@ pub mod logger;
pub mod path;
pub mod progress_bar;
pub mod result;
+pub mod retry;
pub mod sync;
pub mod text_encoding;
pub mod unix;
diff --git a/cli/util/path.rs b/cli/util/path.rs
index e4ae6e7cb..58bed664f 100644
--- a/cli/util/path.rs
+++ b/cli/util/path.rs
@@ -42,21 +42,6 @@ pub fn get_extension(file_path: &Path) -> Option<String> {
.map(|e| e.to_lowercase());
}
-pub fn specifier_has_extension(
- specifier: &ModuleSpecifier,
- searching_ext: &str,
-) -> bool {
- let Some((_, ext)) = specifier.path().rsplit_once('.') else {
- return false;
- };
- let searching_ext = searching_ext.strip_prefix('.').unwrap_or(searching_ext);
- debug_assert!(!searching_ext.contains('.')); // exts like .d.ts are not implemented here
- if ext.len() != searching_ext.len() {
- return false;
- }
- ext.eq_ignore_ascii_case(searching_ext)
-}
-
pub fn get_atomic_dir_path(file_path: &Path) -> PathBuf {
let rand = gen_rand_path_component();
let new_file_name = format!(
@@ -351,18 +336,6 @@ mod test {
}
#[test]
- fn test_specifier_has_extension() {
- fn get(specifier: &str, ext: &str) -> bool {
- specifier_has_extension(&ModuleSpecifier::parse(specifier).unwrap(), ext)
- }
-
- assert!(get("file:///a/b/c.ts", "ts"));
- assert!(get("file:///a/b/c.ts", ".ts"));
- assert!(!get("file:///a/b/c.ts", ".cts"));
- assert!(get("file:///a/b/c.CtS", ".cts"));
- }
-
- #[test]
fn test_to_percent_decoded_str() {
let str = to_percent_decoded_str("%F0%9F%A6%95");
assert_eq!(str, "🦕");
diff --git a/cli/util/progress_bar/renderer.rs b/cli/util/progress_bar/renderer.rs
index a83ceb333..6b08dada1 100644
--- a/cli/util/progress_bar/renderer.rs
+++ b/cli/util/progress_bar/renderer.rs
@@ -193,10 +193,16 @@ impl ProgressBarRenderer for TextOnlyProgressBarRenderer {
}
};
+ // TODO(@marvinhagemeister): We're trying to reconstruct the original
+ // specifier from the resolved one, but we lack the information about
+ // private registries URLs and other things here.
let message = display_entry
.message
.replace("https://registry.npmjs.org/", "npm:")
- .replace("https://jsr.io/", "jsr:");
+ .replace("https://jsr.io/", "jsr:")
+ .replace("%2f", "/")
+ .replace("%2F", "/");
+
display_str.push_str(
&colors::gray(format!(" - {}{}\n", message, bytes_text)).to_string(),
);
diff --git a/cli/util/retry.rs b/cli/util/retry.rs
new file mode 100644
index 000000000..a8febe60d
--- /dev/null
+++ b/cli/util/retry.rs
@@ -0,0 +1,41 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use std::future::Future;
+use std::time::Duration;
+
+pub fn retry<
+ F: FnMut() -> Fut,
+ T,
+ E,
+ Fut: Future<Output = Result<T, E>>,
+ ShouldRetry: FnMut(&E) -> bool,
+>(
+ mut f: F,
+ mut should_retry: ShouldRetry,
+) -> impl Future<Output = Result<T, E>> {
+ const WAITS: [Duration; 3] = [
+ Duration::from_millis(100),
+ Duration::from_millis(250),
+ Duration::from_millis(500),
+ ];
+
+ let mut waits = WAITS.into_iter();
+ async move {
+ let mut first_result = None;
+ loop {
+ let result = f().await;
+ match result {
+ Ok(r) => return Ok(r),
+ Err(e) if !should_retry(&e) => return Err(e),
+ _ => {}
+ }
+ if first_result.is_none() {
+ first_result = Some(result);
+ }
+ let Some(wait) = waits.next() else {
+ return first_result.unwrap();
+ };
+ tokio::time::sleep(wait).await;
+ }
+ }
+}
diff --git a/cli/util/text_encoding.rs b/cli/util/text_encoding.rs
index d2e0832c9..8524e63eb 100644
--- a/cli/util/text_encoding.rs
+++ b/cli/util/text_encoding.rs
@@ -1,6 +1,8 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use std::borrow::Cow;
use std::ops::Range;
+use std::sync::Arc;
use base64::prelude::BASE64_STANDARD;
use base64::Engine;
@@ -9,6 +11,15 @@ use deno_core::ModuleSourceCode;
static SOURCE_MAP_PREFIX: &[u8] =
b"//# sourceMappingURL=data:application/json;base64,";
+pub fn from_utf8_lossy_owned(bytes: Vec<u8>) -> String {
+ match String::from_utf8_lossy(&bytes) {
+ Cow::Owned(code) => code,
+ // SAFETY: `String::from_utf8_lossy` guarantees that the result is valid
+ // UTF-8 if `Cow::Borrowed` is returned.
+ Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(bytes) },
+ }
+}
+
pub fn source_map_from_code(code: &[u8]) -> Option<Vec<u8>> {
let range = find_source_map_range(code)?;
let source_map_range = &code[range];
@@ -85,6 +96,29 @@ fn find_source_map_range(code: &[u8]) -> Option<Range<usize>> {
}
}
+/// Converts an `Arc<str>` to an `Arc<[u8]>`.
+#[allow(dead_code)]
+pub fn arc_str_to_bytes(arc_str: Arc<str>) -> Arc<[u8]> {
+ let raw = Arc::into_raw(arc_str);
+ // SAFETY: This is safe because they have the same memory layout.
+ unsafe { Arc::from_raw(raw as *const [u8]) }
+}
+
+/// Converts an `Arc<u8>` to an `Arc<str>` if able.
+#[allow(dead_code)]
+pub fn arc_u8_to_arc_str(
+ arc_u8: Arc<[u8]>,
+) -> Result<Arc<str>, std::str::Utf8Error> {
+ // Check that the string is valid UTF-8.
+ std::str::from_utf8(&arc_u8)?;
+ // SAFETY: the string is valid UTF-8, and the layout Arc<[u8]> is the same as
+ // Arc<str>. This is proven by the From<Arc<str>> impl for Arc<[u8]> from the
+ // standard library.
+ Ok(unsafe {
+ std::mem::transmute::<std::sync::Arc<[u8]>, std::sync::Arc<str>>(arc_u8)
+ })
+}
+
#[cfg(test)]
mod tests {
use std::sync::Arc;
diff --git a/cli/util/v8.rs b/cli/util/v8.rs
index fb16e67b7..6e690e6f3 100644
--- a/cli/util/v8.rs
+++ b/cli/util/v8.rs
@@ -46,15 +46,14 @@ pub fn init_v8_flags(
.skip(1)
.collect::<Vec<_>>();
- #[allow(clippy::print_stderr)]
if !unrecognized_v8_flags.is_empty() {
for f in unrecognized_v8_flags {
- eprintln!("error: V8 did not recognize flag '{f}'");
+ log::error!("error: V8 did not recognize flag '{f}'");
}
- eprintln!("\nFor a list of V8 flags, use '--v8-flags=--help'");
- std::process::exit(1);
+ log::error!("\nFor a list of V8 flags, use '--v8-flags=--help'");
+ deno_runtime::exit(1);
}
if v8_flags_includes_help {
- std::process::exit(0);
+ deno_runtime::exit(0);
}
}