From 81b4242594178f531da9ae9fa46753e3e70b7ebd Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Wed, 13 Nov 2024 10:06:30 -0800 Subject: [PATCH 01/26] WIP --- cli/args/flags.rs | 80 +++++ cli/args/mod.rs | 6 +- cli/main.rs | 10 + cli/npm/managed/registry.rs | 2 +- cli/npm/managed/resolution.rs | 1 + cli/tools/registry/mod.rs | 2 + cli/tools/registry/pm.rs | 8 + cli/tools/registry/pm/cache_deps.rs | 2 + cli/tools/registry/pm/outdated.rs | 509 ++++++++++++++++++++++++++++ cli/tools/registry/pm/update.rs | 30 ++ 10 files changed, 648 insertions(+), 2 deletions(-) create mode 100644 cli/tools/registry/pm/outdated.rs create mode 100644 cli/tools/registry/pm/update.rs diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 85b8abefe1b0da..aa566cf12d5bb1 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -463,6 +463,8 @@ pub enum DenoSubcommand { Serve(ServeFlags), Task(TaskFlags), Test(TestFlags), + Update(UpdateFlags), + Outdated(OutdatedFlags), Types, Upgrade(UpgradeFlags), Vendor, @@ -470,6 +472,17 @@ pub enum DenoSubcommand { Help(HelpFlags), } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct OutdatedFlags { + pub filters: Vec, + pub compatible: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct UpdateFlags { + pub filters: Vec, +} + impl DenoSubcommand { pub fn is_run(&self) -> bool { matches!(self, Self::Run(_)) @@ -1382,6 +1395,8 @@ pub fn flags_from_vec(args: Vec) -> clap::error::Result { "jupyter" => jupyter_parse(&mut flags, &mut m), "lint" => lint_parse(&mut flags, &mut m)?, "lsp" => lsp_parse(&mut flags, &mut m), + "outdated" => outdated_parse(&mut flags, &mut m)?, + "update" => update_parse(&mut flags, &mut m)?, "repl" => repl_parse(&mut flags, &mut m)?, "run" => run_parse(&mut flags, &mut m, app, false)?, "serve" => serve_parse(&mut flags, &mut m, app)?, @@ -1624,6 +1639,8 @@ pub fn clap_root() -> Command { .subcommand(json_reference_subcommand()) .subcommand(jupyter_subcommand()) .subcommand(uninstall_subcommand()) + .subcommand(outdated_subcommand()) + .subcommand(update_subcommand()) .subcommand(lsp_subcommand()) .subcommand(lint_subcommand()) .subcommand(publish_subcommand()) @@ -2614,6 +2631,41 @@ fn jupyter_subcommand() -> Command { .conflicts_with("install")) } +fn outdated_subcommand() -> Command { + command( + "outdated", + "Check for outdated dependencies", + UnstableArgsConfig::None, + ) + .defer(|cmd| { + cmd + .arg( + Arg::new("filters") + .num_args(0..) + .action(ArgAction::Append) + .help("List of filters used for checking outdated packages"), + ) + .arg( + Arg::new("compatible") + .long("compatible") + .action(ArgAction::SetTrue) + .help("only output versions that satisfy semver requirements"), + ) + }) +} +fn update_subcommand() -> Command { + command("update", "Update dependencies", UnstableArgsConfig::None).defer( + |cmd| { + cmd.arg( + Arg::new("filters") + .num_args(0..) + .action(ArgAction::Append) + .help("List of filters used for updating outdated packages"), + ) + }, + ) +} + fn uninstall_subcommand() -> Command { command( "uninstall", @@ -4329,6 +4381,34 @@ fn remove_parse(flags: &mut Flags, matches: &mut ArgMatches) { }); } +fn outdated_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { + let filters = match matches.remove_many::("filters") { + Some(f) => f.collect(), + None => vec![], + }; + let compatible = matches.get_flag("compatible"); + flags.subcommand = DenoSubcommand::Outdated(OutdatedFlags { + filters, + compatible, + }); + Ok(()) +} + +fn update_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { + let filters = match matches.remove_many::("filters") { + Some(f) => f.collect(), + None => vec![], + }; + flags.subcommand = DenoSubcommand::Update(UpdateFlags { filters }); + Ok(()) +} + fn bench_parse( flags: &mut Flags, matches: &mut ArgMatches, diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 61e1443a75eff7..dc7417ab317fac 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -984,7 +984,9 @@ impl CliOptions { CacheSetting::Only } else if !self.flags.cache_blocklist.is_empty() { CacheSetting::ReloadSome(self.flags.cache_blocklist.clone()) - } else if self.flags.reload { + } else if self.flags.reload + || matches!(self.sub_command(), DenoSubcommand::Update(..)) + { CacheSetting::ReloadAll } else { CacheSetting::Use @@ -1624,6 +1626,8 @@ impl CliOptions { DenoSubcommand::Install(_) | DenoSubcommand::Add(_) | DenoSubcommand::Remove(_) + | DenoSubcommand::Outdated(_) + | DenoSubcommand::Update(_) ) { // For `deno install/add/remove` we want to force the managed resolver so it can set up `node_modules/` directory. return false; diff --git a/cli/main.rs b/cli/main.rs index 7d3ef0e6a0563a..09b7da4ce65022 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -188,6 +188,16 @@ async fn run_subcommand(flags: Arc) -> Result { tools::lint::lint(flags, lint_flags).await } }), + DenoSubcommand::Outdated(outdated_flags) => { + spawn_subcommand(async move { + tools::registry::outdated(flags, outdated_flags).await + }) + } + DenoSubcommand::Update(update_flags) => { + spawn_subcommand(async move { + tools::registry::update(flags, update_flags).await + }) + } DenoSubcommand::Repl(repl_flags) => { spawn_subcommand(async move { tools::repl::run(flags, repl_flags).await }) } diff --git a/cli/npm/managed/registry.rs b/cli/npm/managed/registry.rs index 8f15d619b93ffb..0c80899e3799e9 100644 --- a/cli/npm/managed/registry.rs +++ b/cli/npm/managed/registry.rs @@ -114,7 +114,7 @@ impl CliNpmRegistryApiInner { { // attempt to load from the file cache if let Some(info) = api.load_file_cached_package_info(&name).await { - let result = Some(Arc::new(info)); + let result: Option> = Some(Arc::new(info)); return Ok(result); } } diff --git a/cli/npm/managed/resolution.rs b/cli/npm/managed/resolution.rs index ecfe5cb25cdcf5..903fb5fbcbdef6 100644 --- a/cli/npm/managed/resolution.rs +++ b/cli/npm/managed/resolution.rs @@ -265,6 +265,7 @@ async fn add_package_reqs_to_snapshot( maybe_lockfile: Option>, get_new_snapshot: impl Fn() -> NpmResolutionSnapshot, ) -> deno_npm::resolution::AddPkgReqsResult { + eprintln!("add_package_reqs_to_snapshot: {package_reqs:?}"); let snapshot = get_new_snapshot(); if package_reqs .iter() diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs index 35317e12da4674..e06cd994f4afb6 100644 --- a/cli/tools/registry/mod.rs +++ b/cli/tools/registry/mod.rs @@ -68,7 +68,9 @@ use auth::get_auth_method; use auth::AuthMethod; pub use pm::add; pub use pm::cache_top_level_deps; +pub use pm::outdated; pub use pm::remove; +pub use pm::update; pub use pm::AddCommandName; pub use pm::AddRmPackageReq; use publish_order::PublishOrderGraph; diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index c1ea2c75ea52b7..8575d34464000f 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -32,8 +32,12 @@ use crate::jsr::JsrFetchResolver; use crate::npm::NpmFetchResolver; mod cache_deps; +mod outdated; +mod update; pub use cache_deps::cache_top_level_deps; +pub use outdated::outdated; +pub use update::update; #[derive(Debug, Copy, Clone)] enum ConfigKind { @@ -377,6 +381,10 @@ pub async fn add( (Some(npm), Some(deno)) => { let npm_distance = path_distance(&npm.path, &start_dir); let deno_distance = path_distance(&deno.path, &start_dir); + eprintln!( + "npm: {:?}, deno: {:?}, start: {:?}; distances: {} {}", + &npm.path, &deno.path, &start_dir, npm_distance, deno_distance + ); npm_distance <= deno_distance } (Some(_), None) => true, diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs index d3c8da868c32f9..79c587b74a3b34 100644 --- a/cli/tools/registry/pm/cache_deps.rs +++ b/cli/tools/registry/pm/cache_deps.rs @@ -119,6 +119,8 @@ pub async fn cache_top_level_deps( None, ) .await?; + } else if let Some(lockfile) = lockfile { + lockfile.write_if_changed()?; } Ok(()) diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs new file mode 100644 index 00000000000000..39d7a7f44c9c57 --- /dev/null +++ b/cli/tools/registry/pm/outdated.rs @@ -0,0 +1,509 @@ +use std::sync::Arc; + +use deno_config::deno_json::ConfigFile; +use deno_config::deno_json::ConfigFileRc; +use deno_config::workspace::Workspace; +use deno_core::error::AnyError; +use deno_core::futures::stream::FuturesUnordered; +use deno_core::futures::FutureExt; +use deno_core::futures::StreamExt; +use deno_core::serde_json; +use deno_core::url::Url; +use deno_package_json::PackageJsonDepValue; +use deno_package_json::PackageJsonDepValueParseError; +use deno_package_json::PackageJsonRc; +use deno_semver::jsr::JsrDepPackageReq; +use deno_semver::package::PackageReq; +use deno_semver::Version; +use deno_semver::VersionReq; +use import_map::ImportMap; +use import_map::ImportMapWithDiagnostics; +use import_map::SpecifierMapEntry; +use indexmap::IndexMap; +use tokio::sync::Semaphore; + +use crate::args::CacheSetting; +use crate::args::Flags; +use crate::args::NpmInstallDepsProvider; +use crate::args::OutdatedFlags; +use crate::factory::CliFactory; +use crate::file_fetcher::FileFetcher; +use crate::graph_container::ModuleGraphContainer; +use crate::graph_container::ModuleGraphUpdatePermit; +use crate::jsr::JsrFetchResolver; +use crate::npm::NpmFetchResolver; + +#[derive(Clone)] +enum DepLocation { + DenoJson(ConfigFileRc, KeyPath), + PackageJson(PackageJsonRc, KeyPath), +} + +struct DebugAdapter(T); + +impl<'a> std::fmt::Debug for DebugAdapter<&'a ConfigFileRc> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ConfigFile") + .field("specifier", &self.0.specifier) + .finish() + } +} +impl<'a> std::fmt::Debug for DebugAdapter<&'a PackageJsonRc> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PackageJson") + .field("path", &self.0.path) + .finish() + } +} + +impl std::fmt::Debug for DepLocation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + DepLocation::DenoJson(arc, key_path) => { + let mut debug = f.debug_tuple("DenoJson"); + debug.field(&DebugAdapter(arc)).field(key_path).finish() + } + DepLocation::PackageJson(arc, key_path) => { + let mut debug = f.debug_tuple("PackageJson"); + debug.field(&DebugAdapter(arc)).field(key_path).finish() + } + } + } +} + +#[derive(Clone, Debug)] +enum DepKind { + Npm, + Jsr, +} + +impl DepKind { + fn scheme(&self) -> &'static str { + match self { + DepKind::Npm => "npm", + DepKind::Jsr => "jsr", + } + } +} + +#[derive(Clone, Debug)] +enum KeyPart { + Imports, + Scopes, + Dependencies, + DevDependencies, + String(String), +} + +impl From for KeyPart { + fn from(value: String) -> Self { + KeyPart::String(value) + } +} + +impl From for KeyPart { + fn from(value: PackageJsonDepKind) -> Self { + match value { + PackageJsonDepKind::Normal => Self::Dependencies, + PackageJsonDepKind::Dev => Self::DevDependencies, + } + } +} + +impl KeyPart { + fn as_str(&self) -> &str { + match self { + KeyPart::Imports => "imports", + KeyPart::Scopes => "scopes", + KeyPart::Dependencies => "dependencies", + KeyPart::DevDependencies => "devDependencies", + KeyPart::String(s) => s, + } + } +} + +#[derive(Clone, Debug)] +struct KeyPath { + keys: Vec, +} + +impl KeyPath { + fn from_parts(parts: impl IntoIterator) -> Self { + Self { + keys: parts.into_iter().collect(), + } + } + fn push(&mut self, part: KeyPart) { + self.keys.push(part) + } +} + +#[derive(Clone, Debug)] +struct Dep { + req: PackageReq, + kind: DepKind, + location: DepLocation, + // specifier: String, +} + +#[derive(Debug, Clone)] +struct ResolvedDep { + dep: Dep, + resolved_version: Option, +} + +#[derive(Debug)] +struct WorkspaceDeps { + deps: Vec, +} + +fn import_map_entries<'a>( + import_map: &'a ImportMap, +) -> impl Iterator)> { + import_map + .imports() + .entries() + .map(|entry| { + ( + KeyPath::from_parts([ + KeyPart::Imports, + KeyPart::String(entry.raw_key.into()), + ]), + entry, + ) + }) + .chain(import_map.scopes().flat_map(|scope| { + let path = KeyPath::from_parts([ + KeyPart::Scopes, + scope.raw_key.to_string().into(), + ]); + + scope.imports.entries().map(move |entry| { + let mut full_path = path.clone(); + full_path.push(KeyPart::Imports); + full_path.push(KeyPart::String(entry.raw_key.to_string())); + (full_path, entry) + }) + })) +} + +fn deno_json_import_map( + deno_json: &ConfigFile, +) -> Result { + let mut map = serde_json::Map::with_capacity(2); + if let Some(imports) = &deno_json.json.imports { + map.insert("imports".to_string(), imports.clone()); + } + if let Some(scopes) = &deno_json.json.scopes { + map.insert("scopes".to_string(), scopes.clone()); + } + import_map::parse_from_value( + deno_json.specifier.clone(), + serde_json::Value::Object(map), + ) + .map_err(Into::into) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum PackageJsonDepKind { + Normal, + Dev, +} + +type PackageJsonDeps = IndexMap< + String, + Result< + (PackageJsonDepKind, PackageJsonDepValue), + PackageJsonDepValueParseError, + >, +>; + +/// Resolve the package.json's dependencies. +fn resolve_local_package_json_deps( + package_json: &PackageJsonRc, +) -> PackageJsonDeps { + /// Gets the name and raw version constraint for a registry info or + /// package.json dependency entry taking into account npm package aliases. + fn parse_dep_entry_name_and_raw_version<'a>( + key: &'a str, + value: &'a str, + ) -> (&'a str, &'a str) { + if let Some(package_and_version) = value.strip_prefix("npm:") { + if let Some((name, version)) = package_and_version.rsplit_once('@') { + // if empty, then the name was scoped and there's no version + if name.is_empty() { + (package_and_version, "*") + } else { + (name, version) + } + } else { + (package_and_version, "*") + } + } else { + (key, value) + } + } + + fn parse_entry( + key: &str, + value: &str, + ) -> Result { + if let Some(workspace_key) = value.strip_prefix("workspace:") { + let version_req = VersionReq::parse_from_npm(workspace_key)?; + return Ok(PackageJsonDepValue::Workspace(version_req)); + } + if value.starts_with("file:") + || value.starts_with("git:") + || value.starts_with("http:") + || value.starts_with("https:") + { + return Err(PackageJsonDepValueParseError::Unsupported { + scheme: value.split(':').next().unwrap().to_string(), + }); + } + let (name, version_req) = parse_dep_entry_name_and_raw_version(key, value); + let result = VersionReq::parse_from_npm(version_req); + match result { + Ok(version_req) => Ok(PackageJsonDepValue::Req(PackageReq { + name: name.to_string(), + version_req, + })), + Err(err) => Err(PackageJsonDepValueParseError::VersionReq(err)), + } + } + + fn insert_deps( + deps: Option<&IndexMap>, + result: &mut PackageJsonDeps, + kind: PackageJsonDepKind, + ) { + if let Some(deps) = deps { + for (key, value) in deps { + result.entry(key.to_string()).or_insert_with(|| { + parse_entry(key, value).map(|entry| (kind, entry)) + }); + } + } + } + + let deps = package_json.dependencies.as_ref(); + let dev_deps = package_json.dev_dependencies.as_ref(); + let mut result = IndexMap::new(); + + // favors the deps over dev_deps + insert_deps(deps, &mut result, PackageJsonDepKind::Normal); + insert_deps(dev_deps, &mut result, PackageJsonDepKind::Dev); + + result +} + +impl WorkspaceDeps { + fn from_workspace(workspace: &Arc) -> Result { + let mut deps = Vec::new(); + for deno_json in workspace.deno_jsons() { + let import_map = match deno_json_import_map(deno_json) { + Ok(import_map) => import_map, + Err(e) => { + log::warn!( + "failed to parse imports from {}: {e}", + &deno_json.specifier + ); + continue; + } + }; + for (key_path, entry) in import_map_entries(&import_map.import_map) { + let Some(value) = entry.value else { continue }; + let kind = match value.scheme() { + "npm" => DepKind::Npm, + "jsr" => DepKind::Jsr, + _ => continue, + }; + let req = match JsrDepPackageReq::from_str(value.as_str()) { + Ok(req) => req, + Err(err) => { + log::warn!( + "failed to parse package req \"{}\": {err}", + value.as_str() + ); + continue; + } + }; + deps.push(Dep { + location: DepLocation::DenoJson(deno_json.clone(), key_path), + kind, + req: req.req, + }) + } + } + for package_json in workspace.package_jsons() { + let package_json_deps = resolve_local_package_json_deps(package_json); + for (k, v) in package_json_deps { + let (package_dep_kind, v) = match v { + Ok((k, v)) => (k, v), + Err(e) => { + log::warn!("bad package json dep value: {e}"); + continue; + } + }; + match v { + deno_package_json::PackageJsonDepValue::Req(package_req) => deps + .push(Dep { + kind: DepKind::Npm, + location: DepLocation::PackageJson( + package_json.clone(), + KeyPath::from_parts([package_dep_kind.into(), k.into()]), + ), + req: package_req, + }), + deno_package_json::PackageJsonDepValue::Workspace(_) => continue, + } + } + } + Ok(Self { deps }) + } +} + +pub async fn outdated( + flags: Arc, + outdated_flags: OutdatedFlags, +) -> Result<(), AnyError> { + let factory = CliFactory::from_flags(flags.clone()); + let cli_options = factory.cli_options()?; + let workspace = cli_options.workspace(); + let resolver = factory.workspace_resolver().await?; + let http_client = factory.http_client_provider(); + let deps_http_cache = factory.global_http_cache()?; + let mut file_fetcher = FileFetcher::new( + deps_http_cache.clone(), + CacheSetting::ReloadAll, + true, + http_client.clone(), + Default::default(), + None, + ); + file_fetcher.set_download_log_level(log::Level::Trace); + let file_fetcher = Arc::new(file_fetcher); + let deps_provider = NpmInstallDepsProvider::from_workspace(workspace); + let npm_resolver = + NpmFetchResolver::new(file_fetcher.clone(), cli_options.npmrc().clone()); + let jsr_resolver = JsrFetchResolver::new(file_fetcher.clone()); + + let deps = WorkspaceDeps::from_workspace(workspace)?; + + let mut graph_permit = factory + .main_module_graph_container() + .await? + .acquire_update_permit() + .await; + let root_permissions = factory.root_permissions_container()?; + + let graph = graph_permit.graph_mut(); + factory + .module_load_preparer() + .await? + .prepare_module_load( + graph, + &[], + false, + deno_config::deno_json::TsTypeLib::DenoWindow, + root_permissions.clone(), + None, + ) + .await?; + + eprintln!("deps: {deps:#?}"); + + let reqs = graph.packages.mappings(); + + let real_npm_resolver = factory.npm_resolver().await?; + let snapshot = real_npm_resolver.as_managed().unwrap().snapshot(); + + eprintln!("reqs: {reqs:?}"); + + let mut resolved_deps = Vec::with_capacity(deps.deps.len()); + + for dep in deps.deps { + eprintln!("looking up {}", dep.req); + match dep.kind { + DepKind::Npm => { + let nv = snapshot.package_reqs().get(&dep.req).unwrap(); + eprintln!("npm:{} => {nv}", dep.req); + resolved_deps.push(ResolvedDep { + dep, + resolved_version: Some(nv.version.clone()), + }); + } + DepKind::Jsr => { + let nv = reqs.get(&dep.req).unwrap(); + eprintln!("jsr:{} => {nv}", dep.req); + resolved_deps.push(ResolvedDep { + dep, + resolved_version: Some(nv.version.clone()), + }); + } + } + } + + let sema = Semaphore::new(32); + + let mut package_futs = FuturesUnordered::new(); + + for resolved in resolved_deps { + match resolved.dep.kind { + DepKind::Npm => { + package_futs.push( + async { + let _permit = sema.acquire().await.unwrap(); + let req = if outdated_flags.compatible { + resolved.dep.req.clone() + } else { + PackageReq { + name: resolved.dep.req.name.clone(), + version_req: VersionReq::from_raw_text_and_inner( + "latest".into(), + deno_semver::RangeSetOrTag::Tag("latest".into()), + ), + } + }; + let nv = npm_resolver.req_to_nv(&req).await; + (resolved, nv) + } + .boxed(), + ); + } + DepKind::Jsr => { + package_futs.push( + async { + let _permit = sema.acquire().await.unwrap(); + let req = if outdated_flags.compatible { + resolved.dep.req.clone() + } else { + PackageReq { + name: resolved.dep.req.name.clone(), + version_req: deno_semver::WILDCARD_VERSION_REQ.clone(), + } + }; + let nv = jsr_resolver.req_to_nv(&req).await; + (resolved, nv) + } + .boxed(), + ); + } + }; + } + + while let Some((resolved, latest)) = package_futs.next().await { + let Some(latest) = latest else { + continue; + }; + let resolved_version = resolved.resolved_version.unwrap_or_default(); + if latest.version > resolved_version { + eprintln!( + "Outdated package {} : have {}, latest {}", + resolved.dep.req, resolved_version, latest.version + ); + } + } + + Ok(()) +} diff --git a/cli/tools/registry/pm/update.rs b/cli/tools/registry/pm/update.rs new file mode 100644 index 00000000000000..fcada5d5a81c3b --- /dev/null +++ b/cli/tools/registry/pm/update.rs @@ -0,0 +1,30 @@ +use std::sync::Arc; + +use deno_core::error::AnyError; + +use crate::args::Flags; +use crate::args::UpdateFlags; +use crate::factory::CliFactory; + +pub async fn update( + flags: Arc, + update_flags: UpdateFlags, +) -> Result<(), AnyError> { + let factory = CliFactory::from_flags(flags.clone()); + let cli_options = factory.cli_options()?; + let npm_resolver = factory.npm_resolver().await?; + if let Some(npm_resolver) = npm_resolver.as_managed() { + npm_resolver.ensure_top_level_package_json_install().await?; + let old = npm_resolver + .snapshot() + .package_reqs() + .keys() + .cloned() + .collect::>(); + eprintln!("old: {old:?}"); + npm_resolver.set_package_reqs(&[]).await?; + npm_resolver.set_package_reqs(&old).await?; + } + super::cache_deps::cache_top_level_deps(&factory, None).await?; + Ok(()) +} From fb7d375a1d6f0b66d8fb908f4356de1e4e4c7aac Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Thu, 14 Nov 2024 19:58:00 -0800 Subject: [PATCH 02/26] pieces in place --- cli/npm/managed/mod.rs | 2 +- cli/tools/registry/pm.rs | 28 +- cli/tools/registry/pm/cache_deps.rs | 2 - cli/tools/registry/pm/deps.rs | 827 ++++++++++++++++++++++++++++ cli/tools/registry/pm/outdated.rs | 508 ++--------------- cli/tools/registry/pm/update.rs | 85 ++- 6 files changed, 966 insertions(+), 486 deletions(-) create mode 100644 cli/tools/registry/pm/deps.rs diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 2e64f5f188c27b..7b06504cba550e 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -559,7 +559,7 @@ impl ManagedCliNpmResolver { log::debug!( "All package.json deps resolvable. Skipping top level install." ); - return Ok(false); // everything is already resolvable + // return Ok(false); // everything is already resolvable } let pkg_reqs = pkg_json_remote_pkgs diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index 8575d34464000f..9e447c65ad4bd2 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -16,6 +16,7 @@ use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use deno_semver::Version; use deno_semver::VersionReq; +use deps::KeyPath; use jsonc_parser::cst::CstObject; use jsonc_parser::cst::CstObjectProp; use jsonc_parser::cst::CstRootNode; @@ -32,6 +33,7 @@ use crate::jsr::JsrFetchResolver; use crate::npm::NpmFetchResolver; mod cache_deps; +pub(crate) mod deps; mod outdated; mod update; @@ -39,7 +41,7 @@ pub use cache_deps::cache_top_level_deps; pub use outdated::outdated; pub use update::update; -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Hash)] enum ConfigKind { DenoJson, PackageJson, @@ -90,6 +92,30 @@ impl ConfigUpdater { self.cst.to_string() } + fn get_property_for_mutation( + &mut self, + key_path: &KeyPath, + ) -> Option { + let mut current_node = self.root_object.clone(); + + self.modified = true; + + for (i, part) in key_path.parts.iter().enumerate() { + let s = part.as_str(); + if i < key_path.parts.len().saturating_sub(1) { + let Some(object) = current_node.object_value(s) else { + return None; + }; + current_node = object; + } else { + // last part + return current_node.get(s); + } + } + + None + } + fn add(&mut self, selected: SelectedPackage, dev: bool) { fn insert_index(object: &CstObject, searching_name: &str) -> usize { object diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs index 79c587b74a3b34..d3c8da868c32f9 100644 --- a/cli/tools/registry/pm/cache_deps.rs +++ b/cli/tools/registry/pm/cache_deps.rs @@ -119,8 +119,6 @@ pub async fn cache_top_level_deps( None, ) .await?; - } else if let Some(lockfile) = lockfile { - lockfile.write_if_changed()?; } Ok(()) diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs new file mode 100644 index 00000000000000..d21884f38bfa0f --- /dev/null +++ b/cli/tools/registry/pm/deps.rs @@ -0,0 +1,827 @@ +use std::borrow::Cow; +use std::collections::HashMap; +use std::path::PathBuf; +use std::sync::Arc; + +use deno_ast::ModuleSpecifier; +use deno_config::deno_json::ConfigFile; +use deno_config::deno_json::ConfigFileRc; +use deno_config::workspace::Workspace; +use deno_config::workspace::WorkspaceDirectory; +use deno_core::anyhow::bail; +use deno_core::error::AnyError; +use deno_core::futures::stream::FuturesOrdered; +use deno_core::futures::stream::FuturesUnordered; +use deno_core::futures::FutureExt; +use deno_core::futures::StreamExt; +use deno_core::serde_json; +use deno_graph::FillFromLockfileOptions; +use deno_package_json::PackageJsonDepValue; +use deno_package_json::PackageJsonDepValueParseError; +use deno_package_json::PackageJsonRc; +use deno_runtime::deno_permissions::PermissionsContainer; +use deno_semver::jsr::JsrDepPackageReq; +use deno_semver::jsr::JsrPackageReqReference; +use deno_semver::npm::NpmPackageReqReference; +use deno_semver::package::PackageNv; +use deno_semver::package::PackageReq; +use deno_semver::package::PackageReqReference; +use deno_semver::Version; +use deno_semver::VersionReq; +use import_map::ImportMap; +use import_map::ImportMapWithDiagnostics; +use import_map::SpecifierMapEntry; +use indexmap::IndexMap; +use tokio::sync::Semaphore; + +use crate::args::CliLockfile; +use crate::graph_container::MainModuleGraphContainer; +use crate::graph_container::ModuleGraphContainer; +use crate::graph_container::ModuleGraphUpdatePermit; +use crate::jsr::JsrFetchResolver; +use crate::module_loader::ModuleLoadPreparer; +use crate::npm::CliNpmResolver; +use crate::npm::NpmFetchResolver; + +use super::ConfigUpdater; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ImportMapKind { + Inline, + Outline, +} + +#[derive(Clone)] +pub enum DepLocation { + DenoJson(ConfigFileRc, KeyPath, ImportMapKind), + PackageJson(PackageJsonRc, KeyPath), +} + +impl DepLocation { + pub fn is_deno_json(&self) -> bool { + matches!(self, DepLocation::DenoJson(..)) + } + pub fn is_package_json(&self) -> bool { + matches!(self, DepLocation::PackageJson(..)) + } +} + +struct DebugAdapter(T); + +impl<'a> std::fmt::Debug for DebugAdapter<&'a ConfigFileRc> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ConfigFile") + .field("specifier", &self.0.specifier) + .finish() + } +} +impl<'a> std::fmt::Debug for DebugAdapter<&'a PackageJsonRc> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PackageJson") + .field("path", &self.0.path) + .finish() + } +} + +impl std::fmt::Debug for DepLocation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + DepLocation::DenoJson(arc, key_path, kind) => { + let mut debug = f.debug_tuple("DenoJson"); + debug + .field(&DebugAdapter(arc)) + .field(key_path) + .field(kind) + .finish() + } + DepLocation::PackageJson(arc, key_path) => { + let mut debug = f.debug_tuple("PackageJson"); + debug.field(&DebugAdapter(arc)).field(key_path).finish() + } + } + } +} + +#[derive(Clone, Debug)] +pub enum DepKind { + Npm, + Jsr, +} + +impl DepKind { + fn scheme(&self) -> &'static str { + match self { + DepKind::Npm => "npm", + DepKind::Jsr => "jsr", + } + } +} + +#[derive(Clone, Debug)] +pub enum KeyPart { + Imports, + Scopes, + Dependencies, + DevDependencies, + String(String), +} + +macro_rules! const_assert { + ($x:expr $(,)?) => { + #[allow(unknown_lints, eq_op)] + const _: [(); + 0 - !{ + const ASSERT: bool = $x; + ASSERT + } as usize] = []; + }; +} + +impl From for KeyPart { + fn from(value: String) -> Self { + KeyPart::String(value) + } +} + +impl From for KeyPart { + fn from(value: PackageJsonDepKind) -> Self { + match value { + PackageJsonDepKind::Normal => Self::Dependencies, + PackageJsonDepKind::Dev => Self::DevDependencies, + } + } +} + +impl KeyPart { + pub fn as_str(&self) -> &str { + match self { + KeyPart::Imports => "imports", + KeyPart::Scopes => "scopes", + KeyPart::Dependencies => "dependencies", + KeyPart::DevDependencies => "devDependencies", + KeyPart::String(s) => s, + } + } +} + +#[derive(Clone, Debug)] +pub struct KeyPath { + pub parts: Vec, +} + +impl KeyPath { + fn from_parts(parts: impl IntoIterator) -> Self { + Self { + parts: parts.into_iter().collect(), + } + } + fn push(&mut self, part: KeyPart) { + self.parts.push(part) + } +} + +#[derive(Clone, Debug)] +pub struct Dep { + pub req: PackageReq, + pub kind: DepKind, + pub location: DepLocation, + pub id: DepId, + // specifier: String, +} + +#[derive(Debug, Clone)] +pub struct ResolvedDep { + dep: Dep, + resolved_version: Option, +} + +#[derive(Debug)] +pub struct WorkspaceDeps { + deps: Vec, +} + +fn import_map_entries<'a>( + import_map: &'a ImportMap, +) -> impl Iterator)> { + import_map + .imports() + .entries() + .map(|entry| { + ( + KeyPath::from_parts([ + KeyPart::Imports, + KeyPart::String(entry.raw_key.into()), + ]), + entry, + ) + }) + .chain(import_map.scopes().flat_map(|scope| { + let path = KeyPath::from_parts([ + KeyPart::Scopes, + scope.raw_key.to_string().into(), + ]); + + scope.imports.entries().map(move |entry| { + let mut full_path = path.clone(); + full_path.push(KeyPart::Imports); + full_path.push(KeyPart::String(entry.raw_key.to_string())); + (full_path, entry) + }) + })) +} + +fn deno_json_import_map( + deno_json: &ConfigFile, +) -> Result, AnyError> { + let Some((url, value)) = deno_json.to_import_map_value(|path| { + std::fs::read_to_string(path).map_err(Into::into) + })? + else { + return Ok(None); + }; + + import_map::parse_from_value(deno_json.specifier.clone(), value) + .map_err(Into::into) + .map(|import_map| { + let kind = if &*url == &deno_json.specifier { + ImportMapKind::Inline + } else { + ImportMapKind::Outline + }; + Some((import_map, kind)) + }) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum PackageJsonDepKind { + Normal, + Dev, +} + +type PackageJsonDeps = IndexMap< + String, + Result< + (PackageJsonDepKind, PackageJsonDepValue), + PackageJsonDepValueParseError, + >, +>; + +/// Resolve the package.json's dependencies. +fn resolve_local_package_json_deps( + package_json: &PackageJsonRc, +) -> PackageJsonDeps { + /// Gets the name and raw version constraint for a registry info or + /// package.json dependency entry taking into account npm package aliases. + fn parse_dep_entry_name_and_raw_version<'a>( + key: &'a str, + value: &'a str, + ) -> (&'a str, &'a str) { + if let Some(package_and_version) = value.strip_prefix("npm:") { + if let Some((name, version)) = package_and_version.rsplit_once('@') { + // if empty, then the name was scoped and there's no version + if name.is_empty() { + (package_and_version, "*") + } else { + (name, version) + } + } else { + (package_and_version, "*") + } + } else { + (key, value) + } + } + + fn parse_entry( + key: &str, + value: &str, + ) -> Result { + if let Some(workspace_key) = value.strip_prefix("workspace:") { + let version_req = VersionReq::parse_from_npm(workspace_key)?; + return Ok(PackageJsonDepValue::Workspace(version_req)); + } + if value.starts_with("file:") + || value.starts_with("git:") + || value.starts_with("http:") + || value.starts_with("https:") + { + return Err(PackageJsonDepValueParseError::Unsupported { + scheme: value.split(':').next().unwrap().to_string(), + }); + } + let (name, version_req) = parse_dep_entry_name_and_raw_version(key, value); + let result = VersionReq::parse_from_npm(version_req); + match result { + Ok(version_req) => Ok(PackageJsonDepValue::Req(PackageReq { + name: name.to_string(), + version_req, + })), + Err(err) => Err(PackageJsonDepValueParseError::VersionReq(err)), + } + } + + fn insert_deps( + deps: Option<&IndexMap>, + result: &mut PackageJsonDeps, + kind: PackageJsonDepKind, + ) { + if let Some(deps) = deps { + for (key, value) in deps { + result.entry(key.to_string()).or_insert_with(|| { + parse_entry(key, value).map(|entry| (kind, entry)) + }); + } + } + } + + let deps = package_json.dependencies.as_ref(); + let dev_deps = package_json.dev_dependencies.as_ref(); + let mut result = IndexMap::new(); + + // favors the deps over dev_deps + insert_deps(deps, &mut result, PackageJsonDepKind::Normal); + insert_deps(dev_deps, &mut result, PackageJsonDepKind::Dev); + + result +} + +fn add_deps_from_deno_json(deno_json: &Arc, deps: &mut Vec) { + let (import_map, import_map_kind) = match deno_json_import_map(deno_json) { + Ok(Some((import_map, import_map_kind))) => (import_map, import_map_kind), + Ok(None) => return, + Err(e) => { + log::warn!("failed to parse imports from {}: {e}", &deno_json.specifier); + return; + } + }; + for (key_path, entry) in import_map_entries(&import_map.import_map) { + let Some(value) = entry.value else { continue }; + let kind = match value.scheme() { + "npm" => DepKind::Npm, + "jsr" => DepKind::Jsr, + _ => continue, + }; + let req = match JsrDepPackageReq::from_str(value.as_str()) { + Ok(req) => req, + Err(err) => { + log::warn!("failed to parse package req \"{}\": {err}", value.as_str()); + continue; + } + }; + let id = DepId(deps.len()); + deps.push(Dep { + location: DepLocation::DenoJson( + deno_json.clone(), + key_path, + import_map_kind, + ), + kind, + req: req.req, + id, + }); + } +} + +fn add_deps_from_package_json( + package_json: &PackageJsonRc, + deps: &mut Vec, +) { + let package_json_deps = resolve_local_package_json_deps(package_json); + for (k, v) in package_json_deps { + let (package_dep_kind, v) = match v { + Ok((k, v)) => (k, v), + Err(e) => { + log::warn!("bad package json dep value: {e}"); + continue; + } + }; + match v { + deno_package_json::PackageJsonDepValue::Req(package_req) => { + let id = DepId(deps.len()); + deps.push(Dep { + id, + kind: DepKind::Npm, + location: DepLocation::PackageJson( + package_json.clone(), + KeyPath::from_parts([package_dep_kind.into(), k.into()]), + ), + req: package_req, + }) + } + deno_package_json::PackageJsonDepValue::Workspace(_) => continue, + } + } +} + +fn deps_from_workspace( + workspace: &Arc, +) -> Result, AnyError> { + let mut deps = Vec::with_capacity(32); + for deno_json in workspace.deno_jsons() { + eprintln!("deno_json: {}", deno_json.specifier); + add_deps_from_deno_json(deno_json, &mut deps); + } + for package_json in workspace.package_jsons() { + eprintln!("package_json: {}", package_json.path.display()); + add_deps_from_package_json(package_json, &mut deps); + } + + Ok(deps) +} + +#[derive(Default, Clone)] +enum ResolveState { + #[default] + NotYet, + Unresolved, + Resolved(PackageNv), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct DepId(usize); + +#[derive(Debug, Clone)] +pub enum Change { + Update(DepId, VersionReq), +} + +pub struct DepManager { + deps: Vec, + resolved_versions: Vec>, + latest_versions: Vec>, + + pending_changes: Vec, + + loaded_roots: bool, + module_load_preparer: Arc, + jsr_fetch_resolver: Arc, + npm_fetch_resolver: Arc, + npm_resolver: Arc, + permissions_container: PermissionsContainer, + main_module_graph_container: Arc, + lockfile: Option>, +} + +pub struct DepManagerArgs { + pub module_load_preparer: Arc, + pub jsr_fetch_resolver: Arc, + pub npm_fetch_resolver: Arc, + pub npm_resolver: Arc, + pub permissions_container: PermissionsContainer, + pub main_module_graph_container: Arc, + pub lockfile: Option>, +} + +impl DepManager { + fn with_deps_args(deps: Vec, args: DepManagerArgs) -> Self { + let DepManagerArgs { + module_load_preparer, + jsr_fetch_resolver, + npm_fetch_resolver, + npm_resolver, + permissions_container, + main_module_graph_container, + lockfile, + } = args; + Self { + deps, + resolved_versions: Vec::new(), + latest_versions: Vec::new(), + jsr_fetch_resolver, + loaded_roots: false, + module_load_preparer, + npm_fetch_resolver, + npm_resolver, + permissions_container, + main_module_graph_container, + lockfile, + pending_changes: Vec::new(), + } + } + pub fn from_workspace_dir( + workspace_dir: &Arc, + args: DepManagerArgs, + ) -> Result { + let mut deps = Vec::with_capacity(32); + if let Some(deno_json) = workspace_dir.maybe_deno_json() { + add_deps_from_deno_json(deno_json, &mut deps); + } + if let Some(package_json) = workspace_dir.maybe_pkg_json() { + add_deps_from_package_json(package_json, &mut deps); + } + + Ok(Self::with_deps_args(deps, args)) + } + pub fn from_workspace( + workspace: &Arc, + args: DepManagerArgs, + ) -> Result { + let deps = deps_from_workspace(workspace)?; + Ok(Self::with_deps_args(deps, args)) + } + + async fn load_roots(&mut self) -> Result<(), AnyError> { + if self.loaded_roots { + return Ok(()); + } + + let mut roots = Vec::new(); + let mut info_futures = FuturesUnordered::new(); + for dep in &self.deps { + if dep.location.is_deno_json() { + match dep.kind { + DepKind::Npm => roots.push( + ModuleSpecifier::parse(&format!("npm:/{}/", dep.req)).unwrap(), + ), + DepKind::Jsr => info_futures.push(async { + if let Some(nv) = self.jsr_fetch_resolver.req_to_nv(&dep.req).await + { + if let Some(info) = + self.jsr_fetch_resolver.package_version_info(&nv).await + { + let specifier = + ModuleSpecifier::parse(&format!("jsr:/{}/", dep.req)) + .unwrap(); + return Some((specifier, info)); + } + } + None + }), + } + } + } + + while let Some(info_future) = info_futures.next().await { + if let Some((specifier, info)) = info_future { + let exports = info.exports(); + for (k, _) in exports { + if let Ok(spec) = specifier.join(k) { + roots.push(spec); + } + } + } + } + + let mut graph_permit = self + .main_module_graph_container + .acquire_update_permit() + .await; + let graph = graph_permit.graph_mut(); + // populate the information from the lockfile + if let Some(lockfile) = &self.lockfile { + let lockfile = lockfile.lock(); + graph.fill_from_lockfile(FillFromLockfileOptions { + redirects: lockfile + .content + .redirects + .iter() + .map(|(from, to)| (from.as_str(), to.as_str())), + package_specifiers: lockfile + .content + .packages + .specifiers + .iter() + .map(|(dep, id)| (dep, id.as_str())), + }); + } + self + .module_load_preparer + .prepare_module_load( + graph, + &roots, + false, + deno_config::deno_json::TsTypeLib::DenoWindow, + self.permissions_container.clone(), + None, + ) + .await?; + + graph_permit.commit(); + + Ok(()) + } + + pub async fn resolve_versions(&mut self) -> Result<(), AnyError> { + self.load_roots().await?; + + let graph = self.main_module_graph_container.graph(); + + let mut resolved = Vec::with_capacity(self.deps.len()); + let snapshot = self.npm_resolver.as_managed().unwrap().snapshot(); + let resolved_npm = snapshot.package_reqs(); + let resolved_jsr = graph.packages.mappings(); + for dep in &self.deps { + match dep.kind { + DepKind::Npm => { + let resolved_version = resolved_npm.get(&dep.req).cloned(); + resolved.push(resolved_version); + } + DepKind::Jsr => { + let resolved_version = resolved_jsr.get(&dep.req).cloned(); + resolved.push(resolved_version) + } + } + } + + self.resolved_versions = resolved; + + Ok(()) + } + + pub fn resolved_version( + &self, + dep_id: DepId, + ) -> Result, AnyError> { + if self.resolved_versions.len() < self.deps.len() { + return Err(deno_core::anyhow::anyhow!( + "Versions haven't been resolved yet" + )); + } + + Ok(self.resolved_versions[dep_id.0].as_ref()) + } + + pub fn resolved_versions(&self) -> &[Option] { + &self.resolved_versions + } + + pub async fn fetch_latest_versions( + &mut self, + semver_compatible: bool, + ) -> Result<(), AnyError> { + let latest_tag_req = deno_semver::VersionReq::from_raw_text_and_inner( + "latest".into(), + deno_semver::RangeSetOrTag::Tag("latest".into()), + ); + let mut latest = Vec::with_capacity(self.deps.len()); + + let sema = Semaphore::new(32); + let mut futs = FuturesOrdered::new(); + + for dep in &self.deps { + match dep.kind { + DepKind::Npm => futs.push_back( + async { + let req = if semver_compatible { + Cow::Borrowed(&dep.req) + } else { + Cow::Owned(PackageReq { + name: dep.req.name.clone(), + version_req: latest_tag_req.clone(), + }) + }; + let _permit = sema.acquire().await; + self.npm_fetch_resolver.req_to_nv(&req).await + } + .boxed_local(), + ), + DepKind::Jsr => futs.push_back( + async { + let req = if semver_compatible { + Cow::Borrowed(&dep.req) + } else { + Cow::Owned(PackageReq { + name: dep.req.name.clone(), + version_req: deno_semver::WILDCARD_VERSION_REQ.clone(), + }) + }; + let _permit = sema.acquire().await; + self.jsr_fetch_resolver.req_to_nv(&req).await + } + .boxed_local(), + ), + } + } + while let Some(nv) = futs.next().await { + latest.push(nv); + } + self.latest_versions = latest; + + Ok(()) + } + + pub fn latest_versions(&self) -> &[Option] { + &self.latest_versions + } + + pub fn deps_with_latest_versions( + &self, + ) -> impl IntoIterator)> + '_ { + self + .latest_versions + .iter() + .enumerate() + .map(|(i, latest)| (DepId(i), latest.clone())) + } + + pub fn deps(&self) -> &[Dep] { + &self.deps + } + + pub fn update_dep(&mut self, dep_id: DepId, new_version_req: VersionReq) { + self + .pending_changes + .push(Change::Update(dep_id, new_version_req)) + } + + pub async fn commit_changes(&mut self) -> Result<(), AnyError> { + let changes = std::mem::take(&mut self.pending_changes); + // let mut config_updaters = HashMap::new(); + for change in changes { + match change { + Change::Update(dep_id, version_req) => { + let dep = &self.deps[dep_id.0]; + match &dep.location { + DepLocation::DenoJson(arc, key_path, import_map_kind) => { + if matches!(import_map_kind, ImportMapKind::Outline) { + // not supported + continue; + } + let mut updater = ConfigUpdater::new( + super::ConfigKind::DenoJson, + // TODO: unwrap is sus + arc.specifier.to_file_path().unwrap(), + )?; + let Some(property) = updater.get_property_for_mutation(&key_path) + else { + log::warn!( + "failed to find property at path {key_path:?} for file {}", + arc.specifier + ); + continue; + }; + let value = property.value().unwrap(); + let Some(string) = value.as_string_lit() else { + log::warn!("malformed entry"); + continue; + }; + let Ok(string_value) = string.decoded_value() else { + log::warn!("malformed string: {string:?}"); + continue; + }; + let mut req_reference = match dep.kind { + DepKind::Npm => NpmPackageReqReference::from_str(&string_value) + .unwrap() + .into_inner(), + DepKind::Jsr => JsrPackageReqReference::from_str(&string_value) + .unwrap() + .into_inner(), + }; + req_reference.req.version_req = version_req; + let new_value = + format!("{}:{}", dep.kind.scheme(), req_reference); + property + .set_value(jsonc_parser::cst::CstInputValue::String(new_value)); + updater.commit()?; + } + DepLocation::PackageJson(arc, key_path) => { + eprintln!("here: {dep:?}"); + let mut updater = ConfigUpdater::new( + super::ConfigKind::PackageJson, + arc.path.clone(), + )?; + let Some(property) = updater.get_property_for_mutation(&key_path) + else { + log::warn!( + "failed to find property at path {key_path:?} for file {}", + arc.path.display() + ); + continue; + }; + let value = property.value().unwrap(); + let Some(string) = value.as_string_lit() else { + log::warn!("malformed entry"); + continue; + }; + let Ok(string_value) = string.decoded_value() else { + log::warn!("malformed string: {string:?}"); + continue; + }; + let new_value = if string_value.starts_with("npm:") { + // aliased + let rest = string_value.trim_start_matches("npm:"); + let mut parts = rest.split('@'); + let first = parts.next().unwrap(); + if first.is_empty() { + let scope_and_name = parts.next().unwrap(); + format!("npm:@{scope_and_name}@{version_req}") + } else { + format!("npm:{first}@{version_req}") + } + } else if string_value.contains(":") { + bail!("Unexpected package json dependency string: \"{string_value}\" in {}", arc.path.display()); + } else { + version_req.to_string() + }; + property + .set_value(jsonc_parser::cst::CstInputValue::String(new_value)); + updater.commit()?; + } + } + } + } + } + + Ok(()) + } +} diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs index 39d7a7f44c9c57..99e0eddaa926c4 100644 --- a/cli/tools/registry/pm/outdated.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -1,367 +1,16 @@ use std::sync::Arc; -use deno_config::deno_json::ConfigFile; -use deno_config::deno_json::ConfigFileRc; -use deno_config::workspace::Workspace; use deno_core::error::AnyError; -use deno_core::futures::stream::FuturesUnordered; -use deno_core::futures::FutureExt; -use deno_core::futures::StreamExt; -use deno_core::serde_json; -use deno_core::url::Url; -use deno_package_json::PackageJsonDepValue; -use deno_package_json::PackageJsonDepValueParseError; -use deno_package_json::PackageJsonRc; -use deno_semver::jsr::JsrDepPackageReq; -use deno_semver::package::PackageReq; -use deno_semver::Version; -use deno_semver::VersionReq; -use import_map::ImportMap; -use import_map::ImportMapWithDiagnostics; -use import_map::SpecifierMapEntry; -use indexmap::IndexMap; -use tokio::sync::Semaphore; use crate::args::CacheSetting; use crate::args::Flags; -use crate::args::NpmInstallDepsProvider; use crate::args::OutdatedFlags; use crate::factory::CliFactory; use crate::file_fetcher::FileFetcher; -use crate::graph_container::ModuleGraphContainer; -use crate::graph_container::ModuleGraphUpdatePermit; use crate::jsr::JsrFetchResolver; use crate::npm::NpmFetchResolver; -#[derive(Clone)] -enum DepLocation { - DenoJson(ConfigFileRc, KeyPath), - PackageJson(PackageJsonRc, KeyPath), -} - -struct DebugAdapter(T); - -impl<'a> std::fmt::Debug for DebugAdapter<&'a ConfigFileRc> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ConfigFile") - .field("specifier", &self.0.specifier) - .finish() - } -} -impl<'a> std::fmt::Debug for DebugAdapter<&'a PackageJsonRc> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PackageJson") - .field("path", &self.0.path) - .finish() - } -} - -impl std::fmt::Debug for DepLocation { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - DepLocation::DenoJson(arc, key_path) => { - let mut debug = f.debug_tuple("DenoJson"); - debug.field(&DebugAdapter(arc)).field(key_path).finish() - } - DepLocation::PackageJson(arc, key_path) => { - let mut debug = f.debug_tuple("PackageJson"); - debug.field(&DebugAdapter(arc)).field(key_path).finish() - } - } - } -} - -#[derive(Clone, Debug)] -enum DepKind { - Npm, - Jsr, -} - -impl DepKind { - fn scheme(&self) -> &'static str { - match self { - DepKind::Npm => "npm", - DepKind::Jsr => "jsr", - } - } -} - -#[derive(Clone, Debug)] -enum KeyPart { - Imports, - Scopes, - Dependencies, - DevDependencies, - String(String), -} - -impl From for KeyPart { - fn from(value: String) -> Self { - KeyPart::String(value) - } -} - -impl From for KeyPart { - fn from(value: PackageJsonDepKind) -> Self { - match value { - PackageJsonDepKind::Normal => Self::Dependencies, - PackageJsonDepKind::Dev => Self::DevDependencies, - } - } -} - -impl KeyPart { - fn as_str(&self) -> &str { - match self { - KeyPart::Imports => "imports", - KeyPart::Scopes => "scopes", - KeyPart::Dependencies => "dependencies", - KeyPart::DevDependencies => "devDependencies", - KeyPart::String(s) => s, - } - } -} - -#[derive(Clone, Debug)] -struct KeyPath { - keys: Vec, -} - -impl KeyPath { - fn from_parts(parts: impl IntoIterator) -> Self { - Self { - keys: parts.into_iter().collect(), - } - } - fn push(&mut self, part: KeyPart) { - self.keys.push(part) - } -} - -#[derive(Clone, Debug)] -struct Dep { - req: PackageReq, - kind: DepKind, - location: DepLocation, - // specifier: String, -} - -#[derive(Debug, Clone)] -struct ResolvedDep { - dep: Dep, - resolved_version: Option, -} - -#[derive(Debug)] -struct WorkspaceDeps { - deps: Vec, -} - -fn import_map_entries<'a>( - import_map: &'a ImportMap, -) -> impl Iterator)> { - import_map - .imports() - .entries() - .map(|entry| { - ( - KeyPath::from_parts([ - KeyPart::Imports, - KeyPart::String(entry.raw_key.into()), - ]), - entry, - ) - }) - .chain(import_map.scopes().flat_map(|scope| { - let path = KeyPath::from_parts([ - KeyPart::Scopes, - scope.raw_key.to_string().into(), - ]); - - scope.imports.entries().map(move |entry| { - let mut full_path = path.clone(); - full_path.push(KeyPart::Imports); - full_path.push(KeyPart::String(entry.raw_key.to_string())); - (full_path, entry) - }) - })) -} - -fn deno_json_import_map( - deno_json: &ConfigFile, -) -> Result { - let mut map = serde_json::Map::with_capacity(2); - if let Some(imports) = &deno_json.json.imports { - map.insert("imports".to_string(), imports.clone()); - } - if let Some(scopes) = &deno_json.json.scopes { - map.insert("scopes".to_string(), scopes.clone()); - } - import_map::parse_from_value( - deno_json.specifier.clone(), - serde_json::Value::Object(map), - ) - .map_err(Into::into) -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum PackageJsonDepKind { - Normal, - Dev, -} - -type PackageJsonDeps = IndexMap< - String, - Result< - (PackageJsonDepKind, PackageJsonDepValue), - PackageJsonDepValueParseError, - >, ->; - -/// Resolve the package.json's dependencies. -fn resolve_local_package_json_deps( - package_json: &PackageJsonRc, -) -> PackageJsonDeps { - /// Gets the name and raw version constraint for a registry info or - /// package.json dependency entry taking into account npm package aliases. - fn parse_dep_entry_name_and_raw_version<'a>( - key: &'a str, - value: &'a str, - ) -> (&'a str, &'a str) { - if let Some(package_and_version) = value.strip_prefix("npm:") { - if let Some((name, version)) = package_and_version.rsplit_once('@') { - // if empty, then the name was scoped and there's no version - if name.is_empty() { - (package_and_version, "*") - } else { - (name, version) - } - } else { - (package_and_version, "*") - } - } else { - (key, value) - } - } - - fn parse_entry( - key: &str, - value: &str, - ) -> Result { - if let Some(workspace_key) = value.strip_prefix("workspace:") { - let version_req = VersionReq::parse_from_npm(workspace_key)?; - return Ok(PackageJsonDepValue::Workspace(version_req)); - } - if value.starts_with("file:") - || value.starts_with("git:") - || value.starts_with("http:") - || value.starts_with("https:") - { - return Err(PackageJsonDepValueParseError::Unsupported { - scheme: value.split(':').next().unwrap().to_string(), - }); - } - let (name, version_req) = parse_dep_entry_name_and_raw_version(key, value); - let result = VersionReq::parse_from_npm(version_req); - match result { - Ok(version_req) => Ok(PackageJsonDepValue::Req(PackageReq { - name: name.to_string(), - version_req, - })), - Err(err) => Err(PackageJsonDepValueParseError::VersionReq(err)), - } - } - - fn insert_deps( - deps: Option<&IndexMap>, - result: &mut PackageJsonDeps, - kind: PackageJsonDepKind, - ) { - if let Some(deps) = deps { - for (key, value) in deps { - result.entry(key.to_string()).or_insert_with(|| { - parse_entry(key, value).map(|entry| (kind, entry)) - }); - } - } - } - - let deps = package_json.dependencies.as_ref(); - let dev_deps = package_json.dev_dependencies.as_ref(); - let mut result = IndexMap::new(); - - // favors the deps over dev_deps - insert_deps(deps, &mut result, PackageJsonDepKind::Normal); - insert_deps(dev_deps, &mut result, PackageJsonDepKind::Dev); - - result -} - -impl WorkspaceDeps { - fn from_workspace(workspace: &Arc) -> Result { - let mut deps = Vec::new(); - for deno_json in workspace.deno_jsons() { - let import_map = match deno_json_import_map(deno_json) { - Ok(import_map) => import_map, - Err(e) => { - log::warn!( - "failed to parse imports from {}: {e}", - &deno_json.specifier - ); - continue; - } - }; - for (key_path, entry) in import_map_entries(&import_map.import_map) { - let Some(value) = entry.value else { continue }; - let kind = match value.scheme() { - "npm" => DepKind::Npm, - "jsr" => DepKind::Jsr, - _ => continue, - }; - let req = match JsrDepPackageReq::from_str(value.as_str()) { - Ok(req) => req, - Err(err) => { - log::warn!( - "failed to parse package req \"{}\": {err}", - value.as_str() - ); - continue; - } - }; - deps.push(Dep { - location: DepLocation::DenoJson(deno_json.clone(), key_path), - kind, - req: req.req, - }) - } - } - for package_json in workspace.package_jsons() { - let package_json_deps = resolve_local_package_json_deps(package_json); - for (k, v) in package_json_deps { - let (package_dep_kind, v) = match v { - Ok((k, v)) => (k, v), - Err(e) => { - log::warn!("bad package json dep value: {e}"); - continue; - } - }; - match v { - deno_package_json::PackageJsonDepValue::Req(package_req) => deps - .push(Dep { - kind: DepKind::Npm, - location: DepLocation::PackageJson( - package_json.clone(), - KeyPath::from_parts([package_dep_kind.into(), k.into()]), - ), - req: package_req, - }), - deno_package_json::PackageJsonDepValue::Workspace(_) => continue, - } - } - } - Ok(Self { deps }) - } -} +use super::deps::DepManagerArgs; pub async fn outdated( flags: Arc, @@ -383,125 +32,48 @@ pub async fn outdated( ); file_fetcher.set_download_log_level(log::Level::Trace); let file_fetcher = Arc::new(file_fetcher); - let deps_provider = NpmInstallDepsProvider::from_workspace(workspace); - let npm_resolver = - NpmFetchResolver::new(file_fetcher.clone(), cli_options.npmrc().clone()); - let jsr_resolver = JsrFetchResolver::new(file_fetcher.clone()); - - let deps = WorkspaceDeps::from_workspace(workspace)?; - - let mut graph_permit = factory - .main_module_graph_container() - .await? - .acquire_update_permit() - .await; - let root_permissions = factory.root_permissions_container()?; - - let graph = graph_permit.graph_mut(); - factory - .module_load_preparer() - .await? - .prepare_module_load( - graph, - &[], - false, - deno_config::deno_json::TsTypeLib::DenoWindow, - root_permissions.clone(), - None, - ) + let npm_resolver = Arc::new(NpmFetchResolver::new( + file_fetcher.clone(), + cli_options.npmrc().clone(), + )); + let jsr_resolver = Arc::new(JsrFetchResolver::new(file_fetcher.clone())); + + let mut deps = super::deps::DepManager::from_workspace( + workspace, + DepManagerArgs { + module_load_preparer: factory.module_load_preparer().await?.clone(), + jsr_fetch_resolver: jsr_resolver, + npm_fetch_resolver: npm_resolver, + npm_resolver: factory.npm_resolver().await?.clone(), + permissions_container: factory.root_permissions_container()?.clone(), + main_module_graph_container: factory + .main_module_graph_container() + .await? + .clone(), + lockfile: cli_options.maybe_lockfile().cloned(), + }, + )?; + + deps.resolve_versions().await?; + deps + .fetch_latest_versions(outdated_flags.compatible) .await?; - eprintln!("deps: {deps:#?}"); - - let reqs = graph.packages.mappings(); - - let real_npm_resolver = factory.npm_resolver().await?; - let snapshot = real_npm_resolver.as_managed().unwrap().snapshot(); - - eprintln!("reqs: {reqs:?}"); - - let mut resolved_deps = Vec::with_capacity(deps.deps.len()); - - for dep in deps.deps { - eprintln!("looking up {}", dep.req); - match dep.kind { - DepKind::Npm => { - let nv = snapshot.package_reqs().get(&dep.req).unwrap(); - eprintln!("npm:{} => {nv}", dep.req); - resolved_deps.push(ResolvedDep { - dep, - resolved_version: Some(nv.version.clone()), - }); - } - DepKind::Jsr => { - let nv = reqs.get(&dep.req).unwrap(); - eprintln!("jsr:{} => {nv}", dep.req); - resolved_deps.push(ResolvedDep { - dep, - resolved_version: Some(nv.version.clone()), - }); - } - } - } - - let sema = Semaphore::new(32); - - let mut package_futs = FuturesUnordered::new(); - - for resolved in resolved_deps { - match resolved.dep.kind { - DepKind::Npm => { - package_futs.push( - async { - let _permit = sema.acquire().await.unwrap(); - let req = if outdated_flags.compatible { - resolved.dep.req.clone() - } else { - PackageReq { - name: resolved.dep.req.name.clone(), - version_req: VersionReq::from_raw_text_and_inner( - "latest".into(), - deno_semver::RangeSetOrTag::Tag("latest".into()), - ), - } - }; - let nv = npm_resolver.req_to_nv(&req).await; - (resolved, nv) - } - .boxed(), - ); - } - DepKind::Jsr => { - package_futs.push( - async { - let _permit = sema.acquire().await.unwrap(); - let req = if outdated_flags.compatible { - resolved.dep.req.clone() - } else { - PackageReq { - name: resolved.dep.req.name.clone(), - version_req: deno_semver::WILDCARD_VERSION_REQ.clone(), - } - }; - let nv = jsr_resolver.req_to_nv(&req).await; - (resolved, nv) - } - .boxed(), - ); + for ((dep, resolved_version), latest_version) in deps + .deps() + .iter() + .zip(deps.resolved_versions().iter()) + .zip(deps.latest_versions().iter()) + { + if let Some(resolved_version) = resolved_version { + if let Some(latest_version) = latest_version { + if latest_version > resolved_version { + eprintln!( + "outdated dependency {} : {} -> {}", + dep.req, resolved_version, latest_version + ); + } } - }; - } - - while let Some((resolved, latest)) = package_futs.next().await { - let Some(latest) = latest else { - continue; - }; - let resolved_version = resolved.resolved_version.unwrap_or_default(); - if latest.version > resolved_version { - eprintln!( - "Outdated package {} : have {}, latest {}", - resolved.dep.req, resolved_version, latest.version - ); } } diff --git a/cli/tools/registry/pm/update.rs b/cli/tools/registry/pm/update.rs index fcada5d5a81c3b..a791131880e12d 100644 --- a/cli/tools/registry/pm/update.rs +++ b/cli/tools/registry/pm/update.rs @@ -1,10 +1,27 @@ use std::sync::Arc; use deno_core::error::AnyError; +use deno_semver::Version; +use deno_semver::VersionReq; +use crate::args::CacheSetting; use crate::args::Flags; use crate::args::UpdateFlags; use crate::factory::CliFactory; +use crate::file_fetcher::FileFetcher; +use crate::jsr::JsrFetchResolver; +use crate::npm::NpmFetchResolver; + +use super::deps::DepManagerArgs; + +// fn update_lower_bound(req: VersionReq, version: Version) -> Option { +// match req.inner() { +// deno_semver::RangeSetOrTag::RangeSet(version_range_set) => { +// version_range_set. +// }, +// deno_semver::RangeSetOrTag::Tag(_) => todo!(), +// } +// } pub async fn update( flags: Arc, @@ -12,19 +29,59 @@ pub async fn update( ) -> Result<(), AnyError> { let factory = CliFactory::from_flags(flags.clone()); let cli_options = factory.cli_options()?; - let npm_resolver = factory.npm_resolver().await?; - if let Some(npm_resolver) = npm_resolver.as_managed() { - npm_resolver.ensure_top_level_package_json_install().await?; - let old = npm_resolver - .snapshot() - .package_reqs() - .keys() - .cloned() - .collect::>(); - eprintln!("old: {old:?}"); - npm_resolver.set_package_reqs(&[]).await?; - npm_resolver.set_package_reqs(&old).await?; + let workspace = cli_options.workspace(); + let resolver = factory.workspace_resolver().await?; + let http_client = factory.http_client_provider(); + let deps_http_cache = factory.global_http_cache()?; + let mut file_fetcher = FileFetcher::new( + deps_http_cache.clone(), + CacheSetting::ReloadAll, + true, + http_client.clone(), + Default::default(), + None, + ); + file_fetcher.set_download_log_level(log::Level::Trace); + let file_fetcher = Arc::new(file_fetcher); + let npm_resolver = Arc::new(NpmFetchResolver::new( + file_fetcher.clone(), + cli_options.npmrc().clone(), + )); + let jsr_resolver = Arc::new(JsrFetchResolver::new(file_fetcher.clone())); + + let mut deps = super::deps::DepManager::from_workspace( + workspace, + DepManagerArgs { + module_load_preparer: factory.module_load_preparer().await?.clone(), + jsr_fetch_resolver: jsr_resolver.clone(), + npm_fetch_resolver: npm_resolver, + npm_resolver: factory.npm_resolver().await?.clone(), + permissions_container: factory.root_permissions_container()?.clone(), + main_module_graph_container: factory + .main_module_graph_container() + .await? + .clone(), + lockfile: cli_options.maybe_lockfile().cloned(), + }, + )?; + + deps.fetch_latest_versions(true).await?; + + for (dep, latest) in deps + .deps_with_latest_versions() + .into_iter() + .collect::>() + { + let Some(latest) = latest else { continue }; + let new_req = + VersionReq::parse_from_specifier(format!("^{}", latest.version).as_str()) + .unwrap(); + deps.update_dep(dep, new_req); } - super::cache_deps::cache_top_level_deps(&factory, None).await?; - Ok(()) + + deps.commit_changes().await?; + + super::npm_install_after_modification(flags, Some(jsr_resolver)).await?; + + Ok(()) } From f0210f525e957b84abae03ec898fad8ee7c5fb2f Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Mon, 18 Nov 2024 12:44:52 -0800 Subject: [PATCH 03/26] combine update + outdated, cleanup --- cli/args/flags.rs | 105 ++++--- cli/args/mod.rs | 5 - cli/main.rs | 5 - cli/npm/managed/mod.rs | 2 +- cli/npm/mod.rs | 48 ++- cli/tools/registry/mod.rs | 1 - cli/tools/registry/pm.rs | 2 - cli/tools/registry/pm/deps.rs | 489 ++++++++++++++++++++---------- cli/tools/registry/pm/outdated.rs | 81 ----- cli/tools/registry/pm/update.rs | 257 +++++++++++++--- 10 files changed, 639 insertions(+), 356 deletions(-) delete mode 100644 cli/tools/registry/pm/outdated.rs diff --git a/cli/args/flags.rs b/cli/args/flags.rs index aa566cf12d5bb1..054c8d6319dd33 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -464,7 +464,6 @@ pub enum DenoSubcommand { Task(TaskFlags), Test(TestFlags), Update(UpdateFlags), - Outdated(OutdatedFlags), Types, Upgrade(UpgradeFlags), Vendor, @@ -473,14 +472,16 @@ pub enum DenoSubcommand { } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct OutdatedFlags { - pub filters: Vec, - pub compatible: bool, +pub enum UpdateKind { + Update { latest: bool }, + PrintOutdated { compatible: bool }, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct UpdateFlags { pub filters: Vec, + pub recursive: bool, + pub kind: UpdateKind, } impl DenoSubcommand { @@ -1395,7 +1396,6 @@ pub fn flags_from_vec(args: Vec) -> clap::error::Result { "jupyter" => jupyter_parse(&mut flags, &mut m), "lint" => lint_parse(&mut flags, &mut m)?, "lsp" => lsp_parse(&mut flags, &mut m), - "outdated" => outdated_parse(&mut flags, &mut m)?, "update" => update_parse(&mut flags, &mut m)?, "repl" => repl_parse(&mut flags, &mut m)?, "run" => run_parse(&mut flags, &mut m, app, false)?, @@ -1639,7 +1639,6 @@ pub fn clap_root() -> Command { .subcommand(json_reference_subcommand()) .subcommand(jupyter_subcommand()) .subcommand(uninstall_subcommand()) - .subcommand(outdated_subcommand()) .subcommand(update_subcommand()) .subcommand(lsp_subcommand()) .subcommand(lint_subcommand()) @@ -2631,37 +2630,46 @@ fn jupyter_subcommand() -> Command { .conflicts_with("install")) } -fn outdated_subcommand() -> Command { - command( - "outdated", - "Check for outdated dependencies", - UnstableArgsConfig::None, - ) - .defer(|cmd| { - cmd - .arg( - Arg::new("filters") - .num_args(0..) - .action(ArgAction::Append) - .help("List of filters used for checking outdated packages"), - ) - .arg( - Arg::new("compatible") - .long("compatible") - .action(ArgAction::SetTrue) - .help("only output versions that satisfy semver requirements"), - ) - }) -} fn update_subcommand() -> Command { command("update", "Update dependencies", UnstableArgsConfig::None).defer( |cmd| { - cmd.arg( - Arg::new("filters") - .num_args(0..) - .action(ArgAction::Append) - .help("List of filters used for updating outdated packages"), - ) + cmd + .arg( + Arg::new("filters") + .num_args(0..) + .action(ArgAction::Append) + .help("List of filters used for updating outdated packages"), + ) + .arg( + Arg::new("latest") + .long("latest") + .action(ArgAction::SetTrue) + .help( + "Update to the latest version, regardless of semver constraints", + ), + ) + .arg( + Arg::new("outdated") + .long("outdated") + .action(ArgAction::SetTrue) + .conflicts_with("latest") + .help("print outdated package versions"), + ) + .arg( + Arg::new("compatible") + .long("compatible") + .action(ArgAction::SetTrue) + .help("only output versions that satisfy semver requirements") + .conflicts_with("latest") + .requires("outdated"), + ) + .arg( + Arg::new("recursive") + .long("recursive") + .short('r') + .action(ArgAction::SetTrue) + .help("include all workspace members"), + ) }, ) } @@ -4381,7 +4389,7 @@ fn remove_parse(flags: &mut Flags, matches: &mut ArgMatches) { }); } -fn outdated_parse( +fn update_parse( flags: &mut Flags, matches: &mut ArgMatches, ) -> clap::error::Result<()> { @@ -4389,26 +4397,23 @@ fn outdated_parse( Some(f) => f.collect(), None => vec![], }; - let compatible = matches.get_flag("compatible"); - flags.subcommand = DenoSubcommand::Outdated(OutdatedFlags { + let recursive = matches.get_flag("recursive"); + let outdated = matches.get_flag("outdated"); + let kind = if outdated { + let compatible = matches.get_flag("compatible"); + UpdateKind::PrintOutdated { compatible } + } else { + let latest = matches.get_flag("latest"); + UpdateKind::Update { latest } + }; + flags.subcommand = DenoSubcommand::Update(UpdateFlags { filters, - compatible, + recursive, + kind, }); Ok(()) } -fn update_parse( - flags: &mut Flags, - matches: &mut ArgMatches, -) -> clap::error::Result<()> { - let filters = match matches.remove_many::("filters") { - Some(f) => f.collect(), - None => vec![], - }; - flags.subcommand = DenoSubcommand::Update(UpdateFlags { filters }); - Ok(()) -} - fn bench_parse( flags: &mut Flags, matches: &mut ArgMatches, diff --git a/cli/args/mod.rs b/cli/args/mod.rs index dc7417ab317fac..65059d56d94945 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -984,10 +984,6 @@ impl CliOptions { CacheSetting::Only } else if !self.flags.cache_blocklist.is_empty() { CacheSetting::ReloadSome(self.flags.cache_blocklist.clone()) - } else if self.flags.reload - || matches!(self.sub_command(), DenoSubcommand::Update(..)) - { - CacheSetting::ReloadAll } else { CacheSetting::Use } @@ -1626,7 +1622,6 @@ impl CliOptions { DenoSubcommand::Install(_) | DenoSubcommand::Add(_) | DenoSubcommand::Remove(_) - | DenoSubcommand::Outdated(_) | DenoSubcommand::Update(_) ) { // For `deno install/add/remove` we want to force the managed resolver so it can set up `node_modules/` directory. diff --git a/cli/main.rs b/cli/main.rs index 09b7da4ce65022..0ffa59c9a0bf05 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -188,11 +188,6 @@ async fn run_subcommand(flags: Arc) -> Result { tools::lint::lint(flags, lint_flags).await } }), - DenoSubcommand::Outdated(outdated_flags) => { - spawn_subcommand(async move { - tools::registry::outdated(flags, outdated_flags).await - }) - } DenoSubcommand::Update(update_flags) => { spawn_subcommand(async move { tools::registry::update(flags, update_flags).await diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 7b06504cba550e..2564a0f84da4da 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -500,7 +500,7 @@ impl ManagedCliNpmResolver { self.resolve_pkg_folder_from_pkg_id(&pkg_id) } - fn resolve_pkg_id_from_pkg_req( + pub fn resolve_pkg_id_from_pkg_req( &self, req: &PackageReq, ) -> Result { diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index 0e955ac5b43396..ecac6141e462d5 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -160,6 +160,7 @@ impl NpmFetchResolver { let version = package_info.dist_tags.get(dist_tag)?.clone(); return Some(PackageNv { name, version }); } + let start = std::time::Instant::now(); // Find the first matching version of the package. let mut versions = package_info.versions.keys().collect::>(); versions.sort(); @@ -168,6 +169,8 @@ impl NpmFetchResolver { .rev() .find(|v| req.version_req.tag().is_none() && req.version_req.matches(v)) .cloned()?; + let elapsed = start.elapsed(); + eprintln!("finding version for {name} took {}ms", elapsed.as_millis()); Some(PackageNv { name, version }) }; let nv = maybe_get_nv().await; @@ -187,18 +190,43 @@ impl NpmFetchResolver { let maybe_auth_header = maybe_auth_header_for_npm_registry(registry_config).ok()?; // spawn due to the lsp's `Send` requirement - let file = deno_core::unsync::spawn(async move { - file_fetcher - .fetch_bypass_permissions_with_maybe_auth( - &info_url, - maybe_auth_header, - ) - .await - .ok() + let start = std::time::Instant::now(); + debug_assert!( + tokio::runtime::Handle::current().runtime_flavor() + == tokio::runtime::RuntimeFlavor::CurrentThread + ); + let file = unsafe { + deno_core::unsync::MaskFutureAsSend::new(async move { + file_fetcher + .fetch_bypass_permissions_with_maybe_auth( + &info_url, + maybe_auth_header, + ) + .await + .ok() + }) + } + .await + .into_inner()?; + let elapsed = start.elapsed(); + eprintln!( + "fetching package info for {name} took {}ms", + elapsed.as_millis() + ); + let name2 = name.to_string(); + deno_core::unsync::spawn_blocking(move || { + let start = std::time::Instant::now(); + let res = serde_json::from_slice::(&file.source).ok(); + let elapsed = start.elapsed(); + eprintln!( + "parsing package info for {name2} took {}ms", + elapsed.as_millis() + ); + res }) .await - .ok()??; - serde_json::from_slice::(&file.source).ok() + .ok() + .flatten() }; let info = fetch_package_info().await.map(Arc::new); self.info_by_name.insert(name.to_string(), info.clone()); diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs index e06cd994f4afb6..ea67f13736aef2 100644 --- a/cli/tools/registry/mod.rs +++ b/cli/tools/registry/mod.rs @@ -68,7 +68,6 @@ use auth::get_auth_method; use auth::AuthMethod; pub use pm::add; pub use pm::cache_top_level_deps; -pub use pm::outdated; pub use pm::remove; pub use pm::update; pub use pm::AddCommandName; diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index 9e447c65ad4bd2..a771c0901f62d9 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -34,11 +34,9 @@ use crate::npm::NpmFetchResolver; mod cache_deps; pub(crate) mod deps; -mod outdated; mod update; pub use cache_deps::cache_top_level_deps; -pub use outdated::outdated; pub use update::update; #[derive(Debug, Copy, Clone, Hash)] diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index d21884f38bfa0f..f4479f28cedc77 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; use std::collections::HashMap; -use std::path::PathBuf; +use std::sync::atomic::AtomicBool; use std::sync::Arc; use deno_ast::ModuleSpecifier; @@ -10,6 +10,7 @@ use deno_config::workspace::Workspace; use deno_config::workspace::WorkspaceDirectory; use deno_core::anyhow::bail; use deno_core::error::AnyError; +use deno_core::futures::future::try_join; use deno_core::futures::stream::FuturesOrdered; use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::FutureExt; @@ -20,13 +21,11 @@ use deno_package_json::PackageJsonDepValue; use deno_package_json::PackageJsonDepValueParseError; use deno_package_json::PackageJsonRc; use deno_runtime::deno_permissions::PermissionsContainer; -use deno_semver::jsr::JsrDepPackageReq; use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use deno_semver::package::PackageReqReference; -use deno_semver::Version; use deno_semver::VersionReq; use import_map::ImportMap; use import_map::ImportMapWithDiagnostics; @@ -61,8 +60,20 @@ impl DepLocation { pub fn is_deno_json(&self) -> bool { matches!(self, DepLocation::DenoJson(..)) } - pub fn is_package_json(&self) -> bool { - matches!(self, DepLocation::PackageJson(..)) + + pub fn file_path(&self) -> Cow { + match self { + DepLocation::DenoJson(arc, _, _) => { + Cow::Owned(arc.specifier.to_file_path().unwrap()) + } + DepLocation::PackageJson(arc, _) => Cow::Borrowed(arc.path.as_ref()), + } + } + fn config_kind(&self) -> super::ConfigKind { + match self { + DepLocation::DenoJson(_, _, _) => super::ConfigKind::DenoJson, + DepLocation::PackageJson(_, _) => super::ConfigKind::PackageJson, + } } } @@ -102,14 +113,14 @@ impl std::fmt::Debug for DepLocation { } } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum DepKind { Npm, Jsr, } impl DepKind { - fn scheme(&self) -> &'static str { + pub fn scheme(&self) -> &'static str { match self { DepKind::Npm => "npm", DepKind::Jsr => "jsr", @@ -126,17 +137,6 @@ pub enum KeyPart { String(String), } -macro_rules! const_assert { - ($x:expr $(,)?) => { - #[allow(unknown_lints, eq_op)] - const _: [(); - 0 - !{ - const ASSERT: bool = $x; - ASSERT - } as usize] = []; - }; -} - impl From for KeyPart { fn from(value: String) -> Self { KeyPart::String(value) @@ -185,19 +185,8 @@ pub struct Dep { pub req: PackageReq, pub kind: DepKind, pub location: DepLocation, + #[allow(dead_code)] pub id: DepId, - // specifier: String, -} - -#[derive(Debug, Clone)] -pub struct ResolvedDep { - dep: Dep, - resolved_version: Option, -} - -#[derive(Debug)] -pub struct WorkspaceDeps { - deps: Vec, } fn import_map_entries<'a>( @@ -230,26 +219,42 @@ fn import_map_entries<'a>( })) } +fn to_import_map_value_from_imports( + deno_json: &ConfigFile, +) -> serde_json::Value { + let mut value = serde_json::Map::with_capacity(2); + if let Some(imports) = &deno_json.json.imports { + value.insert("imports".to_string(), imports.clone()); + } + if let Some(scopes) = &deno_json.json.scopes { + value.insert("scopes".to_string(), scopes.clone()); + } + serde_json::Value::Object(value) +} + fn deno_json_import_map( deno_json: &ConfigFile, ) -> Result, AnyError> { - let Some((url, value)) = deno_json.to_import_map_value(|path| { - std::fs::read_to_string(path).map_err(Into::into) - })? - else { - return Ok(None); - }; + let (value, kind) = + if deno_json.json.imports.is_some() || deno_json.json.scopes.is_some() { + ( + to_import_map_value_from_imports(deno_json), + ImportMapKind::Inline, + ) + } else { + match deno_json.to_import_map_path()? { + Some(path) => { + let text = std::fs::read_to_string(&path)?; + let value = serde_json::from_str(&text)?; + (value, ImportMapKind::Outline) + } + None => return Ok(None), + } + }; import_map::parse_from_value(deno_json.specifier.clone(), value) .map_err(Into::into) - .map(|import_map| { - let kind = if &*url == &deno_json.specifier { - ImportMapKind::Inline - } else { - ImportMapKind::Outline - }; - Some((import_map, kind)) - }) + .map(|import_map| Some((import_map, kind))) } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -267,6 +272,7 @@ type PackageJsonDeps = IndexMap< >; /// Resolve the package.json's dependencies. +// TODO(nathanwhit): Remove once we update deno_package_json with dev deps split out fn resolve_local_package_json_deps( package_json: &PackageJsonRc, ) -> PackageJsonDeps { @@ -345,7 +351,11 @@ fn resolve_local_package_json_deps( result } -fn add_deps_from_deno_json(deno_json: &Arc, deps: &mut Vec) { +fn add_deps_from_deno_json( + deno_json: &Arc, + mut filter: impl DepFilter, + deps: &mut Vec, +) { let (import_map, import_map_kind) = match deno_json_import_map(deno_json) { Ok(Some((import_map, import_map_kind))) => (import_map, import_map_kind), Ok(None) => return, @@ -361,13 +371,16 @@ fn add_deps_from_deno_json(deno_json: &Arc, deps: &mut Vec) { "jsr" => DepKind::Jsr, _ => continue, }; - let req = match JsrDepPackageReq::from_str(value.as_str()) { - Ok(req) => req, + let req = match parse_req_reference(value.as_str(), kind) { + Ok(req) => req.req.clone(), Err(err) => { log::warn!("failed to parse package req \"{}\": {err}", value.as_str()); continue; } }; + if !filter.should_include(&req, kind) { + continue; + } let id = DepId(deps.len()); deps.push(Dep { location: DepLocation::DenoJson( @@ -376,7 +389,7 @@ fn add_deps_from_deno_json(deno_json: &Arc, deps: &mut Vec) { import_map_kind, ), kind, - req: req.req, + req, id, }); } @@ -384,10 +397,12 @@ fn add_deps_from_deno_json(deno_json: &Arc, deps: &mut Vec) { fn add_deps_from_package_json( package_json: &PackageJsonRc, + mut filter: impl DepFilter, deps: &mut Vec, ) { let package_json_deps = resolve_local_package_json_deps(package_json); for (k, v) in package_json_deps { + eprintln!("{k}"); let (package_dep_kind, v) = match v { Ok((k, v)) => (k, v), Err(e) => { @@ -397,6 +412,9 @@ fn add_deps_from_package_json( }; match v { deno_package_json::PackageJsonDepValue::Req(package_req) => { + if !filter.should_include(&package_req, DepKind::Npm) { + continue; + } let id = DepId(deps.len()); deps.push(Dep { id, @@ -415,28 +433,21 @@ fn add_deps_from_package_json( fn deps_from_workspace( workspace: &Arc, + dep_filter: impl DepFilter, ) -> Result, AnyError> { let mut deps = Vec::with_capacity(32); for deno_json in workspace.deno_jsons() { eprintln!("deno_json: {}", deno_json.specifier); - add_deps_from_deno_json(deno_json, &mut deps); + add_deps_from_deno_json(deno_json, dep_filter, &mut deps); } for package_json in workspace.package_jsons() { eprintln!("package_json: {}", package_json.path.display()); - add_deps_from_package_json(package_json, &mut deps); + add_deps_from_package_json(package_json, dep_filter, &mut deps); } Ok(deps) } -#[derive(Default, Clone)] -enum ResolveState { - #[default] - NotYet, - Unresolved, - Resolved(PackageNv), -} - #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct DepId(usize); @@ -445,14 +456,59 @@ pub enum Change { Update(DepId, VersionReq), } +pub struct DepData<'a, T> { + inner: &'a [T], +} + +impl<'a, T> DepData<'a, T> { + // pub fn len(&self) -> usize { + // self.inner.len() + // } +} + +impl<'a, T> std::ops::Index for DepData<'a, T> { + type Output = T; + + fn index(&self, index: DepId) -> &Self::Output { + &self.inner[index.0] + } +} + +pub trait DepFilter: Copy { + fn should_include( + &mut self, + package_req: &PackageReq, + dep_kind: DepKind, + ) -> bool; +} + +impl DepFilter for T +where + T: FnMut(&PackageReq, DepKind) -> bool + Copy, +{ + fn should_include<'a>( + &mut self, + package_req: &'a PackageReq, + dep_kind: DepKind, + ) -> bool { + (*self)(package_req, dep_kind) + } +} + +#[derive(Clone, Debug)] +pub struct PackageLatestVersion { + pub semver_compatible: Option, + pub latest: Option, +} + pub struct DepManager { deps: Vec, resolved_versions: Vec>, - latest_versions: Vec>, + latest_versions: Vec, pending_changes: Vec, - loaded_roots: bool, + dependencies_resolved: AtomicBool, module_load_preparer: Arc, jsr_fetch_resolver: Arc, npm_fetch_resolver: Arc, @@ -488,7 +544,7 @@ impl DepManager { resolved_versions: Vec::new(), latest_versions: Vec::new(), jsr_fetch_resolver, - loaded_roots: false, + dependencies_resolved: AtomicBool::new(false), module_load_preparer, npm_fetch_resolver, npm_resolver, @@ -500,31 +556,74 @@ impl DepManager { } pub fn from_workspace_dir( workspace_dir: &Arc, + dep_filter: impl DepFilter, args: DepManagerArgs, ) -> Result { let mut deps = Vec::with_capacity(32); if let Some(deno_json) = workspace_dir.maybe_deno_json() { - add_deps_from_deno_json(deno_json, &mut deps); + add_deps_from_deno_json(deno_json, dep_filter, &mut deps); } if let Some(package_json) = workspace_dir.maybe_pkg_json() { - add_deps_from_package_json(package_json, &mut deps); + add_deps_from_package_json(package_json, dep_filter, &mut deps); } Ok(Self::with_deps_args(deps, args)) } pub fn from_workspace( workspace: &Arc, + dep_filter: impl DepFilter, args: DepManagerArgs, ) -> Result { - let deps = deps_from_workspace(workspace)?; + let deps = deps_from_workspace(workspace, dep_filter)?; Ok(Self::with_deps_args(deps, args)) } - async fn load_roots(&mut self) -> Result<(), AnyError> { - if self.loaded_roots { + async fn run_dependency_resolution(&self) -> Result<(), AnyError> { + if self + .dependencies_resolved + .load(std::sync::atomic::Ordering::Relaxed) + { return Ok(()); } + let mut graph_permit = self + .main_module_graph_container + .acquire_update_permit() + .await; + let graph = graph_permit.graph_mut(); + // populate the information from the lockfile + if let Some(lockfile) = &self.lockfile { + let lockfile = lockfile.lock(); + graph.fill_from_lockfile(FillFromLockfileOptions { + redirects: lockfile + .content + .redirects + .iter() + .map(|(from, to)| (from.as_str(), to.as_str())), + package_specifiers: lockfile + .content + .packages + .specifiers + .iter() + .map(|(dep, id)| (dep, id.as_str())), + }); + } + + let npm_resolver = self.npm_resolver.as_managed().unwrap(); + if self.deps.iter().all(|dep| match dep.kind { + DepKind::Npm => { + npm_resolver.resolve_pkg_id_from_pkg_req(&dep.req).is_ok() + } + DepKind::Jsr => graph.packages.mappings().contains_key(&dep.req), + }) { + self + .dependencies_resolved + .store(true, std::sync::atomic::Ordering::Relaxed); + graph_permit.commit(); + return Ok(()); + } + + npm_resolver.ensure_top_level_package_json_install().await?; let mut roots = Vec::new(); let mut info_futures = FuturesUnordered::new(); for dep in &self.deps { @@ -551,6 +650,8 @@ impl DepManager { } } + eprintln!("roots len: {}", roots.len()); + while let Some(info_future) = info_futures.next().await { if let Some((specifier, info)) = info_future { let exports = info.exports(); @@ -562,28 +663,6 @@ impl DepManager { } } - let mut graph_permit = self - .main_module_graph_container - .acquire_update_permit() - .await; - let graph = graph_permit.graph_mut(); - // populate the information from the lockfile - if let Some(lockfile) = &self.lockfile { - let lockfile = lockfile.lock(); - graph.fill_from_lockfile(FillFromLockfileOptions { - redirects: lockfile - .content - .redirects - .iter() - .map(|(from, to)| (from.as_str(), to.as_str())), - package_specifiers: lockfile - .content - .packages - .specifiers - .iter() - .map(|(dep, id)| (dep, id.as_str())), - }); - } self .module_load_preparer .prepare_module_load( @@ -601,8 +680,8 @@ impl DepManager { Ok(()) } - pub async fn resolve_versions(&mut self) -> Result<(), AnyError> { - self.load_roots().await?; + pub async fn resolve_current_versions(&mut self) -> Result<(), AnyError> { + self.run_dependency_resolution().await?; let graph = self.main_module_graph_container.graph(); @@ -628,32 +707,14 @@ impl DepManager { Ok(()) } - pub fn resolved_version( + async fn load_latest_versions( &self, - dep_id: DepId, - ) -> Result, AnyError> { - if self.resolved_versions.len() < self.deps.len() { - return Err(deno_core::anyhow::anyhow!( - "Versions haven't been resolved yet" - )); - } - - Ok(self.resolved_versions[dep_id.0].as_ref()) - } - - pub fn resolved_versions(&self) -> &[Option] { - &self.resolved_versions - } - - pub async fn fetch_latest_versions( - &mut self, - semver_compatible: bool, - ) -> Result<(), AnyError> { + ) -> Result, AnyError> { let latest_tag_req = deno_semver::VersionReq::from_raw_text_and_inner( "latest".into(), deno_semver::RangeSetOrTag::Tag("latest".into()), ); - let mut latest = Vec::with_capacity(self.deps.len()); + let mut latest_versions = Vec::with_capacity(self.deps.len()); let sema = Semaphore::new(32); let mut futs = FuturesOrdered::new(); @@ -662,60 +723,118 @@ impl DepManager { match dep.kind { DepKind::Npm => futs.push_back( async { - let req = if semver_compatible { - Cow::Borrowed(&dep.req) - } else { - Cow::Owned(PackageReq { - name: dep.req.name.clone(), - version_req: latest_tag_req.clone(), - }) + let semver_req = &dep.req; + let latest_req = PackageReq { + name: dep.req.name.clone(), + version_req: latest_tag_req.clone(), }; let _permit = sema.acquire().await; - self.npm_fetch_resolver.req_to_nv(&req).await + let semver_compatible = + self.npm_fetch_resolver.req_to_nv(&semver_req).await; + let latest = self.npm_fetch_resolver.req_to_nv(&latest_req).await; + PackageLatestVersion { + latest, + semver_compatible, + } } .boxed_local(), ), DepKind::Jsr => futs.push_back( async { - let req = if semver_compatible { - Cow::Borrowed(&dep.req) - } else { - Cow::Owned(PackageReq { - name: dep.req.name.clone(), - version_req: deno_semver::WILDCARD_VERSION_REQ.clone(), - }) + let semver_req = &dep.req; + let latest_req = PackageReq { + name: dep.req.name.clone(), + version_req: deno_semver::WILDCARD_VERSION_REQ.clone(), }; let _permit = sema.acquire().await; - self.jsr_fetch_resolver.req_to_nv(&req).await + let semver_compatible = + self.jsr_fetch_resolver.req_to_nv(&semver_req).await; + let latest = self.jsr_fetch_resolver.req_to_nv(&latest_req).await; + PackageLatestVersion { + latest, + semver_compatible, + } } .boxed_local(), ), } } while let Some(nv) = futs.next().await { - latest.push(nv); + latest_versions.push(nv); } - self.latest_versions = latest; - Ok(()) + Ok(latest_versions) } - pub fn latest_versions(&self) -> &[Option] { - &self.latest_versions + pub async fn resolve_versions(&mut self) -> Result<(), AnyError> { + let (_, latest_versions) = try_join( + self.run_dependency_resolution(), + self.load_latest_versions(), + ) + .await?; + + self.latest_versions = latest_versions; + + self.resolve_current_versions().await?; + + Ok(()) } - pub fn deps_with_latest_versions( + // pub fn resolved_version( + // &self, + // dep_id: DepId, + // ) -> Result, AnyError> { + // if self.resolved_versions.len() < self.deps.len() { + // return Err(deno_core::anyhow::anyhow!( + // "Versions haven't been resolved yet" + // )); + // } + + // Ok(self.resolved_versions[dep_id.0].as_ref()) + // } + + // pub fn resolved_versions(&self) -> DepData<'_, Option> { + // DepData { + // inner: &self.resolved_versions, + // } + // } + + // pub fn latest_versions(&self) -> DepData<'_, PackageLatestVersion> { + // DepData { + // inner: &self.latest_versions, + // } + // } + + // pub fn deps_with_latest_versions( + // &self, + // ) -> impl IntoIterator + '_ { + // self + // .latest_versions + // .iter() + // .enumerate() + // .map(|(i, latest)| (DepId(i), latest.clone())) + // } + + pub fn deps_with_resolved_latest_versions( &self, - ) -> impl IntoIterator)> + '_ { + ) -> impl IntoIterator, PackageLatestVersion)> + '_ + { self - .latest_versions + .resolved_versions .iter() + .zip(self.latest_versions.iter()) .enumerate() - .map(|(i, latest)| (DepId(i), latest.clone())) + .map(|(i, (resolved, latest))| { + (DepId(i), resolved.clone(), latest.clone()) + }) } - pub fn deps(&self) -> &[Dep] { - &self.deps + // pub fn deps(&self) -> DepData<'_, Dep> { + // DepData { inner: &self.deps } + // } + + pub fn get_dep(&self, id: DepId) -> &Dep { + &self.deps[id.0] } pub fn update_dep(&mut self, dep_id: DepId, new_version_req: VersionReq) { @@ -726,10 +845,11 @@ impl DepManager { pub async fn commit_changes(&mut self) -> Result<(), AnyError> { let changes = std::mem::take(&mut self.pending_changes); - // let mut config_updaters = HashMap::new(); + let mut config_updaters = HashMap::new(); for change in changes { match change { Change::Update(dep_id, version_req) => { + // TODO: move most of this to ConfigUpdater let dep = &self.deps[dep_id.0]; match &dep.location { DepLocation::DenoJson(arc, key_path, import_map_kind) => { @@ -737,11 +857,9 @@ impl DepManager { // not supported continue; } - let mut updater = ConfigUpdater::new( - super::ConfigKind::DenoJson, - // TODO: unwrap is sus - arc.specifier.to_file_path().unwrap(), - )?; + let updater = + get_or_create_updater(&mut config_updaters, &dep.location)?; + let Some(property) = updater.get_property_for_mutation(&key_path) else { log::warn!( @@ -750,13 +868,7 @@ impl DepManager { ); continue; }; - let value = property.value().unwrap(); - let Some(string) = value.as_string_lit() else { - log::warn!("malformed entry"); - continue; - }; - let Ok(string_value) = string.decoded_value() else { - log::warn!("malformed string: {string:?}"); + let Some(string_value) = cst_string_literal(&property) else { continue; }; let mut req_reference = match dep.kind { @@ -768,18 +880,19 @@ impl DepManager { .into_inner(), }; req_reference.req.version_req = version_req; - let new_value = + let mut new_value = format!("{}:{}", dep.kind.scheme(), req_reference); + if string_value.ends_with('/') && !new_value.ends_with('/') { + // the display impl for PackageReqReference maps `/` to the root + // subpath, but for the import map the trailing `/` is significant + new_value.push('/'); + } property .set_value(jsonc_parser::cst::CstInputValue::String(new_value)); - updater.commit()?; } DepLocation::PackageJson(arc, key_path) => { - eprintln!("here: {dep:?}"); - let mut updater = ConfigUpdater::new( - super::ConfigKind::PackageJson, - arc.path.clone(), - )?; + let updater = + get_or_create_updater(&mut config_updaters, &dep.location)?; let Some(property) = updater.get_property_for_mutation(&key_path) else { log::warn!( @@ -788,13 +901,7 @@ impl DepManager { ); continue; }; - let value = property.value().unwrap(); - let Some(string) = value.as_string_lit() else { - log::warn!("malformed entry"); - continue; - }; - let Ok(string_value) = string.decoded_value() else { - log::warn!("malformed string: {string:?}"); + let Some(string_value) = cst_string_literal(&property) else { continue; }; let new_value = if string_value.starts_with("npm:") { @@ -815,13 +922,63 @@ impl DepManager { }; property .set_value(jsonc_parser::cst::CstInputValue::String(new_value)); - updater.commit()?; } } } } } + for (_, updater) in config_updaters { + updater.commit()?; + } + Ok(()) } } + +fn get_or_create_updater<'a>( + config_updaters: &'a mut HashMap, + location: &DepLocation, +) -> Result<&'a mut ConfigUpdater, AnyError> { + match config_updaters.entry(location.file_path().into_owned()) { + std::collections::hash_map::Entry::Occupied(occupied_entry) => { + Ok(occupied_entry.into_mut()) + } + std::collections::hash_map::Entry::Vacant(vacant_entry) => { + let updater = ConfigUpdater::new( + location.config_kind(), + location.file_path().into_owned(), + )?; + Ok(vacant_entry.insert(updater)) + } + } +} + +fn cst_string_literal( + property: &jsonc_parser::cst::CstObjectProp, +) -> Option { + // TODO(nathanwhit): ensure this unwrap is safe + let value = property.value().unwrap(); + let Some(string) = value.as_string_lit() else { + log::warn!("malformed entry"); + return None; + }; + let Ok(string_value) = string.decoded_value() else { + log::warn!("malformed string: {string:?}"); + return None; + }; + Some(string_value) +} + +fn parse_req_reference( + input: &str, + kind: DepKind, +) -> Result< + PackageReqReference, + deno_semver::package::PackageReqReferenceParseError, +> { + Ok(match kind { + DepKind::Npm => NpmPackageReqReference::from_str(input)?.into_inner(), + DepKind::Jsr => JsrPackageReqReference::from_str(input)?.into_inner(), + }) +} diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs deleted file mode 100644 index 99e0eddaa926c4..00000000000000 --- a/cli/tools/registry/pm/outdated.rs +++ /dev/null @@ -1,81 +0,0 @@ -use std::sync::Arc; - -use deno_core::error::AnyError; - -use crate::args::CacheSetting; -use crate::args::Flags; -use crate::args::OutdatedFlags; -use crate::factory::CliFactory; -use crate::file_fetcher::FileFetcher; -use crate::jsr::JsrFetchResolver; -use crate::npm::NpmFetchResolver; - -use super::deps::DepManagerArgs; - -pub async fn outdated( - flags: Arc, - outdated_flags: OutdatedFlags, -) -> Result<(), AnyError> { - let factory = CliFactory::from_flags(flags.clone()); - let cli_options = factory.cli_options()?; - let workspace = cli_options.workspace(); - let resolver = factory.workspace_resolver().await?; - let http_client = factory.http_client_provider(); - let deps_http_cache = factory.global_http_cache()?; - let mut file_fetcher = FileFetcher::new( - deps_http_cache.clone(), - CacheSetting::ReloadAll, - true, - http_client.clone(), - Default::default(), - None, - ); - file_fetcher.set_download_log_level(log::Level::Trace); - let file_fetcher = Arc::new(file_fetcher); - let npm_resolver = Arc::new(NpmFetchResolver::new( - file_fetcher.clone(), - cli_options.npmrc().clone(), - )); - let jsr_resolver = Arc::new(JsrFetchResolver::new(file_fetcher.clone())); - - let mut deps = super::deps::DepManager::from_workspace( - workspace, - DepManagerArgs { - module_load_preparer: factory.module_load_preparer().await?.clone(), - jsr_fetch_resolver: jsr_resolver, - npm_fetch_resolver: npm_resolver, - npm_resolver: factory.npm_resolver().await?.clone(), - permissions_container: factory.root_permissions_container()?.clone(), - main_module_graph_container: factory - .main_module_graph_container() - .await? - .clone(), - lockfile: cli_options.maybe_lockfile().cloned(), - }, - )?; - - deps.resolve_versions().await?; - deps - .fetch_latest_versions(outdated_flags.compatible) - .await?; - - for ((dep, resolved_version), latest_version) in deps - .deps() - .iter() - .zip(deps.resolved_versions().iter()) - .zip(deps.latest_versions().iter()) - { - if let Some(resolved_version) = resolved_version { - if let Some(latest_version) = latest_version { - if latest_version > resolved_version { - eprintln!( - "outdated dependency {} : {} -> {}", - dep.req, resolved_version, latest_version - ); - } - } - } - } - - Ok(()) -} diff --git a/cli/tools/registry/pm/update.rs b/cli/tools/registry/pm/update.rs index a791131880e12d..406951d2a7ab6f 100644 --- a/cli/tools/registry/pm/update.rs +++ b/cli/tools/registry/pm/update.rs @@ -1,8 +1,9 @@ use std::sync::Arc; use deno_core::error::AnyError; -use deno_semver::Version; +use deno_semver::package::PackageReq; use deno_semver::VersionReq; +use deno_terminal::colors; use crate::args::CacheSetting; use crate::args::Flags; @@ -11,17 +12,155 @@ use crate::factory::CliFactory; use crate::file_fetcher::FileFetcher; use crate::jsr::JsrFetchResolver; use crate::npm::NpmFetchResolver; +use crate::tools::registry::pm::deps::DepKind; +use super::deps::DepManager; use super::deps::DepManagerArgs; -// fn update_lower_bound(req: VersionReq, version: Version) -> Option { -// match req.inner() { -// deno_semver::RangeSetOrTag::RangeSet(version_range_set) => { -// version_range_set. -// }, -// deno_semver::RangeSetOrTag::Tag(_) => todo!(), -// } -// } +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +struct OutdatedPackage { + kind: DepKind, + latest: String, + semver_compatible: String, + current: String, + name: String, +} + +fn print_outdated_table(packages: &[OutdatedPackage]) { + const HEADINGS: &[&str] = &["Package", "Current", "Update", "Latest"]; + + let mut longest_package = 0; + let mut longest_current = 0; + let mut longest_update = 0; + let mut longest_latest = 0; + + for package in packages { + let name_len = package.kind.scheme().len() + 1 + package.name.len(); + longest_package = longest_package.max(name_len); + longest_current = longest_current.max(package.current.len()); + longest_update = longest_update.max(package.semver_compatible.len()); + longest_latest = longest_latest.max(package.latest.len()); + } + + let package_column_width = longest_package.max(HEADINGS[0].len()) + 2; + let current_column_width = longest_current.max(HEADINGS[1].len()) + 2; + let update_column_width = longest_update.max(HEADINGS[2].len()) + 2; + let latest_column_width = longest_latest.max(HEADINGS[3].len()) + 2; + + eprintln!("package_column_width: {}", package_column_width); + eprintln!("current_column_width: {}", current_column_width); + eprintln!("update_column_width: {}", update_column_width); + eprintln!("latest_column_width: {}", latest_column_width); + + println!( + "┌{}┬{}┬{}┬{}┐", + "─".repeat(package_column_width), + "─".repeat(current_column_width), + "─".repeat(update_column_width), + "─".repeat(latest_column_width) + ); + println!( + "│ {}{} │ {}{} │ {}{} │ {}{} │", + colors::intense_blue(HEADINGS[0]), + " ".repeat(package_column_width - 2 - HEADINGS[0].len()), + colors::intense_blue(HEADINGS[1]), + " ".repeat(current_column_width - 2 - HEADINGS[1].len()), + colors::intense_blue(HEADINGS[2]), + " ".repeat(update_column_width - 2 - HEADINGS[2].len()), + colors::intense_blue(HEADINGS[3]), + " ".repeat(latest_column_width - 2 - HEADINGS[3].len()) + ); + for package in packages { + println!( + "├{}┼{}┼{}┼{}┤", + "─".repeat(package_column_width), + "─".repeat(current_column_width), + "─".repeat(update_column_width), + "─".repeat(latest_column_width) + ); + + print!( + "│{:^package_column_width$}", + format!("{}:{}", package.kind.scheme(), package.name), + package_column_width = package_column_width + ); + print!( + "│{:^current_column_width$}", + package.current, + current_column_width = current_column_width + ); + print!( + "│{:^update_column_width$}", + package.semver_compatible, + update_column_width = update_column_width + ); + println!( + "│{:^latest_column_width$}│", + package.latest, + latest_column_width = latest_column_width + ); + } + + println!( + "└{}┴{}┴{}┴{}┘", + "─".repeat(package_column_width), + "─".repeat(current_column_width), + "─".repeat(update_column_width), + "─".repeat(latest_column_width) + ); +} + +async fn outdated( + deps: &mut DepManager, + compatible: bool, +) -> Result<(), AnyError> { + let mut outdated = Vec::new(); + let mut seen = std::collections::BTreeSet::new(); + for (dep_id, resolved, latest_versions) in + deps.deps_with_resolved_latest_versions() + { + let dep = deps.get_dep(dep_id); + + let Some(resolved) = resolved else { continue }; + + let latest = { + let preferred = if compatible { + &latest_versions.semver_compatible + } else { + &latest_versions.latest + }; + if let Some(v) = preferred { + v + } else { + continue; + } + }; + + if latest > &resolved + && seen.insert((dep.kind, dep.req.name.clone(), resolved.version.clone())) + { + outdated.push(OutdatedPackage { + kind: dep.kind, + name: dep.req.name.clone(), + current: resolved.version.to_string(), + latest: latest_versions + .latest + .map(|l| l.version.to_string()) + .unwrap_or_default(), + semver_compatible: latest_versions + .semver_compatible + .map(|l| l.version.to_string()) + .unwrap_or_default(), + }) + } + } + + if !outdated.is_empty() { + print_outdated_table(&outdated); + } + + Ok(()) +} pub async fn update( flags: Arc, @@ -30,12 +169,11 @@ pub async fn update( let factory = CliFactory::from_flags(flags.clone()); let cli_options = factory.cli_options()?; let workspace = cli_options.workspace(); - let resolver = factory.workspace_resolver().await?; let http_client = factory.http_client_provider(); let deps_http_cache = factory.global_http_cache()?; let mut file_fetcher = FileFetcher::new( deps_http_cache.clone(), - CacheSetting::ReloadAll, + CacheSetting::RespectHeaders, true, http_client.clone(), Default::default(), @@ -49,39 +187,88 @@ pub async fn update( )); let jsr_resolver = Arc::new(JsrFetchResolver::new(file_fetcher.clone())); - let mut deps = super::deps::DepManager::from_workspace( - workspace, - DepManagerArgs { - module_load_preparer: factory.module_load_preparer().await?.clone(), - jsr_fetch_resolver: jsr_resolver.clone(), - npm_fetch_resolver: npm_resolver, - npm_resolver: factory.npm_resolver().await?.clone(), - permissions_container: factory.root_permissions_container()?.clone(), - main_module_graph_container: factory - .main_module_graph_container() - .await? - .clone(), - lockfile: cli_options.maybe_lockfile().cloned(), - }, - )?; - - deps.fetch_latest_versions(true).await?; - - for (dep, latest) in deps - .deps_with_latest_versions() + let args = DepManagerArgs { + module_load_preparer: factory.module_load_preparer().await?.clone(), + jsr_fetch_resolver: jsr_resolver.clone(), + npm_fetch_resolver: npm_resolver, + npm_resolver: factory.npm_resolver().await?.clone(), + permissions_container: factory.root_permissions_container()?.clone(), + main_module_graph_container: factory + .main_module_graph_container() + .await? + .clone(), + lockfile: cli_options.maybe_lockfile().cloned(), + }; + let mut deps = if update_flags.recursive { + super::deps::DepManager::from_workspace( + workspace, + |_: &PackageReq, _: DepKind| true, + args, + )? + } else { + super::deps::DepManager::from_workspace_dir( + &cli_options.start_dir, + |_: &PackageReq, _: DepKind| true, + args, + )? + }; + + // deps.resolve_versions().await?; + // deps.fetch_latest_versions().await?; + deps.resolve_versions().await?; + + match update_flags.kind { + crate::args::UpdateKind::Update { latest } => { + do_update(&mut deps, latest, jsr_resolver, flags).await?; + } + crate::args::UpdateKind::PrintOutdated { compatible } => { + outdated(&mut deps, compatible).await?; + } + } + + Ok(()) +} + +async fn do_update( + deps: &mut DepManager, + update_to_latest: bool, + jsr_resolver: Arc, + flags: Arc, +) -> Result<(), AnyError> { + let mut updated = false; + for (dep_id, resolved, latest_versions) in deps + .deps_with_resolved_latest_versions() .into_iter() .collect::>() { - let Some(latest) = latest else { continue }; + let latest = { + let preferred = if update_to_latest { + latest_versions.latest + } else { + latest_versions.semver_compatible + }; + if let Some(v) = preferred { + v + } else { + continue; + } + }; + let Some(resolved) = resolved else { continue }; + if latest.version <= resolved.version { + continue; + } let new_req = VersionReq::parse_from_specifier(format!("^{}", latest.version).as_str()) .unwrap(); - deps.update_dep(dep, new_req); + deps.update_dep(dep_id, new_req); + updated = true; } deps.commit_changes().await?; - super::npm_install_after_modification(flags, Some(jsr_resolver)).await?; + if updated { + super::npm_install_after_modification(flags, Some(jsr_resolver)).await?; + } - Ok(()) + Ok(()) } From e34eb6fd5b6304615adc8c967c77cf562ee3fec4 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Mon, 18 Nov 2024 16:10:45 -0800 Subject: [PATCH 04/26] Cleanup, add some tests --- cli/npm/managed/resolution.rs | 1 - cli/npm/mod.rs | 48 +++-------- cli/tools/registry/pm/cache_deps.rs | 18 ++-- cli/tools/registry/pm/deps.rs | 17 +++- cli/tools/registry/pm/update.rs | 12 ++- tests/registry/jsr/@denotest/add/0.2.1/mod.ts | 4 + .../jsr/@denotest/add/0.2.1_meta.json | 8 ++ tests/registry/jsr/@denotest/add/meta.json | 3 +- .../@denotest/multiple-exports/0.2.0/add.ts | 1 + .../multiple-exports/0.2.0/data.json | 3 + .../multiple-exports/0.2.0/subtract.ts | 1 + .../multiple-exports/0.2.0_meta.json | 7 ++ .../jsr/@denotest/multiple-exports/meta.json | 3 +- .../jsr/@denotest/subtract/0.2.0/mod.ts | 3 + .../jsr/@denotest/subtract/0.2.0_meta.json | 8 ++ .../registry/jsr/@denotest/subtract/meta.json | 4 +- .../has-patch-versions/0.1.0/package.json | 5 ++ .../has-patch-versions/0.1.1/package.json | 5 ++ .../has-patch-versions/0.2.0/package.json | 5 ++ tests/specs/update/deno_json/__test__.jsonc | 85 +++++++++++++++++++ tests/specs/update/deno_json/deno.json | 19 +++++ tests/specs/update/deno_json/deno.lock | 43 ++++++++++ .../specs/update/deno_json/deno.lock.orig.out | 43 ++++++++++ tests/specs/update/deno_json/outdated.out | 15 ++++ .../update/deno_json/outdated_compatible.out | 7 ++ tests/specs/update/deno_json/print_file.ts | 2 + .../deno_json/update_compatible/deno.json.out | 19 +++++ .../deno_json/update_compatible/deno.lock.out | 43 ++++++++++ .../deno_json/update_compatible/update.out | 9 ++ .../deno_json/update_latest/deno.json.out | 19 +++++ .../deno_json/update_latest/deno.lock.out | 43 ++++++++++ .../update/deno_json/update_latest/update.out | 18 ++++ 32 files changed, 470 insertions(+), 51 deletions(-) create mode 100644 tests/registry/jsr/@denotest/add/0.2.1/mod.ts create mode 100644 tests/registry/jsr/@denotest/add/0.2.1_meta.json create mode 100644 tests/registry/jsr/@denotest/multiple-exports/0.2.0/add.ts create mode 100644 tests/registry/jsr/@denotest/multiple-exports/0.2.0/data.json create mode 100644 tests/registry/jsr/@denotest/multiple-exports/0.2.0/subtract.ts create mode 100644 tests/registry/jsr/@denotest/multiple-exports/0.2.0_meta.json create mode 100644 tests/registry/jsr/@denotest/subtract/0.2.0/mod.ts create mode 100644 tests/registry/jsr/@denotest/subtract/0.2.0_meta.json create mode 100644 tests/registry/npm/@denotest/has-patch-versions/0.1.0/package.json create mode 100644 tests/registry/npm/@denotest/has-patch-versions/0.1.1/package.json create mode 100644 tests/registry/npm/@denotest/has-patch-versions/0.2.0/package.json create mode 100644 tests/specs/update/deno_json/__test__.jsonc create mode 100644 tests/specs/update/deno_json/deno.json create mode 100644 tests/specs/update/deno_json/deno.lock create mode 100644 tests/specs/update/deno_json/deno.lock.orig.out create mode 100644 tests/specs/update/deno_json/outdated.out create mode 100644 tests/specs/update/deno_json/outdated_compatible.out create mode 100644 tests/specs/update/deno_json/print_file.ts create mode 100644 tests/specs/update/deno_json/update_compatible/deno.json.out create mode 100644 tests/specs/update/deno_json/update_compatible/deno.lock.out create mode 100644 tests/specs/update/deno_json/update_compatible/update.out create mode 100644 tests/specs/update/deno_json/update_latest/deno.json.out create mode 100644 tests/specs/update/deno_json/update_latest/deno.lock.out create mode 100644 tests/specs/update/deno_json/update_latest/update.out diff --git a/cli/npm/managed/resolution.rs b/cli/npm/managed/resolution.rs index 903fb5fbcbdef6..ecfe5cb25cdcf5 100644 --- a/cli/npm/managed/resolution.rs +++ b/cli/npm/managed/resolution.rs @@ -265,7 +265,6 @@ async fn add_package_reqs_to_snapshot( maybe_lockfile: Option>, get_new_snapshot: impl Fn() -> NpmResolutionSnapshot, ) -> deno_npm::resolution::AddPkgReqsResult { - eprintln!("add_package_reqs_to_snapshot: {package_reqs:?}"); let snapshot = get_new_snapshot(); if package_reqs .iter() diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index ecac6141e462d5..0e955ac5b43396 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -160,7 +160,6 @@ impl NpmFetchResolver { let version = package_info.dist_tags.get(dist_tag)?.clone(); return Some(PackageNv { name, version }); } - let start = std::time::Instant::now(); // Find the first matching version of the package. let mut versions = package_info.versions.keys().collect::>(); versions.sort(); @@ -169,8 +168,6 @@ impl NpmFetchResolver { .rev() .find(|v| req.version_req.tag().is_none() && req.version_req.matches(v)) .cloned()?; - let elapsed = start.elapsed(); - eprintln!("finding version for {name} took {}ms", elapsed.as_millis()); Some(PackageNv { name, version }) }; let nv = maybe_get_nv().await; @@ -190,43 +187,18 @@ impl NpmFetchResolver { let maybe_auth_header = maybe_auth_header_for_npm_registry(registry_config).ok()?; // spawn due to the lsp's `Send` requirement - let start = std::time::Instant::now(); - debug_assert!( - tokio::runtime::Handle::current().runtime_flavor() - == tokio::runtime::RuntimeFlavor::CurrentThread - ); - let file = unsafe { - deno_core::unsync::MaskFutureAsSend::new(async move { - file_fetcher - .fetch_bypass_permissions_with_maybe_auth( - &info_url, - maybe_auth_header, - ) - .await - .ok() - }) - } - .await - .into_inner()?; - let elapsed = start.elapsed(); - eprintln!( - "fetching package info for {name} took {}ms", - elapsed.as_millis() - ); - let name2 = name.to_string(); - deno_core::unsync::spawn_blocking(move || { - let start = std::time::Instant::now(); - let res = serde_json::from_slice::(&file.source).ok(); - let elapsed = start.elapsed(); - eprintln!( - "parsing package info for {name2} took {}ms", - elapsed.as_millis() - ); - res + let file = deno_core::unsync::spawn(async move { + file_fetcher + .fetch_bypass_permissions_with_maybe_auth( + &info_url, + maybe_auth_header, + ) + .await + .ok() }) .await - .ok() - .flatten() + .ok()??; + serde_json::from_slice::(&file.source).ok() }; let info = fetch_package_info().await.map(Arc::new); self.info_by_name.insert(name.to_string(), info.clone()); diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs index d3c8da868c32f9..593d37639931b9 100644 --- a/cli/tools/registry/pm/cache_deps.rs +++ b/cli/tools/registry/pm/cache_deps.rs @@ -8,6 +8,7 @@ use crate::graph_container::ModuleGraphUpdatePermit; use deno_core::error::AnyError; use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::StreamExt; +use deno_semver::jsr::JsrPackageReqReference; use deno_semver::package::PackageReq; pub async fn cache_top_level_deps( @@ -56,15 +57,20 @@ pub async fn cache_top_level_deps( match specifier.scheme() { "jsr" => { let specifier_str = specifier.as_str(); - let specifier_str = - specifier_str.strip_prefix("jsr:").unwrap_or(specifier_str); - if let Ok(req) = PackageReq::from_str(specifier_str) { - if !seen_reqs.insert(req.clone()) { + if let Ok(req) = JsrPackageReqReference::from_str(specifier_str) { + if let Some(sub_path) = req.sub_path() { + if sub_path.ends_with('/') { + continue; + } + roots.push(specifier.clone()); + continue; + } + if !seen_reqs.insert(req.req().clone()) { continue; } let jsr_resolver = jsr_resolver.clone(); info_futures.push(async move { - if let Some(nv) = jsr_resolver.req_to_nv(&req).await { + if let Some(nv) = jsr_resolver.req_to_nv(req.req()).await { if let Some(info) = jsr_resolver.package_version_info(&nv).await { return Some((specifier.clone(), info)); @@ -72,6 +78,8 @@ pub async fn cache_top_level_deps( } None }); + } else { + eprintln!("badbad: {specifier_str}") } } "npm" => roots.push(specifier.clone()), diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index f4479f28cedc77..3e4b309346bf98 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -189,6 +189,15 @@ pub struct Dep { pub id: DepId, } +impl Dep { + pub fn prefixed_req(&self) -> String { + match self.kind { + DepKind::Npm => format!("npm:{}", self.req), + DepKind::Jsr => format!("jsr:{}", self.req), + } + } +} + fn import_map_entries<'a>( import_map: &'a ImportMap, ) -> impl Iterator)> { @@ -212,7 +221,6 @@ fn import_map_entries<'a>( scope.imports.entries().map(move |entry| { let mut full_path = path.clone(); - full_path.push(KeyPart::Imports); full_path.push(KeyPart::String(entry.raw_key.to_string())); (full_path, entry) }) @@ -887,6 +895,13 @@ impl DepManager { // subpath, but for the import map the trailing `/` is significant new_value.push('/'); } + if string_value + .trim_start_matches(format!("{}:", dep.kind.scheme()).as_str()) + .starts_with('/') + { + // this is gross + new_value = new_value.replace(':', ":/"); + } property .set_value(jsonc_parser::cst::CstInputValue::String(new_value)); } diff --git a/cli/tools/registry/pm/update.rs b/cli/tools/registry/pm/update.rs index 406951d2a7ab6f..9795abef0d372b 100644 --- a/cli/tools/registry/pm/update.rs +++ b/cli/tools/registry/pm/update.rs @@ -235,7 +235,7 @@ async fn do_update( jsr_resolver: Arc, flags: Arc, ) -> Result<(), AnyError> { - let mut updated = false; + let mut updated = HashSet::new(); for (dep_id, resolved, latest_versions) in deps .deps_with_resolved_latest_versions() .into_iter() @@ -260,14 +260,20 @@ async fn do_update( let new_req = VersionReq::parse_from_specifier(format!("^{}", latest.version).as_str()) .unwrap(); + let dep = deps.get_dep(dep_id); + + updated.insert((dep.prefixed_req(), latest.version)); deps.update_dep(dep_id, new_req); - updated = true; } deps.commit_changes().await?; - if updated { + if !updated.is_empty() { super::npm_install_after_modification(flags, Some(jsr_resolver)).await?; + log::info!("Updated {} dependencies:", updated.len()); + for (req, new_version) in updated { + log::info!("{} -> {}", req, new_version); + } } Ok(()) diff --git a/tests/registry/jsr/@denotest/add/0.2.1/mod.ts b/tests/registry/jsr/@denotest/add/0.2.1/mod.ts new file mode 100644 index 00000000000000..864e8dd321d82e --- /dev/null +++ b/tests/registry/jsr/@denotest/add/0.2.1/mod.ts @@ -0,0 +1,4 @@ +// This is renamed to `add()` in 1.0.0. +export function sum(a: number, b: number): number { + return a + b; +} diff --git a/tests/registry/jsr/@denotest/add/0.2.1_meta.json b/tests/registry/jsr/@denotest/add/0.2.1_meta.json new file mode 100644 index 00000000000000..6eebe219854055 --- /dev/null +++ b/tests/registry/jsr/@denotest/add/0.2.1_meta.json @@ -0,0 +1,8 @@ +{ + "exports": { + ".": "./mod.ts" + }, + "moduleGraph1": { + "/mod.ts": {} + } +} diff --git a/tests/registry/jsr/@denotest/add/meta.json b/tests/registry/jsr/@denotest/add/meta.json index 72aea80cc34536..f1a50109d8bc08 100644 --- a/tests/registry/jsr/@denotest/add/meta.json +++ b/tests/registry/jsr/@denotest/add/meta.json @@ -4,6 +4,7 @@ "yanked": true }, "1.0.0": {}, - "0.2.0": {} + "0.2.0": {}, + "0.2.1": {} } } diff --git a/tests/registry/jsr/@denotest/multiple-exports/0.2.0/add.ts b/tests/registry/jsr/@denotest/multiple-exports/0.2.0/add.ts new file mode 100644 index 00000000000000..de02f69024bf76 --- /dev/null +++ b/tests/registry/jsr/@denotest/multiple-exports/0.2.0/add.ts @@ -0,0 +1 @@ +export * from "jsr:@denotest/add@1"; diff --git a/tests/registry/jsr/@denotest/multiple-exports/0.2.0/data.json b/tests/registry/jsr/@denotest/multiple-exports/0.2.0/data.json new file mode 100644 index 00000000000000..885e71c6ccde35 --- /dev/null +++ b/tests/registry/jsr/@denotest/multiple-exports/0.2.0/data.json @@ -0,0 +1,3 @@ +{ + "a": 1 +} \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/multiple-exports/0.2.0/subtract.ts b/tests/registry/jsr/@denotest/multiple-exports/0.2.0/subtract.ts new file mode 100644 index 00000000000000..215c42310d69bb --- /dev/null +++ b/tests/registry/jsr/@denotest/multiple-exports/0.2.0/subtract.ts @@ -0,0 +1 @@ +export * from "jsr:@denotest/subtract@1"; diff --git a/tests/registry/jsr/@denotest/multiple-exports/0.2.0_meta.json b/tests/registry/jsr/@denotest/multiple-exports/0.2.0_meta.json new file mode 100644 index 00000000000000..d9f58b9a61a03f --- /dev/null +++ b/tests/registry/jsr/@denotest/multiple-exports/0.2.0_meta.json @@ -0,0 +1,7 @@ +{ + "exports": { + "./add": "./add.ts", + "./subtract": "./subtract.ts", + "./data-json": "./data.json" + } +} diff --git a/tests/registry/jsr/@denotest/multiple-exports/meta.json b/tests/registry/jsr/@denotest/multiple-exports/meta.json index 02601e4d0d5188..2f4daa84414004 100644 --- a/tests/registry/jsr/@denotest/multiple-exports/meta.json +++ b/tests/registry/jsr/@denotest/multiple-exports/meta.json @@ -1,5 +1,6 @@ { "versions": { - "1.0.0": {} + "1.0.0": {}, + "0.2.0": {} } } diff --git a/tests/registry/jsr/@denotest/subtract/0.2.0/mod.ts b/tests/registry/jsr/@denotest/subtract/0.2.0/mod.ts new file mode 100644 index 00000000000000..74e49ea6fa9601 --- /dev/null +++ b/tests/registry/jsr/@denotest/subtract/0.2.0/mod.ts @@ -0,0 +1,3 @@ +export function sub(a: number, b: number): number { + return a - b; +} diff --git a/tests/registry/jsr/@denotest/subtract/0.2.0_meta.json b/tests/registry/jsr/@denotest/subtract/0.2.0_meta.json new file mode 100644 index 00000000000000..6eebe219854055 --- /dev/null +++ b/tests/registry/jsr/@denotest/subtract/0.2.0_meta.json @@ -0,0 +1,8 @@ +{ + "exports": { + ".": "./mod.ts" + }, + "moduleGraph1": { + "/mod.ts": {} + } +} diff --git a/tests/registry/jsr/@denotest/subtract/meta.json b/tests/registry/jsr/@denotest/subtract/meta.json index 02601e4d0d5188..2f4cee59dfe7f8 100644 --- a/tests/registry/jsr/@denotest/subtract/meta.json +++ b/tests/registry/jsr/@denotest/subtract/meta.json @@ -1,5 +1,7 @@ { + "latest": "1.0.0", "versions": { - "1.0.0": {} + "1.0.0": {}, + "0.2.0": {} } } diff --git a/tests/registry/npm/@denotest/has-patch-versions/0.1.0/package.json b/tests/registry/npm/@denotest/has-patch-versions/0.1.0/package.json new file mode 100644 index 00000000000000..45684d4f5265a9 --- /dev/null +++ b/tests/registry/npm/@denotest/has-patch-versions/0.1.0/package.json @@ -0,0 +1,5 @@ +{ + "name": "@denotest/has-patch-versions", + "version": "0.1.0" + +} \ No newline at end of file diff --git a/tests/registry/npm/@denotest/has-patch-versions/0.1.1/package.json b/tests/registry/npm/@denotest/has-patch-versions/0.1.1/package.json new file mode 100644 index 00000000000000..4483df34e58e21 --- /dev/null +++ b/tests/registry/npm/@denotest/has-patch-versions/0.1.1/package.json @@ -0,0 +1,5 @@ +{ + "name": "@denotest/has-patch-versions", + "version": "0.1.1" + +} \ No newline at end of file diff --git a/tests/registry/npm/@denotest/has-patch-versions/0.2.0/package.json b/tests/registry/npm/@denotest/has-patch-versions/0.2.0/package.json new file mode 100644 index 00000000000000..4483df34e58e21 --- /dev/null +++ b/tests/registry/npm/@denotest/has-patch-versions/0.2.0/package.json @@ -0,0 +1,5 @@ +{ + "name": "@denotest/has-patch-versions", + "version": "0.1.1" + +} \ No newline at end of file diff --git a/tests/specs/update/deno_json/__test__.jsonc b/tests/specs/update/deno_json/__test__.jsonc new file mode 100644 index 00000000000000..22166bec34a4c9 --- /dev/null +++ b/tests/specs/update/deno_json/__test__.jsonc @@ -0,0 +1,85 @@ +{ + "tempDir": true, + "tests": { + // just to make sure install doesn't change the lockfile + "sanity_lockfile_up_to_date": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": [ + "eval", + "const now = Deno.readTextFileSync('./deno.lock'); console.log(now.trim());" + ], + "output": "deno.lock.orig.out" + } + ] + }, + "print_outdated": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "update --outdated", + "output": "outdated.out" + } + ] + }, + "print_outdated_compatible": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "update --outdated --compatible", + "output": "outdated_compatible.out" + } + ] + }, + "update_compatible": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "update", + "output": "./update_compatible/update.out" + }, + { + "args": "-A print_file.ts ./deno.json", + "output": "./update_compatible/deno.json.out" + }, + { + "args": "-A print_file.ts ./deno.lock", + "output": "./update_compatible/deno.lock.out" + } + ] + }, + "update_latest": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "update --latest", + "output": "update_latest/update.out" + }, + { + "args": "-A print_file.ts ./deno.json", + "output": "./update_latest/deno.json.out" + }, + { + "args": "-A print_file.ts ./deno.lock", + "output": "./update_latest/deno.lock.out" + } + ] + } + } +} diff --git a/tests/specs/update/deno_json/deno.json b/tests/specs/update/deno_json/deno.json new file mode 100644 index 00000000000000..4f880e6db9108d --- /dev/null +++ b/tests/specs/update/deno_json/deno.json @@ -0,0 +1,19 @@ +{ + "imports": { + "@denotest/add": "jsr:@denotest/add@^0.2.0", + "@denotest/add/": "jsr:/@denotest/add@^0.2.0/", + "@denotest/subtract": "jsr:@denotest/subtract@^0.2.0", + "@denotest/with-subpath": "jsr:@denotest/multiple-exports@0.2.0/data-json", + "@denotest/breaking-change-between-versions": "npm:@denotest/breaking-change-between-versions@1.0.0", + "@denotest/bin": "npm:@denotest/bin@0.6.0", + "@denotest/has-patch-versions": "npm:@denotest/has-patch-versions@^0.1.0" + }, + "scopes": { + "/foo/": { + "@denotest/add": "jsr:@denotest/add@^0.2.0", + "@denotest/add/": "jsr:/@denotest/add@^0.2.0/", + "@denotest/subtract": "jsr:@denotest/subtract@^0.2.0", + "@denotest/with-subpath": "jsr:@denotest/multiple-exports@0.2.0/data-json" + } + } +} diff --git a/tests/specs/update/deno_json/deno.lock b/tests/specs/update/deno_json/deno.lock new file mode 100644 index 00000000000000..238a9c8a10bd11 --- /dev/null +++ b/tests/specs/update/deno_json/deno.lock @@ -0,0 +1,43 @@ +{ + "version": "4", + "specifiers": { + "jsr:@denotest/add@0.2": "0.2.0", + "jsr:@denotest/multiple-exports@0.2.0": "0.2.0", + "jsr:@denotest/subtract@0.2": "0.2.0", + "npm:@denotest/bin@0.6.0": "0.6.0", + "npm:@denotest/breaking-change-between-versions@1.0.0": "1.0.0", + "npm:@denotest/has-patch-versions@0.1": "0.1.0" + }, + "jsr": { + "@denotest/add@0.2.0": { + "integrity": "a9076d30ecb42b2fc6dd95e7055fbf4e6358b53f550741bd7f60089d19f68848" + }, + "@denotest/multiple-exports@0.2.0": { + "integrity": "efe9748a0c0939c7ac245fee04acc0c42bd7a61874ff71a360c4543e4f5f6b36" + }, + "@denotest/subtract@0.2.0": { + "integrity": "c9650fc559ab2430effc0c7fb1540e3aa89888fbdd926335ccfdeac57eb3a64d" + } + }, + "npm": { + "@denotest/bin@0.6.0": { + "integrity": "sha512-+xIHuFv3j/rkWWF7H6HNAPEBGUtPT9uO4rBnXXNH5NVjsrQJ5c7ZtYp+z7Hk3JnQzt2exGeJrWGvVlndtgC+yw==" + }, + "@denotest/breaking-change-between-versions@1.0.0": { + "integrity": "sha512-EjAhryqWinStR5SeDOCN4PpO8Z9cTiY4DV9nSnoZUaOUsboEtJ/r0uhm4sBiZm3//mW7Kg/5T6dqXG6RtzLusA==" + }, + "@denotest/has-patch-versions@0.1.0": { + "integrity": "sha512-NMn5TOdV07srVJ5G7Co4EzOoF58OMKxSzYx1u83DBLwYgCyRuiIpNNK+qw2mdJrlyYb39FaO+ixOTPcOlbicQA==" + } + }, + "workspace": { + "dependencies": [ + "jsr:@denotest/add@0.2", + "jsr:@denotest/multiple-exports@0.2.0", + "jsr:@denotest/subtract@0.2", + "npm:@denotest/bin@0.6.0", + "npm:@denotest/breaking-change-between-versions@1.0.0", + "npm:@denotest/has-patch-versions@0.1" + ] + } +} diff --git a/tests/specs/update/deno_json/deno.lock.orig.out b/tests/specs/update/deno_json/deno.lock.orig.out new file mode 100644 index 00000000000000..238a9c8a10bd11 --- /dev/null +++ b/tests/specs/update/deno_json/deno.lock.orig.out @@ -0,0 +1,43 @@ +{ + "version": "4", + "specifiers": { + "jsr:@denotest/add@0.2": "0.2.0", + "jsr:@denotest/multiple-exports@0.2.0": "0.2.0", + "jsr:@denotest/subtract@0.2": "0.2.0", + "npm:@denotest/bin@0.6.0": "0.6.0", + "npm:@denotest/breaking-change-between-versions@1.0.0": "1.0.0", + "npm:@denotest/has-patch-versions@0.1": "0.1.0" + }, + "jsr": { + "@denotest/add@0.2.0": { + "integrity": "a9076d30ecb42b2fc6dd95e7055fbf4e6358b53f550741bd7f60089d19f68848" + }, + "@denotest/multiple-exports@0.2.0": { + "integrity": "efe9748a0c0939c7ac245fee04acc0c42bd7a61874ff71a360c4543e4f5f6b36" + }, + "@denotest/subtract@0.2.0": { + "integrity": "c9650fc559ab2430effc0c7fb1540e3aa89888fbdd926335ccfdeac57eb3a64d" + } + }, + "npm": { + "@denotest/bin@0.6.0": { + "integrity": "sha512-+xIHuFv3j/rkWWF7H6HNAPEBGUtPT9uO4rBnXXNH5NVjsrQJ5c7ZtYp+z7Hk3JnQzt2exGeJrWGvVlndtgC+yw==" + }, + "@denotest/breaking-change-between-versions@1.0.0": { + "integrity": "sha512-EjAhryqWinStR5SeDOCN4PpO8Z9cTiY4DV9nSnoZUaOUsboEtJ/r0uhm4sBiZm3//mW7Kg/5T6dqXG6RtzLusA==" + }, + "@denotest/has-patch-versions@0.1.0": { + "integrity": "sha512-NMn5TOdV07srVJ5G7Co4EzOoF58OMKxSzYx1u83DBLwYgCyRuiIpNNK+qw2mdJrlyYb39FaO+ixOTPcOlbicQA==" + } + }, + "workspace": { + "dependencies": [ + "jsr:@denotest/add@0.2", + "jsr:@denotest/multiple-exports@0.2.0", + "jsr:@denotest/subtract@0.2", + "npm:@denotest/bin@0.6.0", + "npm:@denotest/breaking-change-between-versions@1.0.0", + "npm:@denotest/has-patch-versions@0.1" + ] + } +} diff --git a/tests/specs/update/deno_json/outdated.out b/tests/specs/update/deno_json/outdated.out new file mode 100644 index 00000000000000..7d40e42241cc69 --- /dev/null +++ b/tests/specs/update/deno_json/outdated.out @@ -0,0 +1,15 @@ +┌────────────────────────────────────────────────┬─────────┬────────┬────────┐ +│ Package │ Current │ Update │ Latest │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ jsr:@denotest/multiple-exports │ 0.2.0 │ 0.2.0 │ 1.0.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ jsr:@denotest/subtract │ 0.2.0 │ 0.2.0 │ 1.0.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ npm:@denotest/bin │ 0.6.0 │ 0.6.0 │ 1.0.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ jsr:@denotest/add │ 0.2.0 │ 0.2.1 │ 1.0.0 │ +└────────────────────────────────────────────────┴─────────┴────────┴────────┘ diff --git a/tests/specs/update/deno_json/outdated_compatible.out b/tests/specs/update/deno_json/outdated_compatible.out new file mode 100644 index 00000000000000..ec7eea4815bae2 --- /dev/null +++ b/tests/specs/update/deno_json/outdated_compatible.out @@ -0,0 +1,7 @@ +┌──────────────────────────────────┬─────────┬────────┬────────┐ +│ Package │ Current │ Update │ Latest │ +├──────────────────────────────────┼─────────┼────────┼────────┤ +│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ +├──────────────────────────────────┼─────────┼────────┼────────┤ +│ jsr:@denotest/add │ 0.2.0 │ 0.2.1 │ 1.0.0 │ +└──────────────────────────────────┴─────────┴────────┴────────┘ diff --git a/tests/specs/update/deno_json/print_file.ts b/tests/specs/update/deno_json/print_file.ts new file mode 100644 index 00000000000000..c57b6c3a4e56f3 --- /dev/null +++ b/tests/specs/update/deno_json/print_file.ts @@ -0,0 +1,2 @@ +const file = Deno.args[0]; +console.log(Deno.readTextFileSync(file).trim()); diff --git a/tests/specs/update/deno_json/update_compatible/deno.json.out b/tests/specs/update/deno_json/update_compatible/deno.json.out new file mode 100644 index 00000000000000..e453eff3c9c566 --- /dev/null +++ b/tests/specs/update/deno_json/update_compatible/deno.json.out @@ -0,0 +1,19 @@ +{ + "imports": { + "@denotest/add": "jsr:@denotest/add@^0.2.1", + "@denotest/add/": "jsr:/@denotest/add@^0.2.1/", + "@denotest/subtract": "jsr:@denotest/subtract@^0.2.0", + "@denotest/with-subpath": "jsr:@denotest/multiple-exports@0.2.0/data-json", + "@denotest/breaking-change-between-versions": "npm:@denotest/breaking-change-between-versions@1.0.0", + "@denotest/bin": "npm:@denotest/bin@0.6.0", + "@denotest/has-patch-versions": "npm:@denotest/has-patch-versions@^0.1.1" + }, + "scopes": { + "/foo/": { + "@denotest/add": "jsr:@denotest/add@^0.2.1", + "@denotest/add/": "jsr:/@denotest/add@^0.2.1/", + "@denotest/subtract": "jsr:@denotest/subtract@^0.2.0", + "@denotest/with-subpath": "jsr:@denotest/multiple-exports@0.2.0/data-json" + } + } +} diff --git a/tests/specs/update/deno_json/update_compatible/deno.lock.out b/tests/specs/update/deno_json/update_compatible/deno.lock.out new file mode 100644 index 00000000000000..0263d81d53583f --- /dev/null +++ b/tests/specs/update/deno_json/update_compatible/deno.lock.out @@ -0,0 +1,43 @@ +{ + "version": "4", + "specifiers": { + "jsr:@denotest/add@~0.2.1": "0.2.1", + "jsr:@denotest/multiple-exports@0.2.0": "0.2.0", + "jsr:@denotest/subtract@0.2": "0.2.0", + "npm:@denotest/bin@0.6.0": "0.6.0", + "npm:@denotest/breaking-change-between-versions@1.0.0": "1.0.0", + "npm:@denotest/has-patch-versions@~0.1.1": "0.1.1" + }, + "jsr": { + "@denotest/add@0.2.1": { + "integrity": "a9076d30ecb42b2fc6dd95e7055fbf4e6358b53f550741bd7f60089d19f68848" + }, + "@denotest/multiple-exports@0.2.0": { + "integrity": "efe9748a0c0939c7ac245fee04acc0c42bd7a61874ff71a360c4543e4f5f6b36" + }, + "@denotest/subtract@0.2.0": { + "integrity": "c9650fc559ab2430effc0c7fb1540e3aa89888fbdd926335ccfdeac57eb3a64d" + } + }, + "npm": { + "@denotest/bin@0.6.0": { + "integrity": "sha512-+xIHuFv3j/rkWWF7H6HNAPEBGUtPT9uO4rBnXXNH5NVjsrQJ5c7ZtYp+z7Hk3JnQzt2exGeJrWGvVlndtgC+yw==" + }, + "@denotest/breaking-change-between-versions@1.0.0": { + "integrity": "sha512-EjAhryqWinStR5SeDOCN4PpO8Z9cTiY4DV9nSnoZUaOUsboEtJ/r0uhm4sBiZm3//mW7Kg/5T6dqXG6RtzLusA==" + }, + "@denotest/has-patch-versions@0.1.1": { + "integrity": "sha512-BcH2/Hbu6RCkhLpzHTgRM7Vdxhb2bq7WVovWOb56eTe1/2sNDEkpJ5O3gfHMU0WF1MG7Fn+boTgXpEAMfIrA5Q==" + } + }, + "workspace": { + "dependencies": [ + "jsr:@denotest/add@~0.2.1", + "jsr:@denotest/multiple-exports@0.2.0", + "jsr:@denotest/subtract@0.2", + "npm:@denotest/bin@0.6.0", + "npm:@denotest/breaking-change-between-versions@1.0.0", + "npm:@denotest/has-patch-versions@~0.1.1" + ] + } +} diff --git a/tests/specs/update/deno_json/update_compatible/update.out b/tests/specs/update/deno_json/update_compatible/update.out new file mode 100644 index 00000000000000..127cfb8c8ca139 --- /dev/null +++ b/tests/specs/update/deno_json/update_compatible/update.out @@ -0,0 +1,9 @@ +[UNORDERED_START] +Download http://127.0.0.1:4250/@denotest/add/0.2.1/mod.ts +Download http://localhost:4260/@denotest/has-patch-versions/0.1.1.tgz +[UNORDERED_END] +Updated 2 dependencies: +[UNORDERED_START] +jsr:@denotest/add@^0.2.0 -> 0.2.1 +npm:@denotest/has-patch-versions@^0.1.0 -> 0.1.1 +[UNORDERED_END] diff --git a/tests/specs/update/deno_json/update_latest/deno.json.out b/tests/specs/update/deno_json/update_latest/deno.json.out new file mode 100644 index 00000000000000..5e4e99bd66f1e2 --- /dev/null +++ b/tests/specs/update/deno_json/update_latest/deno.json.out @@ -0,0 +1,19 @@ +{ + "imports": { + "@denotest/add": "jsr:@denotest/add@^1.0.0", + "@denotest/add/": "jsr:/@denotest/add@^1.0.0/", + "@denotest/subtract": "jsr:@denotest/subtract@^1.0.0", + "@denotest/with-subpath": "jsr:@denotest/multiple-exports@^1.0.0/data-json", + "@denotest/breaking-change-between-versions": "npm:@denotest/breaking-change-between-versions@^2.0.0", + "@denotest/bin": "npm:@denotest/bin@^1.0.0", + "@denotest/has-patch-versions": "npm:@denotest/has-patch-versions@^0.2.0" + }, + "scopes": { + "/foo/": { + "@denotest/add": "jsr:@denotest/add@^1.0.0", + "@denotest/add/": "jsr:/@denotest/add@^1.0.0/", + "@denotest/subtract": "jsr:@denotest/subtract@^1.0.0", + "@denotest/with-subpath": "jsr:@denotest/multiple-exports@^1.0.0/data-json" + } + } +} diff --git a/tests/specs/update/deno_json/update_latest/deno.lock.out b/tests/specs/update/deno_json/update_latest/deno.lock.out new file mode 100644 index 00000000000000..0a38c948735eab --- /dev/null +++ b/tests/specs/update/deno_json/update_latest/deno.lock.out @@ -0,0 +1,43 @@ +{ + "version": "4", + "specifiers": { + "jsr:@denotest/add@1": "1.0.0", + "jsr:@denotest/multiple-exports@1": "1.0.0", + "jsr:@denotest/subtract@1": "1.0.0", + "npm:@denotest/bin@1": "1.0.0", + "npm:@denotest/breaking-change-between-versions@2": "2.0.0", + "npm:@denotest/has-patch-versions@0.2": "0.1.1" + }, + "jsr": { + "@denotest/add@1.0.0": { + "integrity": "3b2e675c1ad7fba2a45bc251992e01aff08a3c974ac09079b11e6a5b95d4bfcb" + }, + "@denotest/multiple-exports@1.0.0": { + "integrity": "efe9748a0c0939c7ac245fee04acc0c42bd7a61874ff71a360c4543e4f5f6b36" + }, + "@denotest/subtract@1.0.0": { + "integrity": "e178a7101c073e93d9efa6833d5cbf83bc1bc8d509b7c2a5ecbf74265e917597" + } + }, + "npm": { + "@denotest/bin@1.0.0": { + "integrity": "sha512-LokQ7v6N8KiQCVZXuy9MAzaB4kcbI8tDntvy2DI3YOaigDFdghrkv3WJfc7xU9boni5jPcndNsiH9dPJEHZfyQ==" + }, + "@denotest/breaking-change-between-versions@2.0.0": { + "integrity": "sha512-x7q4LVdjFsu6pMsxIs1LPed6hix5p4RcCybssPzRZXOD2VCNBWbNCEDW9JRWiVFzT7+Md0n9czqMMIx+VUA5qA==" + }, + "@denotest/has-patch-versions@0.1.1": { + "integrity": "sha512-BcH2/Hbu6RCkhLpzHTgRM7Vdxhb2bq7WVovWOb56eTe1/2sNDEkpJ5O3gfHMU0WF1MG7Fn+boTgXpEAMfIrA5Q==" + } + }, + "workspace": { + "dependencies": [ + "jsr:@denotest/add@1", + "jsr:@denotest/multiple-exports@1", + "jsr:@denotest/subtract@1", + "npm:@denotest/bin@1", + "npm:@denotest/breaking-change-between-versions@2", + "npm:@denotest/has-patch-versions@0.2" + ] + } +} diff --git a/tests/specs/update/deno_json/update_latest/update.out b/tests/specs/update/deno_json/update_latest/update.out new file mode 100644 index 00000000000000..bd1279bf6a3147 --- /dev/null +++ b/tests/specs/update/deno_json/update_latest/update.out @@ -0,0 +1,18 @@ +[UNORDERED_START] +Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts +Download http://127.0.0.1:4250/@denotest/subtract/1.0.0/mod.ts +Download http://127.0.0.1:4250/@denotest/multiple-exports/1.0.0_meta.json +Download http://127.0.0.1:4250/@denotest/multiple-exports/1.0.0/data.json +Download http://localhost:4260/@denotest/has-patch-versions/0.1.1.tgz +Download http://localhost:4260/@denotest/bin/1.0.0.tgz +Download http://localhost:4260/@denotest/breaking-change-between-versions/2.0.0.tgz +[UNORDERED_END] +Updated 6 dependencies: +[UNORDERED_START] +jsr:@denotest/add@^0.2.0 -> 1.0.0 +jsr:@denotest/multiple-exports@0.2.0 -> 1.0.0 +jsr:@denotest/subtract@^0.2.0 -> 1.0.0 +npm:@denotest/bin@0.6.0 -> 1.0.0 +npm:@denotest/breaking-change-between-versions@1.0.0 -> 2.0.0 +npm:@denotest/has-patch-versions@^0.1.0 -> 0.2.0 +[UNORDERED_END] From 7a9ebf8677b2b6304ef03dea29f878422e2879e7 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Mon, 18 Nov 2024 16:10:57 -0800 Subject: [PATCH 05/26] rm prints --- cli/tools/registry/pm/update.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cli/tools/registry/pm/update.rs b/cli/tools/registry/pm/update.rs index 9795abef0d372b..28b789c05bd505 100644 --- a/cli/tools/registry/pm/update.rs +++ b/cli/tools/registry/pm/update.rs @@ -1,3 +1,4 @@ +use std::collections::HashSet; use std::sync::Arc; use deno_core::error::AnyError; @@ -47,11 +48,6 @@ fn print_outdated_table(packages: &[OutdatedPackage]) { let update_column_width = longest_update.max(HEADINGS[2].len()) + 2; let latest_column_width = longest_latest.max(HEADINGS[3].len()) + 2; - eprintln!("package_column_width: {}", package_column_width); - eprintln!("current_column_width: {}", current_column_width); - eprintln!("update_column_width: {}", update_column_width); - eprintln!("latest_column_width: {}", latest_column_width); - println!( "┌{}┬{}┬{}┬{}┐", "─".repeat(package_column_width), From 137e7329275a5f0b38d8e699e1576bd3000f9516 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Mon, 18 Nov 2024 19:23:10 -0800 Subject: [PATCH 06/26] more cleanup + tests --- cli/tools/registry/pm/cache_deps.rs | 3 - cli/tools/registry/pm/deps.rs | 65 +------------- cli/tools/registry/pm/update.rs | 45 ++++------ .../has-patch-versions/0.2.0/package.json | 3 +- .../update/mixed_workspace/__test__.jsonc | 7 ++ tests/specs/update/mixed_workspace/deno.json | 3 + .../update/mixed_workspace/member-a/deno.json | 7 ++ .../update/mixed_workspace/member-a/mod.ts | 0 .../mixed_workspace/member-b/package.json | 7 ++ .../specs/update/package_json/__test__.jsonc | 84 +++++++++++++++++++ tests/specs/update/package_json/deno.lock | 28 +++++++ .../update/package_json/deno.lock.orig.out | 28 +++++++ tests/specs/update/package_json/outdated.out | 9 ++ .../package_json/outdated_compatible.out | 5 ++ tests/specs/update/package_json/package.json | 9 ++ tests/specs/update/package_json/print_file.ts | 2 + .../update_compatible/deno.lock.out | 28 +++++++ .../update_compatible/package.json.out | 9 ++ .../package_json/update_compatible/update.out | 4 + .../package_json/update_latest/deno.lock.out | 28 +++++++ .../update_latest/package.json.out | 9 ++ .../package_json/update_latest/update.out | 14 ++++ 22 files changed, 299 insertions(+), 98 deletions(-) create mode 100644 tests/specs/update/mixed_workspace/__test__.jsonc create mode 100644 tests/specs/update/mixed_workspace/deno.json create mode 100644 tests/specs/update/mixed_workspace/member-a/deno.json create mode 100644 tests/specs/update/mixed_workspace/member-a/mod.ts create mode 100644 tests/specs/update/mixed_workspace/member-b/package.json create mode 100644 tests/specs/update/package_json/__test__.jsonc create mode 100644 tests/specs/update/package_json/deno.lock create mode 100644 tests/specs/update/package_json/deno.lock.orig.out create mode 100644 tests/specs/update/package_json/outdated.out create mode 100644 tests/specs/update/package_json/outdated_compatible.out create mode 100644 tests/specs/update/package_json/package.json create mode 100644 tests/specs/update/package_json/print_file.ts create mode 100644 tests/specs/update/package_json/update_compatible/deno.lock.out create mode 100644 tests/specs/update/package_json/update_compatible/package.json.out create mode 100644 tests/specs/update/package_json/update_compatible/update.out create mode 100644 tests/specs/update/package_json/update_latest/deno.lock.out create mode 100644 tests/specs/update/package_json/update_latest/package.json.out create mode 100644 tests/specs/update/package_json/update_latest/update.out diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs index 593d37639931b9..f9d67e4d4f4435 100644 --- a/cli/tools/registry/pm/cache_deps.rs +++ b/cli/tools/registry/pm/cache_deps.rs @@ -9,7 +9,6 @@ use deno_core::error::AnyError; use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::StreamExt; use deno_semver::jsr::JsrPackageReqReference; -use deno_semver::package::PackageReq; pub async fn cache_top_level_deps( // todo(dsherret): don't pass the factory into this function. Instead use ctor deps @@ -78,8 +77,6 @@ pub async fn cache_top_level_deps( } None }); - } else { - eprintln!("badbad: {specifier_str}") } } "npm" => roots.push(specifier.clone()), diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index 3e4b309346bf98..ea873bb22bd386 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -191,10 +191,7 @@ pub struct Dep { impl Dep { pub fn prefixed_req(&self) -> String { - match self.kind { - DepKind::Npm => format!("npm:{}", self.req), - DepKind::Jsr => format!("jsr:{}", self.req), - } + format!("{}:{}", self.kind.scheme(), self.req) } } @@ -410,7 +407,6 @@ fn add_deps_from_package_json( ) { let package_json_deps = resolve_local_package_json_deps(package_json); for (k, v) in package_json_deps { - eprintln!("{k}"); let (package_dep_kind, v) = match v { Ok((k, v)) => (k, v), Err(e) => { @@ -445,11 +441,9 @@ fn deps_from_workspace( ) -> Result, AnyError> { let mut deps = Vec::with_capacity(32); for deno_json in workspace.deno_jsons() { - eprintln!("deno_json: {}", deno_json.specifier); add_deps_from_deno_json(deno_json, dep_filter, &mut deps); } for package_json in workspace.package_jsons() { - eprintln!("package_json: {}", package_json.path.display()); add_deps_from_package_json(package_json, dep_filter, &mut deps); } @@ -464,24 +458,6 @@ pub enum Change { Update(DepId, VersionReq), } -pub struct DepData<'a, T> { - inner: &'a [T], -} - -impl<'a, T> DepData<'a, T> { - // pub fn len(&self) -> usize { - // self.inner.len() - // } -} - -impl<'a, T> std::ops::Index for DepData<'a, T> { - type Output = T; - - fn index(&self, index: DepId) -> &Self::Output { - &self.inner[index.0] - } -} - pub trait DepFilter: Copy { fn should_include( &mut self, @@ -788,41 +764,6 @@ impl DepManager { Ok(()) } - // pub fn resolved_version( - // &self, - // dep_id: DepId, - // ) -> Result, AnyError> { - // if self.resolved_versions.len() < self.deps.len() { - // return Err(deno_core::anyhow::anyhow!( - // "Versions haven't been resolved yet" - // )); - // } - - // Ok(self.resolved_versions[dep_id.0].as_ref()) - // } - - // pub fn resolved_versions(&self) -> DepData<'_, Option> { - // DepData { - // inner: &self.resolved_versions, - // } - // } - - // pub fn latest_versions(&self) -> DepData<'_, PackageLatestVersion> { - // DepData { - // inner: &self.latest_versions, - // } - // } - - // pub fn deps_with_latest_versions( - // &self, - // ) -> impl IntoIterator + '_ { - // self - // .latest_versions - // .iter() - // .enumerate() - // .map(|(i, latest)| (DepId(i), latest.clone())) - // } - pub fn deps_with_resolved_latest_versions( &self, ) -> impl IntoIterator, PackageLatestVersion)> + '_ @@ -837,10 +778,6 @@ impl DepManager { }) } - // pub fn deps(&self) -> DepData<'_, Dep> { - // DepData { inner: &self.deps } - // } - pub fn get_dep(&self, id: DepId) -> &Dep { &self.deps[id.0] } diff --git a/cli/tools/registry/pm/update.rs b/cli/tools/registry/pm/update.rs index 28b789c05bd505..ca79c04fa08d97 100644 --- a/cli/tools/registry/pm/update.rs +++ b/cli/tools/registry/pm/update.rs @@ -48,13 +48,12 @@ fn print_outdated_table(packages: &[OutdatedPackage]) { let update_column_width = longest_update.max(HEADINGS[2].len()) + 2; let latest_column_width = longest_latest.max(HEADINGS[3].len()) + 2; - println!( - "┌{}┬{}┬{}┬{}┐", - "─".repeat(package_column_width), - "─".repeat(current_column_width), - "─".repeat(update_column_width), - "─".repeat(latest_column_width) - ); + let package_fill = "─".repeat(package_column_width); + let current_fill = "─".repeat(current_column_width); + let update_fill = "─".repeat(update_column_width); + let latest_fill = "─".repeat(latest_column_width); + + println!("┌{package_fill}┬{current_fill}┬{update_fill}┬{latest_fill}┐"); println!( "│ {}{} │ {}{} │ {}{} │ {}{} │", colors::intense_blue(HEADINGS[0]), @@ -67,43 +66,31 @@ fn print_outdated_table(packages: &[OutdatedPackage]) { " ".repeat(latest_column_width - 2 - HEADINGS[3].len()) ); for package in packages { - println!( - "├{}┼{}┼{}┼{}┤", - "─".repeat(package_column_width), - "─".repeat(current_column_width), - "─".repeat(update_column_width), - "─".repeat(latest_column_width) - ); + println!("├{package_fill}┼{current_fill}┼{update_fill}┼{latest_fill}┤",); print!( - "│{:^package_column_width$}", + "│ {: 0.1.1 diff --git a/tests/specs/update/package_json/update_latest/deno.lock.out b/tests/specs/update/package_json/update_latest/deno.lock.out new file mode 100644 index 00000000000000..59b596ef2f9e81 --- /dev/null +++ b/tests/specs/update/package_json/update_latest/deno.lock.out @@ -0,0 +1,28 @@ +{ + "version": "4", + "specifiers": { + "npm:@denotest/bin@1": "1.0.0", + "npm:@denotest/breaking-change-between-versions@2": "2.0.0", + "npm:@denotest/has-patch-versions@0.2": "0.2.0" + }, + "npm": { + "@denotest/bin@1.0.0": { + "integrity": "sha512-LokQ7v6N8KiQCVZXuy9MAzaB4kcbI8tDntvy2DI3YOaigDFdghrkv3WJfc7xU9boni5jPcndNsiH9dPJEHZfyQ==" + }, + "@denotest/breaking-change-between-versions@2.0.0": { + "integrity": "sha512-x7q4LVdjFsu6pMsxIs1LPed6hix5p4RcCybssPzRZXOD2VCNBWbNCEDW9JRWiVFzT7+Md0n9czqMMIx+VUA5qA==" + }, + "@denotest/has-patch-versions@0.2.0": { + "integrity": "sha512-r+5ZSpZHBgADr7Go++aGDMy7ogVtolEYT9b7Yqkp85Ms+E2TfImvXf0rGY1dTzU8qChfoHihLOrxcaTvdZ+r8A==" + } + }, + "workspace": { + "packageJson": { + "dependencies": [ + "npm:@denotest/bin@1", + "npm:@denotest/breaking-change-between-versions@2", + "npm:@denotest/has-patch-versions@0.2" + ] + } + } +} diff --git a/tests/specs/update/package_json/update_latest/package.json.out b/tests/specs/update/package_json/update_latest/package.json.out new file mode 100644 index 00000000000000..fb483d78bd7cdf --- /dev/null +++ b/tests/specs/update/package_json/update_latest/package.json.out @@ -0,0 +1,9 @@ +{ + "dependencies": { + "@denotest/has-patch-versions": "^0.2.0", + "@denotest/breaking-change-between-versions": "^2.0.0" + }, + "devDependencies": { + "aliased": "npm:@denotest/bin@^1.0.0" + } +} diff --git a/tests/specs/update/package_json/update_latest/update.out b/tests/specs/update/package_json/update_latest/update.out new file mode 100644 index 00000000000000..b0bd51d0e92b9d --- /dev/null +++ b/tests/specs/update/package_json/update_latest/update.out @@ -0,0 +1,14 @@ +[UNORDERED_START] +Download http://localhost:4260/@denotest/bin/1.0.0.tgz +Download http://localhost:4260/@denotest/has-patch-versions/0.2.0.tgz +Download http://localhost:4260/@denotest/breaking-change-between-versions/2.0.0.tgz +Initialize @denotest/has-patch-versions@0.2.0 +Initialize @denotest/breaking-change-between-versions@2.0.0 +Initialize @denotest/bin@1.0.0 +[UNORDERED_END] +Updated 3 dependencies: +[UNORDERED_START] +npm:@denotest/bin@^0.6.0 -> 1.0.0 +npm:@denotest/breaking-change-between-versions@1.0.0 -> 2.0.0 +npm:@denotest/has-patch-versions@^0.1.0 -> 0.2.0 +[UNORDERED_END] From b2aee3dc72d7b24338426a2413d3c99d91749cf9 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 12:20:20 -0800 Subject: [PATCH 07/26] filtering + alias + cleanup --- cli/tools/registry/pm.rs | 4 +- cli/tools/registry/pm/deps.rs | 55 ++- cli/tools/registry/pm/update.rs | 443 ++++++++++++++++-- .../deno_json/update_latest/deno.lock.out | 6 +- .../update/deno_json/update_latest/update.out | 2 +- 5 files changed, 439 insertions(+), 71 deletions(-) diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index a771c0901f62d9..c8d0995afb6b3b 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -856,7 +856,7 @@ async fn npm_install_after_modification( flags: Arc, // explicitly provided to prevent redownloading jsr_resolver: Option>, -) -> Result<(), AnyError> { +) -> Result { // clear the previously cached package.json from memory before reloading it node_resolver::PackageJsonThreadLocalCache::clear(); @@ -874,7 +874,7 @@ async fn npm_install_after_modification( lockfile.write_if_changed()?; } - Ok(()) + Ok(cli_factory) } #[cfg(test)] diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index ea873bb22bd386..74334b236da57b 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -175,6 +175,9 @@ impl KeyPath { parts: parts.into_iter().collect(), } } + fn last(&self) -> Option<&KeyPart> { + self.parts.last() + } fn push(&mut self, part: KeyPart) { self.parts.push(part) } @@ -187,11 +190,13 @@ pub struct Dep { pub location: DepLocation, #[allow(dead_code)] pub id: DepId, + #[allow(dead_code)] + pub alias: Option, } impl Dep { - pub fn prefixed_req(&self) -> String { - format!("{}:{}", self.kind.scheme(), self.req) + pub fn prefixed_name(&self) -> String { + format!("{}:{}", self.kind.scheme(), self.req.name) } } @@ -383,7 +388,9 @@ fn add_deps_from_deno_json( continue; } }; - if !filter.should_include(&req, kind) { + let alias: &str = key_path.last().unwrap().as_str().trim_end_matches('/'); + let alias = (alias != req.name).then(|| alias.to_string()); + if !filter.should_include(alias.as_deref(), &req, kind) { continue; } let id = DepId(deps.len()); @@ -396,6 +403,7 @@ fn add_deps_from_deno_json( kind, req, id, + alias, }); } } @@ -415,8 +423,10 @@ fn add_deps_from_package_json( } }; match v { - deno_package_json::PackageJsonDepValue::Req(package_req) => { - if !filter.should_include(&package_req, DepKind::Npm) { + deno_package_json::PackageJsonDepValue::Req(req) => { + let alias = k.as_str(); + let alias = (alias != req.name).then(|| alias.to_string()); + if !filter.should_include(alias.as_deref(), &req, DepKind::Npm) { continue; } let id = DepId(deps.len()); @@ -427,7 +437,8 @@ fn add_deps_from_package_json( package_json.clone(), KeyPath::from_parts([package_dep_kind.into(), k.into()]), ), - req: package_req, + req, + alias, }) } deno_package_json::PackageJsonDepValue::Workspace(_) => continue, @@ -450,7 +461,7 @@ fn deps_from_workspace( Ok(deps) } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct DepId(usize); #[derive(Debug, Clone)] @@ -461,6 +472,7 @@ pub enum Change { pub trait DepFilter: Copy { fn should_include( &mut self, + alias: Option<&str>, package_req: &PackageReq, dep_kind: DepKind, ) -> bool; @@ -468,14 +480,15 @@ pub trait DepFilter: Copy { impl DepFilter for T where - T: FnMut(&PackageReq, DepKind) -> bool + Copy, + T: FnMut(Option<&str>, &PackageReq, DepKind) -> bool + Copy, { fn should_include<'a>( &mut self, + alias: Option<&'a str>, package_req: &'a PackageReq, dep_kind: DepKind, ) -> bool { - (*self)(package_req, dep_kind) + (*self)(alias, package_req, dep_kind) } } @@ -494,8 +507,9 @@ pub struct DepManager { dependencies_resolved: AtomicBool, module_load_preparer: Arc, - jsr_fetch_resolver: Arc, - npm_fetch_resolver: Arc, + // TODO(nathanwhit): probably shouldn't be pub + pub(crate) jsr_fetch_resolver: Arc, + pub(crate) npm_fetch_resolver: Arc, npm_resolver: Arc, permissions_container: PermissionsContainer, main_module_graph_container: Arc, @@ -513,6 +527,11 @@ pub struct DepManagerArgs { } impl DepManager { + pub fn reloaded_after_modification(self, args: DepManagerArgs) -> Self { + let mut new = Self::with_deps_args(self.deps, args); + new.latest_versions = self.latest_versions; + new + } fn with_deps_args(deps: Vec, args: DepManagerArgs) -> Self { let DepManagerArgs { module_load_preparer, @@ -634,8 +653,6 @@ impl DepManager { } } - eprintln!("roots len: {}", roots.len()); - while let Some(info_future) = info_futures.next().await { if let Some((specifier, info)) = info_future { let exports = info.exports(); @@ -664,6 +681,10 @@ impl DepManager { Ok(()) } + pub fn resolved_version(&self, id: DepId) -> Option<&PackageNv> { + self.resolved_versions[id.0].as_ref() + } + pub async fn resolve_current_versions(&mut self) -> Result<(), AnyError> { self.run_dependency_resolution().await?; @@ -694,6 +715,9 @@ impl DepManager { async fn load_latest_versions( &self, ) -> Result, AnyError> { + if self.latest_versions.len() == self.deps.len() { + return Ok(self.latest_versions.clone()); + } let latest_tag_req = deno_semver::VersionReq::from_raw_text_and_inner( "latest".into(), deno_semver::RangeSetOrTag::Tag("latest".into()), @@ -785,7 +809,7 @@ impl DepManager { pub fn update_dep(&mut self, dep_id: DepId, new_version_req: VersionReq) { self .pending_changes - .push(Change::Update(dep_id, new_version_req)) + .push(Change::Update(dep_id, new_version_req)); } pub async fn commit_changes(&mut self) -> Result<(), AnyError> { @@ -795,7 +819,8 @@ impl DepManager { match change { Change::Update(dep_id, version_req) => { // TODO: move most of this to ConfigUpdater - let dep = &self.deps[dep_id.0]; + let dep = &mut self.deps[dep_id.0]; + dep.req.version_req = version_req.clone(); match &dep.location { DepLocation::DenoJson(arc, key_path, import_map_kind) => { if matches!(import_map_kind, ImportMapKind::Outline) { diff --git a/cli/tools/registry/pm/update.rs b/cli/tools/registry/pm/update.rs index ca79c04fa08d97..6405f39486b733 100644 --- a/cli/tools/registry/pm/update.rs +++ b/cli/tools/registry/pm/update.rs @@ -2,11 +2,13 @@ use std::collections::HashSet; use std::sync::Arc; use deno_core::error::AnyError; +use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use deno_semver::VersionReq; use deno_terminal::colors; use crate::args::CacheSetting; +use crate::args::CliOptions; use crate::args::Flags; use crate::args::UpdateFlags; use crate::factory::CliFactory; @@ -15,8 +17,10 @@ use crate::jsr::JsrFetchResolver; use crate::npm::NpmFetchResolver; use crate::tools::registry::pm::deps::DepKind; +use super::deps::Dep; use super::deps::DepManager; use super::deps::DepManagerArgs; +use super::deps::PackageLatestVersion; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] struct OutdatedPackage { @@ -164,45 +168,47 @@ pub async fn update( ); file_fetcher.set_download_log_level(log::Level::Trace); let file_fetcher = Arc::new(file_fetcher); - let npm_resolver = Arc::new(NpmFetchResolver::new( + let npm_fetch_resolver = Arc::new(NpmFetchResolver::new( file_fetcher.clone(), cli_options.npmrc().clone(), )); - let jsr_resolver = Arc::new(JsrFetchResolver::new(file_fetcher.clone())); + let jsr_fetch_resolver = + Arc::new(JsrFetchResolver::new(file_fetcher.clone())); - let args = DepManagerArgs { - module_load_preparer: factory.module_load_preparer().await?.clone(), - jsr_fetch_resolver: jsr_resolver.clone(), - npm_fetch_resolver: npm_resolver, - npm_resolver: factory.npm_resolver().await?.clone(), - permissions_container: factory.root_permissions_container()?.clone(), - main_module_graph_container: factory - .main_module_graph_container() - .await? - .clone(), - lockfile: cli_options.maybe_lockfile().cloned(), + let args = dep_manager_args( + &factory, + &cli_options, + npm_fetch_resolver.clone(), + jsr_fetch_resolver.clone(), + ) + .await?; + + let filter_set = filter::FilterSet::from_filter_strings( + update_flags.filters.iter().map(|s| s.as_str()), + )?; + + let filter_fn = |alias: Option<&str>, req: &PackageReq, _: DepKind| { + if filter_set.is_empty() { + return true; + } + let name = alias.unwrap_or(&req.name); + filter_set.matches(name) }; let mut deps = if update_flags.recursive { - super::deps::DepManager::from_workspace( - workspace, - |_: &PackageReq, _: DepKind| true, - args, - )? + super::deps::DepManager::from_workspace(workspace, filter_fn, args)? } else { super::deps::DepManager::from_workspace_dir( &cli_options.start_dir, - |_: &PackageReq, _: DepKind| true, + filter_fn, args, )? }; - // deps.resolve_versions().await?; - // deps.fetch_latest_versions().await?; deps.resolve_versions().await?; match update_flags.kind { crate::args::UpdateKind::Update { latest } => { - do_update(&mut deps, latest, jsr_resolver, flags).await?; + do_update(deps, latest, &filter_set, flags).await?; } crate::args::UpdateKind::PrintOutdated { compatible } => { outdated(&mut deps, compatible).await?; @@ -212,52 +218,389 @@ pub async fn update( Ok(()) } +fn choose_new_version_req( + dep: &Dep, + resolved: Option<&PackageNv>, + latest_versions: &PackageLatestVersion, + update_to_latest: bool, + filter_set: &filter::FilterSet, +) -> Option { + let explicit_version_req = if let Some(version_req) = filter_set + .matching_filter(&dep.prefixed_name()) + .version_spec() + .cloned() + { + Some(version_req) + } else { + None + }; + + if let Some(version_req) = explicit_version_req { + if let Some(resolved) = resolved { + // todo(nathanwhit): handle tag + if version_req.tag().is_none() && version_req.matches(&resolved.version) { + return None; + } + } + Some(version_req) + } else { + let preferred = if update_to_latest { + latest_versions.latest.as_ref()? + } else { + latest_versions.semver_compatible.as_ref()? + }; + if preferred.version <= resolved?.version { + return None; + } + Some( + VersionReq::parse_from_specifier( + format!("^{}", preferred.version).as_str(), + ) + .unwrap(), + ) + } +} + async fn do_update( - deps: &mut DepManager, + mut deps: DepManager, update_to_latest: bool, - jsr_resolver: Arc, + filter_set: &filter::FilterSet, flags: Arc, ) -> Result<(), AnyError> { - let mut updated = HashSet::new(); + let mut updated = Vec::new(); + for (dep_id, resolved, latest_versions) in deps .deps_with_resolved_latest_versions() .into_iter() .collect::>() { - let latest = { - let preferred = if update_to_latest { - latest_versions.latest - } else { - latest_versions.semver_compatible - }; - if let Some(v) = preferred { - v - } else { - continue; - } - }; - let Some(resolved) = resolved else { continue }; - if latest.version <= resolved.version { - continue; - } - let new_req = - VersionReq::parse_from_specifier(format!("^{}", latest.version).as_str()) - .unwrap(); let dep = deps.get_dep(dep_id); + let new_version_req = choose_new_version_req( + dep, + resolved.as_ref(), + &latest_versions, + update_to_latest, + filter_set, + ); + let Some(new_version_req) = new_version_req else { + continue; + }; - updated.insert((dep.prefixed_req(), latest.version)); - deps.update_dep(dep_id, new_req); + updated.push((dep_id, new_version_req.clone())); + + deps.update_dep(dep_id, new_version_req); } deps.commit_changes().await?; if !updated.is_empty() { - super::npm_install_after_modification(flags, Some(jsr_resolver)).await?; - log::info!("Updated {} dependencies:", updated.len()); - for (req, new_version) in updated { - log::info!("{} -> {}", req, new_version); + let factory = super::npm_install_after_modification( + flags.clone(), + Some(deps.jsr_fetch_resolver.clone()), + ) + .await?; + + let mut updated_to_versions = HashSet::new(); + let cli_options = factory.cli_options()?; + let args = dep_manager_args( + &factory, + cli_options, + deps.npm_fetch_resolver.clone(), + deps.jsr_fetch_resolver.clone(), + ) + .await?; + + let mut deps = deps.reloaded_after_modification(args); + deps.resolve_current_versions().await?; + for (dep_id, new_version_req) in updated { + let prefixed_name = deps.get_dep(dep_id).prefixed_name(); + if let Some(nv) = deps.resolved_version(dep_id) { + updated_to_versions.insert((prefixed_name, nv.version.clone())); + } else { + log::warn!( + "Failed to resolve version for new version requirement: {} -> {}", + prefixed_name, + new_version_req + ); + } + } + + log::info!( + "Updated {} dependenc{}:", + updated_to_versions.len(), + if updated_to_versions.len() == 1 { + "y" + } else { + "ies" + } + ); + for (prefixed_name, new_version) in updated_to_versions { + log::info!("• {} -> {}", prefixed_name, new_version); } + } else { + log::info!( + "All {}dependencies are up to date.", + if filter_set.is_empty() { + "" + } else { + "matching " + } + ); } Ok(()) } + +async fn dep_manager_args( + factory: &CliFactory, + cli_options: &CliOptions, + npm_fetch_resolver: Arc, + jsr_fetch_resolver: Arc, +) -> Result { + Ok(DepManagerArgs { + module_load_preparer: factory.module_load_preparer().await?.clone(), + jsr_fetch_resolver, + npm_fetch_resolver, + npm_resolver: factory.npm_resolver().await?.clone(), + permissions_container: factory.root_permissions_container()?.clone(), + main_module_graph_container: factory + .main_module_graph_container() + .await? + .clone(), + lockfile: cli_options.maybe_lockfile().cloned(), + }) +} + +mod filter { + use deno_core::anyhow::anyhow; + use deno_core::anyhow::Context; + use deno_core::error::AnyError; + use deno_semver::VersionReq; + + enum FilterKind { + Exclude, + Include, + } + pub struct Filter { + kind: FilterKind, + regex: regex::Regex, + version_spec: Option, + } + + fn pattern_to_regex(pattern: &str) -> Result { + let escaped = regex::escape(pattern); + let unescaped_star = escaped.replace(r"\*", ".*"); + Ok(regex::Regex::new(&format!("^{}$", unescaped_star))?) + } + + impl Filter { + pub fn version_spec(&self) -> Option<&VersionReq> { + self.version_spec.as_ref() + } + pub fn from_str(input: &str) -> Result { + let (kind, first_idx) = if input.starts_with('!') { + (FilterKind::Exclude, 1) + } else { + (FilterKind::Include, 0) + }; + let s = &input[first_idx..]; + let (pattern, version_spec) = if s.starts_with('@') { + if let Some(idx) = s[1..].find('@') { + let (pattern, version_spec) = s.split_at(idx + 1); + ( + pattern, + Some( + VersionReq::parse_from_specifier( + version_spec.trim_start_matches('@'), + ) + .with_context(|| format!("Invalid filter \"{input}\""))?, + ), + ) + } else { + (s, None) + } + } else { + let mut parts = s.split('@'); + let Some(pattern) = parts.next() else { + return Err(anyhow!("Invalid filter \"{input}\"")); + }; + ( + pattern, + parts + .next() + .map(VersionReq::parse_from_specifier) + .transpose() + .with_context(|| format!("Invalid filter \"{input}\""))?, + ) + }; + + Ok(Filter { + kind, + regex: pattern_to_regex(pattern) + .with_context(|| format!("Invalid filter \"{input}\""))?, + version_spec, + }) + } + + pub fn matches(&self, name: &str) -> bool { + self.regex.is_match(name) + } + } + + pub struct FilterSet { + filters: Vec, + has_exclude: bool, + has_include: bool, + } + impl FilterSet { + pub fn from_filter_strings<'a>( + filter_strings: impl IntoIterator, + ) -> Result { + let filters = filter_strings + .into_iter() + .map(|s| Filter::from_str(s)) + .collect::, _>>()?; + let has_exclude = filters + .iter() + .any(|f| matches!(f.kind, FilterKind::Exclude)); + let has_include = filters + .iter() + .any(|f| matches!(f.kind, FilterKind::Include)); + Ok(FilterSet { + filters, + has_exclude, + has_include, + }) + } + + pub fn is_empty(&self) -> bool { + self.filters.is_empty() + } + + pub fn matches(&self, name: &str) -> bool { + self.matching_filter(name).is_included() + } + + pub fn matching_filter(&self, name: &str) -> MatchResult<'_> { + if self.filters.is_empty() { + return MatchResult::Included; + } + let mut matched = None; + for filter in &self.filters { + match filter.kind { + FilterKind::Include => { + if matched.is_none() && filter.matches(name) { + matched = Some(filter); + } + } + FilterKind::Exclude => { + if filter.matches(name) { + return MatchResult::Excluded; + } + } + } + } + if let Some(filter) = matched { + MatchResult::Matches(filter) + } else if self.has_exclude && !self.has_include { + MatchResult::Included + } else { + MatchResult::Excluded + } + } + } + + pub enum MatchResult<'a> { + Matches(&'a Filter), + Included, + Excluded, + } + + impl MatchResult<'_> { + pub fn version_spec(&self) -> Option<&VersionReq> { + match self { + MatchResult::Matches(filter) => filter.version_spec(), + _ => None, + } + } + pub fn is_included(&self) -> bool { + matches!(self, MatchResult::Included | MatchResult::Matches(_)) + } + } + + #[cfg(test)] + mod test { + fn matches_filters<'a, 'b>( + filters: impl IntoIterator, + name: &str, + ) -> bool { + let filters = super::FilterSet::from_filter_strings(filters).unwrap(); + filters.matches(name) + } + + fn version_spec(s: &str) -> deno_semver::VersionReq { + deno_semver::VersionReq::parse_from_specifier(s).unwrap() + } + + #[test] + fn basic_glob() { + assert!(matches_filters(["foo*"], "foo")); + assert!(matches_filters(["foo*"], "foobar")); + assert!(!matches_filters(["foo*"], "barfoo")); + + assert!(matches_filters(["*foo"], "foo")); + assert!(matches_filters(["*foo"], "barfoo")); + assert!(!matches_filters(["*foo"], "foobar")); + + assert!(matches_filters(["@scope/foo*"], "@scope/foobar")); + } + + #[test] + fn basic_glob_with_version() { + assert!(matches_filters(["foo*@1"], "foo",)); + assert!(matches_filters(["foo*@1"], "foobar",)); + assert!(matches_filters(["foo*@1"], "foo-bar",)); + assert!(!matches_filters(["foo*@1"], "barfoo",)); + assert!(matches_filters(["@scope/*@1"], "@scope/foo")); + } + + #[test] + fn glob_exclude() { + assert!(!matches_filters(["!foo*"], "foo")); + assert!(!matches_filters(["!foo*"], "foobar")); + assert!(matches_filters(["!foo*"], "barfoo")); + + assert!(!matches_filters(["!*foo"], "foo")); + assert!(!matches_filters(["!*foo"], "barfoo")); + assert!(matches_filters(["!*foo"], "foobar")); + + assert!(!matches_filters(["!@scope/foo*"], "@scope/foobar")); + } + + #[test] + fn multiple_globs() { + assert!(matches_filters(["foo*", "bar*"], "foo")); + assert!(matches_filters(["foo*", "bar*"], "bar")); + assert!(!matches_filters(["foo*", "bar*"], "baz")); + + assert!(matches_filters(["foo*", "!bar*"], "foo")); + assert!(!matches_filters(["foo*", "!bar*"], "bar")); + assert!(matches_filters(["foo*", "!bar*"], "foobar")); + assert!(!matches_filters(["foo*", "!*bar"], "foobar")); + assert!(!matches_filters(["foo*", "!*bar"], "baz")); + + let filters = + super::FilterSet::from_filter_strings(["foo*@1", "bar*@2"]).unwrap(); + + assert_eq!( + filters.matching_filter("foo").version_spec().cloned(), + Some(version_spec("1")) + ); + + assert_eq!( + filters.matching_filter("bar").version_spec().cloned(), + Some(version_spec("2")) + ); + } + } +} diff --git a/tests/specs/update/deno_json/update_latest/deno.lock.out b/tests/specs/update/deno_json/update_latest/deno.lock.out index 0a38c948735eab..3814b281b40b26 100644 --- a/tests/specs/update/deno_json/update_latest/deno.lock.out +++ b/tests/specs/update/deno_json/update_latest/deno.lock.out @@ -6,7 +6,7 @@ "jsr:@denotest/subtract@1": "1.0.0", "npm:@denotest/bin@1": "1.0.0", "npm:@denotest/breaking-change-between-versions@2": "2.0.0", - "npm:@denotest/has-patch-versions@0.2": "0.1.1" + "npm:@denotest/has-patch-versions@0.2": "0.2.0" }, "jsr": { "@denotest/add@1.0.0": { @@ -26,8 +26,8 @@ "@denotest/breaking-change-between-versions@2.0.0": { "integrity": "sha512-x7q4LVdjFsu6pMsxIs1LPed6hix5p4RcCybssPzRZXOD2VCNBWbNCEDW9JRWiVFzT7+Md0n9czqMMIx+VUA5qA==" }, - "@denotest/has-patch-versions@0.1.1": { - "integrity": "sha512-BcH2/Hbu6RCkhLpzHTgRM7Vdxhb2bq7WVovWOb56eTe1/2sNDEkpJ5O3gfHMU0WF1MG7Fn+boTgXpEAMfIrA5Q==" + "@denotest/has-patch-versions@0.2.0": { + "integrity": "sha512-r+5ZSpZHBgADr7Go++aGDMy7ogVtolEYT9b7Yqkp85Ms+E2TfImvXf0rGY1dTzU8qChfoHihLOrxcaTvdZ+r8A==" } }, "workspace": { diff --git a/tests/specs/update/deno_json/update_latest/update.out b/tests/specs/update/deno_json/update_latest/update.out index bd1279bf6a3147..95e0c1bd5f0697 100644 --- a/tests/specs/update/deno_json/update_latest/update.out +++ b/tests/specs/update/deno_json/update_latest/update.out @@ -3,7 +3,7 @@ Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts Download http://127.0.0.1:4250/@denotest/subtract/1.0.0/mod.ts Download http://127.0.0.1:4250/@denotest/multiple-exports/1.0.0_meta.json Download http://127.0.0.1:4250/@denotest/multiple-exports/1.0.0/data.json -Download http://localhost:4260/@denotest/has-patch-versions/0.1.1.tgz +Download http://localhost:4260/@denotest/has-patch-versions/0.2.0.tgz Download http://localhost:4260/@denotest/bin/1.0.0.tgz Download http://localhost:4260/@denotest/breaking-change-between-versions/2.0.0.tgz [UNORDERED_END] From d9d8274c29a96685de670e37e04d66960291121b Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 13:34:23 -0800 Subject: [PATCH 08/26] rename command, update tests, appease clippy --- cli/args/flags.rs | 143 +++++++++++------- cli/args/mod.rs | 2 +- cli/main.rs | 4 +- cli/tools/registry/mod.rs | 2 +- cli/tools/registry/pm.rs | 12 +- cli/tools/registry/pm/deps.rs | 21 ++- .../registry/pm/{update.rs => outdated.rs} | 97 ++++++------ tests/specs/update/deno_json/__test__.jsonc | 8 +- tests/specs/update/deno_json/outdated.out | 12 +- .../update/deno_json/outdated_compatible.out | 4 +- .../deno_json/update_compatible/update.out | 4 +- .../update/deno_json/update_latest/update.out | 12 +- .../specs/update/package_json/__test__.jsonc | 8 +- tests/specs/update/package_json/outdated.out | 6 +- .../package_json/outdated_compatible.out | 2 +- .../package_json/update_compatible/update.out | 4 +- .../package_json/update_latest/update.out | 6 +- 17 files changed, 190 insertions(+), 157 deletions(-) rename cli/tools/registry/pm/{update.rs => outdated.rs} (89%) diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 054c8d6319dd33..41c61e8970e576 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -463,7 +463,7 @@ pub enum DenoSubcommand { Serve(ServeFlags), Task(TaskFlags), Test(TestFlags), - Update(UpdateFlags), + Outdated(OutdatedFlags), Types, Upgrade(UpgradeFlags), Vendor, @@ -472,16 +472,16 @@ pub enum DenoSubcommand { } #[derive(Clone, Debug, PartialEq, Eq)] -pub enum UpdateKind { +pub enum OutdatedKind { Update { latest: bool }, PrintOutdated { compatible: bool }, } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct UpdateFlags { +pub struct OutdatedFlags { pub filters: Vec, pub recursive: bool, - pub kind: UpdateKind, + pub kind: OutdatedKind, } impl DenoSubcommand { @@ -1214,6 +1214,7 @@ static DENO_HELP: &str = cstr!( deno add jsr:@std/assert | deno add npm:express install Installs dependencies either in the local project or globally to a bin directory uninstall Uninstalls a dependency or an executable script in the installation root's bin directory + outdated Find and update outdated dependencies remove Remove dependencies from the configuration file Tooling: @@ -1396,7 +1397,7 @@ pub fn flags_from_vec(args: Vec) -> clap::error::Result { "jupyter" => jupyter_parse(&mut flags, &mut m), "lint" => lint_parse(&mut flags, &mut m)?, "lsp" => lsp_parse(&mut flags, &mut m), - "update" => update_parse(&mut flags, &mut m)?, + "outdated" => outdated_parse(&mut flags, &mut m)?, "repl" => repl_parse(&mut flags, &mut m)?, "run" => run_parse(&mut flags, &mut m, app, false)?, "serve" => serve_parse(&mut flags, &mut m, app)?, @@ -1639,7 +1640,7 @@ pub fn clap_root() -> Command { .subcommand(json_reference_subcommand()) .subcommand(jupyter_subcommand()) .subcommand(uninstall_subcommand()) - .subcommand(update_subcommand()) + .subcommand(outdated_subcommand()) .subcommand(lsp_subcommand()) .subcommand(lint_subcommand()) .subcommand(publish_subcommand()) @@ -2630,48 +2631,80 @@ fn jupyter_subcommand() -> Command { .conflicts_with("install")) } -fn update_subcommand() -> Command { - command("update", "Update dependencies", UnstableArgsConfig::None).defer( - |cmd| { - cmd - .arg( - Arg::new("filters") - .num_args(0..) - .action(ArgAction::Append) - .help("List of filters used for updating outdated packages"), - ) - .arg( - Arg::new("latest") - .long("latest") - .action(ArgAction::SetTrue) - .help( - "Update to the latest version, regardless of semver constraints", - ), - ) - .arg( - Arg::new("outdated") - .long("outdated") - .action(ArgAction::SetTrue) - .conflicts_with("latest") - .help("print outdated package versions"), - ) - .arg( - Arg::new("compatible") - .long("compatible") - .action(ArgAction::SetTrue) - .help("only output versions that satisfy semver requirements") - .conflicts_with("latest") - .requires("outdated"), - ) - .arg( - Arg::new("recursive") - .long("recursive") - .short('r') - .action(ArgAction::SetTrue) - .help("include all workspace members"), - ) - }, +fn outdated_subcommand() -> Command { + command( + "outdated", + cstr!("Find and update outdated dependencies. +By default, outdated dependencies are only displayed. + +Display outdated dependencies: + deno outdated + deno outdated --compatible + +Update dependencies: + deno outdated --update + deno outdated --update --latest + deno outdated --update + +Filters can be used to select which packages to act on. Filters can include wildcards (*) to match multiple packages. + deno outdated --update --latest \"@std/*\" + deno outdated --update --latest \"react*\" +Note that filters act on their aliases configured in deno.json / package.json, not the actual package names: + Given \"foobar\": \"npm:react@17.0.0\" in deno.json or package.json, the filter \"foobar\" would update npm:react to + the latest version. + deno outdated --update --latest foobar +Filters can be combined, and negative filters can be used to exclude results: + deno outdated --update --latest \"@std/*\" \"!@std/fmt*\" + +Specific version requirements to update to can be specified: + deno outdated --update @std/fmt@^1.0.2 +"), + UnstableArgsConfig::None, ) + .defer(|cmd| { + cmd + .arg( + Arg::new("filters") + .num_args(0..) + .action(ArgAction::Append) + .help(concat!("Filters selecting which packages to act on. Can include wildcards (*) to match multiple packages. ", + "If a version requirement is specified, the matching packages will be updated to the given requirement."), + ) + ) + .arg( + Arg::new("latest") + .long("latest") + .action(ArgAction::SetTrue) + .help( + "Update to the latest version, regardless of semver constraints", + ) + .requires("update") + .conflicts_with("compatible"), + ) + .arg( + Arg::new("update") + .long("update") + .aliases(["up", "upgrade"]) + .short('u') + .action(ArgAction::SetTrue) + .conflicts_with("compatible") + .help("Update dependency versions"), + ) + .arg( + Arg::new("compatible") + .long("compatible") + .action(ArgAction::SetTrue) + .help("Only output versions that satisfy semver requirements") + .conflicts_with("update"), + ) + .arg( + Arg::new("recursive") + .long("recursive") + .short('r') + .action(ArgAction::SetTrue) + .help("include all workspace members"), + ) + }) } fn uninstall_subcommand() -> Command { @@ -4389,7 +4422,7 @@ fn remove_parse(flags: &mut Flags, matches: &mut ArgMatches) { }); } -fn update_parse( +fn outdated_parse( flags: &mut Flags, matches: &mut ArgMatches, ) -> clap::error::Result<()> { @@ -4398,15 +4431,15 @@ fn update_parse( None => vec![], }; let recursive = matches.get_flag("recursive"); - let outdated = matches.get_flag("outdated"); - let kind = if outdated { - let compatible = matches.get_flag("compatible"); - UpdateKind::PrintOutdated { compatible } - } else { + let update = matches.get_flag("update"); + let kind = if update { let latest = matches.get_flag("latest"); - UpdateKind::Update { latest } + OutdatedKind::Update { latest } + } else { + let compatible = matches.get_flag("compatible"); + OutdatedKind::PrintOutdated { compatible } }; - flags.subcommand = DenoSubcommand::Update(UpdateFlags { + flags.subcommand = DenoSubcommand::Outdated(OutdatedFlags { filters, recursive, kind, diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 65059d56d94945..d5484a0816b352 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -1622,7 +1622,7 @@ impl CliOptions { DenoSubcommand::Install(_) | DenoSubcommand::Add(_) | DenoSubcommand::Remove(_) - | DenoSubcommand::Update(_) + | DenoSubcommand::Outdated(_) ) { // For `deno install/add/remove` we want to force the managed resolver so it can set up `node_modules/` directory. return false; diff --git a/cli/main.rs b/cli/main.rs index 0ffa59c9a0bf05..2bf1cc298d64cb 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -188,9 +188,9 @@ async fn run_subcommand(flags: Arc) -> Result { tools::lint::lint(flags, lint_flags).await } }), - DenoSubcommand::Update(update_flags) => { + DenoSubcommand::Outdated(update_flags) => { spawn_subcommand(async move { - tools::registry::update(flags, update_flags).await + tools::registry::outdated(flags, update_flags).await }) } DenoSubcommand::Repl(repl_flags) => { diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs index ea67f13736aef2..09a22d8a9d7d12 100644 --- a/cli/tools/registry/mod.rs +++ b/cli/tools/registry/mod.rs @@ -68,8 +68,8 @@ use auth::get_auth_method; use auth::AuthMethod; pub use pm::add; pub use pm::cache_top_level_deps; +pub use pm::outdated; pub use pm::remove; -pub use pm::update; pub use pm::AddCommandName; pub use pm::AddRmPackageReq; use publish_order::PublishOrderGraph; diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index c8d0995afb6b3b..5718cd3ec116fc 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -34,10 +34,10 @@ use crate::npm::NpmFetchResolver; mod cache_deps; pub(crate) mod deps; -mod update; +mod outdated; pub use cache_deps::cache_top_level_deps; -pub use update::update; +pub use outdated::outdated; #[derive(Debug, Copy, Clone, Hash)] enum ConfigKind { @@ -101,9 +101,7 @@ impl ConfigUpdater { for (i, part) in key_path.parts.iter().enumerate() { let s = part.as_str(); if i < key_path.parts.len().saturating_sub(1) { - let Some(object) = current_node.object_value(s) else { - return None; - }; + let object = current_node.object_value(s)?; current_node = object; } else { // last part @@ -405,10 +403,6 @@ pub async fn add( (Some(npm), Some(deno)) => { let npm_distance = path_distance(&npm.path, &start_dir); let deno_distance = path_distance(&deno.path, &start_dir); - eprintln!( - "npm: {:?}, deno: {:?}, start: {:?}; distances: {} {}", - &npm.path, &deno.path, &start_dir, npm_distance, deno_distance - ); npm_distance <= deno_distance } (Some(_), None) => true, diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index 74334b236da57b..d1110972f738ce 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -1,3 +1,5 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + use std::borrow::Cow; use std::collections::HashMap; use std::sync::atomic::AtomicBool; @@ -195,14 +197,17 @@ pub struct Dep { } impl Dep { + pub fn prefixed_req(&self) -> String { + format!("{}:{}", self.kind.scheme(), self.req) + } pub fn prefixed_name(&self) -> String { format!("{}:{}", self.kind.scheme(), self.req.name) } } -fn import_map_entries<'a>( - import_map: &'a ImportMap, -) -> impl Iterator)> { +fn import_map_entries( + import_map: &ImportMap, +) -> impl Iterator)> { import_map .imports() .entries() @@ -738,7 +743,7 @@ impl DepManager { }; let _permit = sema.acquire().await; let semver_compatible = - self.npm_fetch_resolver.req_to_nv(&semver_req).await; + self.npm_fetch_resolver.req_to_nv(semver_req).await; let latest = self.npm_fetch_resolver.req_to_nv(&latest_req).await; PackageLatestVersion { latest, @@ -756,7 +761,7 @@ impl DepManager { }; let _permit = sema.acquire().await; let semver_compatible = - self.jsr_fetch_resolver.req_to_nv(&semver_req).await; + self.jsr_fetch_resolver.req_to_nv(semver_req).await; let latest = self.jsr_fetch_resolver.req_to_nv(&latest_req).await; PackageLatestVersion { latest, @@ -812,7 +817,7 @@ impl DepManager { .push(Change::Update(dep_id, new_version_req)); } - pub async fn commit_changes(&mut self) -> Result<(), AnyError> { + pub fn commit_changes(&mut self) -> Result<(), AnyError> { let changes = std::mem::take(&mut self.pending_changes); let mut config_updaters = HashMap::new(); for change in changes { @@ -830,7 +835,7 @@ impl DepManager { let updater = get_or_create_updater(&mut config_updaters, &dep.location)?; - let Some(property) = updater.get_property_for_mutation(&key_path) + let Some(property) = updater.get_property_for_mutation(key_path) else { log::warn!( "failed to find property at path {key_path:?} for file {}", @@ -870,7 +875,7 @@ impl DepManager { DepLocation::PackageJson(arc, key_path) => { let updater = get_or_create_updater(&mut config_updaters, &dep.location)?; - let Some(property) = updater.get_property_for_mutation(&key_path) + let Some(property) = updater.get_property_for_mutation(key_path) else { log::warn!( "failed to find property at path {key_path:?} for file {}", diff --git a/cli/tools/registry/pm/update.rs b/cli/tools/registry/pm/outdated.rs similarity index 89% rename from cli/tools/registry/pm/update.rs rename to cli/tools/registry/pm/outdated.rs index 6405f39486b733..8d3ce414cc7e51 100644 --- a/cli/tools/registry/pm/update.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -1,3 +1,5 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + use std::collections::HashSet; use std::sync::Arc; @@ -10,7 +12,7 @@ use deno_terminal::colors; use crate::args::CacheSetting; use crate::args::CliOptions; use crate::args::Flags; -use crate::args::UpdateFlags; +use crate::args::OutdatedFlags; use crate::factory::CliFactory; use crate::file_fetcher::FileFetcher; use crate::jsr::JsrFetchResolver; @@ -31,6 +33,7 @@ struct OutdatedPackage { name: String, } +#[allow(clippy::print_stdout)] fn print_outdated_table(packages: &[OutdatedPackage]) { const HEADINGS: &[&str] = &["Package", "Current", "Update", "Latest"]; @@ -97,7 +100,7 @@ fn print_outdated_table(packages: &[OutdatedPackage]) { println!("└{package_fill}┴{current_fill}┴{update_fill}┴{latest_fill}┘",); } -async fn outdated( +fn print_outdated( deps: &mut DepManager, compatible: bool, ) -> Result<(), AnyError> { @@ -149,9 +152,9 @@ async fn outdated( Ok(()) } -pub async fn update( +pub async fn outdated( flags: Arc, - update_flags: UpdateFlags, + update_flags: OutdatedFlags, ) -> Result<(), AnyError> { let factory = CliFactory::from_flags(flags.clone()); let cli_options = factory.cli_options()?; @@ -177,7 +180,7 @@ pub async fn update( let args = dep_manager_args( &factory, - &cli_options, + cli_options, npm_fetch_resolver.clone(), jsr_fetch_resolver.clone(), ) @@ -207,11 +210,11 @@ pub async fn update( deps.resolve_versions().await?; match update_flags.kind { - crate::args::UpdateKind::Update { latest } => { - do_update(deps, latest, &filter_set, flags).await?; + crate::args::OutdatedKind::Update { latest } => { + update(deps, latest, &filter_set, flags).await?; } - crate::args::UpdateKind::PrintOutdated { compatible } => { - outdated(&mut deps, compatible).await?; + crate::args::OutdatedKind::PrintOutdated { compatible } => { + print_outdated(&mut deps, compatible)?; } } @@ -225,15 +228,10 @@ fn choose_new_version_req( update_to_latest: bool, filter_set: &filter::FilterSet, ) -> Option { - let explicit_version_req = if let Some(version_req) = filter_set + let explicit_version_req = filter_set .matching_filter(&dep.prefixed_name()) .version_spec() - .cloned() - { - Some(version_req) - } else { - None - }; + .cloned(); if let Some(version_req) = explicit_version_req { if let Some(resolved) = resolved { @@ -261,7 +259,7 @@ fn choose_new_version_req( } } -async fn do_update( +async fn update( mut deps: DepManager, update_to_latest: bool, filter_set: &filter::FilterSet, @@ -286,12 +284,12 @@ async fn do_update( continue; }; - updated.push((dep_id, new_version_req.clone())); + updated.push((dep_id, dep.prefixed_req(), new_version_req.clone())); deps.update_dep(dep_id, new_version_req); } - deps.commit_changes().await?; + deps.commit_changes()?; if !updated.is_empty() { let factory = super::npm_install_after_modification( @@ -312,14 +310,13 @@ async fn do_update( let mut deps = deps.reloaded_after_modification(args); deps.resolve_current_versions().await?; - for (dep_id, new_version_req) in updated { - let prefixed_name = deps.get_dep(dep_id).prefixed_name(); + for (dep_id, prefixed_old_req, new_version_req) in updated { if let Some(nv) = deps.resolved_version(dep_id) { - updated_to_versions.insert((prefixed_name, nv.version.clone())); + updated_to_versions.insert((prefixed_old_req, nv.version.clone())); } else { log::warn!( "Failed to resolve version for new version requirement: {} -> {}", - prefixed_name, + prefixed_old_req, new_version_req ); } @@ -334,6 +331,9 @@ async fn do_update( "ies" } ); + let mut updated_to_versions = + updated_to_versions.into_iter().collect::>(); + updated_to_versions.sort_by(|(k, _), (k2, _)| k.cmp(k2)); for (prefixed_name, new_version) in updated_to_versions { log::info!("• {} -> {}", prefixed_name, new_version); } @@ -404,35 +404,36 @@ mod filter { (FilterKind::Include, 0) }; let s = &input[first_idx..]; - let (pattern, version_spec) = if s.starts_with('@') { - if let Some(idx) = s[1..].find('@') { - let (pattern, version_spec) = s.split_at(idx + 1); + let (pattern, version_spec) = + if let Some(scope_name) = s.strip_prefix('@') { + if let Some(idx) = scope_name.find('@') { + let (pattern, version_spec) = s.split_at(idx + 1); + ( + pattern, + Some( + VersionReq::parse_from_specifier( + version_spec.trim_start_matches('@'), + ) + .with_context(|| format!("Invalid filter \"{input}\""))?, + ), + ) + } else { + (s, None) + } + } else { + let mut parts = s.split('@'); + let Some(pattern) = parts.next() else { + return Err(anyhow!("Invalid filter \"{input}\"")); + }; ( pattern, - Some( - VersionReq::parse_from_specifier( - version_spec.trim_start_matches('@'), - ) + parts + .next() + .map(VersionReq::parse_from_specifier) + .transpose() .with_context(|| format!("Invalid filter \"{input}\""))?, - ), ) - } else { - (s, None) - } - } else { - let mut parts = s.split('@'); - let Some(pattern) = parts.next() else { - return Err(anyhow!("Invalid filter \"{input}\"")); }; - ( - pattern, - parts - .next() - .map(VersionReq::parse_from_specifier) - .transpose() - .with_context(|| format!("Invalid filter \"{input}\""))?, - ) - }; Ok(Filter { kind, @@ -458,7 +459,7 @@ mod filter { ) -> Result { let filters = filter_strings .into_iter() - .map(|s| Filter::from_str(s)) + .map(Filter::from_str) .collect::, _>>()?; let has_exclude = filters .iter() diff --git a/tests/specs/update/deno_json/__test__.jsonc b/tests/specs/update/deno_json/__test__.jsonc index 22166bec34a4c9..9e5d182d044c42 100644 --- a/tests/specs/update/deno_json/__test__.jsonc +++ b/tests/specs/update/deno_json/__test__.jsonc @@ -24,7 +24,7 @@ "output": "[WILDCARD]" }, { - "args": "update --outdated", + "args": "outdated", "output": "outdated.out" } ] @@ -36,7 +36,7 @@ "output": "[WILDCARD]" }, { - "args": "update --outdated --compatible", + "args": "outdated --compatible", "output": "outdated_compatible.out" } ] @@ -48,7 +48,7 @@ "output": "[WILDCARD]" }, { - "args": "update", + "args": "outdated --update", "output": "./update_compatible/update.out" }, { @@ -68,7 +68,7 @@ "output": "[WILDCARD]" }, { - "args": "update --latest", + "args": "outdated --update --latest", "output": "update_latest/update.out" }, { diff --git a/tests/specs/update/deno_json/outdated.out b/tests/specs/update/deno_json/outdated.out index 7d40e42241cc69..f2f7c0355e2ae6 100644 --- a/tests/specs/update/deno_json/outdated.out +++ b/tests/specs/update/deno_json/outdated.out @@ -1,15 +1,15 @@ ┌────────────────────────────────────────────────┬─────────┬────────┬────────┐ │ Package │ Current │ Update │ Latest │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ jsr:@denotest/multiple-exports │ 0.2.0 │ 0.2.0 │ 1.0.0 │ +│ jsr:@denotest/multiple-exports │ 0.2.0 │ 0.2.0 │ 1.0.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ jsr:@denotest/subtract │ 0.2.0 │ 0.2.0 │ 1.0.0 │ +│ jsr:@denotest/subtract │ 0.2.0 │ 0.2.0 │ 1.0.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ +│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ +│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ npm:@denotest/bin │ 0.6.0 │ 0.6.0 │ 1.0.0 │ +│ npm:@denotest/bin │ 0.6.0 │ 0.6.0 │ 1.0.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ jsr:@denotest/add │ 0.2.0 │ 0.2.1 │ 1.0.0 │ +│ jsr:@denotest/add │ 0.2.0 │ 0.2.1 │ 1.0.0 │ └────────────────────────────────────────────────┴─────────┴────────┴────────┘ diff --git a/tests/specs/update/deno_json/outdated_compatible.out b/tests/specs/update/deno_json/outdated_compatible.out index ec7eea4815bae2..d3566140772bc4 100644 --- a/tests/specs/update/deno_json/outdated_compatible.out +++ b/tests/specs/update/deno_json/outdated_compatible.out @@ -1,7 +1,7 @@ ┌──────────────────────────────────┬─────────┬────────┬────────┐ │ Package │ Current │ Update │ Latest │ ├──────────────────────────────────┼─────────┼────────┼────────┤ -│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ +│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ ├──────────────────────────────────┼─────────┼────────┼────────┤ -│ jsr:@denotest/add │ 0.2.0 │ 0.2.1 │ 1.0.0 │ +│ jsr:@denotest/add │ 0.2.0 │ 0.2.1 │ 1.0.0 │ └──────────────────────────────────┴─────────┴────────┴────────┘ diff --git a/tests/specs/update/deno_json/update_compatible/update.out b/tests/specs/update/deno_json/update_compatible/update.out index 127cfb8c8ca139..ceac3077b49943 100644 --- a/tests/specs/update/deno_json/update_compatible/update.out +++ b/tests/specs/update/deno_json/update_compatible/update.out @@ -4,6 +4,6 @@ Download http://localhost:4260/@denotest/has-patch-versions/0.1.1.tgz [UNORDERED_END] Updated 2 dependencies: [UNORDERED_START] -jsr:@denotest/add@^0.2.0 -> 0.2.1 -npm:@denotest/has-patch-versions@^0.1.0 -> 0.1.1 +• jsr:@denotest/add@^0.2.0 -> 0.2.1 +• npm:@denotest/has-patch-versions@^0.1.0 -> 0.1.1 [UNORDERED_END] diff --git a/tests/specs/update/deno_json/update_latest/update.out b/tests/specs/update/deno_json/update_latest/update.out index 95e0c1bd5f0697..826c2f98ca2b9c 100644 --- a/tests/specs/update/deno_json/update_latest/update.out +++ b/tests/specs/update/deno_json/update_latest/update.out @@ -9,10 +9,10 @@ Download http://localhost:4260/@denotest/breaking-change-between-versions/2.0.0. [UNORDERED_END] Updated 6 dependencies: [UNORDERED_START] -jsr:@denotest/add@^0.2.0 -> 1.0.0 -jsr:@denotest/multiple-exports@0.2.0 -> 1.0.0 -jsr:@denotest/subtract@^0.2.0 -> 1.0.0 -npm:@denotest/bin@0.6.0 -> 1.0.0 -npm:@denotest/breaking-change-between-versions@1.0.0 -> 2.0.0 -npm:@denotest/has-patch-versions@^0.1.0 -> 0.2.0 +• jsr:@denotest/add@^0.2.0 -> 1.0.0 +• jsr:@denotest/multiple-exports@0.2.0 -> 1.0.0 +• jsr:@denotest/subtract@^0.2.0 -> 1.0.0 +• npm:@denotest/bin@0.6.0 -> 1.0.0 +• npm:@denotest/breaking-change-between-versions@1.0.0 -> 2.0.0 +• npm:@denotest/has-patch-versions@^0.1.0 -> 0.2.0 [UNORDERED_END] diff --git a/tests/specs/update/package_json/__test__.jsonc b/tests/specs/update/package_json/__test__.jsonc index 1d3acc0aa27b5a..1a8e56fe2cabd7 100644 --- a/tests/specs/update/package_json/__test__.jsonc +++ b/tests/specs/update/package_json/__test__.jsonc @@ -23,7 +23,7 @@ "output": "[WILDCARD]" }, { - "args": "update --outdated", + "args": "outdated", "output": "outdated.out" } ] @@ -35,7 +35,7 @@ "output": "[WILDCARD]" }, { - "args": "update --outdated --compatible", + "args": "outdated --compatible", "output": "outdated_compatible.out" } ] @@ -47,7 +47,7 @@ "output": "[WILDCARD]" }, { - "args": "update", + "args": "outdated --update", "output": "./update_compatible/update.out" }, { @@ -67,7 +67,7 @@ "output": "[WILDCARD]" }, { - "args": "update --latest", + "args": "outdated --update --latest", "output": "update_latest/update.out" }, { diff --git a/tests/specs/update/package_json/outdated.out b/tests/specs/update/package_json/outdated.out index a19863a7723c98..c2d4e9d069c239 100644 --- a/tests/specs/update/package_json/outdated.out +++ b/tests/specs/update/package_json/outdated.out @@ -1,9 +1,9 @@ ┌────────────────────────────────────────────────┬─────────┬────────┬────────┐ │ Package │ Current │ Update │ Latest │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ +│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ +│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ npm:@denotest/bin │ 0.6.0 │ 0.6.0 │ 1.0.0 │ +│ npm:@denotest/bin │ 0.6.0 │ 0.6.0 │ 1.0.0 │ └────────────────────────────────────────────────┴─────────┴────────┴────────┘ diff --git a/tests/specs/update/package_json/outdated_compatible.out b/tests/specs/update/package_json/outdated_compatible.out index b3c89b809ae046..8a682c461cbb51 100644 --- a/tests/specs/update/package_json/outdated_compatible.out +++ b/tests/specs/update/package_json/outdated_compatible.out @@ -1,5 +1,5 @@ ┌──────────────────────────────────┬─────────┬────────┬────────┐ │ Package │ Current │ Update │ Latest │ ├──────────────────────────────────┼─────────┼────────┼────────┤ -│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ +│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ └──────────────────────────────────┴─────────┴────────┴────────┘ diff --git a/tests/specs/update/package_json/update_compatible/update.out b/tests/specs/update/package_json/update_compatible/update.out index 6d8cc3b208be1c..c9ec4fb256c44c 100644 --- a/tests/specs/update/package_json/update_compatible/update.out +++ b/tests/specs/update/package_json/update_compatible/update.out @@ -1,4 +1,4 @@ Download http://localhost:4260/@denotest/has-patch-versions/0.1.1.tgz Initialize @denotest/has-patch-versions@0.1.1 -Updated 1 dependencies: -npm:@denotest/has-patch-versions@^0.1.0 -> 0.1.1 +Updated 1 dependency: +• npm:@denotest/has-patch-versions@^0.1.0 -> 0.1.1 diff --git a/tests/specs/update/package_json/update_latest/update.out b/tests/specs/update/package_json/update_latest/update.out index b0bd51d0e92b9d..c52d1edf5f443e 100644 --- a/tests/specs/update/package_json/update_latest/update.out +++ b/tests/specs/update/package_json/update_latest/update.out @@ -8,7 +8,7 @@ Initialize @denotest/bin@1.0.0 [UNORDERED_END] Updated 3 dependencies: [UNORDERED_START] -npm:@denotest/bin@^0.6.0 -> 1.0.0 -npm:@denotest/breaking-change-between-versions@1.0.0 -> 2.0.0 -npm:@denotest/has-patch-versions@^0.1.0 -> 0.2.0 +• npm:@denotest/bin@^0.6.0 -> 1.0.0 +• npm:@denotest/breaking-change-between-versions@1.0.0 -> 2.0.0 +• npm:@denotest/has-patch-versions@^0.1.0 -> 0.2.0 [UNORDERED_END] From e1f64741407bec72369ea1f0a81304249ac60ff0 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 13:51:34 -0800 Subject: [PATCH 09/26] revert unintentional changes --- cli/args/mod.rs | 2 ++ cli/npm/managed/mod.rs | 2 +- cli/npm/managed/registry.rs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cli/args/mod.rs b/cli/args/mod.rs index d5484a0816b352..89dffde7dc4886 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -984,6 +984,8 @@ impl CliOptions { CacheSetting::Only } else if !self.flags.cache_blocklist.is_empty() { CacheSetting::ReloadSome(self.flags.cache_blocklist.clone()) + } else if self.flags.reload { + CacheSetting::ReloadAll } else { CacheSetting::Use } diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index 2564a0f84da4da..88094d51414f74 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -559,7 +559,7 @@ impl ManagedCliNpmResolver { log::debug!( "All package.json deps resolvable. Skipping top level install." ); - // return Ok(false); // everything is already resolvable + return Ok(false); // everything is already resolvable } let pkg_reqs = pkg_json_remote_pkgs diff --git a/cli/npm/managed/registry.rs b/cli/npm/managed/registry.rs index 0c80899e3799e9..8f15d619b93ffb 100644 --- a/cli/npm/managed/registry.rs +++ b/cli/npm/managed/registry.rs @@ -114,7 +114,7 @@ impl CliNpmRegistryApiInner { { // attempt to load from the file cache if let Some(info) = api.load_file_cached_package_info(&name).await { - let result: Option> = Some(Arc::new(info)); + let result = Some(Arc::new(info)); return Ok(result); } } From b9d8badd954a93455fc8ad8d3f2cb1630285eea1 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 14:32:46 -0800 Subject: [PATCH 10/26] Wildcard for denotest/bin --- tests/specs/update/deno_json/deno.lock.orig.out | 2 +- tests/specs/update/deno_json/update_compatible/deno.lock.out | 2 +- tests/specs/update/deno_json/update_latest/deno.lock.out | 2 +- tests/specs/update/package_json/deno.lock.orig.out | 2 +- tests/specs/update/package_json/update_compatible/deno.lock.out | 2 +- tests/specs/update/package_json/update_latest/deno.lock.out | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/specs/update/deno_json/deno.lock.orig.out b/tests/specs/update/deno_json/deno.lock.orig.out index 238a9c8a10bd11..cf2802db989227 100644 --- a/tests/specs/update/deno_json/deno.lock.orig.out +++ b/tests/specs/update/deno_json/deno.lock.orig.out @@ -21,7 +21,7 @@ }, "npm": { "@denotest/bin@0.6.0": { - "integrity": "sha512-+xIHuFv3j/rkWWF7H6HNAPEBGUtPT9uO4rBnXXNH5NVjsrQJ5c7ZtYp+z7Hk3JnQzt2exGeJrWGvVlndtgC+yw==" + "integrity": "[WILDCARD]" }, "@denotest/breaking-change-between-versions@1.0.0": { "integrity": "sha512-EjAhryqWinStR5SeDOCN4PpO8Z9cTiY4DV9nSnoZUaOUsboEtJ/r0uhm4sBiZm3//mW7Kg/5T6dqXG6RtzLusA==" diff --git a/tests/specs/update/deno_json/update_compatible/deno.lock.out b/tests/specs/update/deno_json/update_compatible/deno.lock.out index 0263d81d53583f..421dba1ea63d53 100644 --- a/tests/specs/update/deno_json/update_compatible/deno.lock.out +++ b/tests/specs/update/deno_json/update_compatible/deno.lock.out @@ -21,7 +21,7 @@ }, "npm": { "@denotest/bin@0.6.0": { - "integrity": "sha512-+xIHuFv3j/rkWWF7H6HNAPEBGUtPT9uO4rBnXXNH5NVjsrQJ5c7ZtYp+z7Hk3JnQzt2exGeJrWGvVlndtgC+yw==" + "integrity": "[WILDCARD]" }, "@denotest/breaking-change-between-versions@1.0.0": { "integrity": "sha512-EjAhryqWinStR5SeDOCN4PpO8Z9cTiY4DV9nSnoZUaOUsboEtJ/r0uhm4sBiZm3//mW7Kg/5T6dqXG6RtzLusA==" diff --git a/tests/specs/update/deno_json/update_latest/deno.lock.out b/tests/specs/update/deno_json/update_latest/deno.lock.out index 3814b281b40b26..720d228a415f7c 100644 --- a/tests/specs/update/deno_json/update_latest/deno.lock.out +++ b/tests/specs/update/deno_json/update_latest/deno.lock.out @@ -21,7 +21,7 @@ }, "npm": { "@denotest/bin@1.0.0": { - "integrity": "sha512-LokQ7v6N8KiQCVZXuy9MAzaB4kcbI8tDntvy2DI3YOaigDFdghrkv3WJfc7xU9boni5jPcndNsiH9dPJEHZfyQ==" + "integrity": "[WILDCARD]" }, "@denotest/breaking-change-between-versions@2.0.0": { "integrity": "sha512-x7q4LVdjFsu6pMsxIs1LPed6hix5p4RcCybssPzRZXOD2VCNBWbNCEDW9JRWiVFzT7+Md0n9czqMMIx+VUA5qA==" diff --git a/tests/specs/update/package_json/deno.lock.orig.out b/tests/specs/update/package_json/deno.lock.orig.out index f695ead0065222..80021c5310e7b0 100644 --- a/tests/specs/update/package_json/deno.lock.orig.out +++ b/tests/specs/update/package_json/deno.lock.orig.out @@ -7,7 +7,7 @@ }, "npm": { "@denotest/bin@0.6.0": { - "integrity": "sha512-+xIHuFv3j/rkWWF7H6HNAPEBGUtPT9uO4rBnXXNH5NVjsrQJ5c7ZtYp+z7Hk3JnQzt2exGeJrWGvVlndtgC+yw==" + "integrity": "[WILDCARD]" }, "@denotest/breaking-change-between-versions@1.0.0": { "integrity": "sha512-EjAhryqWinStR5SeDOCN4PpO8Z9cTiY4DV9nSnoZUaOUsboEtJ/r0uhm4sBiZm3//mW7Kg/5T6dqXG6RtzLusA==" diff --git a/tests/specs/update/package_json/update_compatible/deno.lock.out b/tests/specs/update/package_json/update_compatible/deno.lock.out index 2a45487a2b1d3f..448f0880601fc2 100644 --- a/tests/specs/update/package_json/update_compatible/deno.lock.out +++ b/tests/specs/update/package_json/update_compatible/deno.lock.out @@ -7,7 +7,7 @@ }, "npm": { "@denotest/bin@0.6.0": { - "integrity": "sha512-+xIHuFv3j/rkWWF7H6HNAPEBGUtPT9uO4rBnXXNH5NVjsrQJ5c7ZtYp+z7Hk3JnQzt2exGeJrWGvVlndtgC+yw==" + "integrity": "[WILDCARD]" }, "@denotest/breaking-change-between-versions@1.0.0": { "integrity": "sha512-EjAhryqWinStR5SeDOCN4PpO8Z9cTiY4DV9nSnoZUaOUsboEtJ/r0uhm4sBiZm3//mW7Kg/5T6dqXG6RtzLusA==" diff --git a/tests/specs/update/package_json/update_latest/deno.lock.out b/tests/specs/update/package_json/update_latest/deno.lock.out index 59b596ef2f9e81..84f4c7a69592a9 100644 --- a/tests/specs/update/package_json/update_latest/deno.lock.out +++ b/tests/specs/update/package_json/update_latest/deno.lock.out @@ -7,7 +7,7 @@ }, "npm": { "@denotest/bin@1.0.0": { - "integrity": "sha512-LokQ7v6N8KiQCVZXuy9MAzaB4kcbI8tDntvy2DI3YOaigDFdghrkv3WJfc7xU9boni5jPcndNsiH9dPJEHZfyQ==" + "integrity": "[WILDCARD]" }, "@denotest/breaking-change-between-versions@2.0.0": { "integrity": "sha512-x7q4LVdjFsu6pMsxIs1LPed6hix5p4RcCybssPzRZXOD2VCNBWbNCEDW9JRWiVFzT7+Md0n9czqMMIx+VUA5qA==" From 2e170c8ce0e1b792038558bb3354eb3fb63f30c6 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 15:38:03 -0800 Subject: [PATCH 11/26] make tarball hashes deterministic in test server --- tests/specs/update/deno_json/deno.lock | 6 +-- .../specs/update/deno_json/deno.lock.orig.out | 6 +-- .../deno_json/update_compatible/deno.lock.out | 6 +-- .../deno_json/update_latest/deno.lock.out | 6 +-- tests/specs/update/package_json/deno.lock | 6 +-- .../update/package_json/deno.lock.orig.out | 6 +-- .../update_compatible/deno.lock.out | 6 +-- .../package_json/update_latest/deno.lock.out | 6 +-- tests/util/server/src/npm.rs | 48 +++++++++++++++++-- 9 files changed, 67 insertions(+), 29 deletions(-) diff --git a/tests/specs/update/deno_json/deno.lock b/tests/specs/update/deno_json/deno.lock index 238a9c8a10bd11..7c4d5e36906fb7 100644 --- a/tests/specs/update/deno_json/deno.lock +++ b/tests/specs/update/deno_json/deno.lock @@ -21,13 +21,13 @@ }, "npm": { "@denotest/bin@0.6.0": { - "integrity": "sha512-+xIHuFv3j/rkWWF7H6HNAPEBGUtPT9uO4rBnXXNH5NVjsrQJ5c7ZtYp+z7Hk3JnQzt2exGeJrWGvVlndtgC+yw==" + "integrity": "sha512-vCNpxFgQN4fw4ZOp63nbTX1ilcDqNpvXCvYyC8nmfxQQAezsEt095I/YXwMIoMGzWtjCvlMf9kVEYfLuT5oEGQ==" }, "@denotest/breaking-change-between-versions@1.0.0": { - "integrity": "sha512-EjAhryqWinStR5SeDOCN4PpO8Z9cTiY4DV9nSnoZUaOUsboEtJ/r0uhm4sBiZm3//mW7Kg/5T6dqXG6RtzLusA==" + "integrity": "sha512-bzMGYx+DxxPlI74n/VsDAN7Db1BY7Sz2XqxXruMo9dEznsBZu7Ez3i8YQ8n0leTxAiiMk1RCG4zQHPG1aj3xRw==" }, "@denotest/has-patch-versions@0.1.0": { - "integrity": "sha512-NMn5TOdV07srVJ5G7Co4EzOoF58OMKxSzYx1u83DBLwYgCyRuiIpNNK+qw2mdJrlyYb39FaO+ixOTPcOlbicQA==" + "integrity": "sha512-H/MBo0jKDdMsX4AAGEGQbZj70nfNe3oUNZXbohYHhqf9EfpLnXp/7FC29ZdfV4+p6VjEcOGdCtXc6rilE6iYpg==" } }, "workspace": { diff --git a/tests/specs/update/deno_json/deno.lock.orig.out b/tests/specs/update/deno_json/deno.lock.orig.out index cf2802db989227..7c4d5e36906fb7 100644 --- a/tests/specs/update/deno_json/deno.lock.orig.out +++ b/tests/specs/update/deno_json/deno.lock.orig.out @@ -21,13 +21,13 @@ }, "npm": { "@denotest/bin@0.6.0": { - "integrity": "[WILDCARD]" + "integrity": "sha512-vCNpxFgQN4fw4ZOp63nbTX1ilcDqNpvXCvYyC8nmfxQQAezsEt095I/YXwMIoMGzWtjCvlMf9kVEYfLuT5oEGQ==" }, "@denotest/breaking-change-between-versions@1.0.0": { - "integrity": "sha512-EjAhryqWinStR5SeDOCN4PpO8Z9cTiY4DV9nSnoZUaOUsboEtJ/r0uhm4sBiZm3//mW7Kg/5T6dqXG6RtzLusA==" + "integrity": "sha512-bzMGYx+DxxPlI74n/VsDAN7Db1BY7Sz2XqxXruMo9dEznsBZu7Ez3i8YQ8n0leTxAiiMk1RCG4zQHPG1aj3xRw==" }, "@denotest/has-patch-versions@0.1.0": { - "integrity": "sha512-NMn5TOdV07srVJ5G7Co4EzOoF58OMKxSzYx1u83DBLwYgCyRuiIpNNK+qw2mdJrlyYb39FaO+ixOTPcOlbicQA==" + "integrity": "sha512-H/MBo0jKDdMsX4AAGEGQbZj70nfNe3oUNZXbohYHhqf9EfpLnXp/7FC29ZdfV4+p6VjEcOGdCtXc6rilE6iYpg==" } }, "workspace": { diff --git a/tests/specs/update/deno_json/update_compatible/deno.lock.out b/tests/specs/update/deno_json/update_compatible/deno.lock.out index 421dba1ea63d53..994d6f7ac9c992 100644 --- a/tests/specs/update/deno_json/update_compatible/deno.lock.out +++ b/tests/specs/update/deno_json/update_compatible/deno.lock.out @@ -21,13 +21,13 @@ }, "npm": { "@denotest/bin@0.6.0": { - "integrity": "[WILDCARD]" + "integrity": "sha512-vCNpxFgQN4fw4ZOp63nbTX1ilcDqNpvXCvYyC8nmfxQQAezsEt095I/YXwMIoMGzWtjCvlMf9kVEYfLuT5oEGQ==" }, "@denotest/breaking-change-between-versions@1.0.0": { - "integrity": "sha512-EjAhryqWinStR5SeDOCN4PpO8Z9cTiY4DV9nSnoZUaOUsboEtJ/r0uhm4sBiZm3//mW7Kg/5T6dqXG6RtzLusA==" + "integrity": "sha512-bzMGYx+DxxPlI74n/VsDAN7Db1BY7Sz2XqxXruMo9dEznsBZu7Ez3i8YQ8n0leTxAiiMk1RCG4zQHPG1aj3xRw==" }, "@denotest/has-patch-versions@0.1.1": { - "integrity": "sha512-BcH2/Hbu6RCkhLpzHTgRM7Vdxhb2bq7WVovWOb56eTe1/2sNDEkpJ5O3gfHMU0WF1MG7Fn+boTgXpEAMfIrA5Q==" + "integrity": "sha512-slUqYhu6DrPiSdNzmW5aMdW2/osIYnDP0yY3CwgLzAiyK0/cwb0epSpTSyZEmTKXA3rezxxC7ASSsnD34uH1/w==" } }, "workspace": { diff --git a/tests/specs/update/deno_json/update_latest/deno.lock.out b/tests/specs/update/deno_json/update_latest/deno.lock.out index 720d228a415f7c..ad83546ab194b3 100644 --- a/tests/specs/update/deno_json/update_latest/deno.lock.out +++ b/tests/specs/update/deno_json/update_latest/deno.lock.out @@ -21,13 +21,13 @@ }, "npm": { "@denotest/bin@1.0.0": { - "integrity": "[WILDCARD]" + "integrity": "sha512-ZtrWnYYPIzw4a9H1uNeZRZRWuLCpHZZU/SllIyFLqcTLH/3zdRI8UH4Z1Kf+8N++bWGO3fg8Ev4vvS1LoLlidg==" }, "@denotest/breaking-change-between-versions@2.0.0": { - "integrity": "sha512-x7q4LVdjFsu6pMsxIs1LPed6hix5p4RcCybssPzRZXOD2VCNBWbNCEDW9JRWiVFzT7+Md0n9czqMMIx+VUA5qA==" + "integrity": "sha512-3eQpPhhJYbSHaAmpgyVT8IM3+MkxcAQl90Uw8zmuTiFs64Wt3HGzSz74cwPlvfqqesRktm8fBZMmrtxVo3ENzw==" }, "@denotest/has-patch-versions@0.2.0": { - "integrity": "sha512-r+5ZSpZHBgADr7Go++aGDMy7ogVtolEYT9b7Yqkp85Ms+E2TfImvXf0rGY1dTzU8qChfoHihLOrxcaTvdZ+r8A==" + "integrity": "sha512-7XIVGrBMyqnts5/wcc7dn7rVl4IWrCiGUT2GLDSLWnogOMIZBapJJLW9n8Leq1bDTJ3U6aDTEFKz9fVSOwZfLQ==" } }, "workspace": { diff --git a/tests/specs/update/package_json/deno.lock b/tests/specs/update/package_json/deno.lock index f695ead0065222..05253094db79a0 100644 --- a/tests/specs/update/package_json/deno.lock +++ b/tests/specs/update/package_json/deno.lock @@ -7,13 +7,13 @@ }, "npm": { "@denotest/bin@0.6.0": { - "integrity": "sha512-+xIHuFv3j/rkWWF7H6HNAPEBGUtPT9uO4rBnXXNH5NVjsrQJ5c7ZtYp+z7Hk3JnQzt2exGeJrWGvVlndtgC+yw==" + "integrity": "sha512-vCNpxFgQN4fw4ZOp63nbTX1ilcDqNpvXCvYyC8nmfxQQAezsEt095I/YXwMIoMGzWtjCvlMf9kVEYfLuT5oEGQ==" }, "@denotest/breaking-change-between-versions@1.0.0": { - "integrity": "sha512-EjAhryqWinStR5SeDOCN4PpO8Z9cTiY4DV9nSnoZUaOUsboEtJ/r0uhm4sBiZm3//mW7Kg/5T6dqXG6RtzLusA==" + "integrity": "sha512-bzMGYx+DxxPlI74n/VsDAN7Db1BY7Sz2XqxXruMo9dEznsBZu7Ez3i8YQ8n0leTxAiiMk1RCG4zQHPG1aj3xRw==" }, "@denotest/has-patch-versions@0.1.0": { - "integrity": "sha512-NMn5TOdV07srVJ5G7Co4EzOoF58OMKxSzYx1u83DBLwYgCyRuiIpNNK+qw2mdJrlyYb39FaO+ixOTPcOlbicQA==" + "integrity": "sha512-H/MBo0jKDdMsX4AAGEGQbZj70nfNe3oUNZXbohYHhqf9EfpLnXp/7FC29ZdfV4+p6VjEcOGdCtXc6rilE6iYpg==" } }, "workspace": { diff --git a/tests/specs/update/package_json/deno.lock.orig.out b/tests/specs/update/package_json/deno.lock.orig.out index 80021c5310e7b0..05253094db79a0 100644 --- a/tests/specs/update/package_json/deno.lock.orig.out +++ b/tests/specs/update/package_json/deno.lock.orig.out @@ -7,13 +7,13 @@ }, "npm": { "@denotest/bin@0.6.0": { - "integrity": "[WILDCARD]" + "integrity": "sha512-vCNpxFgQN4fw4ZOp63nbTX1ilcDqNpvXCvYyC8nmfxQQAezsEt095I/YXwMIoMGzWtjCvlMf9kVEYfLuT5oEGQ==" }, "@denotest/breaking-change-between-versions@1.0.0": { - "integrity": "sha512-EjAhryqWinStR5SeDOCN4PpO8Z9cTiY4DV9nSnoZUaOUsboEtJ/r0uhm4sBiZm3//mW7Kg/5T6dqXG6RtzLusA==" + "integrity": "sha512-bzMGYx+DxxPlI74n/VsDAN7Db1BY7Sz2XqxXruMo9dEznsBZu7Ez3i8YQ8n0leTxAiiMk1RCG4zQHPG1aj3xRw==" }, "@denotest/has-patch-versions@0.1.0": { - "integrity": "sha512-NMn5TOdV07srVJ5G7Co4EzOoF58OMKxSzYx1u83DBLwYgCyRuiIpNNK+qw2mdJrlyYb39FaO+ixOTPcOlbicQA==" + "integrity": "sha512-H/MBo0jKDdMsX4AAGEGQbZj70nfNe3oUNZXbohYHhqf9EfpLnXp/7FC29ZdfV4+p6VjEcOGdCtXc6rilE6iYpg==" } }, "workspace": { diff --git a/tests/specs/update/package_json/update_compatible/deno.lock.out b/tests/specs/update/package_json/update_compatible/deno.lock.out index 448f0880601fc2..f82a21ee46c785 100644 --- a/tests/specs/update/package_json/update_compatible/deno.lock.out +++ b/tests/specs/update/package_json/update_compatible/deno.lock.out @@ -7,13 +7,13 @@ }, "npm": { "@denotest/bin@0.6.0": { - "integrity": "[WILDCARD]" + "integrity": "sha512-vCNpxFgQN4fw4ZOp63nbTX1ilcDqNpvXCvYyC8nmfxQQAezsEt095I/YXwMIoMGzWtjCvlMf9kVEYfLuT5oEGQ==" }, "@denotest/breaking-change-between-versions@1.0.0": { - "integrity": "sha512-EjAhryqWinStR5SeDOCN4PpO8Z9cTiY4DV9nSnoZUaOUsboEtJ/r0uhm4sBiZm3//mW7Kg/5T6dqXG6RtzLusA==" + "integrity": "sha512-bzMGYx+DxxPlI74n/VsDAN7Db1BY7Sz2XqxXruMo9dEznsBZu7Ez3i8YQ8n0leTxAiiMk1RCG4zQHPG1aj3xRw==" }, "@denotest/has-patch-versions@0.1.1": { - "integrity": "sha512-BcH2/Hbu6RCkhLpzHTgRM7Vdxhb2bq7WVovWOb56eTe1/2sNDEkpJ5O3gfHMU0WF1MG7Fn+boTgXpEAMfIrA5Q==" + "integrity": "sha512-slUqYhu6DrPiSdNzmW5aMdW2/osIYnDP0yY3CwgLzAiyK0/cwb0epSpTSyZEmTKXA3rezxxC7ASSsnD34uH1/w==" } }, "workspace": { diff --git a/tests/specs/update/package_json/update_latest/deno.lock.out b/tests/specs/update/package_json/update_latest/deno.lock.out index 84f4c7a69592a9..9a9b1bad5ebcbc 100644 --- a/tests/specs/update/package_json/update_latest/deno.lock.out +++ b/tests/specs/update/package_json/update_latest/deno.lock.out @@ -7,13 +7,13 @@ }, "npm": { "@denotest/bin@1.0.0": { - "integrity": "[WILDCARD]" + "integrity": "sha512-ZtrWnYYPIzw4a9H1uNeZRZRWuLCpHZZU/SllIyFLqcTLH/3zdRI8UH4Z1Kf+8N++bWGO3fg8Ev4vvS1LoLlidg==" }, "@denotest/breaking-change-between-versions@2.0.0": { - "integrity": "sha512-x7q4LVdjFsu6pMsxIs1LPed6hix5p4RcCybssPzRZXOD2VCNBWbNCEDW9JRWiVFzT7+Md0n9czqMMIx+VUA5qA==" + "integrity": "sha512-3eQpPhhJYbSHaAmpgyVT8IM3+MkxcAQl90Uw8zmuTiFs64Wt3HGzSz74cwPlvfqqesRktm8fBZMmrtxVo3ENzw==" }, "@denotest/has-patch-versions@0.2.0": { - "integrity": "sha512-r+5ZSpZHBgADr7Go++aGDMy7ogVtolEYT9b7Yqkp85Ms+E2TfImvXf0rGY1dTzU8qChfoHihLOrxcaTvdZ+r8A==" + "integrity": "sha512-7XIVGrBMyqnts5/wcc7dn7rVl4IWrCiGUT2GLDSLWnogOMIZBapJJLW9n8Leq1bDTJ3U6aDTEFKz9fVSOwZfLQ==" } }, "workspace": { diff --git a/tests/util/server/src/npm.rs b/tests/util/server/src/npm.rs index 31686fa8543dfb..7d7f7cc1254d73 100644 --- a/tests/util/server/src/npm.rs +++ b/tests/util/server/src/npm.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::fs; +use std::path::Path; use anyhow::Context; use anyhow::Result; @@ -189,6 +190,40 @@ impl TestNpmRegistry { } } +fn append_dir_all( + builder: &mut tar::Builder, + path: &Path, + src_path: &Path, +) -> std::io::Result<()> { + builder.follow_symlinks(true); + let follow = true; + let mode = tar::HeaderMode::Deterministic; + builder.mode(mode); + let mut stack = vec![(src_path.to_path_buf(), true, false)]; + let mut entries = Vec::new(); + while let Some((src, is_dir, is_symlink)) = stack.pop() { + let dest = path.join(src.strip_prefix(&src_path).unwrap()); + // In case of a symlink pointing to a directory, is_dir is false, but src.is_dir() will return true + if is_dir || (is_symlink && follow && src.is_dir()) { + for entry in fs::read_dir(&src)? { + let entry = entry?; + let file_type = entry.file_type()?; + stack.push((entry.path(), file_type.is_dir(), file_type.is_symlink())); + } + if dest != Path::new("") { + entries.push((src, dest)); + } + } else { + entries.push((src, dest)); + } + } + entries.sort(); + for (src, dest) in entries { + builder.append_path_with_name(src, dest)?; + } + Ok(()) +} + fn get_npm_package( registry_hostname: &str, local_path: &str, @@ -228,11 +263,14 @@ fn get_npm_package( GzEncoder::new(&mut tarball_bytes, Compression::default()); { let mut builder = Builder::new(&mut encoder); - builder - .append_dir_all("package", &version_folder) - .with_context(|| { - format!("Error adding tarball for directory: {}", version_folder) - })?; + append_dir_all( + &mut builder, + Path::new("package"), + version_folder.as_path(), + ) + .with_context(|| { + format!("Error adding tarball for directory {}", version_folder,) + })?; builder.finish()?; } encoder.finish()?; From 4890b5148eeb98df4d3bfe3c08f4bd27603fdd24 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 15:38:17 -0800 Subject: [PATCH 12/26] add no-lock flag --- cli/args/flags.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 41c61e8970e576..3e8200b86231c3 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -2671,6 +2671,9 @@ Specific version requirements to update to can be specified: "If a version requirement is specified, the matching packages will be updated to the given requirement."), ) ) + .arg( + no_lock_arg() + ) .arg( Arg::new("latest") .long("latest") From 087efae4b5024cfee3ead3f073c235a0a24d527a Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 15:38:46 -0800 Subject: [PATCH 13/26] appease clippy --- tests/util/server/src/npm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/util/server/src/npm.rs b/tests/util/server/src/npm.rs index 7d7f7cc1254d73..91b52711dfffcb 100644 --- a/tests/util/server/src/npm.rs +++ b/tests/util/server/src/npm.rs @@ -202,7 +202,7 @@ fn append_dir_all( let mut stack = vec![(src_path.to_path_buf(), true, false)]; let mut entries = Vec::new(); while let Some((src, is_dir, is_symlink)) = stack.pop() { - let dest = path.join(src.strip_prefix(&src_path).unwrap()); + let dest = path.join(src.strip_prefix(src_path).unwrap()); // In case of a symlink pointing to a directory, is_dir is false, but src.is_dir() will return true if is_dir || (is_symlink && follow && src.is_dir()) { for entry in fs::read_dir(&src)? { From 9890d5f5022c9b5fc95fbf358d4214c6f83db1fb Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 15:40:51 -0800 Subject: [PATCH 14/26] cleanup + ack --- tests/util/server/src/npm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/util/server/src/npm.rs b/tests/util/server/src/npm.rs index 91b52711dfffcb..8177766fefc033 100644 --- a/tests/util/server/src/npm.rs +++ b/tests/util/server/src/npm.rs @@ -190,13 +190,13 @@ impl TestNpmRegistry { } } +// NOTE: extracted out partially from the `tar` crate, all credits to the original authors fn append_dir_all( builder: &mut tar::Builder, path: &Path, src_path: &Path, ) -> std::io::Result<()> { builder.follow_symlinks(true); - let follow = true; let mode = tar::HeaderMode::Deterministic; builder.mode(mode); let mut stack = vec![(src_path.to_path_buf(), true, false)]; @@ -204,7 +204,7 @@ fn append_dir_all( while let Some((src, is_dir, is_symlink)) = stack.pop() { let dest = path.join(src.strip_prefix(src_path).unwrap()); // In case of a symlink pointing to a directory, is_dir is false, but src.is_dir() will return true - if is_dir || (is_symlink && follow && src.is_dir()) { + if is_dir || (is_symlink && src.is_dir()) { for entry in fs::read_dir(&src)? { let entry = entry?; let file_type = entry.file_type()?; From 60bad20d90333fb7153ddb17822403433ec7dbe9 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 15:52:20 -0800 Subject: [PATCH 15/26] needs lock arg too --- cli/args/flags.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 3e8200b86231c3..7f761c6102b015 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -2671,9 +2671,8 @@ Specific version requirements to update to can be specified: "If a version requirement is specified, the matching packages will be updated to the given requirement."), ) ) - .arg( - no_lock_arg() - ) + .arg(no_lock_arg()) + .arg(lock_arg()) .arg( Arg::new("latest") .long("latest") From fee823dc2bb89560221cd05c9e7b869b8e159de3 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 15:52:33 -0800 Subject: [PATCH 16/26] fix filter against alias --- cli/tools/registry/pm/outdated.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs index 8d3ce414cc7e51..a9d77d9f7ab315 100644 --- a/cli/tools/registry/pm/outdated.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -229,7 +229,7 @@ fn choose_new_version_req( filter_set: &filter::FilterSet, ) -> Option { let explicit_version_req = filter_set - .matching_filter(&dep.prefixed_name()) + .matching_filter(dep.alias.as_deref().unwrap_or(&dep.req.name)) .version_spec() .cloned(); From 4e3b20c5975ef9912f23cf934ff8e3ef612e834b Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 16:04:22 -0800 Subject: [PATCH 17/26] filter spec tests --- .../@denotest/multiple-exports/0.5.0/add.ts | 1 + .../multiple-exports/0.5.0/data.json | 3 +++ .../multiple-exports/0.5.0/subtract.ts | 1 + .../multiple-exports/0.5.0_meta.json | 7 +++++++ .../jsr/@denotest/multiple-exports/meta.json | 1 + tests/specs/update/deno_json/__test__.jsonc | 16 ++++++++++++++++ .../update/deno_json/filtered/deno.json.out | 19 +++++++++++++++++++ .../update/deno_json/filtered/update.out | 10 ++++++++++ .../specs/update/package_json/__test__.jsonc | 16 ++++++++++++++++ .../package_json/filtered/package.json.out | 9 +++++++++ .../update/package_json/filtered/update.out | 9 +++++++++ 11 files changed, 92 insertions(+) create mode 100644 tests/registry/jsr/@denotest/multiple-exports/0.5.0/add.ts create mode 100644 tests/registry/jsr/@denotest/multiple-exports/0.5.0/data.json create mode 100644 tests/registry/jsr/@denotest/multiple-exports/0.5.0/subtract.ts create mode 100644 tests/registry/jsr/@denotest/multiple-exports/0.5.0_meta.json create mode 100644 tests/specs/update/deno_json/filtered/deno.json.out create mode 100644 tests/specs/update/deno_json/filtered/update.out create mode 100644 tests/specs/update/package_json/filtered/package.json.out create mode 100644 tests/specs/update/package_json/filtered/update.out diff --git a/tests/registry/jsr/@denotest/multiple-exports/0.5.0/add.ts b/tests/registry/jsr/@denotest/multiple-exports/0.5.0/add.ts new file mode 100644 index 00000000000000..de02f69024bf76 --- /dev/null +++ b/tests/registry/jsr/@denotest/multiple-exports/0.5.0/add.ts @@ -0,0 +1 @@ +export * from "jsr:@denotest/add@1"; diff --git a/tests/registry/jsr/@denotest/multiple-exports/0.5.0/data.json b/tests/registry/jsr/@denotest/multiple-exports/0.5.0/data.json new file mode 100644 index 00000000000000..885e71c6ccde35 --- /dev/null +++ b/tests/registry/jsr/@denotest/multiple-exports/0.5.0/data.json @@ -0,0 +1,3 @@ +{ + "a": 1 +} \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/multiple-exports/0.5.0/subtract.ts b/tests/registry/jsr/@denotest/multiple-exports/0.5.0/subtract.ts new file mode 100644 index 00000000000000..215c42310d69bb --- /dev/null +++ b/tests/registry/jsr/@denotest/multiple-exports/0.5.0/subtract.ts @@ -0,0 +1 @@ +export * from "jsr:@denotest/subtract@1"; diff --git a/tests/registry/jsr/@denotest/multiple-exports/0.5.0_meta.json b/tests/registry/jsr/@denotest/multiple-exports/0.5.0_meta.json new file mode 100644 index 00000000000000..d9f58b9a61a03f --- /dev/null +++ b/tests/registry/jsr/@denotest/multiple-exports/0.5.0_meta.json @@ -0,0 +1,7 @@ +{ + "exports": { + "./add": "./add.ts", + "./subtract": "./subtract.ts", + "./data-json": "./data.json" + } +} diff --git a/tests/registry/jsr/@denotest/multiple-exports/meta.json b/tests/registry/jsr/@denotest/multiple-exports/meta.json index 2f4daa84414004..aaaf18a184ff65 100644 --- a/tests/registry/jsr/@denotest/multiple-exports/meta.json +++ b/tests/registry/jsr/@denotest/multiple-exports/meta.json @@ -1,6 +1,7 @@ { "versions": { "1.0.0": {}, + "0.5.0": {}, "0.2.0": {} } } diff --git a/tests/specs/update/deno_json/__test__.jsonc b/tests/specs/update/deno_json/__test__.jsonc index 9e5d182d044c42..8b4aa26b5c082d 100644 --- a/tests/specs/update/deno_json/__test__.jsonc +++ b/tests/specs/update/deno_json/__test__.jsonc @@ -80,6 +80,22 @@ "output": "./update_latest/deno.lock.out" } ] + }, + "update_filtered": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "outdated --update --latest @denotest/add @denotest/b* !@denotest/breaking* @denotest/with-subpath@0.5.0", + "output": "filtered/update.out" + }, + { + "args": "-A print_file.ts ./deno.json", + "output": "filtered/deno.json.out" + } + ] } } } diff --git a/tests/specs/update/deno_json/filtered/deno.json.out b/tests/specs/update/deno_json/filtered/deno.json.out new file mode 100644 index 00000000000000..4458e2d037b92b --- /dev/null +++ b/tests/specs/update/deno_json/filtered/deno.json.out @@ -0,0 +1,19 @@ +{ + "imports": { + "@denotest/add": "jsr:@denotest/add@^1.0.0", + "@denotest/add/": "jsr:/@denotest/add@^1.0.0/", + "@denotest/subtract": "jsr:@denotest/subtract@^0.2.0", + "@denotest/with-subpath": "jsr:@denotest/multiple-exports@0.5.0/data-json", + "@denotest/breaking-change-between-versions": "npm:@denotest/breaking-change-between-versions@1.0.0", + "@denotest/bin": "npm:@denotest/bin@^1.0.0", + "@denotest/has-patch-versions": "npm:@denotest/has-patch-versions@^0.1.0" + }, + "scopes": { + "/foo/": { + "@denotest/add": "jsr:@denotest/add@^1.0.0", + "@denotest/add/": "jsr:/@denotest/add@^1.0.0/", + "@denotest/subtract": "jsr:@denotest/subtract@^0.2.0", + "@denotest/with-subpath": "jsr:@denotest/multiple-exports@0.5.0/data-json" + } + } +} diff --git a/tests/specs/update/deno_json/filtered/update.out b/tests/specs/update/deno_json/filtered/update.out new file mode 100644 index 00000000000000..9a6c4c9c1fbd80 --- /dev/null +++ b/tests/specs/update/deno_json/filtered/update.out @@ -0,0 +1,10 @@ +[UNORDERED_START] +Download http://localhost:4260/@denotest/bin/1.0.0.tgz +Download http://127.0.0.1:4250/@denotest/multiple-exports/0.5.0_meta.json +Download http://127.0.0.1:4250/@denotest/multiple-exports/0.5.0/data.json +Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts +[UNORDERED_END] +Updated 3 dependencies: +• jsr:@denotest/add@^0.2.0 -> 1.0.0 +• jsr:@denotest/multiple-exports@0.2.0 -> 0.5.0 +• npm:@denotest/bin@0.6.0 -> 1.0.0 diff --git a/tests/specs/update/package_json/__test__.jsonc b/tests/specs/update/package_json/__test__.jsonc index 1a8e56fe2cabd7..19d576dfc00194 100644 --- a/tests/specs/update/package_json/__test__.jsonc +++ b/tests/specs/update/package_json/__test__.jsonc @@ -79,6 +79,22 @@ "output": "update_latest/deno.lock.out" } ] + }, + "update_filtered": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "outdated --update --latest @denotest/has-patch* aliased@0.7.0", + "output": "filtered/update.out" + }, + { + "args": "-A print_file.ts ./package.json", + "output": "filtered/package.json.out" + } + ] } } } diff --git a/tests/specs/update/package_json/filtered/package.json.out b/tests/specs/update/package_json/filtered/package.json.out new file mode 100644 index 00000000000000..39faff22d668ce --- /dev/null +++ b/tests/specs/update/package_json/filtered/package.json.out @@ -0,0 +1,9 @@ +{ + "dependencies": { + "@denotest/has-patch-versions": "^0.2.0", + "@denotest/breaking-change-between-versions": "1.0.0" + }, + "devDependencies": { + "aliased": "npm:@denotest/bin@0.7.0" + } +} diff --git a/tests/specs/update/package_json/filtered/update.out b/tests/specs/update/package_json/filtered/update.out new file mode 100644 index 00000000000000..100469bb7eb56a --- /dev/null +++ b/tests/specs/update/package_json/filtered/update.out @@ -0,0 +1,9 @@ +[UNORDERED_START] +Download http://localhost:4260/@denotest/bin/0.7.0.tgz +Download http://localhost:4260/@denotest/has-patch-versions/0.2.0.tgz +Initialize @denotest/has-patch-versions@0.2.0 +Initialize @denotest/bin@0.7.0 +[UNORDERED_END] +Updated 2 dependencies: +• npm:@denotest/bin@^0.6.0 -> 0.7.0 +• npm:@denotest/has-patch-versions@^0.1.0 -> 0.2.0 From 26e9e50e22f99d6b4dcca7394b763cca95fc49bc Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 16:13:27 -0800 Subject: [PATCH 18/26] appease clippy --- cli/tools/registry/pm/deps.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index d1110972f738ce..eb51c135609dbc 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -200,9 +200,6 @@ impl Dep { pub fn prefixed_req(&self) -> String { format!("{}:{}", self.kind.scheme(), self.req) } - pub fn prefixed_name(&self) -> String { - format!("{}:{}", self.kind.scheme(), self.req.name) - } } fn import_map_entries( From 6aa675eacec534a4b32564a8b919f34561e828bb Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 16:52:55 -0800 Subject: [PATCH 19/26] mixed workspace tests --- cli/tools/registry/pm/deps.rs | 8 +- .../update/mixed_workspace/__test__.jsonc | 150 +++++++++++++++++- tests/specs/update/mixed_workspace/deno.json | 5 +- tests/specs/update/mixed_workspace/deno.lock | 55 +++++++ .../update/mixed_workspace/deno.lock.orig.out | 55 +++++++ .../filtered/member_a_deno.json.out | 10 ++ .../filtered/member_b_package.json.out | 8 + .../mixed_workspace/filtered/update.out | 12 ++ .../update/mixed_workspace/member-a/deno.json | 5 +- .../mixed_workspace/member-b/package.json | 3 +- .../update/mixed_workspace/print_file.ts | 2 + .../print_outdated/member_a.out | 9 ++ .../print_outdated/member_b.out | 7 + .../print_outdated/recursive.out | 15 ++ .../mixed_workspace/print_outdated/root.out | 5 + .../update_latest/recursive/update.out | 16 ++ .../update_latest/root/deno.json.out | 6 + .../update_latest/root/update.out | 3 + .../update_latest/subdir/member_a.out | 10 ++ .../subdir/member_a_deno.json.out | 10 ++ .../update_latest/subdir/member_b.out | 7 + .../subdir/member_b_package.json.out | 8 + 22 files changed, 403 insertions(+), 6 deletions(-) create mode 100644 tests/specs/update/mixed_workspace/deno.lock create mode 100644 tests/specs/update/mixed_workspace/deno.lock.orig.out create mode 100644 tests/specs/update/mixed_workspace/filtered/member_a_deno.json.out create mode 100644 tests/specs/update/mixed_workspace/filtered/member_b_package.json.out create mode 100644 tests/specs/update/mixed_workspace/filtered/update.out create mode 100644 tests/specs/update/mixed_workspace/print_file.ts create mode 100644 tests/specs/update/mixed_workspace/print_outdated/member_a.out create mode 100644 tests/specs/update/mixed_workspace/print_outdated/member_b.out create mode 100644 tests/specs/update/mixed_workspace/print_outdated/recursive.out create mode 100644 tests/specs/update/mixed_workspace/print_outdated/root.out create mode 100644 tests/specs/update/mixed_workspace/update_latest/recursive/update.out create mode 100644 tests/specs/update/mixed_workspace/update_latest/root/deno.json.out create mode 100644 tests/specs/update/mixed_workspace/update_latest/root/update.out create mode 100644 tests/specs/update/mixed_workspace/update_latest/subdir/member_a.out create mode 100644 tests/specs/update/mixed_workspace/update_latest/subdir/member_a_deno.json.out create mode 100644 tests/specs/update/mixed_workspace/update_latest/subdir/member_b.out create mode 100644 tests/specs/update/mixed_workspace/update_latest/subdir/member_b_package.json.out diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index eb51c135609dbc..1fef5322d017e2 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -566,7 +566,13 @@ impl DepManager { ) -> Result { let mut deps = Vec::with_capacity(32); if let Some(deno_json) = workspace_dir.maybe_deno_json() { - add_deps_from_deno_json(deno_json, dep_filter, &mut deps); + if deno_json.specifier.scheme() != "file" { + bail!("remote deno.json files are not supported"); + } + let path = deno_json.specifier.to_file_path().unwrap(); + if path.parent().unwrap() == workspace_dir.dir_path() { + add_deps_from_deno_json(deno_json, dep_filter, &mut deps); + } } if let Some(package_json) = workspace_dir.maybe_pkg_json() { add_deps_from_package_json(package_json, dep_filter, &mut deps); diff --git a/tests/specs/update/mixed_workspace/__test__.jsonc b/tests/specs/update/mixed_workspace/__test__.jsonc index cfba66c7bfb728..8c846467d47894 100644 --- a/tests/specs/update/mixed_workspace/__test__.jsonc +++ b/tests/specs/update/mixed_workspace/__test__.jsonc @@ -1,7 +1,153 @@ { + "tempDir": true, "tests": { - "foo": { - "steps": [] + // just to make sure install doesn't change the lockfile + "sanity_lockfile_up_to_date": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": [ + "eval", + "const now = Deno.readTextFileSync('./deno.lock'); console.log(now.trim());" + ], + "output": "deno.lock.orig.out" + } + ] + }, + "print_outdated_root": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "outdated", + "output": "print_outdated/root.out" + } + ] + }, + "print_outdated_recursive": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "outdated --recursive", + "output": "print_outdated/recursive.out" + } + ] + }, + "print_outdated_subdir": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "cwd": "member-a", + "args": "outdated", + "output": "print_outdated/member_a.out" + }, + { + "cwd": "member-b", + "args": "outdated", + "output": "print_outdated/member_b.out" + } + ] + }, + "update_latest_root": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "outdated --update --latest", + "output": "update_latest/root/update.out" + }, + { + "args": "-A print_file.ts ./deno.json", + "output": "./update_latest/root/deno.json.out" + } + ] + }, + "update_latest_subdir": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "cwd": "member-a", + "args": "outdated --update --latest", + "output": "update_latest/subdir/member_a.out" + }, + { + "args": "-A print_file.ts ./member-a/deno.json", + "output": "update_latest/subdir/member_a_deno.json.out" + }, + { + "cwd": "member-b", + "args": "outdated --update --latest", + "output": "update_latest/subdir/member_b.out" + }, + { + "args": "-A print_file.ts ./member-b/package.json", + "output": "update_latest/subdir/member_b_package.json.out" + } + ] + }, + "update_latest_recursive": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "outdated --update --latest --recursive", + "output": "update_latest/recursive/update.out" + }, + { + "args": "-A print_file.ts ./deno.json", + "output": "update_latest/root/deno.json.out" + }, + { + "args": "-A print_file.ts ./member-a/deno.json", + "output": "update_latest/subdir/member_a_deno.json.out" + }, + { + "args": "-A print_file.ts ./member-b/package.json", + "output": "update_latest/subdir/member_b_package.json.out" + } + ] + }, + "update_filtered": { + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "outdated --update --latest --recursive @denotest/add @denotest/sub* !@denotest/breaking* aliased @denotest/with-subpath@0.5.0", + "output": "filtered/update.out" + }, + { + "args": "-A print_file.ts ./deno.json", + "output": "./update_latest/root/deno.json.out" + }, + { + "args": "-A print_file.ts ./member-a/deno.json", + "output": "./filtered/member_a_deno.json.out" + }, + { + "args": "-A print_file.ts ./member-b/package.json", + "output": "./filtered/member_b_package.json.out" + } + ] } } } diff --git a/tests/specs/update/mixed_workspace/deno.json b/tests/specs/update/mixed_workspace/deno.json index 4b3dff609b3adc..e4034d3a31f0e9 100644 --- a/tests/specs/update/mixed_workspace/deno.json +++ b/tests/specs/update/mixed_workspace/deno.json @@ -1,3 +1,6 @@ { - "workspace": ["./member-a", "./member-b"] + "workspace": ["./member-a", "./member-b"], + "imports": { + "@denotest/subtract": "jsr:@denotest/subtract@^0.2.0" + } } diff --git a/tests/specs/update/mixed_workspace/deno.lock b/tests/specs/update/mixed_workspace/deno.lock new file mode 100644 index 00000000000000..23613ce58dfb2f --- /dev/null +++ b/tests/specs/update/mixed_workspace/deno.lock @@ -0,0 +1,55 @@ +{ + "version": "4", + "specifiers": { + "jsr:@denotest/add@0.2": "0.2.1", + "jsr:@denotest/multiple-exports@0.2.0": "0.2.0", + "jsr:@denotest/subtract@0.2": "0.2.0", + "npm:@denotest/bin@0.6.0": "0.6.0", + "npm:@denotest/breaking-change-between-versions@1.0.0": "1.0.0", + "npm:@denotest/has-patch-versions@0.1.0": "0.1.0" + }, + "jsr": { + "@denotest/add@0.2.1": { + "integrity": "a9076d30ecb42b2fc6dd95e7055fbf4e6358b53f550741bd7f60089d19f68848" + }, + "@denotest/multiple-exports@0.2.0": { + "integrity": "efe9748a0c0939c7ac245fee04acc0c42bd7a61874ff71a360c4543e4f5f6b36" + }, + "@denotest/subtract@0.2.0": { + "integrity": "c9650fc559ab2430effc0c7fb1540e3aa89888fbdd926335ccfdeac57eb3a64d" + } + }, + "npm": { + "@denotest/bin@0.6.0": { + "integrity": "sha512-vCNpxFgQN4fw4ZOp63nbTX1ilcDqNpvXCvYyC8nmfxQQAezsEt095I/YXwMIoMGzWtjCvlMf9kVEYfLuT5oEGQ==" + }, + "@denotest/breaking-change-between-versions@1.0.0": { + "integrity": "sha512-bzMGYx+DxxPlI74n/VsDAN7Db1BY7Sz2XqxXruMo9dEznsBZu7Ez3i8YQ8n0leTxAiiMk1RCG4zQHPG1aj3xRw==" + }, + "@denotest/has-patch-versions@0.1.0": { + "integrity": "sha512-H/MBo0jKDdMsX4AAGEGQbZj70nfNe3oUNZXbohYHhqf9EfpLnXp/7FC29ZdfV4+p6VjEcOGdCtXc6rilE6iYpg==" + } + }, + "workspace": { + "dependencies": [ + "jsr:@denotest/subtract@0.2" + ], + "members": { + "member-a": { + "dependencies": [ + "jsr:@denotest/add@0.2", + "jsr:@denotest/multiple-exports@0.2.0", + "npm:@denotest/breaking-change-between-versions@1.0.0" + ] + }, + "member-b": { + "packageJson": { + "dependencies": [ + "npm:@denotest/bin@0.6.0", + "npm:@denotest/has-patch-versions@0.1.0" + ] + } + } + } + } +} diff --git a/tests/specs/update/mixed_workspace/deno.lock.orig.out b/tests/specs/update/mixed_workspace/deno.lock.orig.out new file mode 100644 index 00000000000000..23613ce58dfb2f --- /dev/null +++ b/tests/specs/update/mixed_workspace/deno.lock.orig.out @@ -0,0 +1,55 @@ +{ + "version": "4", + "specifiers": { + "jsr:@denotest/add@0.2": "0.2.1", + "jsr:@denotest/multiple-exports@0.2.0": "0.2.0", + "jsr:@denotest/subtract@0.2": "0.2.0", + "npm:@denotest/bin@0.6.0": "0.6.0", + "npm:@denotest/breaking-change-between-versions@1.0.0": "1.0.0", + "npm:@denotest/has-patch-versions@0.1.0": "0.1.0" + }, + "jsr": { + "@denotest/add@0.2.1": { + "integrity": "a9076d30ecb42b2fc6dd95e7055fbf4e6358b53f550741bd7f60089d19f68848" + }, + "@denotest/multiple-exports@0.2.0": { + "integrity": "efe9748a0c0939c7ac245fee04acc0c42bd7a61874ff71a360c4543e4f5f6b36" + }, + "@denotest/subtract@0.2.0": { + "integrity": "c9650fc559ab2430effc0c7fb1540e3aa89888fbdd926335ccfdeac57eb3a64d" + } + }, + "npm": { + "@denotest/bin@0.6.0": { + "integrity": "sha512-vCNpxFgQN4fw4ZOp63nbTX1ilcDqNpvXCvYyC8nmfxQQAezsEt095I/YXwMIoMGzWtjCvlMf9kVEYfLuT5oEGQ==" + }, + "@denotest/breaking-change-between-versions@1.0.0": { + "integrity": "sha512-bzMGYx+DxxPlI74n/VsDAN7Db1BY7Sz2XqxXruMo9dEznsBZu7Ez3i8YQ8n0leTxAiiMk1RCG4zQHPG1aj3xRw==" + }, + "@denotest/has-patch-versions@0.1.0": { + "integrity": "sha512-H/MBo0jKDdMsX4AAGEGQbZj70nfNe3oUNZXbohYHhqf9EfpLnXp/7FC29ZdfV4+p6VjEcOGdCtXc6rilE6iYpg==" + } + }, + "workspace": { + "dependencies": [ + "jsr:@denotest/subtract@0.2" + ], + "members": { + "member-a": { + "dependencies": [ + "jsr:@denotest/add@0.2", + "jsr:@denotest/multiple-exports@0.2.0", + "npm:@denotest/breaking-change-between-versions@1.0.0" + ] + }, + "member-b": { + "packageJson": { + "dependencies": [ + "npm:@denotest/bin@0.6.0", + "npm:@denotest/has-patch-versions@0.1.0" + ] + } + } + } + } +} diff --git a/tests/specs/update/mixed_workspace/filtered/member_a_deno.json.out b/tests/specs/update/mixed_workspace/filtered/member_a_deno.json.out new file mode 100644 index 00000000000000..2b622efee554c2 --- /dev/null +++ b/tests/specs/update/mixed_workspace/filtered/member_a_deno.json.out @@ -0,0 +1,10 @@ +{ + "name": "@denotest/member-a", + "exports": "./mod.ts", + "imports": { + "@denotest/add": "jsr:@denotest/add@^1.0.0", + "@denotest/add/": "jsr:/@denotest/add@^1.0.0/", + "@denotest/with-subpath": "jsr:@denotest/multiple-exports@0.5.0/data-json", + "@denotest/breaking-change-between-versions": "npm:@denotest/breaking-change-between-versions@1.0.0" + } +} diff --git a/tests/specs/update/mixed_workspace/filtered/member_b_package.json.out b/tests/specs/update/mixed_workspace/filtered/member_b_package.json.out new file mode 100644 index 00000000000000..7e582feeabb116 --- /dev/null +++ b/tests/specs/update/mixed_workspace/filtered/member_b_package.json.out @@ -0,0 +1,8 @@ +{ + "name": "@denotest/member-b", + "version": "0.1.0", + "dependencies": { + "@denotest/has-patch-versions": "0.1.0", + "aliased": "npm:@denotest/bin@^1.0.0" + } +} diff --git a/tests/specs/update/mixed_workspace/filtered/update.out b/tests/specs/update/mixed_workspace/filtered/update.out new file mode 100644 index 00000000000000..04d9914d852d0a --- /dev/null +++ b/tests/specs/update/mixed_workspace/filtered/update.out @@ -0,0 +1,12 @@ +[UNORDERED_START] +Download http://localhost:4260/@denotest/bin/1.0.0.tgz +Download http://127.0.0.1:4250/@denotest/multiple-exports/0.5.0_meta.json +Download http://127.0.0.1:4250/@denotest/multiple-exports/0.5.0/data.json +Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts +Download http://127.0.0.1:4250/@denotest/subtract/1.0.0/mod.ts +[UNORDERED_END] +Updated 4 dependencies: +• jsr:@denotest/add@^0.2.0 -> 1.0.0 +• jsr:@denotest/multiple-exports@0.2.0 -> 0.5.0 +• jsr:@denotest/subtract@^0.2.0 -> 1.0.0 +• npm:@denotest/bin@0.6.0 -> 1.0.0 diff --git a/tests/specs/update/mixed_workspace/member-a/deno.json b/tests/specs/update/mixed_workspace/member-a/deno.json index 036b80bddbe6a7..0340d3bb920117 100644 --- a/tests/specs/update/mixed_workspace/member-a/deno.json +++ b/tests/specs/update/mixed_workspace/member-a/deno.json @@ -2,6 +2,9 @@ "name": "@denotest/member-a", "exports": "./mod.ts", "imports": { - "@denotest/add": "jsr:@denotest/add@0.2.0" + "@denotest/add": "jsr:@denotest/add@^0.2.0", + "@denotest/add/": "jsr:/@denotest/add@^0.2.0/", + "@denotest/with-subpath": "jsr:@denotest/multiple-exports@0.2.0/data-json", + "@denotest/breaking-change-between-versions": "npm:@denotest/breaking-change-between-versions@1.0.0" } } diff --git a/tests/specs/update/mixed_workspace/member-b/package.json b/tests/specs/update/mixed_workspace/member-b/package.json index 2af986eb49b124..0f9929a478c962 100644 --- a/tests/specs/update/mixed_workspace/member-b/package.json +++ b/tests/specs/update/mixed_workspace/member-b/package.json @@ -2,6 +2,7 @@ "name": "@denotest/member-b", "version": "0.1.0", "dependencies": { - "@denotest/has-patch-versions": "0.1.0" + "@denotest/has-patch-versions": "0.1.0", + "aliased": "npm:@denotest/bin@0.6.0" } } diff --git a/tests/specs/update/mixed_workspace/print_file.ts b/tests/specs/update/mixed_workspace/print_file.ts new file mode 100644 index 00000000000000..c57b6c3a4e56f3 --- /dev/null +++ b/tests/specs/update/mixed_workspace/print_file.ts @@ -0,0 +1,2 @@ +const file = Deno.args[0]; +console.log(Deno.readTextFileSync(file).trim()); diff --git a/tests/specs/update/mixed_workspace/print_outdated/member_a.out b/tests/specs/update/mixed_workspace/print_outdated/member_a.out new file mode 100644 index 00000000000000..2574e4177fd7f5 --- /dev/null +++ b/tests/specs/update/mixed_workspace/print_outdated/member_a.out @@ -0,0 +1,9 @@ +┌────────────────────────────────────────────────┬─────────┬────────┬────────┐ +│ Package │ Current │ Update │ Latest │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ jsr:@denotest/multiple-exports │ 0.2.0 │ 0.2.0 │ 1.0.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ jsr:@denotest/add │ 0.2.1 │ 0.2.1 │ 1.0.0 │ +└────────────────────────────────────────────────┴─────────┴────────┴────────┘ diff --git a/tests/specs/update/mixed_workspace/print_outdated/member_b.out b/tests/specs/update/mixed_workspace/print_outdated/member_b.out new file mode 100644 index 00000000000000..fc8ef320a8dc14 --- /dev/null +++ b/tests/specs/update/mixed_workspace/print_outdated/member_b.out @@ -0,0 +1,7 @@ +┌──────────────────────────────────┬─────────┬────────┬────────┐ +│ Package │ Current │ Update │ Latest │ +├──────────────────────────────────┼─────────┼────────┼────────┤ +│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.0 │ 0.2.0 │ +├──────────────────────────────────┼─────────┼────────┼────────┤ +│ npm:@denotest/bin │ 0.6.0 │ 0.6.0 │ 1.0.0 │ +└──────────────────────────────────┴─────────┴────────┴────────┘ diff --git a/tests/specs/update/mixed_workspace/print_outdated/recursive.out b/tests/specs/update/mixed_workspace/print_outdated/recursive.out new file mode 100644 index 00000000000000..fedf3380e1e47b --- /dev/null +++ b/tests/specs/update/mixed_workspace/print_outdated/recursive.out @@ -0,0 +1,15 @@ +┌────────────────────────────────────────────────┬─────────┬────────┬────────┐ +│ Package │ Current │ Update │ Latest │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ jsr:@denotest/subtract │ 0.2.0 │ 0.2.0 │ 1.0.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ jsr:@denotest/multiple-exports │ 0.2.0 │ 0.2.0 │ 1.0.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ jsr:@denotest/add │ 0.2.1 │ 0.2.1 │ 1.0.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.0 │ 0.2.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ npm:@denotest/bin │ 0.6.0 │ 0.6.0 │ 1.0.0 │ +└────────────────────────────────────────────────┴─────────┴────────┴────────┘ diff --git a/tests/specs/update/mixed_workspace/print_outdated/root.out b/tests/specs/update/mixed_workspace/print_outdated/root.out new file mode 100644 index 00000000000000..c7934edc79fa66 --- /dev/null +++ b/tests/specs/update/mixed_workspace/print_outdated/root.out @@ -0,0 +1,5 @@ +┌────────────────────────┬─────────┬────────┬────────┐ +│ Package │ Current │ Update │ Latest │ +├────────────────────────┼─────────┼────────┼────────┤ +│ jsr:@denotest/subtract │ 0.2.0 │ 0.2.0 │ 1.0.0 │ +└────────────────────────┴─────────┴────────┴────────┘ diff --git a/tests/specs/update/mixed_workspace/update_latest/recursive/update.out b/tests/specs/update/mixed_workspace/update_latest/recursive/update.out new file mode 100644 index 00000000000000..fa8d97c055603c --- /dev/null +++ b/tests/specs/update/mixed_workspace/update_latest/recursive/update.out @@ -0,0 +1,16 @@ +[UNORDERED_START] +Download http://localhost:4260/@denotest/breaking-change-between-versions/2.0.0.tgz +Download http://localhost:4260/@denotest/has-patch-versions/0.2.0.tgz +Download http://localhost:4260/@denotest/bin/1.0.0.tgz +Download http://127.0.0.1:4250/@denotest/multiple-exports/1.0.0_meta.json +Download http://127.0.0.1:4250/@denotest/multiple-exports/1.0.0/data.json +Download http://127.0.0.1:4250/@denotest/subtract/1.0.0/mod.ts +Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts +[UNORDERED_END] +Updated 6 dependencies: +• jsr:@denotest/add@^0.2.0 -> 1.0.0 +• jsr:@denotest/multiple-exports@0.2.0 -> 1.0.0 +• jsr:@denotest/subtract@^0.2.0 -> 1.0.0 +• npm:@denotest/bin@0.6.0 -> 1.0.0 +• npm:@denotest/breaking-change-between-versions@1.0.0 -> 2.0.0 +• npm:@denotest/has-patch-versions@0.1.0 -> 0.2.0 diff --git a/tests/specs/update/mixed_workspace/update_latest/root/deno.json.out b/tests/specs/update/mixed_workspace/update_latest/root/deno.json.out new file mode 100644 index 00000000000000..0317e6c39f66e6 --- /dev/null +++ b/tests/specs/update/mixed_workspace/update_latest/root/deno.json.out @@ -0,0 +1,6 @@ +{ + "workspace": ["./member-a", "./member-b"], + "imports": { + "@denotest/subtract": "jsr:@denotest/subtract@^1.0.0" + } +} diff --git a/tests/specs/update/mixed_workspace/update_latest/root/update.out b/tests/specs/update/mixed_workspace/update_latest/root/update.out new file mode 100644 index 00000000000000..aba980e9e1f4ee --- /dev/null +++ b/tests/specs/update/mixed_workspace/update_latest/root/update.out @@ -0,0 +1,3 @@ +Download http://127.0.0.1:4250/@denotest/subtract/1.0.0/mod.ts +Updated 1 dependency: +• jsr:@denotest/subtract@^0.2.0 -> 1.0.0 diff --git a/tests/specs/update/mixed_workspace/update_latest/subdir/member_a.out b/tests/specs/update/mixed_workspace/update_latest/subdir/member_a.out new file mode 100644 index 00000000000000..24a6ecebfd7899 --- /dev/null +++ b/tests/specs/update/mixed_workspace/update_latest/subdir/member_a.out @@ -0,0 +1,10 @@ +[UNORDERED_START] +Download http://localhost:4260/@denotest/breaking-change-between-versions/2.0.0.tgz +Download http://127.0.0.1:4250/@denotest/multiple-exports/1.0.0_meta.json +Download http://127.0.0.1:4250/@denotest/multiple-exports/1.0.0/data.json +Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts +[UNORDERED_END] +Updated 3 dependencies: +• jsr:@denotest/add@^0.2.0 -> 1.0.0 +• jsr:@denotest/multiple-exports@0.2.0 -> 1.0.0 +• npm:@denotest/breaking-change-between-versions@1.0.0 -> 2.0.0 diff --git a/tests/specs/update/mixed_workspace/update_latest/subdir/member_a_deno.json.out b/tests/specs/update/mixed_workspace/update_latest/subdir/member_a_deno.json.out new file mode 100644 index 00000000000000..9210123b828976 --- /dev/null +++ b/tests/specs/update/mixed_workspace/update_latest/subdir/member_a_deno.json.out @@ -0,0 +1,10 @@ +{ + "name": "@denotest/member-a", + "exports": "./mod.ts", + "imports": { + "@denotest/add": "jsr:@denotest/add@^1.0.0", + "@denotest/add/": "jsr:/@denotest/add@^1.0.0/", + "@denotest/with-subpath": "jsr:@denotest/multiple-exports@^1.0.0/data-json", + "@denotest/breaking-change-between-versions": "npm:@denotest/breaking-change-between-versions@^2.0.0" + } +} diff --git a/tests/specs/update/mixed_workspace/update_latest/subdir/member_b.out b/tests/specs/update/mixed_workspace/update_latest/subdir/member_b.out new file mode 100644 index 00000000000000..a3d9b0592b32c2 --- /dev/null +++ b/tests/specs/update/mixed_workspace/update_latest/subdir/member_b.out @@ -0,0 +1,7 @@ +[UNORDERED_START] +Download http://localhost:4260/@denotest/bin/1.0.0.tgz +Download http://localhost:4260/@denotest/has-patch-versions/0.2.0.tgz +[UNORDERED_END] +Updated 2 dependencies: +• npm:@denotest/bin@0.6.0 -> 1.0.0 +• npm:@denotest/has-patch-versions@0.1.0 -> 0.2.0 diff --git a/tests/specs/update/mixed_workspace/update_latest/subdir/member_b_package.json.out b/tests/specs/update/mixed_workspace/update_latest/subdir/member_b_package.json.out new file mode 100644 index 00000000000000..1426fcd7f8114f --- /dev/null +++ b/tests/specs/update/mixed_workspace/update_latest/subdir/member_b_package.json.out @@ -0,0 +1,8 @@ +{ + "name": "@denotest/member-b", + "version": "0.1.0", + "dependencies": { + "@denotest/has-patch-versions": "^0.2.0", + "aliased": "npm:@denotest/bin@^1.0.0" + } +} From 6c54fe6450881f7a5f698a4d638b981f8b87c2e1 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 16:56:11 -0800 Subject: [PATCH 20/26] try to make more deterministic --- tests/util/server/src/npm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/util/server/src/npm.rs b/tests/util/server/src/npm.rs index 8177766fefc033..841485f0616080 100644 --- a/tests/util/server/src/npm.rs +++ b/tests/util/server/src/npm.rs @@ -217,7 +217,7 @@ fn append_dir_all( entries.push((src, dest)); } } - entries.sort(); + entries.sort_by(|(_, a), (_, b)| a.cmp(b)); for (src, dest) in entries { builder.append_path_with_name(src, dest)?; } From c71b3c2f6613ad11c47367a957052d889ef17a6b Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Tue, 19 Nov 2024 19:25:04 -0800 Subject: [PATCH 21/26] fix divergence between windows and unix tarballs --- tests/util/server/src/npm.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/tests/util/server/src/npm.rs b/tests/util/server/src/npm.rs index 841485f0616080..081989ddb5fed1 100644 --- a/tests/util/server/src/npm.rs +++ b/tests/util/server/src/npm.rs @@ -195,7 +195,7 @@ fn append_dir_all( builder: &mut tar::Builder, path: &Path, src_path: &Path, -) -> std::io::Result<()> { +) -> Result<()> { builder.follow_symlinks(true); let mode = tar::HeaderMode::Deterministic; builder.mode(mode); @@ -219,7 +219,27 @@ fn append_dir_all( } entries.sort_by(|(_, a), (_, b)| a.cmp(b)); for (src, dest) in entries { - builder.append_path_with_name(src, dest)?; + let mut header = tar::Header::new_gnu(); + let metadata = src.metadata().with_context(|| { + format!("trying to get metadata for {}", src.display()) + })?; + header.set_metadata_in_mode(&metadata, mode); + // this is what `tar` sets the mtime to on unix in deterministic mode, on windows it uses a different + // value, which causes the tarball to have a different hash on windows. force it to be the same + // to ensure the same output on all platforms + header.set_mtime(1153704088); + + let data = if src.is_file() { + Box::new( + fs::File::open(&src) + .with_context(|| format!("trying to open file {}", src.display()))?, + ) as Box + } else { + Box::new(std::io::empty()) as Box + }; + builder + .append_data(&mut header, dest, data) + .with_context(|| "appending data")?; } Ok(()) } From ffd8ac69208cc9e8a316697d4002c49e3e30294a Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Wed, 20 Nov 2024 12:01:57 -0800 Subject: [PATCH 22/26] address review comments --- cli/args/flags.rs | 69 ++++++++++++++++++- cli/tools/registry/pm/deps.rs | 13 ++-- cli/tools/registry/pm/outdated.rs | 1 + tests/specs/update/deno_json/outdated.out | 6 +- .../update/deno_json/outdated_compatible.out | 4 +- .../deno_json/update_compatible/update.out | 2 - .../update/deno_json/update_latest/update.out | 2 - .../print_outdated/member_a.out | 4 +- .../print_outdated/recursive.out | 6 +- tests/specs/update/package_json/outdated.out | 4 +- 10 files changed, 88 insertions(+), 23 deletions(-) diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 7f761c6102b015..4e28c467bd8d4a 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -2686,7 +2686,6 @@ Specific version requirements to update to can be specified: .arg( Arg::new("update") .long("update") - .aliases(["up", "upgrade"]) .short('u') .action(ArgAction::SetTrue) .conflicts_with("compatible") @@ -11347,4 +11346,72 @@ Usage: deno repl [OPTIONS] [-- [ARGS]...]\n" assert!(r.is_err()); } } + + #[test] + fn outdated_subcommand() { + let cases = [ + ( + svec![], + OutdatedFlags { + filters: vec![], + kind: OutdatedKind::PrintOutdated { compatible: false }, + recursive: false, + }, + ), + ( + svec!["--recursive"], + OutdatedFlags { + filters: vec![], + kind: OutdatedKind::PrintOutdated { compatible: false }, + recursive: true, + }, + ), + ( + svec!["--recursive", "--compatible"], + OutdatedFlags { + filters: vec![], + kind: OutdatedKind::PrintOutdated { compatible: true }, + recursive: true, + }, + ), + ( + svec!["--update"], + OutdatedFlags { + filters: vec![], + kind: OutdatedKind::Update { latest: false }, + recursive: false, + }, + ), + ( + svec!["--update", "--latest"], + OutdatedFlags { + filters: vec![], + kind: OutdatedKind::Update { latest: true }, + recursive: false, + }, + ), + ( + svec!["--update", "--recursive"], + OutdatedFlags { + filters: vec![], + kind: OutdatedKind::Update { latest: false }, + recursive: true, + }, + ), + ( + svec!["--update", "@foo/bar"], + OutdatedFlags { + filters: svec!["@foo/bar"], + kind: OutdatedKind::Update { latest: false }, + recursive: true, + }, + ), + ]; + for (input, expected) in cases { + let mut args = svec!["deno", "outdated"]; + args.extend(input); + let r = flags_from_vec(args).unwrap(); + assert_eq!(r.subcommand, DenoSubcommand::Outdated(expected)); + } + } } diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index 1fef5322d017e2..24fb29dd652f06 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -117,8 +117,8 @@ impl std::fmt::Debug for DepLocation { #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum DepKind { - Npm, Jsr, + Npm, } impl DepKind { @@ -452,7 +452,7 @@ fn deps_from_workspace( workspace: &Arc, dep_filter: impl DepFilter, ) -> Result, AnyError> { - let mut deps = Vec::with_capacity(32); + let mut deps = Vec::with_capacity(256); for deno_json in workspace.deno_jsons() { add_deps_from_deno_json(deno_json, dep_filter, &mut deps); } @@ -564,7 +564,7 @@ impl DepManager { dep_filter: impl DepFilter, args: DepManagerArgs, ) -> Result { - let mut deps = Vec::with_capacity(32); + let mut deps = Vec::with_capacity(256); if let Some(deno_json) = workspace_dir.maybe_deno_json() { if deno_json.specifier.scheme() != "file" { bail!("remote deno.json files are not supported"); @@ -732,7 +732,8 @@ impl DepManager { ); let mut latest_versions = Vec::with_capacity(self.deps.len()); - let sema = Semaphore::new(32); + let npm_sema = Semaphore::new(32); + let jsr_sema = Semaphore::new(32); let mut futs = FuturesOrdered::new(); for dep in &self.deps { @@ -744,7 +745,7 @@ impl DepManager { name: dep.req.name.clone(), version_req: latest_tag_req.clone(), }; - let _permit = sema.acquire().await; + let _permit = npm_sema.acquire().await; let semver_compatible = self.npm_fetch_resolver.req_to_nv(semver_req).await; let latest = self.npm_fetch_resolver.req_to_nv(&latest_req).await; @@ -762,7 +763,7 @@ impl DepManager { name: dep.req.name.clone(), version_req: deno_semver::WILDCARD_VERSION_REQ.clone(), }; - let _permit = sema.acquire().await; + let _permit = jsr_sema.acquire().await; let semver_compatible = self.jsr_fetch_resolver.req_to_nv(semver_req).await; let latest = self.jsr_fetch_resolver.req_to_nv(&latest_req).await; diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs index a9d77d9f7ab315..6721e774586ec3 100644 --- a/cli/tools/registry/pm/outdated.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -146,6 +146,7 @@ fn print_outdated( } if !outdated.is_empty() { + outdated.sort(); print_outdated_table(&outdated); } diff --git a/tests/specs/update/deno_json/outdated.out b/tests/specs/update/deno_json/outdated.out index f2f7c0355e2ae6..07ff9f3416a8f1 100644 --- a/tests/specs/update/deno_json/outdated.out +++ b/tests/specs/update/deno_json/outdated.out @@ -5,11 +5,11 @@ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ │ jsr:@denotest/subtract │ 0.2.0 │ 0.2.0 │ 1.0.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ +│ jsr:@denotest/add │ 0.2.0 │ 0.2.1 │ 1.0.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ +│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ │ npm:@denotest/bin │ 0.6.0 │ 0.6.0 │ 1.0.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ jsr:@denotest/add │ 0.2.0 │ 0.2.1 │ 1.0.0 │ +│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ └────────────────────────────────────────────────┴─────────┴────────┴────────┘ diff --git a/tests/specs/update/deno_json/outdated_compatible.out b/tests/specs/update/deno_json/outdated_compatible.out index d3566140772bc4..54511a537f2fdd 100644 --- a/tests/specs/update/deno_json/outdated_compatible.out +++ b/tests/specs/update/deno_json/outdated_compatible.out @@ -1,7 +1,7 @@ ┌──────────────────────────────────┬─────────┬────────┬────────┐ │ Package │ Current │ Update │ Latest │ ├──────────────────────────────────┼─────────┼────────┼────────┤ -│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ -├──────────────────────────────────┼─────────┼────────┼────────┤ │ jsr:@denotest/add │ 0.2.0 │ 0.2.1 │ 1.0.0 │ +├──────────────────────────────────┼─────────┼────────┼────────┤ +│ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ └──────────────────────────────────┴─────────┴────────┴────────┘ diff --git a/tests/specs/update/deno_json/update_compatible/update.out b/tests/specs/update/deno_json/update_compatible/update.out index ceac3077b49943..7ea00dcbe97634 100644 --- a/tests/specs/update/deno_json/update_compatible/update.out +++ b/tests/specs/update/deno_json/update_compatible/update.out @@ -3,7 +3,5 @@ Download http://127.0.0.1:4250/@denotest/add/0.2.1/mod.ts Download http://localhost:4260/@denotest/has-patch-versions/0.1.1.tgz [UNORDERED_END] Updated 2 dependencies: -[UNORDERED_START] • jsr:@denotest/add@^0.2.0 -> 0.2.1 • npm:@denotest/has-patch-versions@^0.1.0 -> 0.1.1 -[UNORDERED_END] diff --git a/tests/specs/update/deno_json/update_latest/update.out b/tests/specs/update/deno_json/update_latest/update.out index 826c2f98ca2b9c..2ef5af44171657 100644 --- a/tests/specs/update/deno_json/update_latest/update.out +++ b/tests/specs/update/deno_json/update_latest/update.out @@ -8,11 +8,9 @@ Download http://localhost:4260/@denotest/bin/1.0.0.tgz Download http://localhost:4260/@denotest/breaking-change-between-versions/2.0.0.tgz [UNORDERED_END] Updated 6 dependencies: -[UNORDERED_START] • jsr:@denotest/add@^0.2.0 -> 1.0.0 • jsr:@denotest/multiple-exports@0.2.0 -> 1.0.0 • jsr:@denotest/subtract@^0.2.0 -> 1.0.0 • npm:@denotest/bin@0.6.0 -> 1.0.0 • npm:@denotest/breaking-change-between-versions@1.0.0 -> 2.0.0 • npm:@denotest/has-patch-versions@^0.1.0 -> 0.2.0 -[UNORDERED_END] diff --git a/tests/specs/update/mixed_workspace/print_outdated/member_a.out b/tests/specs/update/mixed_workspace/print_outdated/member_a.out index 2574e4177fd7f5..8699aac2bfbfa2 100644 --- a/tests/specs/update/mixed_workspace/print_outdated/member_a.out +++ b/tests/specs/update/mixed_workspace/print_outdated/member_a.out @@ -3,7 +3,7 @@ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ │ jsr:@denotest/multiple-exports │ 0.2.0 │ 0.2.0 │ 1.0.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ -├────────────────────────────────────────────────┼─────────┼────────┼────────┤ │ jsr:@denotest/add │ 0.2.1 │ 0.2.1 │ 1.0.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ └────────────────────────────────────────────────┴─────────┴────────┴────────┘ diff --git a/tests/specs/update/mixed_workspace/print_outdated/recursive.out b/tests/specs/update/mixed_workspace/print_outdated/recursive.out index fedf3380e1e47b..ca03776a9ca3a4 100644 --- a/tests/specs/update/mixed_workspace/print_outdated/recursive.out +++ b/tests/specs/update/mixed_workspace/print_outdated/recursive.out @@ -1,15 +1,15 @@ ┌────────────────────────────────────────────────┬─────────┬────────┬────────┐ │ Package │ Current │ Update │ Latest │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ jsr:@denotest/subtract │ 0.2.0 │ 0.2.0 │ 1.0.0 │ -├────────────────────────────────────────────────┼─────────┼────────┼────────┤ │ jsr:@denotest/multiple-exports │ 0.2.0 │ 0.2.0 │ 1.0.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ +│ jsr:@denotest/subtract │ 0.2.0 │ 0.2.0 │ 1.0.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ │ jsr:@denotest/add │ 0.2.1 │ 0.2.1 │ 1.0.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ │ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.0 │ 0.2.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ │ npm:@denotest/bin │ 0.6.0 │ 0.6.0 │ 1.0.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ └────────────────────────────────────────────────┴─────────┴────────┴────────┘ diff --git a/tests/specs/update/package_json/outdated.out b/tests/specs/update/package_json/outdated.out index c2d4e9d069c239..d672aace7f08ad 100644 --- a/tests/specs/update/package_json/outdated.out +++ b/tests/specs/update/package_json/outdated.out @@ -3,7 +3,7 @@ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ │ npm:@denotest/has-patch-versions │ 0.1.0 │ 0.1.1 │ 0.2.0 │ ├────────────────────────────────────────────────┼─────────┼────────┼────────┤ -│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ -├────────────────────────────────────────────────┼─────────┼────────┼────────┤ │ npm:@denotest/bin │ 0.6.0 │ 0.6.0 │ 1.0.0 │ +├────────────────────────────────────────────────┼─────────┼────────┼────────┤ +│ npm:@denotest/breaking-change-between-versions │ 1.0.0 │ 1.0.0 │ 2.0.0 │ └────────────────────────────────────────────────┴─────────┴────────┴────────┘ From d68ebb640adc0b4a8527d93a509a6b23629323ed Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Wed, 20 Nov 2024 12:14:51 -0800 Subject: [PATCH 23/26] fix test --- cli/args/flags.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 4e28c467bd8d4a..2134231ec508b2 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -11403,15 +11403,20 @@ Usage: deno repl [OPTIONS] [-- [ARGS]...]\n" OutdatedFlags { filters: svec!["@foo/bar"], kind: OutdatedKind::Update { latest: false }, - recursive: true, + recursive: false, }, ), ]; for (input, expected) in cases { let mut args = svec!["deno", "outdated"]; args.extend(input); - let r = flags_from_vec(args).unwrap(); - assert_eq!(r.subcommand, DenoSubcommand::Outdated(expected)); + let r = flags_from_vec(args.clone()).unwrap(); + assert_eq!( + r.subcommand, + DenoSubcommand::Outdated(expected), + "incorrect result for args: {:?}", + args + ); } } } From 90075723680e94104fe8e2dc789573599ac2359d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 20 Nov 2024 22:52:41 +0100 Subject: [PATCH 24/26] improve output --- cli/tools/registry/pm/deps.rs | 6 --- cli/tools/registry/pm/outdated.rs | 67 +++++++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index 24fb29dd652f06..4778d6f3278178 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -196,12 +196,6 @@ pub struct Dep { pub alias: Option, } -impl Dep { - pub fn prefixed_req(&self) -> String { - format!("{}:{}", self.kind.scheme(), self.req) - } -} - fn import_map_entries( import_map: &ImportMap, ) -> impl Iterator)> { diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs index 6721e774586ec3..c5520f6e9439d5 100644 --- a/cli/tools/registry/pm/outdated.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -285,7 +285,12 @@ async fn update( continue; }; - updated.push((dep_id, dep.prefixed_req(), new_version_req.clone())); + updated.push(( + dep_id, + format!("{}:{}", dep.kind.scheme(), dep.req.name), + deps.resolved_version(dep.id).map(|a| a.clone()), + new_version_req.clone(), + )); deps.update_dep(dep_id, new_version_req); } @@ -311,13 +316,19 @@ async fn update( let mut deps = deps.reloaded_after_modification(args); deps.resolve_current_versions().await?; - for (dep_id, prefixed_old_req, new_version_req) in updated { + for (dep_id, package_name, maybe_current_version, new_version_req) in + updated + { if let Some(nv) = deps.resolved_version(dep_id) { - updated_to_versions.insert((prefixed_old_req, nv.version.clone())); + updated_to_versions.insert(( + package_name, + maybe_current_version, + nv.version.clone(), + )); } else { log::warn!( "Failed to resolve version for new version requirement: {} -> {}", - prefixed_old_req, + package_name, new_version_req ); } @@ -334,9 +345,51 @@ async fn update( ); let mut updated_to_versions = updated_to_versions.into_iter().collect::>(); - updated_to_versions.sort_by(|(k, _), (k2, _)| k.cmp(k2)); - for (prefixed_name, new_version) in updated_to_versions { - log::info!("• {} -> {}", prefixed_name, new_version); + updated_to_versions.sort_by(|(k, _, _), (k2, _, _)| k.cmp(k2)); + let max_name = updated_to_versions + .iter() + .map(|(name, _, _)| name.len()) + .max() + .unwrap_or(0); + let max_old = updated_to_versions + .iter() + .map(|(_, maybe_current, _)| { + maybe_current + .as_ref() + .map(|v| v.version.to_string().len()) + .unwrap_or(0) + }) + .max() + .unwrap_or(0); + let max_new = updated_to_versions + .iter() + .map(|(_, _, new_version)| new_version.to_string().len()) + .max() + .unwrap_or(0); + + for (package_name, maybe_current_version, new_version) in + updated_to_versions + { + let current_version = if let Some(current_version) = maybe_current_version + { + current_version.version.to_string() + } else { + "".to_string() + }; + + log::info!( + " - {}{} {}{} -> {}{}", + format!( + "{}{}", + colors::gray(package_name[0..4].to_string()), + package_name[4..].to_string() + ), + " ".repeat(max_name - package_name.len()), + " ".repeat(max_old - current_version.len()), + colors::gray(¤t_version), + " ".repeat(max_new - new_version.to_string().len()), + colors::green(&new_version), + ); } } else { log::info!( From 1ae1661d87dd220d15623057c9e0f78db1c7d940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 20 Nov 2024 23:03:17 +0100 Subject: [PATCH 25/26] update expectations --- tests/specs/update/deno_json/filtered/update.out | 6 +++--- .../update/deno_json/update_compatible/update.out | 4 ++-- .../specs/update/deno_json/update_latest/update.out | 12 ++++++------ .../specs/update/mixed_workspace/filtered/update.out | 8 ++++---- .../update_latest/recursive/update.out | 12 ++++++------ .../mixed_workspace/update_latest/root/update.out | 2 +- .../update_latest/subdir/member_a.out | 6 +++--- .../update_latest/subdir/member_b.out | 4 ++-- tests/specs/update/package_json/filtered/update.out | 4 ++-- .../update/package_json/update_compatible/update.out | 2 +- .../update/package_json/update_latest/update.out | 6 +++--- 11 files changed, 33 insertions(+), 33 deletions(-) diff --git a/tests/specs/update/deno_json/filtered/update.out b/tests/specs/update/deno_json/filtered/update.out index 9a6c4c9c1fbd80..ce16c6f6b710f7 100644 --- a/tests/specs/update/deno_json/filtered/update.out +++ b/tests/specs/update/deno_json/filtered/update.out @@ -5,6 +5,6 @@ Download http://127.0.0.1:4250/@denotest/multiple-exports/0.5.0/data.json Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts [UNORDERED_END] Updated 3 dependencies: -• jsr:@denotest/add@^0.2.0 -> 1.0.0 -• jsr:@denotest/multiple-exports@0.2.0 -> 0.5.0 -• npm:@denotest/bin@0.6.0 -> 1.0.0 + - jsr:@denotest/add 0.2.0 -> 1.0.0 + - jsr:@denotest/multiple-exports 0.2.0 -> 0.5.0 + - npm:@denotest/bin 0.6.0 -> 1.0.0 diff --git a/tests/specs/update/deno_json/update_compatible/update.out b/tests/specs/update/deno_json/update_compatible/update.out index 7ea00dcbe97634..17c5358fe650d5 100644 --- a/tests/specs/update/deno_json/update_compatible/update.out +++ b/tests/specs/update/deno_json/update_compatible/update.out @@ -3,5 +3,5 @@ Download http://127.0.0.1:4250/@denotest/add/0.2.1/mod.ts Download http://localhost:4260/@denotest/has-patch-versions/0.1.1.tgz [UNORDERED_END] Updated 2 dependencies: -• jsr:@denotest/add@^0.2.0 -> 0.2.1 -• npm:@denotest/has-patch-versions@^0.1.0 -> 0.1.1 + - jsr:@denotest/add 0.2.0 -> 0.2.1 + - npm:@denotest/has-patch-versions 0.1.0 -> 0.1.1 diff --git a/tests/specs/update/deno_json/update_latest/update.out b/tests/specs/update/deno_json/update_latest/update.out index 2ef5af44171657..35e1ef9fe5dacb 100644 --- a/tests/specs/update/deno_json/update_latest/update.out +++ b/tests/specs/update/deno_json/update_latest/update.out @@ -8,9 +8,9 @@ Download http://localhost:4260/@denotest/bin/1.0.0.tgz Download http://localhost:4260/@denotest/breaking-change-between-versions/2.0.0.tgz [UNORDERED_END] Updated 6 dependencies: -• jsr:@denotest/add@^0.2.0 -> 1.0.0 -• jsr:@denotest/multiple-exports@0.2.0 -> 1.0.0 -• jsr:@denotest/subtract@^0.2.0 -> 1.0.0 -• npm:@denotest/bin@0.6.0 -> 1.0.0 -• npm:@denotest/breaking-change-between-versions@1.0.0 -> 2.0.0 -• npm:@denotest/has-patch-versions@^0.1.0 -> 0.2.0 + - jsr:@denotest/add 0.2.0 -> 1.0.0 + - jsr:@denotest/multiple-exports 0.2.0 -> 1.0.0 + - jsr:@denotest/subtract 0.2.0 -> 1.0.0 + - npm:@denotest/bin 0.6.0 -> 1.0.0 + - npm:@denotest/breaking-change-between-versions 1.0.0 -> 2.0.0 + - npm:@denotest/has-patch-versions 0.1.0 -> 0.2.0 diff --git a/tests/specs/update/mixed_workspace/filtered/update.out b/tests/specs/update/mixed_workspace/filtered/update.out index 04d9914d852d0a..26543e51676186 100644 --- a/tests/specs/update/mixed_workspace/filtered/update.out +++ b/tests/specs/update/mixed_workspace/filtered/update.out @@ -6,7 +6,7 @@ Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts Download http://127.0.0.1:4250/@denotest/subtract/1.0.0/mod.ts [UNORDERED_END] Updated 4 dependencies: -• jsr:@denotest/add@^0.2.0 -> 1.0.0 -• jsr:@denotest/multiple-exports@0.2.0 -> 0.5.0 -• jsr:@denotest/subtract@^0.2.0 -> 1.0.0 -• npm:@denotest/bin@0.6.0 -> 1.0.0 + - jsr:@denotest/add 0.2.1 -> 1.0.0 + - jsr:@denotest/multiple-exports 0.2.0 -> 0.5.0 + - jsr:@denotest/subtract 0.2.0 -> 1.0.0 + - npm:@denotest/bin 0.6.0 -> 1.0.0 diff --git a/tests/specs/update/mixed_workspace/update_latest/recursive/update.out b/tests/specs/update/mixed_workspace/update_latest/recursive/update.out index fa8d97c055603c..ef6e36ded5e732 100644 --- a/tests/specs/update/mixed_workspace/update_latest/recursive/update.out +++ b/tests/specs/update/mixed_workspace/update_latest/recursive/update.out @@ -8,9 +8,9 @@ Download http://127.0.0.1:4250/@denotest/subtract/1.0.0/mod.ts Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts [UNORDERED_END] Updated 6 dependencies: -• jsr:@denotest/add@^0.2.0 -> 1.0.0 -• jsr:@denotest/multiple-exports@0.2.0 -> 1.0.0 -• jsr:@denotest/subtract@^0.2.0 -> 1.0.0 -• npm:@denotest/bin@0.6.0 -> 1.0.0 -• npm:@denotest/breaking-change-between-versions@1.0.0 -> 2.0.0 -• npm:@denotest/has-patch-versions@0.1.0 -> 0.2.0 + - jsr:@denotest/add 0.2.1 -> 1.0.0 + - jsr:@denotest/multiple-exports 0.2.0 -> 1.0.0 + - jsr:@denotest/subtract 0.2.0 -> 1.0.0 + - npm:@denotest/bin 0.6.0 -> 1.0.0 + - npm:@denotest/breaking-change-between-versions 1.0.0 -> 2.0.0 + - npm:@denotest/has-patch-versions 0.1.0 -> 0.2.0 diff --git a/tests/specs/update/mixed_workspace/update_latest/root/update.out b/tests/specs/update/mixed_workspace/update_latest/root/update.out index aba980e9e1f4ee..15d21621e8604c 100644 --- a/tests/specs/update/mixed_workspace/update_latest/root/update.out +++ b/tests/specs/update/mixed_workspace/update_latest/root/update.out @@ -1,3 +1,3 @@ Download http://127.0.0.1:4250/@denotest/subtract/1.0.0/mod.ts Updated 1 dependency: -• jsr:@denotest/subtract@^0.2.0 -> 1.0.0 + - jsr:@denotest/subtract 0.2.0 -> 1.0.0 diff --git a/tests/specs/update/mixed_workspace/update_latest/subdir/member_a.out b/tests/specs/update/mixed_workspace/update_latest/subdir/member_a.out index 24a6ecebfd7899..f16e0b7f2a940a 100644 --- a/tests/specs/update/mixed_workspace/update_latest/subdir/member_a.out +++ b/tests/specs/update/mixed_workspace/update_latest/subdir/member_a.out @@ -5,6 +5,6 @@ Download http://127.0.0.1:4250/@denotest/multiple-exports/1.0.0/data.json Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts [UNORDERED_END] Updated 3 dependencies: -• jsr:@denotest/add@^0.2.0 -> 1.0.0 -• jsr:@denotest/multiple-exports@0.2.0 -> 1.0.0 -• npm:@denotest/breaking-change-between-versions@1.0.0 -> 2.0.0 + - jsr:@denotest/add 0.2.1 -> 1.0.0 + - jsr:@denotest/multiple-exports 0.2.0 -> 1.0.0 + - npm:@denotest/breaking-change-between-versions 1.0.0 -> 2.0.0 diff --git a/tests/specs/update/mixed_workspace/update_latest/subdir/member_b.out b/tests/specs/update/mixed_workspace/update_latest/subdir/member_b.out index a3d9b0592b32c2..5ca3297e20ee84 100644 --- a/tests/specs/update/mixed_workspace/update_latest/subdir/member_b.out +++ b/tests/specs/update/mixed_workspace/update_latest/subdir/member_b.out @@ -3,5 +3,5 @@ Download http://localhost:4260/@denotest/bin/1.0.0.tgz Download http://localhost:4260/@denotest/has-patch-versions/0.2.0.tgz [UNORDERED_END] Updated 2 dependencies: -• npm:@denotest/bin@0.6.0 -> 1.0.0 -• npm:@denotest/has-patch-versions@0.1.0 -> 0.2.0 + - npm:@denotest/bin 0.6.0 -> 1.0.0 + - npm:@denotest/has-patch-versions 0.1.0 -> 0.2.0 diff --git a/tests/specs/update/package_json/filtered/update.out b/tests/specs/update/package_json/filtered/update.out index 100469bb7eb56a..3093281f10fb84 100644 --- a/tests/specs/update/package_json/filtered/update.out +++ b/tests/specs/update/package_json/filtered/update.out @@ -5,5 +5,5 @@ Initialize @denotest/has-patch-versions@0.2.0 Initialize @denotest/bin@0.7.0 [UNORDERED_END] Updated 2 dependencies: -• npm:@denotest/bin@^0.6.0 -> 0.7.0 -• npm:@denotest/has-patch-versions@^0.1.0 -> 0.2.0 + - npm:@denotest/bin 0.6.0 -> 0.7.0 + - npm:@denotest/has-patch-versions 0.1.0 -> 0.2.0 diff --git a/tests/specs/update/package_json/update_compatible/update.out b/tests/specs/update/package_json/update_compatible/update.out index c9ec4fb256c44c..4c3c740791992a 100644 --- a/tests/specs/update/package_json/update_compatible/update.out +++ b/tests/specs/update/package_json/update_compatible/update.out @@ -1,4 +1,4 @@ Download http://localhost:4260/@denotest/has-patch-versions/0.1.1.tgz Initialize @denotest/has-patch-versions@0.1.1 Updated 1 dependency: -• npm:@denotest/has-patch-versions@^0.1.0 -> 0.1.1 + - npm:@denotest/has-patch-versions 0.1.0 -> 0.1.1 diff --git a/tests/specs/update/package_json/update_latest/update.out b/tests/specs/update/package_json/update_latest/update.out index c52d1edf5f443e..a24ae1e32b36cf 100644 --- a/tests/specs/update/package_json/update_latest/update.out +++ b/tests/specs/update/package_json/update_latest/update.out @@ -8,7 +8,7 @@ Initialize @denotest/bin@1.0.0 [UNORDERED_END] Updated 3 dependencies: [UNORDERED_START] -• npm:@denotest/bin@^0.6.0 -> 1.0.0 -• npm:@denotest/breaking-change-between-versions@1.0.0 -> 2.0.0 -• npm:@denotest/has-patch-versions@^0.1.0 -> 0.2.0 + - npm:@denotest/bin 0.6.0 -> 1.0.0 + - npm:@denotest/breaking-change-between-versions 1.0.0 -> 2.0.0 + - npm:@denotest/has-patch-versions 0.1.0 -> 0.2.0 [UNORDERED_END] From 4b0c1a3458530ef5f9c7db0cb5e840d908e3e696 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker Date: Wed, 20 Nov 2024 14:15:44 -0800 Subject: [PATCH 26/26] appease clippy --- cli/tools/registry/pm/outdated.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs index c5520f6e9439d5..2a29014267d67e 100644 --- a/cli/tools/registry/pm/outdated.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -288,7 +288,7 @@ async fn update( updated.push(( dep_id, format!("{}:{}", dep.kind.scheme(), dep.req.name), - deps.resolved_version(dep.id).map(|a| a.clone()), + deps.resolved_version(dep.id).cloned(), new_version_req.clone(), ));