From d22195e7416e7923e2868e3f250abb457f115fc6 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 14 Oct 2024 12:41:34 +0530 Subject: fix(ext/napi): pass user context to napi_threadsafe_fn finalizers (#26229) Fixes https://github.com/denoland/deno/issues/26228 --- cli/napi/node_api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cli') diff --git a/cli/napi/node_api.rs b/cli/napi/node_api.rs index 2efb71c26..4497a4695 100644 --- a/cli/napi/node_api.rs +++ b/cli/napi/node_api.rs @@ -692,7 +692,7 @@ impl Drop for TsFn { if let Some(finalizer) = self.thread_finalize_cb { unsafe { - (finalizer)(self.env as _, self.thread_finalize_data, ptr::null_mut()); + (finalizer)(self.env as _, self.thread_finalize_data, self.context); } } } -- cgit v1.2.3 From 3eda179220370e9c9093427fb694eed746548008 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Mon, 14 Oct 2024 13:29:50 +0100 Subject: feat(cli): improve deno info output for npm packages (#25906) --- cli/tools/info.rs | 65 +++++++++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 38 deletions(-) (limited to 'cli') diff --git a/cli/tools/info.rs b/cli/tools/info.rs index 1c83abe3b..7f8d68ae1 100644 --- a/cli/tools/info.rs +++ b/cli/tools/info.rs @@ -17,6 +17,7 @@ use deno_graph::Module; use deno_graph::ModuleError; use deno_graph::ModuleGraph; use deno_graph::Resolution; +use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::NpmPackageId; use deno_npm::NpmResolutionPackage; @@ -47,6 +48,7 @@ pub async fn info( let module_graph_creator = factory.module_graph_creator().await?; let npm_resolver = factory.npm_resolver().await?; let maybe_lockfile = cli_options.maybe_lockfile(); + let npmrc = cli_options.npmrc(); let resolver = factory.workspace_resolver().await?; let maybe_import_specifier = @@ -88,7 +90,8 @@ pub async fn info( JSON_SCHEMA_VERSION.into(), ); } - add_npm_packages_to_json(&mut json_graph, npm_resolver.as_ref()); + + add_npm_packages_to_json(&mut json_graph, npm_resolver.as_ref(), npmrc); display::write_json_to_stdout(&json_graph)?; } else { let mut output = String::new(); @@ -185,6 +188,7 @@ fn print_cache_info( fn add_npm_packages_to_json( json: &mut serde_json::Value, npm_resolver: &dyn CliNpmResolver, + npmrc: &ResolvedNpmRc, ) { let Some(npm_resolver) = npm_resolver.as_managed() else { return; // does not include byonm to deno info's output @@ -195,45 +199,28 @@ fn add_npm_packages_to_json( let json = json.as_object_mut().unwrap(); let modules = json.get_mut("modules").and_then(|m| m.as_array_mut()); if let Some(modules) = modules { - if modules.len() == 1 - && modules[0].get("kind").and_then(|k| k.as_str()) == Some("npm") - { - // If there is only one module and it's "external", then that means - // someone provided an npm specifier as a cli argument. In this case, - // we want to show which npm package the cli argument resolved to. - let module = &mut modules[0]; - let maybe_package = module - .get("specifier") - .and_then(|k| k.as_str()) - .and_then(|specifier| NpmPackageNvReference::from_str(specifier).ok()) - .and_then(|package_ref| { - snapshot - .resolve_package_from_deno_module(package_ref.nv()) - .ok() - }); - if let Some(pkg) = maybe_package { - if let Some(module) = module.as_object_mut() { - module - .insert("npmPackage".to_string(), pkg.id.as_serialized().into()); - } - } - } else { - // Filter out npm package references from the modules and instead - // have them only listed as dependencies. This is done because various - // npm specifiers modules in the graph are really just unresolved - // references. So there could be listed multiple npm specifiers - // that would resolve to a single npm package. - for i in (0..modules.len()).rev() { - if matches!( - modules[i].get("kind").and_then(|k| k.as_str()), - Some("npm") | Some("external") - ) { - modules.remove(i); + for module in modules.iter_mut() { + if matches!(module.get("kind").and_then(|k| k.as_str()), Some("npm")) { + // If there is only one module and it's "external", then that means + // someone provided an npm specifier as a cli argument. In this case, + // we want to show which npm package the cli argument resolved to. + let maybe_package = module + .get("specifier") + .and_then(|k| k.as_str()) + .and_then(|specifier| NpmPackageNvReference::from_str(specifier).ok()) + .and_then(|package_ref| { + snapshot + .resolve_package_from_deno_module(package_ref.nv()) + .ok() + }); + if let Some(pkg) = maybe_package { + if let Some(module) = module.as_object_mut() { + module + .insert("npmPackage".to_string(), pkg.id.as_serialized().into()); + } } } - } - for module in modules.iter_mut() { let dependencies = module .get_mut("dependencies") .and_then(|d| d.as_array_mut()); @@ -265,7 +252,7 @@ fn add_npm_packages_to_json( let mut json_packages = serde_json::Map::with_capacity(sorted_packages.len()); for pkg in sorted_packages { let mut kv = serde_json::Map::new(); - kv.insert("name".to_string(), pkg.id.nv.name.to_string().into()); + kv.insert("name".to_string(), pkg.id.nv.name.clone().into()); kv.insert("version".to_string(), pkg.id.nv.version.to_string().into()); let mut deps = pkg.dependencies.values().collect::>(); deps.sort(); @@ -274,6 +261,8 @@ fn add_npm_packages_to_json( .map(|id| serde_json::Value::String(id.as_serialized())) .collect::>(); kv.insert("dependencies".to_string(), deps.into()); + let registry_url = npmrc.get_registry_url(&pkg.id.nv.name); + kv.insert("registryUrl".to_string(), registry_url.to_string().into()); json_packages.insert(pkg.id.as_serialized(), kv.into()); } -- cgit v1.2.3 From dfbf03eee798da4f42a1bffeebd8edd1d0095406 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 14 Oct 2024 18:01:51 +0530 Subject: perf: use fast calls for microtask ops (#26236) Updates deno_core to 0.312.0 --- cli/util/fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cli') diff --git a/cli/util/fs.rs b/cli/util/fs.rs index a021ec19c..2ad3affc8 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -277,7 +277,7 @@ pub fn write_file_2>( /// Similar to `std::fs::canonicalize()` but strips UNC prefixes on Windows. pub fn canonicalize_path(path: &Path) -> Result { - 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 -- cgit v1.2.3 From c5449d71da2d623e866d733b6db180a6f94ff7c6 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 14 Oct 2024 15:35:52 -0400 Subject: fix(install): support installing npm package with alias (#26246) Just tried this out today and it wasn't properly implemented in https://github.com/denoland/deno/pull/24156 --- cli/tools/registry/pm.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'cli') diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index 5dc042620..3276acfbf 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -363,7 +363,14 @@ fn package_json_dependency_entry( selected: SelectedPackage, ) -> (String, String) { if let Some(npm_package) = selected.package_name.strip_prefix("npm:") { - (npm_package.into(), selected.version_req) + if selected.import_name == npm_package { + (npm_package.into(), selected.version_req) + } else { + ( + selected.import_name, + format!("npm:{}@{}", npm_package, selected.version_req), + ) + } } else if let Some(jsr_package) = selected.package_name.strip_prefix("jsr:") { let jsr_package = jsr_package.strip_prefix('@').unwrap_or(jsr_package); let scope_replaced = jsr_package.replace('/', "__"); @@ -741,6 +748,9 @@ fn generate_imports(mut packages_to_version: Vec<(String, String)>) -> String { let mut contents = vec![]; let len = packages_to_version.len(); for (index, (package, version)) in packages_to_version.iter().enumerate() { + if index == 0 { + contents.push(String::new()); // force a newline at the start + } // TODO(bartlomieju): fix it, once we start support specifying version on the cli contents.push(format!("\"{}\": \"{}\"", package, version)); if index != len - 1 { -- cgit v1.2.3 From 1a0cb5b5312941521ab021cfe9eaed498f35900b Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 14 Oct 2024 20:48:39 -0400 Subject: feat(unstable): `--unstable-detect-cjs` for respecting explicit `"type": "commonjs"` (#26149) When using the `--unstable-detect-cjs` flag or adding `"unstable": ["detect-cjs"]` to a deno.json, it will make a JS file CJS if the closest package.json contains `"type": "commonjs"` and the file is not an ESM module (no TLA, no `import.meta`, no `import`/`export`). --- cli/args/flags.rs | 115 ++++++++++++++++++++++++---------------- cli/args/mod.rs | 31 +++++------ cli/cache/mod.rs | 57 +++++++++++++++++++- cli/cache/parsed_source.rs | 40 ++++++++++++++ cli/factory.rs | 13 +++++ cli/graph_util.rs | 11 ++++ cli/module_loader.rs | 89 ++++++++++++++----------------- cli/node.rs | 29 +++++++--- cli/resolver.rs | 50 ++++++++--------- cli/schemas/config-file.v1.json | 1 + cli/standalone/binary.rs | 1 + cli/standalone/mod.rs | 5 +- cli/tools/compile.rs | 10 ++++ cli/util/text_encoding.rs | 18 +++++++ cli/worker.rs | 32 +++++++++-- 15 files changed, 348 insertions(+), 154 deletions(-) (limited to 'cli') diff --git a/cli/args/flags.rs b/cli/args/flags.rs index d59e5ac1a..acaf74a67 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -575,7 +575,8 @@ fn parse_packages_allowed_scripts(s: &str) -> Result { pub struct UnstableConfig { // TODO(bartlomieju): remove in Deno 2.5 pub legacy_flag_enabled: bool, // --unstable - pub bare_node_builtins: bool, // --unstable-bare-node-builts + pub bare_node_builtins: bool, + pub detect_cjs: bool, pub sloppy_imports: bool, pub features: Vec, // --unstabe-kv --unstable-cron } @@ -1528,7 +1529,7 @@ pub fn clap_root() -> Command { ); run_args(Command::new("deno"), true) - .args(unstable_args(UnstableArgsConfig::ResolutionAndRuntime)) + .with_unstable_args(UnstableArgsConfig::ResolutionAndRuntime) .next_line_help(false) .bin_name("deno") .styles( @@ -1630,7 +1631,7 @@ fn command( ) -> Command { Command::new(name) .about(about) - .args(unstable_args(unstable_args_config)) + .with_unstable_args(unstable_args_config) } fn help_subcommand(app: &Command) -> Command { @@ -4142,23 +4143,29 @@ enum UnstableArgsConfig { ResolutionAndRuntime, } -struct UnstableArgsIter { - idx: usize, - cfg: UnstableArgsConfig, +trait CommandExt { + fn with_unstable_args(self, cfg: UnstableArgsConfig) -> Self; } -impl Iterator for UnstableArgsIter { - type Item = Arg; +impl CommandExt for Command { + fn with_unstable_args(self, cfg: UnstableArgsConfig) -> Self { + let mut next_display_order = { + let mut value = 1000; + move || { + value += 1; + value + } + }; - fn next(&mut self) -> Option { - let arg = if self.idx == 0 { + let mut cmd = self.arg( Arg::new("unstable") - .long("unstable") - .help(cstr!("Enable all unstable features and APIs. Instead of using this flag, consider enabling individual unstable features + .long("unstable") + .help(cstr!("Enable all unstable features and APIs. Instead of using this flag, consider enabling individual unstable features To view the list of individual unstable feature flags, run this command again with --help=unstable")) - .action(ArgAction::SetTrue) - .hide(matches!(self.cfg, UnstableArgsConfig::None)) - } else if self.idx == 1 { + .action(ArgAction::SetTrue) + .hide(matches!(cfg, UnstableArgsConfig::None)) + .display_order(next_display_order()) + ).arg( Arg::new("unstable-bare-node-builtins") .long("unstable-bare-node-builtins") .help("Enable unstable bare node builtins feature") @@ -4166,20 +4173,36 @@ impl Iterator for UnstableArgsIter { .value_parser(FalseyValueParser::new()) .action(ArgAction::SetTrue) .hide(true) - .long_help(match self.cfg { + .long_help(match cfg { + UnstableArgsConfig::None => None, + UnstableArgsConfig::ResolutionOnly + | UnstableArgsConfig::ResolutionAndRuntime => Some("true"), + }) + .help_heading(UNSTABLE_HEADING) + .display_order(next_display_order()), + ).arg( + Arg::new("unstable-detect-cjs") + .long("unstable-detect-cjs") + .help("Reads the package.json type field in a project to treat .js files as .cjs") + .value_parser(FalseyValueParser::new()) + .action(ArgAction::SetTrue) + .hide(true) + .long_help(match cfg { UnstableArgsConfig::None => None, UnstableArgsConfig::ResolutionOnly | UnstableArgsConfig::ResolutionAndRuntime => Some("true"), }) .help_heading(UNSTABLE_HEADING) - } else if self.idx == 2 { + .display_order(next_display_order()) + ).arg( Arg::new("unstable-byonm") .long("unstable-byonm") .value_parser(FalseyValueParser::new()) .action(ArgAction::SetTrue) .hide(true) .help_heading(UNSTABLE_HEADING) - } else if self.idx == 3 { + .display_order(next_display_order()), + ).arg( Arg::new("unstable-sloppy-imports") .long("unstable-sloppy-imports") .help("Enable unstable resolving of specifiers by extension probing, .js to .ts, and directory probing") @@ -4187,40 +4210,39 @@ impl Iterator for UnstableArgsIter { .value_parser(FalseyValueParser::new()) .action(ArgAction::SetTrue) .hide(true) - .long_help(match self.cfg { + .long_help(match cfg { UnstableArgsConfig::None => None, UnstableArgsConfig::ResolutionOnly | UnstableArgsConfig::ResolutionAndRuntime => Some("true") }) .help_heading(UNSTABLE_HEADING) - } else if self.idx > 3 { - let granular_flag = crate::UNSTABLE_GRANULAR_FLAGS.get(self.idx - 4)?; - Arg::new(format!("unstable-{}", granular_flag.name)) - .long(format!("unstable-{}", granular_flag.name)) - .help(granular_flag.help_text) - .action(ArgAction::SetTrue) - .hide(true) - .help_heading(UNSTABLE_HEADING) - // we don't render long help, so using it here as a sort of metadata - .long_help(if granular_flag.show_in_help { - match self.cfg { - UnstableArgsConfig::None | UnstableArgsConfig::ResolutionOnly => { - None + .display_order(next_display_order()) + ); + + for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS.iter() { + cmd = cmd.arg( + Arg::new(format!("unstable-{}", granular_flag.name)) + .long(format!("unstable-{}", granular_flag.name)) + .help(granular_flag.help_text) + .action(ArgAction::SetTrue) + .hide(true) + .help_heading(UNSTABLE_HEADING) + // we don't render long help, so using it here as a sort of metadata + .long_help(if granular_flag.show_in_help { + match cfg { + UnstableArgsConfig::None | UnstableArgsConfig::ResolutionOnly => { + None + } + UnstableArgsConfig::ResolutionAndRuntime => Some("true"), } - UnstableArgsConfig::ResolutionAndRuntime => Some("true"), - } - } else { - None - }) - } else { - return None; - }; - self.idx += 1; - Some(arg.display_order(self.idx + 1000)) - } -} + } else { + None + }) + .display_order(next_display_order()), + ); + } -fn unstable_args(cfg: UnstableArgsConfig) -> impl IntoIterator { - UnstableArgsIter { idx: 0, cfg } + cmd + } } fn allow_scripts_arg_parse( @@ -5678,6 +5700,7 @@ fn unstable_args_parse( flags.unstable_config.bare_node_builtins = matches.get_flag("unstable-bare-node-builtins"); + flags.unstable_config.detect_cjs = matches.get_flag("unstable-detect-cjs"); flags.unstable_config.sloppy_imports = matches.get_flag("unstable-sloppy-imports"); diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 07906a86a..f905e186b 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -1576,6 +1576,11 @@ impl CliOptions { || self.workspace().has_unstable("bare-node-builtins") } + pub fn unstable_detect_cjs(&self) -> bool { + self.flags.unstable_config.detect_cjs + || self.workspace().has_unstable("detect-cjs") + } + fn byonm_enabled(&self) -> bool { // check if enabled via unstable self.node_modules_dir().ok().flatten() == Some(NodeModulesDirMode::Manual) @@ -1620,21 +1625,17 @@ impl CliOptions { }); if !from_config_file.is_empty() { - // collect unstable granular flags - let mut all_valid_unstable_flags: Vec<&str> = - crate::UNSTABLE_GRANULAR_FLAGS - .iter() - .map(|granular_flag| granular_flag.name) - .collect(); - - let mut another_unstable_flags = Vec::from([ - "sloppy-imports", - "byonm", - "bare-node-builtins", - "fmt-component", - ]); - // add more unstable flags to the same vector holding granular flags - all_valid_unstable_flags.append(&mut another_unstable_flags); + let all_valid_unstable_flags: Vec<&str> = crate::UNSTABLE_GRANULAR_FLAGS + .iter() + .map(|granular_flag| granular_flag.name) + .chain([ + "sloppy-imports", + "byonm", + "bare-node-builtins", + "fmt-component", + "detect-cjs", + ]) + .collect(); // check and warn if the unstable flag of config file isn't supported, by // iterating through the vector holding the unstable flags diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index 628502c50..ded163b4e 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -9,10 +9,13 @@ use crate::file_fetcher::FetchPermissionsOptionRef; use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileOrRedirect; use crate::npm::CliNpmResolver; +use crate::resolver::CliNodeResolver; use crate::util::fs::atomic_write_file_with_retries; use crate::util::fs::atomic_write_file_with_retries_and_fs; use crate::util::fs::AtomicWriteFileFsAdapter; use crate::util::path::specifier_has_extension; +use crate::util::text_encoding::arc_str_to_bytes; +use crate::util::text_encoding::from_utf8_lossy_owned; use deno_ast::MediaType; use deno_core::futures; @@ -57,6 +60,7 @@ pub use fast_check::FastCheckCache; pub use incremental::IncrementalCache; pub use module_info::ModuleInfoCache; pub use node::NodeAnalysisCache; +pub use parsed_source::EsmOrCjsChecker; pub use parsed_source::LazyGraphSourceParser; pub use parsed_source::ParsedSourceCache; @@ -177,37 +181,46 @@ pub struct FetchCacherOptions { pub permissions: PermissionsContainer, /// If we're publishing for `deno publish`. pub is_deno_publish: bool, + pub unstable_detect_cjs: bool, } /// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides /// a concise interface to the DENO_DIR when building module graphs. pub struct FetchCacher { - file_fetcher: Arc, pub file_header_overrides: HashMap>, + esm_or_cjs_checker: Arc, + file_fetcher: Arc, global_http_cache: Arc, + node_resolver: Arc, npm_resolver: Arc, module_info_cache: Arc, permissions: PermissionsContainer, - cache_info_enabled: bool, is_deno_publish: bool, + unstable_detect_cjs: bool, + cache_info_enabled: bool, } impl FetchCacher { pub fn new( + esm_or_cjs_checker: Arc, file_fetcher: Arc, global_http_cache: Arc, + node_resolver: Arc, npm_resolver: Arc, module_info_cache: Arc, options: FetchCacherOptions, ) -> Self { Self { file_fetcher, + esm_or_cjs_checker, global_http_cache, + node_resolver, npm_resolver, module_info_cache, file_header_overrides: options.file_header_overrides, permissions: options.permissions, is_deno_publish: options.is_deno_publish, + unstable_detect_cjs: options.unstable_detect_cjs, cache_info_enabled: false, } } @@ -282,6 +295,46 @@ impl Loader for FetchCacher { }, )))); } + + if self.unstable_detect_cjs && specifier_has_extension(specifier, "js") { + if let Ok(Some(pkg_json)) = + self.node_resolver.get_closest_package_json(specifier) + { + if pkg_json.typ == "commonjs" { + if let Ok(path) = specifier.to_file_path() { + if let Ok(bytes) = std::fs::read(&path) { + let text: Arc = from_utf8_lossy_owned(bytes).into(); + let is_es_module = match self.esm_or_cjs_checker.is_esm( + specifier, + text.clone(), + MediaType::JavaScript, + ) { + Ok(value) => value, + Err(err) => { + return Box::pin(futures::future::ready(Err(err.into()))); + } + }; + if !is_es_module { + self.node_resolver.mark_cjs_resolution(specifier.clone()); + return Box::pin(futures::future::ready(Ok(Some( + LoadResponse::External { + specifier: specifier.clone(), + }, + )))); + } else { + return Box::pin(futures::future::ready(Ok(Some( + LoadResponse::Module { + specifier: specifier.clone(), + content: arc_str_to_bytes(text), + maybe_headers: None, + }, + )))); + } + } + } + } + } + } } if self.is_deno_publish diff --git a/cli/cache/parsed_source.rs b/cli/cache/parsed_source.rs index e956361f4..df6e45c35 100644 --- a/cli/cache/parsed_source.rs +++ b/cli/cache/parsed_source.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; +use deno_ast::ParseDiagnostic; use deno_ast::ParsedSource; use deno_core::parking_lot::Mutex; use deno_graph::CapturingModuleParser; @@ -149,3 +150,42 @@ impl deno_graph::ParsedSourceStore for ParsedSourceCache { } } } + +pub struct EsmOrCjsChecker { + parsed_source_cache: Arc, +} + +impl EsmOrCjsChecker { + pub fn new(parsed_source_cache: Arc) -> Self { + Self { + parsed_source_cache, + } + } + + pub fn is_esm( + &self, + specifier: &ModuleSpecifier, + source: Arc, + media_type: MediaType, + ) -> Result { + // todo(dsherret): add a file cache here to avoid parsing with swc on each run + let source = match self.parsed_source_cache.get_parsed_source(specifier) { + Some(source) => source.clone(), + None => { + let source = deno_ast::parse_program(deno_ast::ParseParams { + specifier: specifier.clone(), + text: source, + media_type, + capture_tokens: true, // capture because it's used for cjs export analysis + scope_analysis: false, + maybe_syntax: None, + })?; + self + .parsed_source_cache + .set_parsed_source(specifier.clone(), source.clone()); + source + } + }; + Ok(source.is_module()) + } +} diff --git a/cli/factory.rs b/cli/factory.rs index b96a133e9..25f355110 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -14,6 +14,7 @@ use crate::cache::CodeCache; use crate::cache::DenoDir; use crate::cache::DenoDirProvider; use crate::cache::EmitCache; +use crate::cache::EsmOrCjsChecker; use crate::cache::GlobalHttpCache; use crate::cache::HttpCache; use crate::cache::LocalHttpCache; @@ -171,6 +172,7 @@ struct CliFactoryServices { http_client_provider: Deferred>, emit_cache: Deferred>, emitter: Deferred>, + esm_or_cjs_checker: Deferred>, fs: Deferred>, main_graph_container: Deferred>, maybe_inspector_server: Deferred>>, @@ -298,6 +300,12 @@ impl CliFactory { .get_or_init(|| ProgressBar::new(ProgressBarStyle::TextOnly)) } + pub fn esm_or_cjs_checker(&self) -> &Arc { + self.services.esm_or_cjs_checker.get_or_init(|| { + Arc::new(EsmOrCjsChecker::new(self.parsed_source_cache().clone())) + }) + } + pub fn global_http_cache(&self) -> Result<&Arc, AnyError> { self.services.global_http_cache.get_or_try_init(|| { Ok(Arc::new(GlobalHttpCache::new( @@ -579,6 +587,7 @@ impl CliFactory { node_analysis_cache, self.fs().clone(), node_resolver, + Some(self.parsed_source_cache().clone()), ); Ok(Arc::new(NodeCodeTranslator::new( @@ -619,8 +628,10 @@ impl CliFactory { Ok(Arc::new(ModuleGraphBuilder::new( cli_options.clone(), self.caches()?.clone(), + self.esm_or_cjs_checker().clone(), self.fs().clone(), self.resolver().await?.clone(), + self.cli_node_resolver().await?.clone(), self.npm_resolver().await?.clone(), self.module_info_cache()?.clone(), self.parsed_source_cache().clone(), @@ -792,6 +803,7 @@ impl CliFactory { Ok(CliMainWorkerFactory::new( self.blob_store().clone(), + self.cjs_resolutions().clone(), if cli_options.code_cache_enabled() { Some(self.code_cache()?.clone()) } else { @@ -896,6 +908,7 @@ impl CliFactory { node_ipc: cli_options.node_ipc_fd(), serve_port: cli_options.serve_port(), serve_host: cli_options.serve_host(), + unstable_detect_cjs: cli_options.unstable_detect_cjs(), }) } } diff --git a/cli/graph_util.rs b/cli/graph_util.rs index e2f6246e7..e67ae7821 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -6,6 +6,7 @@ use crate::args::CliLockfile; use crate::args::CliOptions; use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS; use crate::cache; +use crate::cache::EsmOrCjsChecker; use crate::cache::GlobalHttpCache; use crate::cache::ModuleInfoCache; use crate::cache::ParsedSourceCache; @@ -14,6 +15,7 @@ use crate::errors::get_error_class_name; use crate::file_fetcher::FileFetcher; use crate::npm::CliNpmResolver; use crate::resolver::CliGraphResolver; +use crate::resolver::CliNodeResolver; use crate::resolver::CliSloppyImportsResolver; use crate::resolver::SloppyImportsCachedFs; use crate::tools::check; @@ -379,8 +381,10 @@ pub struct BuildFastCheckGraphOptions<'a> { pub struct ModuleGraphBuilder { options: Arc, caches: Arc, + esm_or_cjs_checker: Arc, fs: Arc, resolver: Arc, + node_resolver: Arc, npm_resolver: Arc, module_info_cache: Arc, parsed_source_cache: Arc, @@ -396,8 +400,10 @@ impl ModuleGraphBuilder { pub fn new( options: Arc, caches: Arc, + esm_or_cjs_checker: Arc, fs: Arc, resolver: Arc, + node_resolver: Arc, npm_resolver: Arc, module_info_cache: Arc, parsed_source_cache: Arc, @@ -410,8 +416,10 @@ impl ModuleGraphBuilder { Self { options, caches, + esm_or_cjs_checker, fs, resolver, + node_resolver, npm_resolver, module_info_cache, parsed_source_cache, @@ -691,8 +699,10 @@ impl ModuleGraphBuilder { permissions: PermissionsContainer, ) -> cache::FetchCacher { cache::FetchCacher::new( + self.esm_or_cjs_checker.clone(), self.file_fetcher.clone(), self.global_http_cache.clone(), + self.node_resolver.clone(), self.npm_resolver.clone(), self.module_info_cache.clone(), cache::FetchCacherOptions { @@ -702,6 +712,7 @@ impl ModuleGraphBuilder { self.options.sub_command(), crate::args::DenoSubcommand::Publish { .. } ), + unstable_detect_cjs: self.options.unstable_detect_cjs(), }, ) } diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 293f41dda..37d42f78e 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -331,15 +331,23 @@ impl maybe_referrer: Option<&ModuleSpecifier>, requested_module_type: RequestedModuleType, ) -> Result { - let code_source = if let Some(result) = self - .shared - .npm_module_loader - .load_if_in_npm_package(specifier, maybe_referrer) - .await - { - result? - } else { - self.load_prepared_module(specifier, maybe_referrer).await? + let code_source = match self.load_prepared_module(specifier).await? { + Some(code_source) => code_source, + None => { + if self.shared.npm_module_loader.if_in_npm_package(specifier) { + self + .shared + .npm_module_loader + .load(specifier, maybe_referrer) + .await? + } else { + let mut msg = format!("Loading unprepared module: {specifier}"); + if let Some(referrer) = maybe_referrer { + msg = format!("{}, imported from: {}", msg, referrer.as_str()); + } + return Err(anyhow!(msg)); + } + } }; let code = if self.shared.is_inspecting { // we need the code with the source map in order for @@ -514,17 +522,12 @@ impl async fn load_prepared_module( &self, specifier: &ModuleSpecifier, - maybe_referrer: Option<&ModuleSpecifier>, - ) -> Result { + ) -> Result, AnyError> { // Note: keep this in sync with the sync version below let graph = self.graph_container.graph(); - match self.load_prepared_module_or_defer_emit( - &graph, - specifier, - maybe_referrer, - ) { - Ok(CodeOrDeferredEmit::Code(code_source)) => Ok(code_source), - Ok(CodeOrDeferredEmit::DeferredEmit { + match self.load_prepared_module_or_defer_emit(&graph, specifier)? { + Some(CodeOrDeferredEmit::Code(code_source)) => Ok(Some(code_source)), + Some(CodeOrDeferredEmit::DeferredEmit { specifier, media_type, source, @@ -537,30 +540,25 @@ impl // at this point, we no longer need the parsed source in memory, so free it self.parsed_source_cache.free(specifier); - Ok(ModuleCodeStringSource { + Ok(Some(ModuleCodeStringSource { code: ModuleSourceCode::Bytes(transpile_result), found_url: specifier.clone(), media_type, - }) + })) } - Err(err) => Err(err), + None => Ok(None), } } fn load_prepared_module_sync( &self, specifier: &ModuleSpecifier, - maybe_referrer: Option<&ModuleSpecifier>, - ) -> Result { + ) -> Result, AnyError> { // Note: keep this in sync with the async version above let graph = self.graph_container.graph(); - match self.load_prepared_module_or_defer_emit( - &graph, - specifier, - maybe_referrer, - ) { - Ok(CodeOrDeferredEmit::Code(code_source)) => Ok(code_source), - Ok(CodeOrDeferredEmit::DeferredEmit { + match self.load_prepared_module_or_defer_emit(&graph, specifier)? { + Some(CodeOrDeferredEmit::Code(code_source)) => Ok(Some(code_source)), + Some(CodeOrDeferredEmit::DeferredEmit { specifier, media_type, source, @@ -572,13 +570,13 @@ impl // at this point, we no longer need the parsed source in memory, so free it self.parsed_source_cache.free(specifier); - Ok(ModuleCodeStringSource { + Ok(Some(ModuleCodeStringSource { code: ModuleSourceCode::Bytes(transpile_result), found_url: specifier.clone(), media_type, - }) + })) } - Err(err) => Err(err), + None => Ok(None), } } @@ -586,8 +584,7 @@ impl &self, graph: &'graph ModuleGraph, specifier: &ModuleSpecifier, - maybe_referrer: Option<&ModuleSpecifier>, - ) -> Result, AnyError> { + ) -> Result>, AnyError> { if specifier.scheme() == "node" { // Node built-in modules should be handled internally. unreachable!("Deno bug. {} was misconfigured internally.", specifier); @@ -599,11 +596,11 @@ impl media_type, specifier, .. - })) => Ok(CodeOrDeferredEmit::Code(ModuleCodeStringSource { + })) => Ok(Some(CodeOrDeferredEmit::Code(ModuleCodeStringSource { code: ModuleSourceCode::String(source.clone().into()), found_url: specifier.clone(), media_type: *media_type, - })), + }))), Some(deno_graph::Module::Js(JsModule { source, media_type, @@ -624,11 +621,11 @@ impl | MediaType::Cts | MediaType::Jsx | MediaType::Tsx => { - return Ok(CodeOrDeferredEmit::DeferredEmit { + return Ok(Some(CodeOrDeferredEmit::DeferredEmit { specifier, media_type: *media_type, source, - }); + })); } MediaType::TsBuildInfo | MediaType::Wasm | MediaType::SourceMap => { panic!("Unexpected media type {media_type} for {specifier}") @@ -638,24 +635,18 @@ impl // at this point, we no longer need the parsed source in memory, so free it self.parsed_source_cache.free(specifier); - Ok(CodeOrDeferredEmit::Code(ModuleCodeStringSource { + Ok(Some(CodeOrDeferredEmit::Code(ModuleCodeStringSource { code: ModuleSourceCode::String(code), found_url: specifier.clone(), media_type: *media_type, - })) + }))) } Some( deno_graph::Module::External(_) | deno_graph::Module::Node(_) | deno_graph::Module::Npm(_), ) - | None => { - let mut msg = format!("Loading unprepared module: {specifier}"); - if let Some(referrer) = maybe_referrer { - msg = format!("{}, imported from: {}", msg, referrer.as_str()); - } - Err(anyhow!(msg)) - } + | None => Ok(None), } } } @@ -828,7 +819,7 @@ impl ModuleLoader "wasm" | "file" | "http" | "https" | "data" | "blob" => (), _ => return None, } - let source = self.0.load_prepared_module_sync(&specifier, None).ok()?; + let source = self.0.load_prepared_module_sync(&specifier).ok()??; source_map_from_code(source.code.as_bytes()) } diff --git a/cli/node.rs b/cli/node.rs index a3cee8dde..733d5f871 100644 --- a/cli/node.rs +++ b/cli/node.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; use deno_core::error::AnyError; +use deno_graph::ParsedSourceStore; use deno_runtime::deno_fs; use deno_runtime::deno_node::DenoFsNodeResolverEnv; use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis; @@ -16,6 +17,7 @@ use serde::Serialize; use crate::cache::CacheDBHash; use crate::cache::NodeAnalysisCache; +use crate::cache::ParsedSourceCache; use crate::resolver::CliNodeResolver; use crate::util::fs::canonicalize_path_maybe_not_exists; @@ -56,6 +58,7 @@ pub struct CliCjsCodeAnalyzer { cache: NodeAnalysisCache, fs: deno_fs::FileSystemRc, node_resolver: Arc, + parsed_source_cache: Option>, } impl CliCjsCodeAnalyzer { @@ -63,11 +66,13 @@ impl CliCjsCodeAnalyzer { cache: NodeAnalysisCache, fs: deno_fs::FileSystemRc, node_resolver: Arc, + parsed_source_cache: Option>, ) -> Self { Self { cache, fs, node_resolver, + parsed_source_cache, } } @@ -107,18 +112,26 @@ impl CliCjsCodeAnalyzer { } } + let maybe_parsed_source = self + .parsed_source_cache + .as_ref() + .and_then(|c| c.remove_parsed_source(specifier)); + let analysis = deno_core::unsync::spawn_blocking({ let specifier = specifier.clone(); let source: Arc = source.into(); move || -> Result<_, deno_ast::ParseDiagnostic> { - let parsed_source = deno_ast::parse_program(deno_ast::ParseParams { - specifier, - text: source, - media_type, - capture_tokens: true, - scope_analysis: false, - maybe_syntax: None, - })?; + let parsed_source = + maybe_parsed_source.map(Ok).unwrap_or_else(|| { + deno_ast::parse_program(deno_ast::ParseParams { + specifier, + text: source, + media_type, + capture_tokens: true, + scope_analysis: false, + maybe_syntax: None, + }) + })?; if parsed_source.is_script() { let analysis = parsed_source.analyze_cjs(); Ok(CliCjsAnalysis::Cjs { diff --git a/cli/resolver.rs b/cli/resolver.rs index 7804261b8..84c671268 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -43,7 +43,6 @@ use node_resolver::NodeModuleKind; use node_resolver::NodeResolution; use node_resolver::NodeResolutionMode; use node_resolver::PackageJson; -use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; @@ -53,7 +52,9 @@ use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS; use crate::node::CliNodeCodeTranslator; use crate::npm::CliNpmResolver; use crate::npm::InnerCliNpmResolverRef; +use crate::util::path::specifier_has_extension; use crate::util::sync::AtomicFlag; +use crate::util::text_encoding::from_utf8_lossy_owned; pub struct ModuleCodeStringSource { pub code: ModuleSourceCode, @@ -215,7 +216,7 @@ impl CliNodeResolver { referrer: &ModuleSpecifier, mode: NodeResolutionMode, ) -> Result { - let referrer_kind = if self.cjs_resolutions.contains(referrer) { + let referrer_kind = if self.cjs_resolutions.is_known_cjs(referrer) { NodeModuleKind::Cjs } else { NodeModuleKind::Esm @@ -310,9 +311,7 @@ impl CliNodeResolver { if self.in_npm_package(&specifier) { let resolution = self.node_resolver.url_to_node_resolution(specifier)?; - if let NodeResolution::CommonJs(specifier) = &resolution { - self.cjs_resolutions.insert(specifier.clone()); - } + let resolution = self.handle_node_resolution(resolution); return Ok(Some(resolution.into_url())); } } @@ -333,12 +332,17 @@ impl CliNodeResolver { ) -> NodeResolution { if let NodeResolution::CommonJs(specifier) = &resolution { // remember that this was a common js resolution - self.cjs_resolutions.insert(specifier.clone()); + self.mark_cjs_resolution(specifier.clone()); } resolution } + + pub fn mark_cjs_resolution(&self, specifier: ModuleSpecifier) { + self.cjs_resolutions.insert(specifier); + } } +// todo(dsherret): move to module_loader.rs #[derive(Clone)] pub struct NpmModuleLoader { cjs_resolutions: Arc, @@ -362,18 +366,9 @@ impl NpmModuleLoader { } } - pub async fn load_if_in_npm_package( - &self, - specifier: &ModuleSpecifier, - maybe_referrer: Option<&ModuleSpecifier>, - ) -> Option> { - if self.node_resolver.in_npm_package(specifier) - || (specifier.scheme() == "file" && specifier.path().ends_with(".cjs")) - { - Some(self.load(specifier, maybe_referrer).await) - } else { - None - } + pub fn if_in_npm_package(&self, specifier: &ModuleSpecifier) -> bool { + self.node_resolver.in_npm_package(specifier) + || self.cjs_resolutions.is_known_cjs(specifier) } pub async fn load( @@ -418,16 +413,9 @@ impl NpmModuleLoader { } })?; - let code = if self.cjs_resolutions.contains(specifier) - || (specifier.scheme() == "file" && specifier.path().ends_with(".cjs")) - { + let code = if self.cjs_resolutions.is_known_cjs(specifier) { // translate cjs to esm if it's cjs and inject node globals - let code = match String::from_utf8_lossy(&code) { - 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(code) }, - }; + let code = from_utf8_lossy_owned(code); ModuleSourceCode::String( self .node_code_translator @@ -452,8 +440,12 @@ impl NpmModuleLoader { pub struct CjsResolutionStore(DashSet); impl CjsResolutionStore { - pub fn contains(&self, specifier: &ModuleSpecifier) -> bool { - self.0.contains(specifier) + pub fn is_known_cjs(&self, specifier: &ModuleSpecifier) -> bool { + if specifier.scheme() != "file" { + return false; + } + + specifier_has_extension(specifier, "cjs") || self.0.contains(specifier) } pub fn insert(&self, specifier: ModuleSpecifier) { diff --git a/cli/schemas/config-file.v1.json b/cli/schemas/config-file.v1.json index af18e6f21..4a6239f0d 100644 --- a/cli/schemas/config-file.v1.json +++ b/cli/schemas/config-file.v1.json @@ -528,6 +528,7 @@ "bare-node-builtins", "byonm", "cron", + "detect-cjs", "ffi", "fs", "http", diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index 1290a238f..6e747bed4 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -622,6 +622,7 @@ impl<'a> DenoCompileBinaryWriter<'a> { unstable_config: UnstableConfig { legacy_flag_enabled: false, bare_node_builtins: cli_options.unstable_bare_node_builtins(), + detect_cjs: cli_options.unstable_detect_cjs(), sloppy_imports: cli_options.unstable_sloppy_imports(), features: cli_options.unstable_features(), }, diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 258de0dad..60018228b 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -586,6 +586,7 @@ pub async fn run( node_analysis_cache, fs.clone(), cli_node_resolver.clone(), + None, ); let node_code_translator = Arc::new(NodeCodeTranslator::new( cjs_esm_code_analyzer, @@ -651,7 +652,7 @@ pub async fn run( workspace_resolver, node_resolver: cli_node_resolver.clone(), npm_module_loader: Arc::new(NpmModuleLoader::new( - cjs_resolutions, + cjs_resolutions.clone(), node_code_translator, fs.clone(), cli_node_resolver, @@ -696,6 +697,7 @@ pub async fn run( }); let worker_factory = CliMainWorkerFactory::new( Arc::new(BlobStore::default()), + cjs_resolutions, // Code cache is not supported for standalone binary yet. None, feature_checker, @@ -738,6 +740,7 @@ pub async fn run( node_ipc: None, serve_port: None, serve_host: None, + unstable_detect_cjs: metadata.unstable_config.detect_cjs, }, ); diff --git a/cli/tools/compile.rs b/cli/tools/compile.rs index c1f98bc08..3cc4414fc 100644 --- a/cli/tools/compile.rs +++ b/cli/tools/compile.rs @@ -54,6 +54,16 @@ pub async fn compile( ); } + if cli_options.unstable_detect_cjs() { + log::warn!( + concat!( + "{} --unstable-detect-cjs is not properly supported in deno compile. ", + "The compiled executable may encounter runtime errors.", + ), + crate::colors::yellow("Warning"), + ); + } + let output_path = resolve_compile_executable_output_path( http_client, &compile_flags, diff --git a/cli/util/text_encoding.rs b/cli/util/text_encoding.rs index d2e0832c9..0b7601cb9 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) -> 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> { let range = find_source_map_range(code)?; let source_map_range = &code[range]; @@ -85,6 +96,13 @@ fn find_source_map_range(code: &[u8]) -> Option> { } } +/// Converts an `Arc` to an `Arc<[u8]>`. +pub fn arc_str_to_bytes(arc_str: Arc) -> 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]) } +} + #[cfg(test)] mod tests { use std::sync::Arc; diff --git a/cli/worker.rs b/cli/worker.rs index cc18c0d15..489b2dd93 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -51,9 +51,11 @@ use crate::args::DenoSubcommand; use crate::args::StorageKeyResolver; use crate::errors; use crate::npm::CliNpmResolver; +use crate::resolver::CjsResolutionStore; use crate::util::checksum; use crate::util::file_watcher::WatcherCommunicator; use crate::util::file_watcher::WatcherRestartMode; +use crate::util::path::specifier_has_extension; use crate::version; pub struct ModuleLoaderAndSourceMapGetter { @@ -120,11 +122,13 @@ pub struct CliMainWorkerOptions { pub node_ipc: Option, pub serve_port: Option, pub serve_host: Option, + pub unstable_detect_cjs: bool, } struct SharedWorkerState { blob_store: Arc, broadcast_channel: InMemoryBroadcastChannel, + cjs_resolution_store: Arc, code_cache: Option>, compiled_wasm_module_store: CompiledWasmModuleStore, feature_checker: Arc, @@ -422,6 +426,7 @@ impl CliMainWorkerFactory { #[allow(clippy::too_many_arguments)] pub fn new( blob_store: Arc, + cjs_resolution_store: Arc, code_cache: Option>, feature_checker: Arc, fs: Arc, @@ -441,6 +446,7 @@ impl CliMainWorkerFactory { shared: Arc::new(SharedWorkerState { blob_store, broadcast_channel: Default::default(), + cjs_resolution_store, code_cache, compiled_wasm_module_store: Default::default(), feature_checker, @@ -486,6 +492,9 @@ impl CliMainWorkerFactory { stdio: deno_runtime::deno_io::Stdio, ) -> Result { let shared = &self.shared; + let ModuleLoaderAndSourceMapGetter { module_loader } = shared + .module_loader_factory + .create_for_main(permissions.clone()); let (main_module, is_main_cjs) = if let Ok(package_ref) = NpmPackageReqReference::from_specifier(&main_module) { @@ -526,13 +535,28 @@ impl CliMainWorkerFactory { let is_main_cjs = matches!(node_resolution, NodeResolution::CommonJs(_)); (node_resolution.into_url(), is_main_cjs) } else { - let is_cjs = main_module.path().ends_with(".cjs"); + let is_maybe_cjs_js_ext = self.shared.options.unstable_detect_cjs + && specifier_has_extension(&main_module, "js") + && self + .shared + .node_resolver + .get_closest_package_json(&main_module) + .ok() + .flatten() + .map(|pkg_json| pkg_json.typ == "commonjs") + .unwrap_or(false); + let is_cjs = if is_maybe_cjs_js_ext { + // fill the cjs resolution store by preparing the module load + module_loader + .prepare_load(&main_module, None, false) + .await?; + self.shared.cjs_resolution_store.is_known_cjs(&main_module) + } else { + specifier_has_extension(&main_module, "cjs") + }; (main_module, is_cjs) }; - let ModuleLoaderAndSourceMapGetter { module_loader } = shared - .module_loader_factory - .create_for_main(permissions.clone()); let maybe_inspector_server = shared.maybe_inspector_server.clone(); let create_web_worker_cb = -- cgit v1.2.3 From 9f0a447f7cbcdf1e38cc0d52a5da70a50ea0179b Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Tue, 15 Oct 2024 11:21:47 +0900 Subject: fix(cli): named export takes precedence over default export in doc testing (#26112) This commit fixes the issue of import name conflict in case the named export and default export have the same name by letting named export take precedence over default export. Fixes #26009 --- cli/util/extract.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) (limited to 'cli') diff --git a/cli/util/extract.rs b/cli/util/extract.rs index 841cf6eb0..873b7e7f2 100644 --- a/cli/util/extract.rs +++ b/cli/util/extract.rs @@ -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, @@ -1137,6 +1141,30 @@ 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, + }], + }, ]; for test in tests { @@ -1326,6 +1354,28 @@ 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, + }], + }, ]; for test in tests { @@ -1581,6 +1631,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 { -- cgit v1.2.3 From ae6a2b23bae83795bd973414216a89c839dd8fda Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 14 Oct 2024 23:57:31 -0400 Subject: fix: do not panic running remote cjs module (#26259) Instead error. --- cli/worker.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'cli') diff --git a/cli/worker.rs b/cli/worker.rs index 489b2dd93..e230197d2 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -552,7 +552,8 @@ impl CliMainWorkerFactory { .await?; self.shared.cjs_resolution_store.is_known_cjs(&main_module) } else { - specifier_has_extension(&main_module, "cjs") + main_module.scheme() == "file" + && specifier_has_extension(&main_module, "cjs") }; (main_module, is_cjs) }; -- cgit v1.2.3 From 7bfec3817310c6197ef49694a51d53365ac8d038 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Tue, 15 Oct 2024 16:44:21 +0900 Subject: fix(repl): remove check flags (#26140) This change removes the handling of `--check` and `--no-check` flags from `deno repl` subcommand. Currently these flags don't have effects, and the help output for these options are incorrect and confusing. closes #26042 --- cli/args/flags.rs | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) (limited to 'cli') diff --git a/cli/args/flags.rs b/cli/args/flags.rs index acaf74a67..7b0cee5bd 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -2769,8 +2769,13 @@ It is especially useful for quick prototyping and checking snippets of code. TypeScript is supported, however it is not type-checked, only transpiled." ), UnstableArgsConfig::ResolutionAndRuntime) - .defer(|cmd| runtime_args(cmd, true, true, true) - .arg(check_arg(false)) + .defer(|cmd| { + let cmd = compile_args_without_check_args(cmd); + let cmd = inspect_args(cmd); + let cmd = permission_args(cmd, None); + let cmd = runtime_misc_args(cmd); + + cmd .arg( Arg::new("eval-file") .long("eval-file") @@ -2789,7 +2794,7 @@ TypeScript is supported, however it is not type-checked, only transpiled." .after_help(cstr!("Environment variables: DENO_REPL_HISTORY Set REPL history file path. History file is disabled when the value is empty. [default: $DENO_DIR/deno_history.txt]")) - ) + }) .arg(env_file_arg()) .arg( Arg::new("args") @@ -3662,6 +3667,10 @@ fn runtime_args( } else { app }; + runtime_misc_args(app) +} + +fn runtime_misc_args(app: Command) -> Command { app .arg(frozen_lockfile_arg()) .arg(cached_only_arg()) @@ -4880,8 +4889,18 @@ fn repl_parse( flags: &mut Flags, matches: &mut ArgMatches, ) -> clap::error::Result<()> { - runtime_args_parse(flags, matches, true, true, true)?; - unsafely_ignore_certificate_errors_parse(flags, matches); + unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime); + compile_args_without_check_parse(flags, matches)?; + cached_only_arg_parse(flags, matches); + frozen_lockfile_arg_parse(flags, matches); + permission_args_parse(flags, matches)?; + inspect_arg_parse(flags, matches); + location_arg_parse(flags, matches); + v8_flags_arg_parse(flags, matches); + seed_arg_parse(flags, matches); + enable_testing_features_arg_parse(flags, matches); + env_file_arg_parse(flags, matches); + strace_ops_parse(flags, matches); let eval_files = matches .remove_many::("eval-file") @@ -7429,7 +7448,7 @@ mod tests { #[test] fn repl_with_flags() { #[rustfmt::skip] - let r = flags_from_vec(svec!["deno", "repl", "-A", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--unsafely-ignore-certificate-errors", "--env=.example.env"]); + let r = flags_from_vec(svec!["deno", "repl", "-A", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--unsafely-ignore-certificate-errors", "--env=.example.env"]); assert_eq!( r.unwrap(), Flags { @@ -7477,7 +7496,6 @@ mod tests { allow_write: Some(vec![]), ..Default::default() }, - type_check_mode: TypeCheckMode::None, ..Flags::default() } ); @@ -7499,7 +7517,6 @@ mod tests { eval: None, is_default_command: false, }), - type_check_mode: TypeCheckMode::None, ..Flags::default() } ); -- cgit v1.2.3 From 533a9b108677f1560fe55882771a0be2bb0b0fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E7=82=B3=E6=9D=83?= <695601626@qq.com> Date: Wed, 16 Oct 2024 00:10:07 +0800 Subject: chore: upgrade to rust 1.81.0 (#26261) --- cli/lsp/tsc.rs | 2 +- cli/npm/common.rs | 2 +- cli/util/fs.rs | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'cli') diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index c8b5c47f8..cfab39b20 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -3939,7 +3939,7 @@ pub struct OutliningSpan { kind: OutliningSpanKind, } -const FOLD_END_PAIR_CHARACTERS: &[u8] = &[b'}', b']', b')', b'`']; +const FOLD_END_PAIR_CHARACTERS: &[u8] = b"}])`"; impl OutliningSpan { pub fn to_folding_range( diff --git a/cli/npm/common.rs b/cli/npm/common.rs index a3a828e74..de282310a 100644 --- a/cli/npm/common.rs +++ b/cli/npm/common.rs @@ -40,7 +40,7 @@ pub fn maybe_auth_header_for_npm_registry( header::AUTHORIZATION, header::HeaderValue::from_str(&format!( "Basic {}", - BASE64_STANDARD.encode(&format!( + BASE64_STANDARD.encode(format!( "{}:{}", username.unwrap(), password.unwrap() diff --git a/cli/util/fs.rs b/cli/util/fs.rs index 2ad3affc8..2c34f486a 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); -- cgit v1.2.3 From 797405fc61b2d155941506fb53d498076e121017 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Tue, 15 Oct 2024 09:38:42 -0700 Subject: fix(add): create deno.json when running `deno add jsr:` (#26275) Fixes https://github.com/denoland/deno/issues/26119. Originally I wanted to put them in package.json if there's no deno.json, but on second thought it makes more sense to just create a deno.json --- cli/tools/registry/pm.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'cli') diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index 3276acfbf..ff900d113 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -400,14 +400,17 @@ impl std::fmt::Display for AddCommandName { fn load_configs( flags: &Arc, + has_jsr_specifiers: impl FnOnce() -> bool, ) -> Result<(CliFactory, Option, Option), AnyError> { let cli_factory = CliFactory::from_flags(flags.clone()); let options = cli_factory.cli_options()?; let npm_config = NpmConfig::from_options(options)?; let (cli_factory, deno_config) = match DenoConfig::from_options(options)? { Some(config) => (cli_factory, Some(config)), - None if npm_config.is_some() => (cli_factory, None), - None => { + None if npm_config.is_some() && !has_jsr_specifiers() => { + (cli_factory, None) + } + _ => { let factory = create_deno_json(flags, options)?; let options = factory.cli_options()?.clone(); ( @@ -427,7 +430,9 @@ pub async fn add( add_flags: AddFlags, cmd_name: AddCommandName, ) -> Result<(), AnyError> { - let (cli_factory, npm_config, deno_config) = load_configs(&flags)?; + let (cli_factory, npm_config, deno_config) = load_configs(&flags, || { + add_flags.packages.iter().any(|s| s.starts_with("jsr:")) + })?; let mut npm_config = ConfigUpdater::maybe_new(npm_config).await?; let mut deno_config = ConfigUpdater::maybe_new(deno_config).await?; @@ -764,7 +769,7 @@ pub async fn remove( flags: Arc, remove_flags: RemoveFlags, ) -> Result<(), AnyError> { - let (_, npm_config, deno_config) = load_configs(&flags)?; + let (_, npm_config, deno_config) = load_configs(&flags, || false)?; let mut configs = [ ConfigUpdater::maybe_new(npm_config).await?, -- cgit v1.2.3 From 38888061698f230f2288cf520daf86d781c395bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 15 Oct 2024 17:59:28 +0100 Subject: refactor: always apply hint when formatting JsError (#26252) Moves code for generating suggestions and hint to `cli/fmt_errors.rs`. This effectively applies suggestion to any place that format JS errors in terminal, like `deno test`. Addresses https://github.com/denoland/deno/pull/26218#issuecomment-2409139055 --- cli/main.rs | 97 ++----------------------------------------------------------- 1 file changed, 2 insertions(+), 95 deletions(-) (limited to 'cli') diff --git a/cli/main.rs b/cli/main.rs index ddb6078af..360307d75 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -47,8 +47,7 @@ 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_with_suggestions; -use deno_runtime::fmt_errors::FixSuggestion; +use deno_runtime::fmt_errors::format_js_error; use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics; use deno_terminal::colors; use factory::CliFactory; @@ -362,104 +361,12 @@ fn exit_with_message(message: &str, code: i32) -> ! { std::process::exit(code); } -fn get_suggestions_for_terminal_errors(e: &JsError) -> Vec { - if let Some(msg) = &e.message { - if msg.contains("module is not defined") - || msg.contains("exports is not defined") - { - return vec![ - FixSuggestion::info( - "Deno does not support CommonJS modules without `.cjs` extension.", - ), - FixSuggestion::hint( - "Rewrite this module to ESM or change the file extension to `.cjs`.", - ), - ]; - } else if msg.contains("openKv is not a function") { - return vec![ - FixSuggestion::info("Deno.openKv() is an unstable API."), - FixSuggestion::hint( - "Run again with `--unstable-kv` flag to enable this API.", - ), - ]; - } else if msg.contains("cron is not a function") { - return vec![ - FixSuggestion::info("Deno.cron() is an unstable API."), - FixSuggestion::hint( - "Run again with `--unstable-cron` flag to enable this API.", - ), - ]; - } else if msg.contains("WebSocketStream is not defined") { - return vec![ - FixSuggestion::info("new WebSocketStream() is an unstable API."), - FixSuggestion::hint( - "Run again with `--unstable-net` flag to enable this API.", - ), - ]; - } else if msg.contains("Temporal is not defined") { - return vec![ - FixSuggestion::info("Temporal is an unstable API."), - FixSuggestion::hint( - "Run again with `--unstable-temporal` flag to enable this API.", - ), - ]; - } else if msg.contains("BroadcastChannel is not defined") { - return vec![ - FixSuggestion::info("BroadcastChannel is an unstable API."), - FixSuggestion::hint( - "Run again with `--unstable-broadcast-channel` flag to enable this API.", - ), - ]; - } else if msg.contains("window is not defined") { - return vec![ - FixSuggestion::info("window global is not available in Deno 2."), - FixSuggestion::hint("Replace `window` with `globalThis`."), - ]; - } else if msg.contains("UnsafeWindowSurface is not a constructor") { - return vec![ - FixSuggestion::info("Deno.UnsafeWindowSurface is an unstable API."), - FixSuggestion::hint( - "Run again with `--unstable-webgpu` flag to enable this API.", - ), - ]; - // Try to capture errors like: - // ``` - // Uncaught Error: Cannot find module '../build/Release/canvas.node' - // Require stack: - // - /.../deno/npm/registry.npmjs.org/canvas/2.11.2/lib/bindings.js - // - /.../.cache/deno/npm/registry.npmjs.org/canvas/2.11.2/lib/canvas.js - // ``` - } else if msg.contains("Cannot find module") - && msg.contains("Require stack") - && msg.contains(".node'") - { - return vec![ - FixSuggestion::info_multiline( - &[ - "Trying to execute an npm package using Node-API addons,", - "these packages require local `node_modules` directory to be present." - ] - ), - FixSuggestion::hint_multiline( - &[ - "Add `\"nodeModulesDir\": \"auto\" option to `deno.json`, and then run", - "`deno install --allow-scripts=npm: --entrypoint